blob: 36cec746000a2cc783534c9731a7356516fbef3c [file] [log] [blame]
Daniel Veillard6eadf632003-01-23 18:29:16 +00001/*
2 * relaxng.c : implementation of the Relax-NG handling and validity checking
3 *
4 * See Copyright for the status of this software.
5 *
6 * Daniel Veillard <veillard@redhat.com>
7 */
8
Daniel Veillardd41f4f42003-01-29 21:07:52 +00009/**
10 * TODO:
Daniel Veillardd41f4f42003-01-29 21:07:52 +000011 * - error reporting
Daniel Veillarde2a5a082003-02-02 14:35:17 +000012 * - simplification of the resulting compiled trees:
13 * - NOT_ALLOWED
14 * - EMPTY
Daniel Veillard1ed7f362003-02-03 10:57:45 +000015 * - handle namespace declarations as attributes.
Daniel Veillardf4b4f982003-02-13 11:02:08 +000016 * - add support for DTD compatibility spec
17 * http://www.oasis-open.org/committees/relax-ng/compatibility-20011203.html
Daniel Veillardd41f4f42003-01-29 21:07:52 +000018 */
19
Daniel Veillard6eadf632003-01-23 18:29:16 +000020#define IN_LIBXML
21#include "libxml.h"
22
23#ifdef LIBXML_SCHEMAS_ENABLED
24
25#include <string.h>
26#include <stdio.h>
27#include <libxml/xmlmemory.h>
28#include <libxml/parser.h>
29#include <libxml/parserInternals.h>
30#include <libxml/hash.h>
31#include <libxml/uri.h>
32
33#include <libxml/relaxng.h>
34
35#include <libxml/xmlschemastypes.h>
36#include <libxml/xmlautomata.h>
37#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000038#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000039
40/*
41 * The Relax-NG namespace
42 */
43static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
44 "http://relaxng.org/ns/structure/1.0";
45
46#define IS_RELAXNG(node, type) \
47 ((node != NULL) && (node->ns != NULL) && \
48 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
49 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
50
51
Daniel Veillard71531f32003-02-05 13:19:53 +000052/* #define DEBUG 1 */ /* very verbose output */
53/* #define DEBUG_CONTENT 1 */
54/* #define DEBUG_TYPE 1 */
55/* #define DEBUG_VALID 1 */
Daniel Veillarde5b110b2003-02-04 14:43:39 +000056/* #define DEBUG_INTERLEAVE 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000057
58#define UNBOUNDED (1 << 30)
59#define TODO \
60 xmlGenericError(xmlGenericErrorContext, \
61 "Unimplemented block at %s:%d\n", \
62 __FILE__, __LINE__);
63
64typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
65typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
66
67typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
68typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
69
Daniel Veillardd41f4f42003-01-29 21:07:52 +000070typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
71typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
72
Daniel Veillarda9d912d2003-02-01 17:43:10 +000073typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
74typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
75
Daniel Veillard6eadf632003-01-23 18:29:16 +000076typedef enum {
77 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
78 XML_RELAXNG_COMBINE_CHOICE, /* choice */
79 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
80} xmlRelaxNGCombine;
81
82typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
83typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
84
85struct _xmlRelaxNGGrammar {
86 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
87 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
88 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
89 xmlRelaxNGDefinePtr start; /* <start> content */
90 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000091 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000092 xmlHashTablePtr defs; /* define* */
93 xmlHashTablePtr refs; /* references */
94};
95
96
Daniel Veillard6eadf632003-01-23 18:29:16 +000097typedef enum {
98 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
99 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +0000100 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000101 XML_RELAXNG_TEXT, /* textual content */
102 XML_RELAXNG_ELEMENT, /* an element */
103 XML_RELAXNG_DATATYPE, /* extenal data type definition */
104 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
105 XML_RELAXNG_LIST, /* a list of patterns */
106 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
107 XML_RELAXNG_DEF, /* a definition */
108 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000109 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000110 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000111 XML_RELAXNG_OPTIONAL, /* optional patterns */
112 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
113 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
114 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
115 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000116 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
117 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000118} xmlRelaxNGType;
119
120struct _xmlRelaxNGDefine {
121 xmlRelaxNGType type; /* the type of definition */
122 xmlNodePtr node; /* the node in the source */
123 xmlChar *name; /* the element local name if present */
124 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000125 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000126 void *data; /* data lib or specific pointer */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000127 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000128 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000129 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
130 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000131 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000132 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
133};
134
135/**
136 * _xmlRelaxNG:
137 *
138 * A RelaxNGs definition
139 */
140struct _xmlRelaxNG {
141 xmlRelaxNGGrammarPtr topgrammar;
142 xmlDocPtr doc;
143
144 xmlHashTablePtr defs; /* define */
145 xmlHashTablePtr refs; /* references */
Daniel Veillard419a7682003-02-03 23:22:49 +0000146 xmlHashTablePtr documents; /* all the documents loaded */
147 xmlHashTablePtr includes; /* all the includes loaded */
148 int defNr; /* number of defines used */
149 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000150 void *_private; /* unused by the library for users or bindings */
151};
152
153typedef enum {
154 XML_RELAXNG_ERR_OK = 0,
155 XML_RELAXNG_ERR_NOROOT = 1,
156 XML_RELAXNG_ERR_
157} xmlRelaxNGValidError;
158
159#define XML_RELAXNG_IN_ATTRIBUTE 1
160
161struct _xmlRelaxNGParserCtxt {
162 void *userData; /* user specific data block */
163 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
164 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
165 xmlRelaxNGValidError err;
166
167 xmlRelaxNGPtr schema; /* The schema in use */
168 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000169 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000170 int flags; /* parser flags */
171 int nbErrors; /* number of errors at parse time */
172 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000173 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000174 xmlRelaxNGDefinePtr def; /* the current define */
175
176 int nbInterleaves;
177 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000178
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000179 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000180 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000181 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000182 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000183
Daniel Veillard419a7682003-02-03 23:22:49 +0000184 int defNr; /* number of defines used */
185 int defMax; /* number of defines aloocated */
186 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
187
Daniel Veillard6eadf632003-01-23 18:29:16 +0000188 const char *buffer;
189 int size;
190
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000191 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000192 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000193 int docNr; /* Depth of the parsing stack */
194 int docMax; /* Max depth of the parsing stack */
195 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000196
197 /* the include stack */
198 xmlRelaxNGIncludePtr inc; /* Current parsed include */
199 int incNr; /* Depth of the include parsing stack */
200 int incMax; /* Max depth of the parsing stack */
201 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000202};
203
204#define FLAGS_IGNORABLE 1
205#define FLAGS_NEGATIVE 2
206
207/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000208 * xmlRelaxNGInterleaveGroup:
209 *
210 * A RelaxNGs partition set associated to lists of definitions
211 */
212typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
213typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
214struct _xmlRelaxNGInterleaveGroup {
215 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
216 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
217};
218
219/**
220 * xmlRelaxNGPartitions:
221 *
222 * A RelaxNGs partition associated to an interleave group
223 */
224typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
225typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
226struct _xmlRelaxNGPartition {
227 int nbgroups; /* number of groups in the partitions */
228 xmlRelaxNGInterleaveGroupPtr *groups;
229};
230
231/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000232 * xmlRelaxNGValidState:
233 *
234 * A RelaxNGs validation state
235 */
236#define MAX_ATTR 20
237typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
238typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
239struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000240 xmlNodePtr node; /* the current node */
241 xmlNodePtr seq; /* the sequence of children left to validate */
242 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000243 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000244 xmlChar *value; /* the value when operating on string */
245 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000246 xmlAttrPtr attrs[1]; /* the array of attributes */
247};
248
249/**
250 * xmlRelaxNGValidCtxt:
251 *
252 * A RelaxNGs validation context
253 */
254
255struct _xmlRelaxNGValidCtxt {
256 void *userData; /* user specific data block */
257 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
258 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
259
260 xmlRelaxNGPtr schema; /* The schema in use */
261 xmlDocPtr doc; /* the document being validated */
262 xmlRelaxNGValidStatePtr state; /* the current validation state */
263 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000264 int depth; /* validation depth */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000265};
266
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000267/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000268 * xmlRelaxNGInclude:
269 *
270 * Structure associated to a RelaxNGs document element
271 */
272struct _xmlRelaxNGInclude {
273 xmlChar *href; /* the normalized href value */
274 xmlDocPtr doc; /* the associated XML document */
275 xmlRelaxNGDefinePtr content;/* the definitions */
276 xmlRelaxNGPtr schema; /* the schema */
277};
278
279/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000280 * xmlRelaxNGDocument:
281 *
282 * Structure associated to a RelaxNGs document element
283 */
284struct _xmlRelaxNGDocument {
285 xmlChar *href; /* the normalized href value */
286 xmlDocPtr doc; /* the associated XML document */
287 xmlRelaxNGDefinePtr content;/* the definitions */
288 xmlRelaxNGPtr schema; /* the schema */
289};
290
Daniel Veillard6eadf632003-01-23 18:29:16 +0000291/************************************************************************
292 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000293 * Preliminary type checking interfaces *
294 * *
295 ************************************************************************/
296/**
297 * xmlRelaxNGTypeHave:
298 * @data: data needed for the library
299 * @type: the type name
300 * @value: the value to check
301 *
302 * Function provided by a type library to check if a type is exported
303 *
304 * Returns 1 if yes, 0 if no and -1 in case of error.
305 */
306typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
307
308/**
309 * xmlRelaxNGTypeCheck:
310 * @data: data needed for the library
311 * @type: the type name
312 * @value: the value to check
313 *
314 * Function provided by a type library to check if a value match a type
315 *
316 * Returns 1 if yes, 0 if no and -1 in case of error.
317 */
318typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
319 const xmlChar *value);
320
321/**
322 * xmlRelaxNGTypeCompare:
323 * @data: data needed for the library
324 * @type: the type name
325 * @value1: the first value
326 * @value2: the second value
327 *
328 * Function provided by a type library to compare two values accordingly
329 * to a type.
330 *
331 * Returns 1 if yes, 0 if no and -1 in case of error.
332 */
333typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
334 const xmlChar *value1,
335 const xmlChar *value2);
336typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
337typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
338struct _xmlRelaxNGTypeLibrary {
339 const xmlChar *namespace; /* the datatypeLibrary value */
340 void *data; /* data needed for the library */
341 xmlRelaxNGTypeHave have; /* the export function */
342 xmlRelaxNGTypeCheck check; /* the checking function */
343 xmlRelaxNGTypeCompare comp; /* the compare function */
344};
345
346/************************************************************************
347 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000348 * Allocation functions *
349 * *
350 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000351static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
352static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000353static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000354
355/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000356 * xmlRelaxNGFreeDocument:
357 * @docu: a document structure
358 *
359 * Deallocate a RelaxNG document structure.
360 */
361static void
362xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
363{
364 if (docu == NULL)
365 return;
366
367 if (docu->href != NULL)
368 xmlFree(docu->href);
369 if (docu->doc != NULL)
370 xmlFreeDoc(docu->doc);
371 if (docu->schema != NULL)
372 xmlRelaxNGFree(docu->schema);
373 xmlFree(docu);
374}
375
376/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000377 * xmlRelaxNGFreeInclude:
378 * @incl: a include structure
379 *
380 * Deallocate a RelaxNG include structure.
381 */
382static void
383xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
384{
385 if (incl == NULL)
386 return;
387
388 if (incl->href != NULL)
389 xmlFree(incl->href);
390 if (incl->doc != NULL)
391 xmlFreeDoc(incl->doc);
392 if (incl->schema != NULL)
393 xmlRelaxNGFree(incl->schema);
394 xmlFree(incl);
395}
396
397/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000398 * xmlRelaxNGNewRelaxNG:
399 * @ctxt: a Relax-NG validation context (optional)
400 *
401 * Allocate a new RelaxNG structure.
402 *
403 * Returns the newly allocated structure or NULL in case or error
404 */
405static xmlRelaxNGPtr
406xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
407{
408 xmlRelaxNGPtr ret;
409
410 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
411 if (ret == NULL) {
412 if ((ctxt != NULL) && (ctxt->error != NULL))
413 ctxt->error(ctxt->userData, "Out of memory\n");
414 ctxt->nbErrors++;
415 return (NULL);
416 }
417 memset(ret, 0, sizeof(xmlRelaxNG));
418
419 return (ret);
420}
421
422/**
423 * xmlRelaxNGFree:
424 * @schema: a schema structure
425 *
426 * Deallocate a RelaxNG structure.
427 */
428void
429xmlRelaxNGFree(xmlRelaxNGPtr schema)
430{
431 if (schema == NULL)
432 return;
433
Daniel Veillard6eadf632003-01-23 18:29:16 +0000434 if (schema->topgrammar != NULL)
435 xmlRelaxNGFreeGrammar(schema->topgrammar);
436 if (schema->doc != NULL)
437 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000438 if (schema->documents != NULL)
439 xmlHashFree(schema->documents, (xmlHashDeallocator)
440 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000441 if (schema->includes != NULL)
442 xmlHashFree(schema->includes, (xmlHashDeallocator)
443 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000444 if (schema->defTab != NULL) {
445 int i;
446
447 for (i = 0;i < schema->defNr;i++)
448 xmlRelaxNGFreeDefine(schema->defTab[i]);
449 xmlFree(schema->defTab);
450 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000451
452 xmlFree(schema);
453}
454
455/**
456 * xmlRelaxNGNewGrammar:
457 * @ctxt: a Relax-NG validation context (optional)
458 *
459 * Allocate a new RelaxNG grammar.
460 *
461 * Returns the newly allocated structure or NULL in case or error
462 */
463static xmlRelaxNGGrammarPtr
464xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
465{
466 xmlRelaxNGGrammarPtr ret;
467
468 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
469 if (ret == NULL) {
470 if ((ctxt != NULL) && (ctxt->error != NULL))
471 ctxt->error(ctxt->userData, "Out of memory\n");
472 ctxt->nbErrors++;
473 return (NULL);
474 }
475 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
476
477 return (ret);
478}
479
480/**
481 * xmlRelaxNGFreeGrammar:
482 * @grammar: a grammar structure
483 *
484 * Deallocate a RelaxNG grammar structure.
485 */
486static void
487xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
488{
489 if (grammar == NULL)
490 return;
491
Daniel Veillard419a7682003-02-03 23:22:49 +0000492 if (grammar->next != NULL) {
493 xmlRelaxNGFreeGrammar(grammar->next);
494 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000495 if (grammar->refs != NULL) {
496 xmlHashFree(grammar->refs, NULL);
497 }
498 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000499 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000500 }
501
502 xmlFree(grammar);
503}
504
505/**
506 * xmlRelaxNGNewDefine:
507 * @ctxt: a Relax-NG validation context
508 * @node: the node in the input document.
509 *
510 * Allocate a new RelaxNG define.
511 *
512 * Returns the newly allocated structure or NULL in case or error
513 */
514static xmlRelaxNGDefinePtr
515xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
516{
517 xmlRelaxNGDefinePtr ret;
518
Daniel Veillard419a7682003-02-03 23:22:49 +0000519 if (ctxt->defMax == 0) {
520 ctxt->defMax = 16;
521 ctxt->defNr = 0;
522 ctxt->defTab = (xmlRelaxNGDefinePtr *)
523 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
524 if (ctxt->defTab == NULL) {
525 if ((ctxt != NULL) && (ctxt->error != NULL))
526 ctxt->error(ctxt->userData, "Out of memory\n");
527 ctxt->nbErrors++;
528 return (NULL);
529 }
530 } else if (ctxt->defMax <= ctxt->defNr) {
531 xmlRelaxNGDefinePtr *tmp;
532 ctxt->defMax *= 2;
533 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
534 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
535 if (tmp == NULL) {
536 if ((ctxt != NULL) && (ctxt->error != NULL))
537 ctxt->error(ctxt->userData, "Out of memory\n");
538 ctxt->nbErrors++;
539 return (NULL);
540 }
541 ctxt->defTab = tmp;
542 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000543 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
544 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000545 if ((ctxt != NULL) && (ctxt->error != NULL))
546 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000547 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000548 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000549 }
550 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000551 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000552 ret->node = node;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000553 return (ret);
554}
555
556/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000557 * xmlRelaxNGFreePartition:
558 * @partitions: a partition set structure
559 *
560 * Deallocate RelaxNG partition set structures.
561 */
562static void
563xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
564 xmlRelaxNGInterleaveGroupPtr group;
565 int j;
566
567 if (partitions != NULL) {
568 if (partitions->groups != NULL) {
569 for (j = 0;j < partitions->nbgroups;j++) {
570 group = partitions->groups[j];
571 if (group != NULL) {
572 if (group->defs != NULL)
573 xmlFree(group->defs);
574 xmlFree(group);
575 }
576 }
577 xmlFree(partitions->groups);
578 }
579 xmlFree(partitions);
580 }
581}
582/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000583 * xmlRelaxNGFreeDefine:
584 * @define: a define structure
585 *
586 * Deallocate a RelaxNG define structure.
587 */
588static void
589xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
590{
591 if (define == NULL)
592 return;
593
Daniel Veillard419a7682003-02-03 23:22:49 +0000594 if ((define->data != NULL) &&
595 (define->type == XML_RELAXNG_INTERLEAVE))
596 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000597 if (define->name != NULL)
598 xmlFree(define->name);
599 if (define->ns != NULL)
600 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000601 if (define->value != NULL)
602 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000603 xmlFree(define);
604}
605
606/**
607 * xmlRelaxNGNewValidState:
608 * @ctxt: a Relax-NG validation context
609 * @node: the current node or NULL for the document
610 *
611 * Allocate a new RelaxNG validation state
612 *
613 * Returns the newly allocated structure or NULL in case or error
614 */
615static xmlRelaxNGValidStatePtr
616xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
617{
618 xmlRelaxNGValidStatePtr ret;
619 xmlAttrPtr attr;
620 xmlAttrPtr attrs[MAX_ATTR];
621 int nbAttrs = 0;
622 xmlNodePtr root = NULL;
623
624 if (node == NULL) {
625 root = xmlDocGetRootElement(ctxt->doc);
626 if (root == NULL)
627 return(NULL);
628 } else {
629 attr = node->properties;
630 while (attr != NULL) {
631 if (nbAttrs < MAX_ATTR)
632 attrs[nbAttrs++] = attr;
633 else
634 nbAttrs++;
635 attr = attr->next;
636 }
637 }
638
639 if (nbAttrs < MAX_ATTR)
640 attrs[nbAttrs] = NULL;
641 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
642 nbAttrs * sizeof(xmlAttrPtr));
643 if (ret == NULL) {
644 if ((ctxt != NULL) && (ctxt->error != NULL))
645 ctxt->error(ctxt->userData, "Out of memory\n");
646 return (NULL);
647 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000648 ret->value = NULL;
649 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000650 if (node == NULL) {
651 ret->node = (xmlNodePtr) ctxt->doc;
652 ret->seq = root;
653 ret->nbAttrs = 0;
654 } else {
655 ret->node = node;
656 ret->seq = node->children;
657 ret->nbAttrs = nbAttrs;
658 if (nbAttrs > 0) {
659 if (nbAttrs < MAX_ATTR) {
660 memcpy(&(ret->attrs[0]), attrs,
661 sizeof(xmlAttrPtr) * (nbAttrs + 1));
662 } else {
663 attr = node->properties;
664 nbAttrs = 0;
665 while (attr != NULL) {
666 ret->attrs[nbAttrs++] = attr;
667 attr = attr->next;
668 }
669 ret->attrs[nbAttrs] = NULL;
670 }
671 }
672 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000673 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000674 return (ret);
675}
676
677/**
678 * xmlRelaxNGCopyValidState:
679 * @ctxt: a Relax-NG validation context
680 * @state: a validation state
681 *
682 * Copy the validation state
683 *
684 * Returns the newly allocated structure or NULL in case or error
685 */
686static xmlRelaxNGValidStatePtr
687xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
688 xmlRelaxNGValidStatePtr state)
689{
690 xmlRelaxNGValidStatePtr ret;
691 unsigned int size;
692
693 if (state == NULL)
694 return(NULL);
695
696 size = sizeof(xmlRelaxNGValidState) +
697 state->nbAttrs * sizeof(xmlAttrPtr);
698 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
699 if (ret == NULL) {
700 if ((ctxt != NULL) && (ctxt->error != NULL))
701 ctxt->error(ctxt->userData, "Out of memory\n");
702 return (NULL);
703 }
704 memcpy(ret, state, size);
705 return(ret);
706}
707
708/**
709 * xmlRelaxNGFreeValidState:
710 * @state: a validation state structure
711 *
712 * Deallocate a RelaxNG validation state structure.
713 */
714static void
715xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
716{
717 if (state == NULL)
718 return;
719
720 xmlFree(state);
721}
722
723/************************************************************************
724 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000725 * Document functions *
726 * *
727 ************************************************************************/
728static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
729 xmlDocPtr doc);
730
731/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000732 * xmlRelaxNGIncludePush:
733 * @ctxt: the parser context
734 * @value: the element doc
735 *
736 * Pushes a new include on top of the include stack
737 *
738 * Returns 0 in case of error, the index in the stack otherwise
739 */
740static int
741xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
742 xmlRelaxNGIncludePtr value)
743{
744 if (ctxt->incTab == NULL) {
745 ctxt->incMax = 4;
746 ctxt->incNr = 0;
747 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
748 ctxt->incMax * sizeof(ctxt->incTab[0]));
749 if (ctxt->incTab == NULL) {
750 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
751 return (0);
752 }
753 }
754 if (ctxt->incNr >= ctxt->incMax) {
755 ctxt->incMax *= 2;
756 ctxt->incTab =
757 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
758 ctxt->incMax *
759 sizeof(ctxt->incTab[0]));
760 if (ctxt->incTab == NULL) {
761 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
762 return (0);
763 }
764 }
765 ctxt->incTab[ctxt->incNr] = value;
766 ctxt->inc = value;
767 return (ctxt->incNr++);
768}
769
770/**
771 * xmlRelaxNGIncludePop:
772 * @ctxt: the parser context
773 *
774 * Pops the top include from the include stack
775 *
776 * Returns the include just removed
777 */
778static xmlRelaxNGIncludePtr
779xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
780{
781 xmlRelaxNGIncludePtr ret;
782
783 if (ctxt->incNr <= 0)
784 return (0);
785 ctxt->incNr--;
786 if (ctxt->incNr > 0)
787 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
788 else
789 ctxt->inc = NULL;
790 ret = ctxt->incTab[ctxt->incNr];
791 ctxt->incTab[ctxt->incNr] = 0;
792 return (ret);
793}
794
795/**
796 * xmlRelaxNGLoadInclude:
797 * @ctxt: the parser context
798 * @URL: the normalized URL
799 * @node: the include node.
800 *
801 * First lookup if the document is already loaded into the parser context,
802 * check against recursion. If not found the resource is loaded and
803 * the content is preprocessed before being returned back to the caller.
804 *
805 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
806 */
807static xmlRelaxNGIncludePtr
808xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
809 xmlNodePtr node) {
810 xmlRelaxNGIncludePtr ret = NULL;
811 xmlDocPtr doc;
812 int i;
813 xmlNodePtr root, tmp, tmp2, cur;
814
815 /*
816 * check against recursion in the stack
817 */
818 for (i = 0;i < ctxt->incNr;i++) {
819 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
820 if (ctxt->error != NULL)
821 ctxt->error(ctxt->userData,
822 "Detected an externalRef recursion for %s\n",
823 URL);
824 ctxt->nbErrors++;
825 return(NULL);
826 }
827 }
828
829 /*
830 * Lookup in the hash table
831 */
832 if (ctxt->includes == NULL) {
833 ctxt->includes = xmlHashCreate(10);
834 if (ctxt->includes == NULL) {
835 if (ctxt->error != NULL)
836 ctxt->error(ctxt->userData,
837 "Failed to allocate hash table for document\n");
838 ctxt->nbErrors++;
839 return(NULL);
840 }
841 } else {
842 ret = xmlHashLookup(ctxt->includes, URL);
843 if (ret != NULL)
844 return(ret);
845 }
846
847
848 /*
849 * load the document
850 */
851 doc = xmlParseFile((const char *) URL);
852 if (doc == NULL) {
853 if (ctxt->error != NULL)
854 ctxt->error(ctxt->userData,
855 "xmlRelaxNG: could not load %s\n", URL);
856 ctxt->nbErrors++;
857 return (NULL);
858 }
859
860 /*
861 * Allocate the document structures and register it first.
862 */
863 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
864 if (ret == NULL) {
865 if (ctxt->error != NULL)
866 ctxt->error(ctxt->userData,
867 "xmlRelaxNG: allocate memory for doc %s\n", URL);
868 ctxt->nbErrors++;
869 xmlFreeDoc(doc);
870 return (NULL);
871 }
872 memset(ret, 0, sizeof(xmlRelaxNGInclude));
873 ret->doc = doc;
874 ret->href = xmlStrdup(URL);
875
876 /*
877 * push it on the stack and register it in the hash table
878 */
879 xmlHashAddEntry(ctxt->includes, URL, ret);
880 xmlRelaxNGIncludePush(ctxt, ret);
881
882 /*
883 * Some preprocessing of the document content, this include recursing
884 * in the include stack.
885 */
886 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
887 if (doc == NULL) {
888 /* xmlFreeDoc(ctxt->include); */
889 ctxt->inc = NULL;
890 return(NULL);
891 }
892
893 /*
894 * Pop up the include from the stack
895 */
896 xmlRelaxNGIncludePop(ctxt);
897
898 /*
899 * Check that the top element is a grammar
900 */
901 root = xmlDocGetRootElement(doc);
902 if (root == NULL) {
903 if (ctxt->error != NULL)
904 ctxt->error(ctxt->userData,
905 "xmlRelaxNG: included document is empty %s\n", URL);
906 ctxt->nbErrors++;
907 xmlFreeDoc(doc);
908 return (NULL);
909 }
910 if (!IS_RELAXNG(root, "grammar")) {
911 if (ctxt->error != NULL)
912 ctxt->error(ctxt->userData,
913 "xmlRelaxNG: included document %s root is not a grammar\n",
914 URL);
915 ctxt->nbErrors++;
916 xmlFreeDoc(doc);
917 return (NULL);
918 }
919
920 /*
921 * Elimination of redefined rules in the include.
922 */
923 cur = node->children;
924 while (cur != NULL) {
925 if (IS_RELAXNG(cur, "start")) {
926 int found = 0;
927
928 tmp = root->children;
929 while (tmp != NULL) {
930 tmp2 = tmp->next;
931 if (IS_RELAXNG(tmp, "start")) {
932 found = 1;
933 xmlUnlinkNode(tmp);
934 xmlFreeNode(tmp);
935 }
936 tmp = tmp2;
937 }
938 if (!found) {
939 if (ctxt->error != NULL)
940 ctxt->error(ctxt->userData,
941 "xmlRelaxNG: include %s has a start but not the included grammar\n",
942 URL);
943 ctxt->nbErrors++;
944 }
945 } else if (IS_RELAXNG(cur, "define")) {
946 xmlChar *name, *name2;
947
948 name = xmlGetProp(cur, BAD_CAST "name");
949 if (name == NULL) {
950 if (ctxt->error != NULL)
951 ctxt->error(ctxt->userData,
952 "xmlRelaxNG: include %s has define without name\n",
953 URL);
954 ctxt->nbErrors++;
955 } else {
956 int found = 0;
957
Daniel Veillardd2298792003-02-14 16:54:11 +0000958 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000959 tmp = root->children;
960 while (tmp != NULL) {
961 tmp2 = tmp->next;
962 if (IS_RELAXNG(tmp, "define")) {
963 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +0000964 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000965 if (name2 != NULL) {
966 if (xmlStrEqual(name, name2)) {
967 found = 1;
968 xmlUnlinkNode(tmp);
969 xmlFreeNode(tmp);
970 }
971 xmlFree(name2);
972 }
973 }
974 tmp = tmp2;
975 }
976 if (!found) {
977 if (ctxt->error != NULL)
978 ctxt->error(ctxt->userData,
979 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
980 URL, name);
981 ctxt->nbErrors++;
982 }
983 xmlFree(name);
984 }
985 }
986 cur = cur->next;
987 }
988
989
990 return(ret);
991}
992
993/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000994 * xmlRelaxNGDocumentPush:
995 * @ctxt: the parser context
996 * @value: the element doc
997 *
998 * Pushes a new doc on top of the doc stack
999 *
1000 * Returns 0 in case of error, the index in the stack otherwise
1001 */
1002static int
1003xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1004 xmlRelaxNGDocumentPtr value)
1005{
1006 if (ctxt->docTab == NULL) {
1007 ctxt->docMax = 4;
1008 ctxt->docNr = 0;
1009 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1010 ctxt->docMax * sizeof(ctxt->docTab[0]));
1011 if (ctxt->docTab == NULL) {
1012 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1013 return (0);
1014 }
1015 }
1016 if (ctxt->docNr >= ctxt->docMax) {
1017 ctxt->docMax *= 2;
1018 ctxt->docTab =
1019 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1020 ctxt->docMax *
1021 sizeof(ctxt->docTab[0]));
1022 if (ctxt->docTab == NULL) {
1023 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1024 return (0);
1025 }
1026 }
1027 ctxt->docTab[ctxt->docNr] = value;
1028 ctxt->doc = value;
1029 return (ctxt->docNr++);
1030}
1031
1032/**
1033 * xmlRelaxNGDocumentPop:
1034 * @ctxt: the parser context
1035 *
1036 * Pops the top doc from the doc stack
1037 *
1038 * Returns the doc just removed
1039 */
1040static xmlRelaxNGDocumentPtr
1041xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1042{
1043 xmlRelaxNGDocumentPtr ret;
1044
1045 if (ctxt->docNr <= 0)
1046 return (0);
1047 ctxt->docNr--;
1048 if (ctxt->docNr > 0)
1049 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1050 else
1051 ctxt->doc = NULL;
1052 ret = ctxt->docTab[ctxt->docNr];
1053 ctxt->docTab[ctxt->docNr] = 0;
1054 return (ret);
1055}
1056
1057/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001058 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001059 * @ctxt: the parser context
1060 * @URL: the normalized URL
1061 * @ns: the inherited ns if any
1062 *
1063 * First lookup if the document is already loaded into the parser context,
1064 * check against recursion. If not found the resource is loaded and
1065 * the content is preprocessed before being returned back to the caller.
1066 *
1067 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1068 */
1069static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001070xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001071 const xmlChar *ns) {
1072 xmlRelaxNGDocumentPtr ret = NULL;
1073 xmlDocPtr doc;
1074 xmlNodePtr root;
1075 int i;
1076
1077 /*
1078 * check against recursion in the stack
1079 */
1080 for (i = 0;i < ctxt->docNr;i++) {
1081 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1082 if (ctxt->error != NULL)
1083 ctxt->error(ctxt->userData,
1084 "Detected an externalRef recursion for %s\n",
1085 URL);
1086 ctxt->nbErrors++;
1087 return(NULL);
1088 }
1089 }
1090
1091 /*
1092 * Lookup in the hash table
1093 */
1094 if (ctxt->documents == NULL) {
1095 ctxt->documents = xmlHashCreate(10);
1096 if (ctxt->documents == NULL) {
1097 if (ctxt->error != NULL)
1098 ctxt->error(ctxt->userData,
1099 "Failed to allocate hash table for document\n");
1100 ctxt->nbErrors++;
1101 return(NULL);
1102 }
1103 } else {
1104 if (ns == NULL)
1105 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1106 else
1107 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1108 if (ret != NULL)
1109 return(ret);
1110 }
1111
1112
1113 /*
1114 * load the document
1115 */
1116 doc = xmlParseFile((const char *) URL);
1117 if (doc == NULL) {
1118 if (ctxt->error != NULL)
1119 ctxt->error(ctxt->userData,
1120 "xmlRelaxNG: could not load %s\n", URL);
1121 ctxt->nbErrors++;
1122 return (NULL);
1123 }
1124
1125 /*
1126 * Allocate the document structures and register it first.
1127 */
1128 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1129 if (ret == NULL) {
1130 if (ctxt->error != NULL)
1131 ctxt->error(ctxt->userData,
1132 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1133 ctxt->nbErrors++;
1134 xmlFreeDoc(doc);
1135 return (NULL);
1136 }
1137 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1138 ret->doc = doc;
1139 ret->href = xmlStrdup(URL);
1140
1141 /*
1142 * transmit the ns if needed
1143 */
1144 if (ns != NULL) {
1145 root = xmlDocGetRootElement(doc);
1146 if (root != NULL) {
1147 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1148 xmlSetProp(root, BAD_CAST"ns", ns);
1149 }
1150 }
1151 }
1152
1153 /*
1154 * push it on the stack and register it in the hash table
1155 */
1156 if (ns == NULL)
1157 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1158 else
1159 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1160 xmlRelaxNGDocumentPush(ctxt, ret);
1161
1162 /*
1163 * Some preprocessing of the document content
1164 */
1165 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1166 if (doc == NULL) {
1167 xmlFreeDoc(ctxt->document);
1168 ctxt->doc = NULL;
1169 return(NULL);
1170 }
1171
1172 xmlRelaxNGDocumentPop(ctxt);
1173
1174 return(ret);
1175}
1176
1177/************************************************************************
1178 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001179 * Error functions *
1180 * *
1181 ************************************************************************/
1182
1183#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001184 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1185 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001186 "error detected at %s:%d\n", \
1187 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001188
1189#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001190 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001191 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1192#define VALID_ERROR2(a, b) \
1193 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1194 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1195#define VALID_ERROR3(a, b, c) \
1196 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1197 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001198
Daniel Veillardd2298792003-02-14 16:54:11 +00001199#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001200static const char *
1201xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1202 if (def == NULL)
1203 return("none");
1204 switch(def->type) {
1205 case XML_RELAXNG_EMPTY: return("empty");
1206 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1207 case XML_RELAXNG_EXCEPT: return("except");
1208 case XML_RELAXNG_TEXT: return("text");
1209 case XML_RELAXNG_ELEMENT: return("element");
1210 case XML_RELAXNG_DATATYPE: return("datatype");
1211 case XML_RELAXNG_VALUE: return("value");
1212 case XML_RELAXNG_LIST: return("list");
1213 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1214 case XML_RELAXNG_DEF: return("def");
1215 case XML_RELAXNG_REF: return("ref");
1216 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1217 case XML_RELAXNG_PARENTREF: return("parentRef");
1218 case XML_RELAXNG_OPTIONAL: return("optional");
1219 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1220 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1221 case XML_RELAXNG_CHOICE: return("choice");
1222 case XML_RELAXNG_GROUP: return("group");
1223 case XML_RELAXNG_INTERLEAVE: return("interleave");
1224 case XML_RELAXNG_START: return("start");
1225 }
1226 return("unknown");
1227}
Daniel Veillardd2298792003-02-14 16:54:11 +00001228#endif
1229
Daniel Veillard6eadf632003-01-23 18:29:16 +00001230#if 0
1231/**
1232 * xmlRelaxNGErrorContext:
1233 * @ctxt: the parsing context
1234 * @schema: the schema being built
1235 * @node: the node being processed
1236 * @child: the child being processed
1237 *
1238 * Dump a RelaxNGType structure
1239 */
1240static void
1241xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1242 xmlNodePtr node, xmlNodePtr child)
1243{
1244 int line = 0;
1245 const xmlChar *file = NULL;
1246 const xmlChar *name = NULL;
1247 const char *type = "error";
1248
1249 if ((ctxt == NULL) || (ctxt->error == NULL))
1250 return;
1251
1252 if (child != NULL)
1253 node = child;
1254
1255 if (node != NULL) {
1256 if ((node->type == XML_DOCUMENT_NODE) ||
1257 (node->type == XML_HTML_DOCUMENT_NODE)) {
1258 xmlDocPtr doc = (xmlDocPtr) node;
1259
1260 file = doc->URL;
1261 } else {
1262 /*
1263 * Try to find contextual informations to report
1264 */
1265 if (node->type == XML_ELEMENT_NODE) {
1266 line = (int) node->content;
1267 } else if ((node->prev != NULL) &&
1268 (node->prev->type == XML_ELEMENT_NODE)) {
1269 line = (int) node->prev->content;
1270 } else if ((node->parent != NULL) &&
1271 (node->parent->type == XML_ELEMENT_NODE)) {
1272 line = (int) node->parent->content;
1273 }
1274 if ((node->doc != NULL) && (node->doc->URL != NULL))
1275 file = node->doc->URL;
1276 if (node->name != NULL)
1277 name = node->name;
1278 }
1279 }
1280
1281 if (ctxt != NULL)
1282 type = "compilation error";
1283 else if (schema != NULL)
1284 type = "runtime error";
1285
1286 if ((file != NULL) && (line != 0) && (name != NULL))
1287 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1288 type, file, line, name);
1289 else if ((file != NULL) && (name != NULL))
1290 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1291 type, file, name);
1292 else if ((file != NULL) && (line != 0))
1293 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1294 else if (file != NULL)
1295 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1296 else if (name != NULL)
1297 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1298 else
1299 ctxt->error(ctxt->userData, "%s\n", type);
1300}
1301#endif
1302
1303/************************************************************************
1304 * *
1305 * Type library hooks *
1306 * *
1307 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001308static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1309 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001310
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001311/**
1312 * xmlRelaxNGSchemaTypeHave:
1313 * @data: data needed for the library
1314 * @type: the type name
1315 *
1316 * Check if the given type is provided by
1317 * the W3C XMLSchema Datatype library.
1318 *
1319 * Returns 1 if yes, 0 if no and -1 in case of error.
1320 */
1321static int
1322xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001323 const xmlChar *type) {
1324 xmlSchemaTypePtr typ;
1325
1326 if (type == NULL)
1327 return(-1);
1328 typ = xmlSchemaGetPredefinedType(type,
1329 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1330 if (typ == NULL)
1331 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001332 return(1);
1333}
1334
1335/**
1336 * xmlRelaxNGSchemaTypeCheck:
1337 * @data: data needed for the library
1338 * @type: the type name
1339 * @value: the value to check
1340 *
1341 * Check if the given type and value are validated by
1342 * the W3C XMLSchema Datatype library.
1343 *
1344 * Returns 1 if yes, 0 if no and -1 in case of error.
1345 */
1346static int
1347xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001348 const xmlChar *type,
1349 const xmlChar *value) {
1350 xmlSchemaTypePtr typ;
1351 int ret;
1352
1353 /*
1354 * TODO: the type should be cached ab provided back, interface subject
1355 * to changes.
1356 * TODO: handle facets, may require an additional interface and keep
1357 * the value returned from the validation.
1358 */
1359 if ((type == NULL) || (value == NULL))
1360 return(-1);
1361 typ = xmlSchemaGetPredefinedType(type,
1362 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1363 if (typ == NULL)
1364 return(-1);
1365 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1366 if (ret == 0)
1367 return(1);
1368 if (ret > 0)
1369 return(0);
1370 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001371}
1372
1373/**
1374 * xmlRelaxNGSchemaTypeCompare:
1375 * @data: data needed for the library
1376 * @type: the type name
1377 * @value1: the first value
1378 * @value2: the second value
1379 *
1380 * Compare two values accordingly a type from the W3C XMLSchema
1381 * Datatype library.
1382 *
1383 * Returns 1 if yes, 0 if no and -1 in case of error.
1384 */
1385static int
1386xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1387 const xmlChar *type ATTRIBUTE_UNUSED,
1388 const xmlChar *value1 ATTRIBUTE_UNUSED,
1389 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1390 TODO
1391 return(1);
1392}
1393
1394/**
1395 * xmlRelaxNGDefaultTypeHave:
1396 * @data: data needed for the library
1397 * @type: the type name
1398 *
1399 * Check if the given type is provided by
1400 * the default datatype library.
1401 *
1402 * Returns 1 if yes, 0 if no and -1 in case of error.
1403 */
1404static int
1405xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1406 if (type == NULL)
1407 return(-1);
1408 if (xmlStrEqual(type, BAD_CAST "string"))
1409 return(1);
1410 if (xmlStrEqual(type, BAD_CAST "token"))
1411 return(1);
1412 return(0);
1413}
1414
1415/**
1416 * xmlRelaxNGDefaultTypeCheck:
1417 * @data: data needed for the library
1418 * @type: the type name
1419 * @value: the value to check
1420 *
1421 * Check if the given type and value are validated by
1422 * the default datatype library.
1423 *
1424 * Returns 1 if yes, 0 if no and -1 in case of error.
1425 */
1426static int
1427xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1428 const xmlChar *type ATTRIBUTE_UNUSED,
1429 const xmlChar *value ATTRIBUTE_UNUSED) {
1430 return(1);
1431}
1432
1433/**
1434 * xmlRelaxNGDefaultTypeCompare:
1435 * @data: data needed for the library
1436 * @type: the type name
1437 * @value1: the first value
1438 * @value2: the second value
1439 *
1440 * Compare two values accordingly a type from the default
1441 * datatype library.
1442 *
1443 * Returns 1 if yes, 0 if no and -1 in case of error.
1444 */
1445static int
1446xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1447 const xmlChar *type ATTRIBUTE_UNUSED,
1448 const xmlChar *value1 ATTRIBUTE_UNUSED,
1449 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001450 int ret = -1;
1451
1452 if (xmlStrEqual(type, BAD_CAST "string")) {
1453 ret = xmlStrEqual(value1, value2);
1454 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1455 if (!xmlStrEqual(value1, value2)) {
1456 xmlChar *nval, *nvalue;
1457
1458 /*
1459 * TODO: trivial optimizations are possible by
1460 * computing at compile-time
1461 */
1462 nval = xmlRelaxNGNormalize(NULL, value1);
1463 nvalue = xmlRelaxNGNormalize(NULL, value2);
1464
1465 if ((nval == NULL) || (nvalue == NULL) ||
1466 (!xmlStrEqual(nval, nvalue)))
1467 ret = -1;
1468 if (nval != NULL)
1469 xmlFree(nval);
1470 if (nvalue != NULL)
1471 xmlFree(nvalue);
1472 }
1473 }
1474 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001475}
1476
1477static int xmlRelaxNGTypeInitialized = 0;
1478static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1479
1480/**
1481 * xmlRelaxNGFreeTypeLibrary:
1482 * @lib: the type library structure
1483 * @namespace: the URI bound to the library
1484 *
1485 * Free the structure associated to the type library
1486 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001487static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001488xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1489 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1490 if (lib == NULL)
1491 return;
1492 if (lib->namespace != NULL)
1493 xmlFree((xmlChar *)lib->namespace);
1494 xmlFree(lib);
1495}
1496
1497/**
1498 * xmlRelaxNGRegisterTypeLibrary:
1499 * @namespace: the URI bound to the library
1500 * @data: data associated to the library
1501 * @have: the provide function
1502 * @check: the checking function
1503 * @comp: the comparison function
1504 *
1505 * Register a new type library
1506 *
1507 * Returns 0 in case of success and -1 in case of error.
1508 */
1509static int
1510xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1511 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1512 xmlRelaxNGTypeCompare comp) {
1513 xmlRelaxNGTypeLibraryPtr lib;
1514 int ret;
1515
1516 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1517 (check == NULL) || (comp == NULL))
1518 return(-1);
1519 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1520 xmlGenericError(xmlGenericErrorContext,
1521 "Relax-NG types library '%s' already registered\n",
1522 namespace);
1523 return(-1);
1524 }
1525 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1526 if (lib == NULL) {
1527 xmlGenericError(xmlGenericErrorContext,
1528 "Relax-NG types library '%s' malloc() failed\n",
1529 namespace);
1530 return (-1);
1531 }
1532 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1533 lib->namespace = xmlStrdup(namespace);
1534 lib->data = data;
1535 lib->have = have;
1536 lib->comp = comp;
1537 lib->check = check;
1538 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1539 if (ret < 0) {
1540 xmlGenericError(xmlGenericErrorContext,
1541 "Relax-NG types library failed to register '%s'\n",
1542 namespace);
1543 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1544 return(-1);
1545 }
1546 return(0);
1547}
1548
1549/**
1550 * xmlRelaxNGInitTypes:
1551 *
1552 * Initilize the default type libraries.
1553 *
1554 * Returns 0 in case of success and -1 in case of error.
1555 */
1556static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001557xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001558 if (xmlRelaxNGTypeInitialized != 0)
1559 return(0);
1560 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1561 if (xmlRelaxNGRegisteredTypes == NULL) {
1562 xmlGenericError(xmlGenericErrorContext,
1563 "Failed to allocate sh table for Relax-NG types\n");
1564 return(-1);
1565 }
1566 xmlRelaxNGRegisterTypeLibrary(
1567 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1568 NULL,
1569 xmlRelaxNGSchemaTypeHave,
1570 xmlRelaxNGSchemaTypeCheck,
1571 xmlRelaxNGSchemaTypeCompare);
1572 xmlRelaxNGRegisterTypeLibrary(
1573 xmlRelaxNGNs,
1574 NULL,
1575 xmlRelaxNGDefaultTypeHave,
1576 xmlRelaxNGDefaultTypeCheck,
1577 xmlRelaxNGDefaultTypeCompare);
1578 xmlRelaxNGTypeInitialized = 1;
1579 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001580}
1581
1582/**
1583 * xmlRelaxNGCleanupTypes:
1584 *
1585 * Cleanup the default Schemas type library associated to RelaxNG
1586 */
1587void
1588xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001589 if (xmlRelaxNGTypeInitialized == 0)
1590 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001591 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001592 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1593 xmlRelaxNGFreeTypeLibrary);
1594 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001595}
1596
1597/************************************************************************
1598 * *
1599 * Parsing functions *
1600 * *
1601 ************************************************************************/
1602
1603static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1604 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1605static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1606 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1607static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001608 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001609static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1610 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001611static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1612 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001613static int xmlRelaxNGParseGrammarContent(
1614 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001615static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1616 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1617 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001618static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1619 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001620
1621
1622#define IS_BLANK_NODE(n) \
1623 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1624
1625/**
1626 * xmlRelaxNGIsBlank:
1627 * @str: a string
1628 *
1629 * Check if a string is ignorable c.f. 4.2. Whitespace
1630 *
1631 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1632 */
1633static int
1634xmlRelaxNGIsBlank(xmlChar *str) {
1635 if (str == NULL)
1636 return(1);
1637 while (*str != 0) {
1638 if (!(IS_BLANK(*str))) return(0);
1639 str++;
1640 }
1641 return(1);
1642}
1643
Daniel Veillard6eadf632003-01-23 18:29:16 +00001644/**
1645 * xmlRelaxNGGetDataTypeLibrary:
1646 * @ctxt: a Relax-NG parser context
1647 * @node: the current data or value element
1648 *
1649 * Applies algorithm from 4.3. datatypeLibrary attribute
1650 *
1651 * Returns the datatypeLibary value or NULL if not found
1652 */
1653static xmlChar *
1654xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1655 xmlNodePtr node) {
1656 xmlChar *ret, *escape;
1657
Daniel Veillard6eadf632003-01-23 18:29:16 +00001658 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1659 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1660 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001661 if (ret[0] == 0) {
1662 xmlFree(ret);
1663 return(NULL);
1664 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001665 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001666 if (escape == NULL) {
1667 return(ret);
1668 }
1669 xmlFree(ret);
1670 return(escape);
1671 }
1672 }
1673 node = node->parent;
1674 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001675 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1676 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001677 if (ret[0] == 0) {
1678 xmlFree(ret);
1679 return(NULL);
1680 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001681 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1682 if (escape == NULL) {
1683 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001684 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001685 xmlFree(ret);
1686 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001687 }
1688 node = node->parent;
1689 }
1690 return(NULL);
1691}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001692
1693/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001694 * xmlRelaxNGParseValue:
1695 * @ctxt: a Relax-NG parser context
1696 * @node: the data node.
1697 *
1698 * parse the content of a RelaxNG value node.
1699 *
1700 * Returns the definition pointer or NULL in case of error
1701 */
1702static xmlRelaxNGDefinePtr
1703xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1704 xmlRelaxNGDefinePtr def = NULL;
1705 xmlRelaxNGTypeLibraryPtr lib;
1706 xmlChar *type;
1707 xmlChar *library;
1708 int tmp;
1709
1710 def = xmlRelaxNGNewDefine(ctxt, node);
1711 if (def == NULL)
1712 return(NULL);
1713 def->type = XML_RELAXNG_VALUE;
1714
1715 type = xmlGetProp(node, BAD_CAST "type");
1716 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001717 xmlRelaxNGNormExtSpace(type);
1718 if (xmlValidateNCName(type, 0)) {
1719 if (ctxt->error != NULL)
1720 ctxt->error(ctxt->userData,
1721 "value type '%s' is not an NCName\n",
1722 type);
1723 ctxt->nbErrors++;
1724 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001725 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1726 if (library == NULL)
1727 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1728
1729 def->name = type;
1730 def->ns = library;
1731
1732 lib = (xmlRelaxNGTypeLibraryPtr)
1733 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1734 if (lib == NULL) {
1735 if (ctxt->error != NULL)
1736 ctxt->error(ctxt->userData,
1737 "Use of unregistered type library '%s'\n",
1738 library);
1739 ctxt->nbErrors++;
1740 def->data = NULL;
1741 } else {
1742 def->data = lib;
1743 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001744 if (ctxt->error != NULL)
1745 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001746 "Internal error with type library '%s': no 'have'\n",
1747 library);
1748 ctxt->nbErrors++;
1749 } else {
1750 tmp = lib->have(lib->data, def->name);
1751 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001752 if (ctxt->error != NULL)
1753 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001754 "Error type '%s' is not exported by type library '%s'\n",
1755 def->name, library);
1756 ctxt->nbErrors++;
1757 }
1758 }
1759 }
1760 }
1761 if (node->children == NULL) {
1762 if (ctxt->error != NULL)
1763 ctxt->error(ctxt->userData,
1764 "Element <value> has no content\n");
1765 ctxt->nbErrors++;
1766 } else if ((node->children->type != XML_TEXT_NODE) ||
1767 (node->children->next != NULL)) {
1768 if (ctxt->error != NULL)
1769 ctxt->error(ctxt->userData,
1770 "Expecting a single text value for <value>content\n");
1771 ctxt->nbErrors++;
1772 } else {
1773 def->value = xmlNodeGetContent(node);
1774 if (def->value == NULL) {
1775 if (ctxt->error != NULL)
1776 ctxt->error(ctxt->userData,
1777 "Element <value> has no content\n");
1778 ctxt->nbErrors++;
1779 }
1780 }
1781 /* TODO check ahead of time that the value is okay per the type */
1782 return(def);
1783}
1784
1785/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001786 * xmlRelaxNGParseData:
1787 * @ctxt: a Relax-NG parser context
1788 * @node: the data node.
1789 *
1790 * parse the content of a RelaxNG data node.
1791 *
1792 * Returns the definition pointer or NULL in case of error
1793 */
1794static xmlRelaxNGDefinePtr
1795xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1796 xmlRelaxNGDefinePtr def = NULL;
1797 xmlRelaxNGTypeLibraryPtr lib;
1798 xmlChar *type;
1799 xmlChar *library;
1800 xmlNodePtr content;
1801 int tmp;
1802
1803 type = xmlGetProp(node, BAD_CAST "type");
1804 if (type == NULL) {
1805 if (ctxt->error != NULL)
1806 ctxt->error(ctxt->userData,
1807 "data has no type\n");
1808 ctxt->nbErrors++;
1809 return(NULL);
1810 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001811 xmlRelaxNGNormExtSpace(type);
1812 if (xmlValidateNCName(type, 0)) {
1813 if (ctxt->error != NULL)
1814 ctxt->error(ctxt->userData,
1815 "data type '%s' is not an NCName\n",
1816 type);
1817 ctxt->nbErrors++;
1818 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001819 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1820 if (library == NULL)
1821 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1822
1823 def = xmlRelaxNGNewDefine(ctxt, node);
1824 if (def == NULL) {
1825 xmlFree(type);
1826 return(NULL);
1827 }
1828 def->type = XML_RELAXNG_DATATYPE;
1829 def->name = type;
1830 def->ns = library;
1831
1832 lib = (xmlRelaxNGTypeLibraryPtr)
1833 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1834 if (lib == NULL) {
1835 if (ctxt->error != NULL)
1836 ctxt->error(ctxt->userData,
1837 "Use of unregistered type library '%s'\n",
1838 library);
1839 ctxt->nbErrors++;
1840 def->data = NULL;
1841 } else {
1842 def->data = lib;
1843 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001844 if (ctxt->error != NULL)
1845 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001846 "Internal error with type library '%s': no 'have'\n",
1847 library);
1848 ctxt->nbErrors++;
1849 } else {
1850 tmp = lib->have(lib->data, def->name);
1851 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001852 if (ctxt->error != NULL)
1853 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001854 "Error type '%s' is not exported by type library '%s'\n",
1855 def->name, library);
1856 ctxt->nbErrors++;
1857 }
1858 }
1859 }
1860 content = node->children;
1861 while (content != NULL) {
1862 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001863 ctxt->nbErrors++;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001864 content = content->next;
1865 }
1866
1867 return(def);
1868}
1869
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001870/**
1871 * xmlRelaxNGCompareElemDefLists:
1872 * @ctxt: a Relax-NG parser context
1873 * @defs1: the first list of element defs
1874 * @defs2: the second list of element defs
1875 *
1876 * Compare the 2 lists of element definitions. The comparison is
1877 * that if both lists do not accept the same QNames, it returns 1
1878 * If the 2 lists can accept the same QName the comparison returns 0
1879 *
1880 * Returns 1 disttinct, 0 if equal
1881 */
1882static int
1883xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1884 xmlRelaxNGDefinePtr *def1,
1885 xmlRelaxNGDefinePtr *def2) {
1886 xmlRelaxNGDefinePtr *basedef2 = def2;
1887
Daniel Veillard154877e2003-01-30 12:17:05 +00001888 if ((def1 == NULL) || (def2 == NULL))
1889 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001890 if ((*def1 == NULL) || (*def2 == NULL))
1891 return(1);
1892 while (*def1 != NULL) {
1893 while ((*def2) != NULL) {
1894 if ((*def1)->name == NULL) {
1895 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1896 return(0);
1897 } else if ((*def2)->name == NULL) {
1898 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1899 return(0);
1900 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
1901 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
1902 return(0);
1903 }
1904 def2++;
1905 }
1906 def2 = basedef2;
1907 def1++;
1908 }
1909 return(1);
1910}
1911
1912/**
1913 * xmlRelaxNGGetElements:
1914 * @ctxt: a Relax-NG parser context
1915 * @def: the interleave definition
1916 *
1917 * Compute the list of top elements a definition can generate
1918 *
1919 * Returns a list of elements or NULL if none was found.
1920 */
1921static xmlRelaxNGDefinePtr *
1922xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
1923 xmlRelaxNGDefinePtr def) {
1924 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
1925 int len = 0;
1926 int max = 0;
1927
1928 parent = NULL;
1929 cur = def;
1930 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00001931 if ((cur->type == XML_RELAXNG_ELEMENT) ||
1932 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001933 if (ret == NULL) {
1934 max = 10;
1935 ret = (xmlRelaxNGDefinePtr *)
1936 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
1937 if (ret == NULL) {
1938 if (ctxt->error != NULL)
1939 ctxt->error(ctxt->userData,
1940 "Out of memory in element search\n");
1941 ctxt->nbErrors++;
1942 return(NULL);
1943 }
1944 } else if (max <= len) {
1945 max *= 2;
1946 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
1947 if (ret == NULL) {
1948 if (ctxt->error != NULL)
1949 ctxt->error(ctxt->userData,
1950 "Out of memory in element search\n");
1951 ctxt->nbErrors++;
1952 return(NULL);
1953 }
1954 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00001955 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001956 ret[len] = NULL;
1957 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
1958 (cur->type == XML_RELAXNG_INTERLEAVE) ||
1959 (cur->type == XML_RELAXNG_GROUP) ||
1960 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00001961 (cur->type == XML_RELAXNG_ZEROORMORE) ||
1962 (cur->type == XML_RELAXNG_OPTIONAL) ||
1963 (cur->type == XML_RELAXNG_REF) ||
1964 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001965 /*
1966 * Don't go within elements or attributes or string values.
1967 * Just gather the element top list
1968 */
1969 if (cur->content != NULL) {
1970 parent = cur;
1971 cur = cur->content;
1972 tmp = cur;
1973 while (tmp != NULL) {
1974 tmp->parent = parent;
1975 tmp = tmp->next;
1976 }
1977 continue;
1978 }
1979 }
Daniel Veillard154877e2003-01-30 12:17:05 +00001980 if (cur == def)
1981 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001982 if (cur->next != NULL) {
1983 cur = cur->next;
1984 continue;
1985 }
1986 do {
1987 cur = cur->parent;
1988 if (cur == NULL) break;
1989 if (cur == def) return(ret);
1990 if (cur->next != NULL) {
1991 cur = cur->next;
1992 break;
1993 }
1994 } while (cur != NULL);
1995 }
1996 return(ret);
1997}
1998
1999/**
2000 * xmlRelaxNGComputeInterleaves:
2001 * @def: the interleave definition
2002 * @ctxt: a Relax-NG parser context
2003 * @node: the data node.
2004 *
2005 * A lot of work for preprocessing interleave definitions
2006 * is potentially needed to get a decent execution speed at runtime
2007 * - trying to get a total order on the element nodes generated
2008 * by the interleaves, order the list of interleave definitions
2009 * following that order.
2010 * - if <text/> is used to handle mixed content, it is better to
2011 * flag this in the define and simplify the runtime checking
2012 * algorithm
2013 */
2014static void
2015xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2016 xmlRelaxNGParserCtxtPtr ctxt,
2017 xmlChar *name ATTRIBUTE_UNUSED) {
2018 xmlRelaxNGDefinePtr cur;
2019
2020 xmlRelaxNGDefinePtr *list = NULL;
2021 xmlRelaxNGPartitionPtr partitions = NULL;
2022 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2023 xmlRelaxNGInterleaveGroupPtr group;
2024 int i,j,ret;
2025 int nbgroups = 0;
2026 int nbchild = 0;
2027
2028#ifdef DEBUG_INTERLEAVE
2029 xmlGenericError(xmlGenericErrorContext,
2030 "xmlRelaxNGComputeInterleaves(%s)\n",
2031 name);
2032#endif
2033 cur = def->content;
2034 while (cur != NULL) {
2035 nbchild++;
2036 cur = cur->next;
2037 }
2038
2039#ifdef DEBUG_INTERLEAVE
2040 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2041#endif
2042 groups = (xmlRelaxNGInterleaveGroupPtr *)
2043 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2044 if (groups == NULL)
2045 goto error;
2046 cur = def->content;
2047 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002048 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2049 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2050 if (groups[nbgroups] == NULL)
2051 goto error;
2052 groups[nbgroups]->rule = cur;
2053 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
2054 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002055 cur = cur->next;
2056 }
2057 list = NULL;
2058#ifdef DEBUG_INTERLEAVE
2059 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2060#endif
2061
2062 /*
2063 * Let's check that all rules makes a partitions according to 7.4
2064 */
2065 partitions = (xmlRelaxNGPartitionPtr)
2066 xmlMalloc(sizeof(xmlRelaxNGPartition));
2067 if (partitions == NULL)
2068 goto error;
2069 partitions->nbgroups = nbgroups;
2070 for (i = 0;i < nbgroups;i++) {
2071 group = groups[i];
2072 for (j = i+1;j < nbgroups;j++) {
2073 if (groups[j] == NULL)
2074 continue;
2075 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2076 groups[j]->defs);
2077 if (ret == 0) {
2078 if (ctxt->error != NULL)
2079 ctxt->error(ctxt->userData,
2080 "Element or text conflicts in interleave\n");
2081 ctxt->nbErrors++;
2082 }
2083 }
2084 }
2085 partitions->groups = groups;
2086
2087 /*
2088 * Free Up the child list, and save the partition list back in the def
2089 */
2090 def->data = partitions;
2091 return;
2092
2093error:
2094 if (ctxt->error != NULL)
2095 ctxt->error(ctxt->userData,
2096 "Out of memory in interleave computation\n");
2097 ctxt->nbErrors++;
2098 if (list == NULL)
2099 xmlFree(list);
2100 if (groups != NULL) {
2101 for (i = 0;i < nbgroups;i++)
2102 if (groups[i] != NULL) {
2103 if (groups[i]->defs != NULL)
2104 xmlFree(groups[i]->defs);
2105 xmlFree(groups[i]);
2106 }
2107 xmlFree(groups);
2108 }
2109 xmlRelaxNGFreePartition(partitions);
2110}
2111
2112/**
2113 * xmlRelaxNGParseInterleave:
2114 * @ctxt: a Relax-NG parser context
2115 * @node: the data node.
2116 *
2117 * parse the content of a RelaxNG interleave node.
2118 *
2119 * Returns the definition pointer or NULL in case of error
2120 */
2121static xmlRelaxNGDefinePtr
2122xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2123 xmlRelaxNGDefinePtr def = NULL;
2124 xmlRelaxNGDefinePtr last = NULL, cur;
2125 xmlNodePtr child;
2126
2127 def = xmlRelaxNGNewDefine(ctxt, node);
2128 if (def == NULL) {
2129 return(NULL);
2130 }
2131 def->type = XML_RELAXNG_INTERLEAVE;
2132
2133 if (ctxt->interleaves == NULL)
2134 ctxt->interleaves = xmlHashCreate(10);
2135 if (ctxt->interleaves == NULL) {
2136 if (ctxt->error != NULL)
2137 ctxt->error(ctxt->userData,
2138 "Failed to create interleaves hash table\n");
2139 ctxt->nbErrors++;
2140 } else {
2141 char name[32];
2142
2143 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2144 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2145 if (ctxt->error != NULL)
2146 ctxt->error(ctxt->userData,
2147 "Failed to add %s to hash table\n", name);
2148 ctxt->nbErrors++;
2149 }
2150 }
2151 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002152 if (child == NULL) {
2153 if (ctxt->error != NULL)
2154 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2155 ctxt->nbErrors++;
2156 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002157 while (child != NULL) {
2158 if (IS_RELAXNG(child, "element")) {
2159 cur = xmlRelaxNGParseElement(ctxt, child);
2160 } else {
2161 cur = xmlRelaxNGParsePattern(ctxt, child);
2162 }
2163 if (cur != NULL) {
2164 cur->parent = def;
2165 if (last == NULL) {
2166 def->content = last = cur;
2167 } else {
2168 last->next = cur;
2169 last = cur;
2170 }
2171 }
2172 child = child->next;
2173 }
2174
2175 return(def);
2176}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002177
2178/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002179 * xmlRelaxNGParseInclude:
2180 * @ctxt: a Relax-NG parser context
2181 * @node: the include node
2182 *
2183 * Integrate the content of an include node in the current grammar
2184 *
2185 * Returns 0 in case of success or -1 in case of error
2186 */
2187static int
2188xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2189 xmlRelaxNGIncludePtr incl;
2190 xmlNodePtr root;
2191 int ret = 0, tmp;
2192
2193 incl = node->_private;
2194 if (incl == NULL) {
2195 if (ctxt->error != NULL)
2196 ctxt->error(ctxt->userData,
2197 "Include node has no data\n");
2198 ctxt->nbErrors++;
2199 return(-1);
2200 }
2201 root = xmlDocGetRootElement(incl->doc);
2202 if (root == NULL) {
2203 if (ctxt->error != NULL)
2204 ctxt->error(ctxt->userData,
2205 "Include document is empty\n");
2206 ctxt->nbErrors++;
2207 return(-1);
2208 }
2209 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2210 if (ctxt->error != NULL)
2211 ctxt->error(ctxt->userData,
2212 "Include document root is not a grammar\n");
2213 ctxt->nbErrors++;
2214 return(-1);
2215 }
2216
2217 /*
2218 * Merge the definition from both the include and the internal list
2219 */
2220 if (root->children != NULL) {
2221 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2222 if (tmp != 0)
2223 ret = -1;
2224 }
2225 if (node->children != NULL) {
2226 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2227 if (tmp != 0)
2228 ret = -1;
2229 }
2230 return(ret);
2231}
2232
2233/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002234 * xmlRelaxNGParseDefine:
2235 * @ctxt: a Relax-NG parser context
2236 * @node: the define node
2237 *
2238 * parse the content of a RelaxNG define element node.
2239 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002240 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002241 */
2242static int
2243xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2244 xmlChar *name;
2245 int ret = 0, tmp;
2246 xmlRelaxNGDefinePtr def;
2247 const xmlChar *olddefine;
2248
2249 name = xmlGetProp(node, BAD_CAST "name");
2250 if (name == NULL) {
2251 if (ctxt->error != NULL)
2252 ctxt->error(ctxt->userData,
2253 "define has no name\n");
2254 ctxt->nbErrors++;
2255 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002256 xmlRelaxNGNormExtSpace(name);
2257 if (xmlValidateNCName(name, 0)) {
2258 if (ctxt->error != NULL)
2259 ctxt->error(ctxt->userData,
2260 "define name '%s' is not an NCName\n",
2261 name);
2262 ctxt->nbErrors++;
2263 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002264 def = xmlRelaxNGNewDefine(ctxt, node);
2265 if (def == NULL) {
2266 xmlFree(name);
2267 return(-1);
2268 }
2269 def->type = XML_RELAXNG_DEF;
2270 def->name = name;
2271 if (node->children == NULL) {
2272 if (ctxt->error != NULL)
2273 ctxt->error(ctxt->userData,
2274 "define has no children\n");
2275 ctxt->nbErrors++;
2276 } else {
2277 olddefine = ctxt->define;
2278 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002279 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002280 ctxt->define = olddefine;
2281 }
2282 if (ctxt->grammar->defs == NULL)
2283 ctxt->grammar->defs = xmlHashCreate(10);
2284 if (ctxt->grammar->defs == NULL) {
2285 if (ctxt->error != NULL)
2286 ctxt->error(ctxt->userData,
2287 "Could not create definition hash\n");
2288 ctxt->nbErrors++;
2289 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002290 } else {
2291 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2292 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002293 xmlRelaxNGDefinePtr prev;
2294
2295 prev = xmlHashLookup(ctxt->grammar->defs, name);
2296 if (prev == NULL) {
2297 if (ctxt->error != NULL)
2298 ctxt->error(ctxt->userData,
2299 "Internal error on define aggregation of %s\n",
2300 name);
2301 ctxt->nbErrors++;
2302 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002303 } else {
2304 while (prev->nextHash != NULL)
2305 prev = prev->nextHash;
2306 prev->nextHash = def;
2307 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002308 }
2309 }
2310 }
2311 return(ret);
2312}
2313
2314/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002315 * xmlRelaxNGProcessExternalRef:
2316 * @ctxt: the parser context
2317 * @node: the externlRef node
2318 *
2319 * Process and compile an externlRef node
2320 *
2321 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2322 */
2323static xmlRelaxNGDefinePtr
2324xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2325 xmlRelaxNGDocumentPtr docu;
2326 xmlNodePtr root, tmp;
2327 xmlChar *ns;
2328 int newNs = 0;
2329 xmlRelaxNGDefinePtr def;
2330
2331 docu = node->_private;
2332 if (docu != NULL) {
2333 def = xmlRelaxNGNewDefine(ctxt, node);
2334 if (def == NULL)
2335 return(NULL);
2336 def->type = XML_RELAXNG_EXTERNALREF;
2337
2338 if (docu->content == NULL) {
2339 /*
2340 * Then do the parsing for good
2341 */
2342 root = xmlDocGetRootElement(docu->doc);
2343 if (root == NULL) {
2344 if (ctxt->error != NULL)
2345 ctxt->error(ctxt->userData,
2346 "xmlRelaxNGParse: %s is empty\n",
2347 ctxt->URL);
2348 ctxt->nbErrors++;
2349 return (NULL);
2350 }
2351 /*
2352 * ns transmission rules
2353 */
2354 ns = xmlGetProp(root, BAD_CAST "ns");
2355 if (ns == NULL) {
2356 tmp = node;
2357 while ((tmp != NULL) &&
2358 (tmp->type == XML_ELEMENT_NODE)) {
2359 ns = xmlGetProp(tmp, BAD_CAST "ns");
2360 if (ns != NULL) {
2361 break;
2362 }
2363 tmp = tmp->parent;
2364 }
2365 if (ns != NULL) {
2366 xmlSetProp(root, BAD_CAST "ns", ns);
2367 newNs = 1;
2368 xmlFree(ns);
2369 }
2370 } else {
2371 xmlFree(ns);
2372 }
2373
2374 /*
2375 * Parsing to get a precompiled schemas.
2376 */
2377 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
2378 if ((docu->schema != NULL) &&
2379 (docu->schema->topgrammar != NULL)) {
2380 docu->content = docu->schema->topgrammar->start;
2381 }
2382
2383 /*
2384 * the externalRef may be reused in a different ns context
2385 */
2386 if (newNs == 1) {
2387 xmlUnsetProp(root, BAD_CAST "ns");
2388 }
2389 }
2390 def->content = docu->content;
2391 } else {
2392 def = NULL;
2393 }
2394 return(def);
2395}
2396
2397/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002398 * xmlRelaxNGParsePattern:
2399 * @ctxt: a Relax-NG parser context
2400 * @node: the pattern node.
2401 *
2402 * parse the content of a RelaxNG pattern node.
2403 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002404 * Returns the definition pointer or NULL in case of error or if no
2405 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002406 */
2407static xmlRelaxNGDefinePtr
2408xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2409 xmlRelaxNGDefinePtr def = NULL;
2410
Daniel Veillardd2298792003-02-14 16:54:11 +00002411 if (node == NULL) {
2412 return(NULL);
2413 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002414 if (IS_RELAXNG(node, "element")) {
2415 def = xmlRelaxNGParseElement(ctxt, node);
2416 } else if (IS_RELAXNG(node, "attribute")) {
2417 def = xmlRelaxNGParseAttribute(ctxt, node);
2418 } else if (IS_RELAXNG(node, "empty")) {
2419 def = xmlRelaxNGNewDefine(ctxt, node);
2420 if (def == NULL)
2421 return(NULL);
2422 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002423 if (node->children != NULL) {
2424 if (ctxt->error != NULL)
2425 ctxt->error(ctxt->userData, "empty: had a child node\n");
2426 ctxt->nbErrors++;
2427 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002428 } else if (IS_RELAXNG(node, "text")) {
2429 def = xmlRelaxNGNewDefine(ctxt, node);
2430 if (def == NULL)
2431 return(NULL);
2432 def->type = XML_RELAXNG_TEXT;
2433 if (node->children != NULL) {
2434 if (ctxt->error != NULL)
2435 ctxt->error(ctxt->userData, "text: had a child node\n");
2436 ctxt->nbErrors++;
2437 }
2438 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2439 def = xmlRelaxNGNewDefine(ctxt, node);
2440 if (def == NULL)
2441 return(NULL);
2442 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002443 if (node->children == NULL) {
2444 if (ctxt->error != NULL)
2445 ctxt->error(ctxt->userData,
2446 "Element %s is empty\n", node->name);
2447 ctxt->nbErrors++;
2448 } else {
2449 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2450 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002451 } else if (IS_RELAXNG(node, "oneOrMore")) {
2452 def = xmlRelaxNGNewDefine(ctxt, node);
2453 if (def == NULL)
2454 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002455 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002456 if (node->children == NULL) {
2457 if (ctxt->error != NULL)
2458 ctxt->error(ctxt->userData,
2459 "Element %s is empty\n", node->name);
2460 ctxt->nbErrors++;
2461 } else {
2462 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2463 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002464 } else if (IS_RELAXNG(node, "optional")) {
2465 def = xmlRelaxNGNewDefine(ctxt, node);
2466 if (def == NULL)
2467 return(NULL);
2468 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002469 if (node->children == NULL) {
2470 if (ctxt->error != NULL)
2471 ctxt->error(ctxt->userData,
2472 "Element %s is empty\n", node->name);
2473 ctxt->nbErrors++;
2474 } else {
2475 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2476 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002477 } else if (IS_RELAXNG(node, "choice")) {
2478 def = xmlRelaxNGNewDefine(ctxt, node);
2479 if (def == NULL)
2480 return(NULL);
2481 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002482 if (node->children == NULL) {
2483 if (ctxt->error != NULL)
2484 ctxt->error(ctxt->userData,
2485 "Element %s is empty\n", node->name);
2486 ctxt->nbErrors++;
2487 } else {
2488 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2489 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002490 } else if (IS_RELAXNG(node, "group")) {
2491 def = xmlRelaxNGNewDefine(ctxt, node);
2492 if (def == NULL)
2493 return(NULL);
2494 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002495 if (node->children == NULL) {
2496 if (ctxt->error != NULL)
2497 ctxt->error(ctxt->userData,
2498 "Element %s is empty\n", node->name);
2499 ctxt->nbErrors++;
2500 } else {
2501 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2502 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002503 } else if (IS_RELAXNG(node, "ref")) {
2504 def = xmlRelaxNGNewDefine(ctxt, node);
2505 if (def == NULL)
2506 return(NULL);
2507 def->type = XML_RELAXNG_REF;
2508 def->name = xmlGetProp(node, BAD_CAST "name");
2509 if (def->name == NULL) {
2510 if (ctxt->error != NULL)
2511 ctxt->error(ctxt->userData,
2512 "ref has no name\n");
2513 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002514 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002515 xmlRelaxNGNormExtSpace(def->name);
2516 if (xmlValidateNCName(def->name, 0)) {
2517 if (ctxt->error != NULL)
2518 ctxt->error(ctxt->userData,
2519 "ref name '%s' is not an NCName\n",
2520 def->name);
2521 ctxt->nbErrors++;
2522 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002523 if ((ctxt->define != NULL) &&
2524 (xmlStrEqual(ctxt->define, def->name))) {
2525 if (ctxt->error != NULL)
2526 ctxt->error(ctxt->userData,
2527 "Recursive reference to %s not in an element\n",
2528 def->name);
2529 ctxt->nbErrors++;
2530 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002531 }
2532 if (node->children != NULL) {
2533 if (ctxt->error != NULL)
2534 ctxt->error(ctxt->userData,
2535 "ref is not empty\n");
2536 ctxt->nbErrors++;
2537 }
2538 if (ctxt->grammar->refs == NULL)
2539 ctxt->grammar->refs = xmlHashCreate(10);
2540 if (ctxt->grammar->refs == NULL) {
2541 if (ctxt->error != NULL)
2542 ctxt->error(ctxt->userData,
2543 "Could not create references hash\n");
2544 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002545 def = NULL;
2546 } else {
2547 int tmp;
2548
2549 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2550 if (tmp < 0) {
2551 xmlRelaxNGDefinePtr prev;
2552
2553 prev = (xmlRelaxNGDefinePtr)
2554 xmlHashLookup(ctxt->grammar->refs, def->name);
2555 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002556 if (def->name != NULL) {
2557 if (ctxt->error != NULL)
2558 ctxt->error(ctxt->userData,
2559 "Error refs definitions '%s'\n",
2560 def->name);
2561 } else {
2562 if (ctxt->error != NULL)
2563 ctxt->error(ctxt->userData,
2564 "Error refs definitions\n");
2565 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002566 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002567 def = NULL;
2568 } else {
2569 def->nextHash = prev->nextHash;
2570 prev->nextHash = def;
2571 }
2572 }
2573 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002574 } else if (IS_RELAXNG(node, "data")) {
2575 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002576#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002577 } else if (IS_RELAXNG(node, "define")) {
2578 xmlRelaxNGParseDefine(ctxt, node);
2579 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002580#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002581 } else if (IS_RELAXNG(node, "value")) {
2582 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002583 } else if (IS_RELAXNG(node, "list")) {
2584 def = xmlRelaxNGNewDefine(ctxt, node);
2585 if (def == NULL)
2586 return(NULL);
2587 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002588 if (node->children == NULL) {
2589 if (ctxt->error != NULL)
2590 ctxt->error(ctxt->userData,
2591 "Element %s is empty\n", node->name);
2592 ctxt->nbErrors++;
2593 } else {
2594 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2595 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002596 } else if (IS_RELAXNG(node, "interleave")) {
2597 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002598 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002599 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002600 } else if (IS_RELAXNG(node, "notAllowed")) {
2601 def = xmlRelaxNGNewDefine(ctxt, node);
2602 if (def == NULL)
2603 return(NULL);
2604 def->type = XML_RELAXNG_NOT_ALLOWED;
2605 if (node->children != NULL) {
2606 if (ctxt->error != NULL)
2607 ctxt->error(ctxt->userData,
2608 "xmlRelaxNGParse: notAllowed element is not empty\n");
2609 ctxt->nbErrors++;
2610 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002611 } else if (IS_RELAXNG(node, "grammar")) {
2612 xmlRelaxNGGrammarPtr grammar, old;
2613 xmlRelaxNGGrammarPtr oldparent;
2614
2615 oldparent = ctxt->parentgrammar;
2616 old = ctxt->grammar;
2617 ctxt->parentgrammar = old;
2618 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2619 if (old != NULL) {
2620 ctxt->grammar = old;
2621 ctxt->parentgrammar = oldparent;
2622 if (grammar != NULL) {
2623 grammar->next = old->next;
2624 old->next = grammar;
2625 }
2626 }
2627 if (grammar != NULL)
2628 def = grammar->start;
2629 else
2630 def = NULL;
2631 } else if (IS_RELAXNG(node, "parentRef")) {
2632 if (ctxt->parentgrammar == NULL) {
2633 if (ctxt->error != NULL)
2634 ctxt->error(ctxt->userData,
2635 "Use of parentRef without a parent grammar\n");
2636 ctxt->nbErrors++;
2637 return(NULL);
2638 }
2639 def = xmlRelaxNGNewDefine(ctxt, node);
2640 if (def == NULL)
2641 return(NULL);
2642 def->type = XML_RELAXNG_PARENTREF;
2643 def->name = xmlGetProp(node, BAD_CAST "name");
2644 if (def->name == NULL) {
2645 if (ctxt->error != NULL)
2646 ctxt->error(ctxt->userData,
2647 "parentRef has no name\n");
2648 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00002649 } else {
2650 xmlRelaxNGNormExtSpace(def->name);
2651 if (xmlValidateNCName(def->name, 0)) {
2652 if (ctxt->error != NULL)
2653 ctxt->error(ctxt->userData,
2654 "parentRef name '%s' is not an NCName\n",
2655 def->name);
2656 ctxt->nbErrors++;
2657 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002658 }
2659 if (node->children != NULL) {
2660 if (ctxt->error != NULL)
2661 ctxt->error(ctxt->userData,
2662 "parentRef is not empty\n");
2663 ctxt->nbErrors++;
2664 }
2665 if (ctxt->parentgrammar->refs == NULL)
2666 ctxt->parentgrammar->refs = xmlHashCreate(10);
2667 if (ctxt->parentgrammar->refs == NULL) {
2668 if (ctxt->error != NULL)
2669 ctxt->error(ctxt->userData,
2670 "Could not create references hash\n");
2671 ctxt->nbErrors++;
2672 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002673 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00002674 int tmp;
2675
2676 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2677 if (tmp < 0) {
2678 xmlRelaxNGDefinePtr prev;
2679
2680 prev = (xmlRelaxNGDefinePtr)
2681 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2682 if (prev == NULL) {
2683 if (ctxt->error != NULL)
2684 ctxt->error(ctxt->userData,
2685 "Internal error parentRef definitions '%s'\n",
2686 def->name);
2687 ctxt->nbErrors++;
2688 def = NULL;
2689 } else {
2690 def->nextHash = prev->nextHash;
2691 prev->nextHash = def;
2692 }
2693 }
2694 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002695 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002696 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002697 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00002698 } else {
2699 if (ctxt->error != NULL)
2700 ctxt->error(ctxt->userData,
2701 "Unexpected node %s is not a pattern\n",
2702 node->name);
2703 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002704 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002705 }
2706 return(def);
2707}
2708
2709/**
2710 * xmlRelaxNGParseAttribute:
2711 * @ctxt: a Relax-NG parser context
2712 * @node: the element node
2713 *
2714 * parse the content of a RelaxNG attribute node.
2715 *
2716 * Returns the definition pointer or NULL in case of error.
2717 */
2718static xmlRelaxNGDefinePtr
2719xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002720 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002721 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002722 int old_flags;
2723
2724 ret = xmlRelaxNGNewDefine(ctxt, node);
2725 if (ret == NULL)
2726 return(NULL);
2727 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002728 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002729 child = node->children;
2730 if (child == NULL) {
2731 if (ctxt->error != NULL)
2732 ctxt->error(ctxt->userData,
2733 "xmlRelaxNGParseattribute: attribute has no children\n");
2734 ctxt->nbErrors++;
2735 return(ret);
2736 }
2737 old_flags = ctxt->flags;
2738 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002739 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2740 if (cur != NULL)
2741 child = child->next;
2742
Daniel Veillardd2298792003-02-14 16:54:11 +00002743 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002744 cur = xmlRelaxNGParsePattern(ctxt, child);
2745 if (cur != NULL) {
2746 switch (cur->type) {
2747 case XML_RELAXNG_EMPTY:
2748 case XML_RELAXNG_NOT_ALLOWED:
2749 case XML_RELAXNG_TEXT:
2750 case XML_RELAXNG_ELEMENT:
2751 case XML_RELAXNG_DATATYPE:
2752 case XML_RELAXNG_VALUE:
2753 case XML_RELAXNG_LIST:
2754 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002755 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002756 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002757 case XML_RELAXNG_DEF:
2758 case XML_RELAXNG_ONEORMORE:
2759 case XML_RELAXNG_ZEROORMORE:
2760 case XML_RELAXNG_OPTIONAL:
2761 case XML_RELAXNG_CHOICE:
2762 case XML_RELAXNG_GROUP:
2763 case XML_RELAXNG_INTERLEAVE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002764 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002765 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002766 break;
2767 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002768 if (ctxt->error != NULL)
2769 ctxt->error(ctxt->userData,
2770 "attribute has an attribute child\n");
2771 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002772 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002773 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00002774 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00002775 if (ctxt->error != NULL)
2776 ctxt->error(ctxt->userData,
2777 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002778 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002779 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002780 }
2781 }
2782 child = child->next;
2783 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002784 if (child != NULL) {
2785 if (ctxt->error != NULL)
2786 ctxt->error(ctxt->userData, "attribute has multiple children\n");
2787 ctxt->nbErrors++;
2788 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002789 ctxt->flags = old_flags;
2790 return(ret);
2791}
2792
2793/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002794 * xmlRelaxNGParseExceptNameClass:
2795 * @ctxt: a Relax-NG parser context
2796 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002797 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002798 *
2799 * parse the content of a RelaxNG nameClass node.
2800 *
2801 * Returns the definition pointer or NULL in case of error.
2802 */
2803static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002804xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2805 xmlNodePtr node, int attr) {
2806 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2807 xmlNodePtr child;
2808
Daniel Veillardd2298792003-02-14 16:54:11 +00002809 if (!IS_RELAXNG(node, "except")) {
2810 if (ctxt->error != NULL)
2811 ctxt->error(ctxt->userData,
2812 "Expecting an except node\n");
2813 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00002814 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00002815 }
2816 if (node->next != NULL) {
2817 if (ctxt->error != NULL)
2818 ctxt->error(ctxt->userData,
2819 "exceptNameClass allows only a single except node\n");
2820 ctxt->nbErrors++;
2821 }
Daniel Veillard144fae12003-02-03 13:17:57 +00002822 if (node->children == NULL) {
2823 if (ctxt->error != NULL)
2824 ctxt->error(ctxt->userData,
2825 "except has no content\n");
2826 ctxt->nbErrors++;
2827 return(NULL);
2828 }
2829
2830 ret = xmlRelaxNGNewDefine(ctxt, node);
2831 if (ret == NULL)
2832 return(NULL);
2833 ret->type = XML_RELAXNG_EXCEPT;
2834 child = node->children;
2835 while (child != NULL) {
2836 cur = xmlRelaxNGNewDefine(ctxt, child);
2837 if (cur == NULL)
2838 break;
2839 if (attr)
2840 cur->type = XML_RELAXNG_ATTRIBUTE;
2841 else
2842 cur->type = XML_RELAXNG_ELEMENT;
2843
Daniel Veillard419a7682003-02-03 23:22:49 +00002844 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00002845 if (last == NULL) {
2846 ret->content = cur;
2847 } else {
2848 last->next = cur;
2849 }
2850 last = cur;
2851 }
2852 child = child->next;
2853 }
2854
2855 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002856}
2857
2858/**
2859 * xmlRelaxNGParseNameClass:
2860 * @ctxt: a Relax-NG parser context
2861 * @node: the nameClass node
2862 * @def: the current definition
2863 *
2864 * parse the content of a RelaxNG nameClass node.
2865 *
2866 * Returns the definition pointer or NULL in case of error.
2867 */
2868static xmlRelaxNGDefinePtr
2869xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2870 xmlRelaxNGDefinePtr def) {
2871 xmlRelaxNGDefinePtr ret = def;
2872 xmlChar *val;
2873
2874 if (IS_RELAXNG(node, "name")) {
2875 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002876 xmlRelaxNGNormExtSpace(val);
2877 if (xmlValidateNCName(val, 0)) {
2878 if (ctxt->error != NULL) {
2879 if (node->parent != NULL)
2880 ctxt->error(ctxt->userData,
2881 "Element %s name '%s' is not an NCName\n",
2882 node->parent->name, val);
2883 else
2884 ctxt->error(ctxt->userData,
2885 "name '%s' is not an NCName\n",
2886 val);
2887 }
2888 ctxt->nbErrors++;
2889 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002890 ret->name = val;
2891 val = xmlGetProp(node, BAD_CAST "ns");
2892 ret->ns = val;
2893 } else if (IS_RELAXNG(node, "anyName")) {
2894 ret->name = NULL;
2895 ret->ns = NULL;
2896 if (node->children != NULL) {
2897 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002898 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2899 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002900 }
2901 } else if (IS_RELAXNG(node, "nsName")) {
2902 ret->name = NULL;
2903 ret->ns = xmlGetProp(node, BAD_CAST "ns");
2904 if (ret->ns == NULL) {
2905 if (ctxt->error != NULL)
2906 ctxt->error(ctxt->userData,
2907 "nsName has no ns attribute\n");
2908 ctxt->nbErrors++;
2909 }
2910 if (node->children != NULL) {
2911 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00002912 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
2913 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002914 }
2915 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002916 if (node->children == NULL) {
2917 if (ctxt->error != NULL)
2918 ctxt->error(ctxt->userData,
2919 "Element choice is empty\n");
2920 ctxt->nbErrors++;
2921 } else {
2922 xmlNodePtr child;
2923 xmlRelaxNGDefinePtr last = NULL, tmp;
2924
2925 child = node->children;
2926 while (child != NULL) {
2927 tmp = xmlRelaxNGParseExceptNameClass(ctxt, child,
2928 (def->type == XML_RELAXNG_ATTRIBUTE));
2929 if (tmp != NULL) {
2930 if (last == NULL) {
2931 last = ret->nameClass = tmp;
2932 } else {
2933 last->next = tmp;
2934 last = tmp;
2935 }
2936 }
2937 child = child->next;
2938 }
2939 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002940 } else {
2941 if (ctxt->error != NULL)
2942 ctxt->error(ctxt->userData,
2943 "expecting name, anyName, nsName or choice : got %s\n",
2944 node->name);
2945 ctxt->nbErrors++;
2946 return(NULL);
2947 }
2948 return(ret);
2949}
2950
2951/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002952 * xmlRelaxNGParseElement:
2953 * @ctxt: a Relax-NG parser context
2954 * @node: the element node
2955 *
2956 * parse the content of a RelaxNG element node.
2957 *
2958 * Returns the definition pointer or NULL in case of error.
2959 */
2960static xmlRelaxNGDefinePtr
2961xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2962 xmlRelaxNGDefinePtr ret, cur, last;
2963 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002964 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002965
2966 ret = xmlRelaxNGNewDefine(ctxt, node);
2967 if (ret == NULL)
2968 return(NULL);
2969 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002970 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002971 child = node->children;
2972 if (child == NULL) {
2973 if (ctxt->error != NULL)
2974 ctxt->error(ctxt->userData,
2975 "xmlRelaxNGParseElement: element has no children\n");
2976 ctxt->nbErrors++;
2977 return(ret);
2978 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002979 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2980 if (cur != NULL)
2981 child = child->next;
2982
Daniel Veillard6eadf632003-01-23 18:29:16 +00002983 if (child == NULL) {
2984 if (ctxt->error != NULL)
2985 ctxt->error(ctxt->userData,
2986 "xmlRelaxNGParseElement: element has no content\n");
2987 ctxt->nbErrors++;
2988 return(ret);
2989 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002990 olddefine = ctxt->define;
2991 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002992 last = NULL;
2993 while (child != NULL) {
2994 cur = xmlRelaxNGParsePattern(ctxt, child);
2995 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002996 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002997 switch (cur->type) {
2998 case XML_RELAXNG_EMPTY:
2999 case XML_RELAXNG_NOT_ALLOWED:
3000 case XML_RELAXNG_TEXT:
3001 case XML_RELAXNG_ELEMENT:
3002 case XML_RELAXNG_DATATYPE:
3003 case XML_RELAXNG_VALUE:
3004 case XML_RELAXNG_LIST:
3005 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003006 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003007 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003008 case XML_RELAXNG_DEF:
3009 case XML_RELAXNG_ZEROORMORE:
3010 case XML_RELAXNG_ONEORMORE:
3011 case XML_RELAXNG_OPTIONAL:
3012 case XML_RELAXNG_CHOICE:
3013 case XML_RELAXNG_GROUP:
3014 case XML_RELAXNG_INTERLEAVE:
3015 if (last == NULL) {
3016 ret->content = last = cur;
3017 } else {
3018 if ((last->type == XML_RELAXNG_ELEMENT) &&
3019 (ret->content == last)) {
3020 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3021 if (ret->content != NULL) {
3022 ret->content->type = XML_RELAXNG_GROUP;
3023 ret->content->content = last;
3024 } else {
3025 ret->content = last;
3026 }
3027 }
3028 last->next = cur;
3029 last = cur;
3030 }
3031 break;
3032 case XML_RELAXNG_ATTRIBUTE:
3033 cur->next = ret->attrs;
3034 ret->attrs = cur;
3035 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003036 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00003037 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003038 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003039 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003040 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003041 }
3042 }
3043 child = child->next;
3044 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003045 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003046 return(ret);
3047}
3048
3049/**
3050 * xmlRelaxNGParsePatterns:
3051 * @ctxt: a Relax-NG parser context
3052 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003053 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003054 *
3055 * parse the content of a RelaxNG start node.
3056 *
3057 * Returns the definition pointer or NULL in case of error.
3058 */
3059static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003060xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3061 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003062 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003063
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003064 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003065 while (nodes != NULL) {
3066 if (IS_RELAXNG(nodes, "element")) {
3067 cur = xmlRelaxNGParseElement(ctxt, nodes);
3068 if (def == NULL) {
3069 def = last = cur;
3070 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003071 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3072 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003073 def = xmlRelaxNGNewDefine(ctxt, nodes);
3074 def->type = XML_RELAXNG_GROUP;
3075 def->content = last;
3076 }
3077 last->next = cur;
3078 last = cur;
3079 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003080 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003081 } else {
3082 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003083 if (cur != NULL) {
3084 if (def == NULL) {
3085 def = last = cur;
3086 } else {
3087 last->next = cur;
3088 last = cur;
3089 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003090 }
3091 }
3092 nodes = nodes->next;
3093 }
3094 return(def);
3095}
3096
3097/**
3098 * xmlRelaxNGParseStart:
3099 * @ctxt: a Relax-NG parser context
3100 * @nodes: start children nodes
3101 *
3102 * parse the content of a RelaxNG start node.
3103 *
3104 * Returns 0 in case of success, -1 in case of error
3105 */
3106static int
3107xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3108 int ret = 0;
3109 xmlRelaxNGDefinePtr def = NULL;
3110
Daniel Veillardd2298792003-02-14 16:54:11 +00003111 if (nodes == NULL) {
3112 if (ctxt->error != NULL)
3113 ctxt->error(ctxt->userData,
3114 "start has no children\n");
3115 ctxt->nbErrors++;
3116 return(-1);
3117 }
3118 if (IS_RELAXNG(nodes, "empty")) {
3119 def = xmlRelaxNGNewDefine(ctxt, nodes);
3120 if (def == NULL)
3121 return(-1);
3122 def->type = XML_RELAXNG_EMPTY;
3123 if (nodes->children != NULL) {
3124 if (ctxt->error != NULL)
3125 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003126 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003127 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003128 ctxt->grammar->start = def;
3129 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3130 def = xmlRelaxNGNewDefine(ctxt, nodes);
3131 if (def == NULL)
3132 return(-1);
3133 def->type = XML_RELAXNG_NOT_ALLOWED;
3134 if (nodes->children != NULL) {
3135 if (ctxt->error != NULL)
3136 ctxt->error(ctxt->userData,
3137 "element notAllowed is not empty\n");
3138 ctxt->nbErrors++;
3139 }
3140 ctxt->grammar->start = def;
3141 } else {
3142 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
3143 ctxt->grammar->start = def;
3144 }
3145 nodes = nodes->next;
3146 if (nodes != NULL) {
3147 if (ctxt->error != NULL)
3148 ctxt->error(ctxt->userData,
3149 "start more than one children\n");
3150 ctxt->nbErrors++;
3151 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003152 }
3153 return(ret);
3154}
3155
3156/**
3157 * xmlRelaxNGParseGrammarContent:
3158 * @ctxt: a Relax-NG parser context
3159 * @nodes: grammar children nodes
3160 *
3161 * parse the content of a RelaxNG grammar node.
3162 *
3163 * Returns 0 in case of success, -1 in case of error
3164 */
3165static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003166xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003167{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003168 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003169
3170 if (nodes == NULL) {
3171 if (ctxt->error != NULL)
3172 ctxt->error(ctxt->userData,
3173 "grammar has no children\n");
3174 ctxt->nbErrors++;
3175 return(-1);
3176 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003177 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003178 if (IS_RELAXNG(nodes, "start")) {
3179 if (nodes->children == NULL) {
3180 if (ctxt->error != NULL)
3181 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003182 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003183 ctxt->nbErrors++;
3184 } else {
3185 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3186 if (tmp != 0)
3187 ret = -1;
3188 }
3189 } else if (IS_RELAXNG(nodes, "define")) {
3190 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3191 if (tmp != 0)
3192 ret = -1;
3193 } else if (IS_RELAXNG(nodes, "include")) {
3194 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3195 if (tmp != 0)
3196 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003197 } else {
3198 if (ctxt->error != NULL)
3199 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003200 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003201 ctxt->nbErrors++;
3202 ret = -1;
3203 }
3204 nodes = nodes->next;
3205 }
3206 return (ret);
3207}
3208
3209/**
3210 * xmlRelaxNGCheckReference:
3211 * @ref: the ref
3212 * @ctxt: a Relax-NG parser context
3213 * @name: the name associated to the defines
3214 *
3215 * Applies the 4.17. combine attribute rule for all the define
3216 * element of a given grammar using the same name.
3217 */
3218static void
3219xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3220 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3221 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003222 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003223
3224 grammar = ctxt->grammar;
3225 if (grammar == NULL) {
3226 if (ctxt->error != NULL)
3227 ctxt->error(ctxt->userData,
3228 "Internal error: no grammar in CheckReference %s\n",
3229 name);
3230 ctxt->nbErrors++;
3231 return;
3232 }
3233 if (ref->content != NULL) {
3234 if (ctxt->error != NULL)
3235 ctxt->error(ctxt->userData,
3236 "Internal error: reference has content in CheckReference %s\n",
3237 name);
3238 ctxt->nbErrors++;
3239 return;
3240 }
3241 if (grammar->defs != NULL) {
3242 def = xmlHashLookup(grammar->defs, name);
3243 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003244 cur = ref;
3245 while (cur != NULL) {
3246 cur->content = def;
3247 cur = cur->nextHash;
3248 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003249 } else {
3250 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003251 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003252 }
3253 }
3254 /*
3255 * TODO: make a closure and verify there is no loop !
3256 */
3257}
3258
3259/**
3260 * xmlRelaxNGCheckCombine:
3261 * @define: the define(s) list
3262 * @ctxt: a Relax-NG parser context
3263 * @name: the name associated to the defines
3264 *
3265 * Applies the 4.17. combine attribute rule for all the define
3266 * element of a given grammar using the same name.
3267 */
3268static void
3269xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3270 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3271 xmlChar *combine;
3272 int choiceOrInterleave = -1;
3273 int missing = 0;
3274 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3275
3276 if (define->nextHash == NULL)
3277 return;
3278 cur = define;
3279 while (cur != NULL) {
3280 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3281 if (combine != NULL) {
3282 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3283 if (choiceOrInterleave == -1)
3284 choiceOrInterleave = 1;
3285 else if (choiceOrInterleave == 0) {
3286 if (ctxt->error != NULL)
3287 ctxt->error(ctxt->userData,
3288 "Defines for %s use both 'choice' and 'interleave'\n",
3289 name);
3290 ctxt->nbErrors++;
3291 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003292 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003293 if (choiceOrInterleave == -1)
3294 choiceOrInterleave = 0;
3295 else if (choiceOrInterleave == 1) {
3296 if (ctxt->error != NULL)
3297 ctxt->error(ctxt->userData,
3298 "Defines for %s use both 'choice' and 'interleave'\n",
3299 name);
3300 ctxt->nbErrors++;
3301 }
3302 } else {
3303 if (ctxt->error != NULL)
3304 ctxt->error(ctxt->userData,
3305 "Defines for %s use unknown combine value '%s''\n",
3306 name, combine);
3307 ctxt->nbErrors++;
3308 }
3309 xmlFree(combine);
3310 } else {
3311 if (missing == 0)
3312 missing = 1;
3313 else {
3314 if (ctxt->error != NULL)
3315 ctxt->error(ctxt->userData,
3316 "Some defines for %s lacks the combine attribute\n",
3317 name);
3318 ctxt->nbErrors++;
3319 }
3320 }
3321
3322 cur = cur->nextHash;
3323 }
3324#ifdef DEBUG
3325 xmlGenericError(xmlGenericErrorContext,
3326 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3327 name, choiceOrInterleave);
3328#endif
3329 if (choiceOrInterleave == -1)
3330 choiceOrInterleave = 0;
3331 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3332 if (cur == NULL)
3333 return;
3334 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003335 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003336 else
3337 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003338 tmp = define;
3339 last = NULL;
3340 while (tmp != NULL) {
3341 if (tmp->content != NULL) {
3342 if (tmp->content->next != NULL) {
3343 /*
3344 * we need first to create a wrapper.
3345 */
3346 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3347 if (tmp2 == NULL)
3348 break;
3349 tmp2->type = XML_RELAXNG_GROUP;
3350 tmp2->content = tmp->content;
3351 } else {
3352 tmp2 = tmp->content;
3353 }
3354 if (last == NULL) {
3355 cur->content = tmp2;
3356 } else {
3357 last->next = tmp2;
3358 }
3359 last = tmp2;
3360 tmp->content = NULL;
3361 }
3362 tmp = tmp->nextHash;
3363 }
3364 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003365 if (choiceOrInterleave == 0) {
3366 if (ctxt->interleaves == NULL)
3367 ctxt->interleaves = xmlHashCreate(10);
3368 if (ctxt->interleaves == NULL) {
3369 if (ctxt->error != NULL)
3370 ctxt->error(ctxt->userData,
3371 "Failed to create interleaves hash table\n");
3372 ctxt->nbErrors++;
3373 } else {
3374 char tmpname[32];
3375
3376 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3377 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3378 if (ctxt->error != NULL)
3379 ctxt->error(ctxt->userData,
3380 "Failed to add %s to hash table\n", tmpname);
3381 ctxt->nbErrors++;
3382 }
3383 }
3384 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003385}
3386
3387/**
3388 * xmlRelaxNGCombineStart:
3389 * @ctxt: a Relax-NG parser context
3390 * @grammar: the grammar
3391 *
3392 * Applies the 4.17. combine rule for all the start
3393 * element of a given grammar.
3394 */
3395static void
3396xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3397 xmlRelaxNGGrammarPtr grammar) {
3398 xmlRelaxNGDefinePtr starts;
3399 xmlChar *combine;
3400 int choiceOrInterleave = -1;
3401 int missing = 0;
3402 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3403
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003404 starts = grammar->startList;
3405 if ((starts == NULL) || (starts->nextHash == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003406 return;
3407 cur = starts;
3408 while (cur != NULL) {
3409 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3410 if (combine != NULL) {
3411 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3412 if (choiceOrInterleave == -1)
3413 choiceOrInterleave = 1;
3414 else if (choiceOrInterleave == 0) {
3415 if (ctxt->error != NULL)
3416 ctxt->error(ctxt->userData,
3417 "<start> use both 'choice' and 'interleave'\n");
3418 ctxt->nbErrors++;
3419 }
3420 } else if (xmlStrEqual(combine, BAD_CAST "choice")) {
3421 if (choiceOrInterleave == -1)
3422 choiceOrInterleave = 0;
3423 else if (choiceOrInterleave == 1) {
3424 if (ctxt->error != NULL)
3425 ctxt->error(ctxt->userData,
3426 "<start> use both 'choice' and 'interleave'\n");
3427 ctxt->nbErrors++;
3428 }
3429 } else {
3430 if (ctxt->error != NULL)
3431 ctxt->error(ctxt->userData,
3432 "<start> uses unknown combine value '%s''\n", combine);
3433 ctxt->nbErrors++;
3434 }
3435 xmlFree(combine);
3436 } else {
3437 if (missing == 0)
3438 missing = 1;
3439 else {
3440 if (ctxt->error != NULL)
3441 ctxt->error(ctxt->userData,
3442 "Some <start> elements lacks the combine attribute\n");
3443 ctxt->nbErrors++;
3444 }
3445 }
3446
3447 cur = cur->nextHash;
3448 }
3449#ifdef DEBUG
3450 xmlGenericError(xmlGenericErrorContext,
3451 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3452 choiceOrInterleave);
3453#endif
3454 if (choiceOrInterleave == -1)
3455 choiceOrInterleave = 0;
3456 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3457 if (cur == NULL)
3458 return;
3459 if (choiceOrInterleave == 0)
3460 cur->type = XML_RELAXNG_CHOICE;
3461 else
3462 cur->type = XML_RELAXNG_INTERLEAVE;
3463 tmp = starts;
3464 last = NULL;
3465 while (tmp != NULL) {
3466 if (tmp->content != NULL) {
3467 if (tmp->content->next != NULL) {
3468 /*
3469 * we need first to create a wrapper.
3470 */
3471 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3472 if (tmp2 == NULL)
3473 break;
3474 tmp2->type = XML_RELAXNG_GROUP;
3475 tmp2->content = tmp->content;
3476 } else {
3477 tmp2 = tmp->content;
3478 }
3479 if (last == NULL) {
3480 cur->content = tmp2;
3481 } else {
3482 last->next = tmp2;
3483 }
3484 last = tmp2;
3485 tmp->content = NULL;
3486 }
3487 tmp = tmp->nextHash;
3488 }
3489 starts->content = cur;
3490}
3491
3492/**
3493 * xmlRelaxNGParseGrammar:
3494 * @ctxt: a Relax-NG parser context
3495 * @nodes: grammar children nodes
3496 *
3497 * parse a Relax-NG <grammar> node
3498 *
3499 * Returns the internal xmlRelaxNGGrammarPtr built or
3500 * NULL in case of error
3501 */
3502static xmlRelaxNGGrammarPtr
3503xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3504 xmlRelaxNGGrammarPtr ret, tmp, old;
3505
Daniel Veillard6eadf632003-01-23 18:29:16 +00003506 ret = xmlRelaxNGNewGrammar(ctxt);
3507 if (ret == NULL)
3508 return(NULL);
3509
3510 /*
3511 * Link the new grammar in the tree
3512 */
3513 ret->parent = ctxt->grammar;
3514 if (ctxt->grammar != NULL) {
3515 tmp = ctxt->grammar->children;
3516 if (tmp == NULL) {
3517 ctxt->grammar->children = ret;
3518 } else {
3519 while (tmp->next != NULL)
3520 tmp = tmp->next;
3521 tmp->next = ret;
3522 }
3523 }
3524
3525 old = ctxt->grammar;
3526 ctxt->grammar = ret;
3527 xmlRelaxNGParseGrammarContent(ctxt, nodes);
3528 ctxt->grammar = ret;
3529
3530 /*
3531 * Apply 4.17 mergingd rules to defines and starts
3532 */
3533 xmlRelaxNGCombineStart(ctxt, ret);
3534 if (ret->defs != NULL) {
3535 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
3536 ctxt);
3537 }
3538
3539 /*
3540 * link together defines and refs in this grammar
3541 */
3542 if (ret->refs != NULL) {
3543 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
3544 ctxt);
3545 }
3546 ctxt->grammar = old;
3547 return(ret);
3548}
3549
3550/**
3551 * xmlRelaxNGParseDocument:
3552 * @ctxt: a Relax-NG parser context
3553 * @node: the root node of the RelaxNG schema
3554 *
3555 * parse a Relax-NG definition resource and build an internal
3556 * xmlRelaxNG struture which can be used to validate instances.
3557 *
3558 * Returns the internal XML RelaxNG structure built or
3559 * NULL in case of error
3560 */
3561static xmlRelaxNGPtr
3562xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3563 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003564 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00003565 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003566
3567 if ((ctxt == NULL) || (node == NULL))
3568 return (NULL);
3569
3570 schema = xmlRelaxNGNewRelaxNG(ctxt);
3571 if (schema == NULL)
3572 return(NULL);
3573
Daniel Veillard276be4a2003-01-24 01:03:34 +00003574 olddefine = ctxt->define;
3575 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003576 if (IS_RELAXNG(node, "grammar")) {
3577 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3578 } else {
3579 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
3580 if (schema->topgrammar == NULL) {
3581 return(schema);
3582 }
3583 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00003584 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003585 ctxt->grammar = schema->topgrammar;
3586 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00003587 if (old != NULL)
3588 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003589 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003590 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003591
3592#ifdef DEBUG
3593 if (schema == NULL)
3594 xmlGenericError(xmlGenericErrorContext,
3595 "xmlRelaxNGParseDocument() failed\n");
3596#endif
3597
3598 return (schema);
3599}
3600
3601/************************************************************************
3602 * *
3603 * Reading RelaxNGs *
3604 * *
3605 ************************************************************************/
3606
3607/**
3608 * xmlRelaxNGNewParserCtxt:
3609 * @URL: the location of the schema
3610 *
3611 * Create an XML RelaxNGs parse context for that file/resource expected
3612 * to contain an XML RelaxNGs file.
3613 *
3614 * Returns the parser context or NULL in case of error
3615 */
3616xmlRelaxNGParserCtxtPtr
3617xmlRelaxNGNewParserCtxt(const char *URL) {
3618 xmlRelaxNGParserCtxtPtr ret;
3619
3620 if (URL == NULL)
3621 return(NULL);
3622
3623 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3624 if (ret == NULL) {
3625 xmlGenericError(xmlGenericErrorContext,
3626 "Failed to allocate new schama parser context for %s\n", URL);
3627 return (NULL);
3628 }
3629 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3630 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003631 ret->error = xmlGenericError;
3632 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003633 return (ret);
3634}
3635
3636/**
3637 * xmlRelaxNGNewMemParserCtxt:
3638 * @buffer: a pointer to a char array containing the schemas
3639 * @size: the size of the array
3640 *
3641 * Create an XML RelaxNGs parse context for that memory buffer expected
3642 * to contain an XML RelaxNGs file.
3643 *
3644 * Returns the parser context or NULL in case of error
3645 */
3646xmlRelaxNGParserCtxtPtr
3647xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
3648 xmlRelaxNGParserCtxtPtr ret;
3649
3650 if ((buffer == NULL) || (size <= 0))
3651 return(NULL);
3652
3653 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
3654 if (ret == NULL) {
3655 xmlGenericError(xmlGenericErrorContext,
3656 "Failed to allocate new schama parser context\n");
3657 return (NULL);
3658 }
3659 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
3660 ret->buffer = buffer;
3661 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003662 ret->error = xmlGenericError;
3663 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003664 return (ret);
3665}
3666
3667/**
3668 * xmlRelaxNGFreeParserCtxt:
3669 * @ctxt: the schema parser context
3670 *
3671 * Free the resources associated to the schema parser context
3672 */
3673void
3674xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
3675 if (ctxt == NULL)
3676 return;
3677 if (ctxt->URL != NULL)
3678 xmlFree(ctxt->URL);
3679 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003680 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003681 if (ctxt->interleaves != NULL)
3682 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003683 if (ctxt->documents != NULL)
3684 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
3685 xmlRelaxNGFreeDocument);
3686 if (ctxt->docTab != NULL)
3687 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003688 if (ctxt->incTab != NULL)
3689 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00003690 if (ctxt->defTab != NULL) {
3691 int i;
3692
3693 for (i = 0;i < ctxt->defNr;i++)
3694 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
3695 xmlFree(ctxt->defTab);
3696 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003697 xmlFree(ctxt);
3698}
3699
Daniel Veillard6eadf632003-01-23 18:29:16 +00003700/**
Daniel Veillardd2298792003-02-14 16:54:11 +00003701 * xmlRelaxNGNormExtSpace:
3702 * @value: a value
3703 *
3704 * Removes the leading and ending spaces of the value
3705 * The string is modified "in situ"
3706 */
3707static void
3708xmlRelaxNGNormExtSpace(xmlChar *value) {
3709 xmlChar *start = value;
3710 xmlChar *cur = value;
3711 if (value == NULL)
3712 return;
3713
3714 while (IS_BLANK(*cur)) cur++;
3715 if (cur == start) {
3716 do {
3717 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3718 if (*cur == 0)
3719 return;
3720 start = cur;
3721 while (IS_BLANK(*cur)) cur++;
3722 if (*cur == 0) {
3723 *start = 0;
3724 return;
3725 }
3726 } while (1);
3727 } else {
3728 do {
3729 while ((*cur != 0) && (!IS_BLANK(*cur)))
3730 *start++ = *cur++;
3731 if (*cur == 0) {
3732 *start = 0;
3733 return;
3734 }
3735 /* don't try to normalize the inner spaces */
3736 while (IS_BLANK(*cur)) cur++;
3737 *start++ = *cur++;
3738 if (*cur == 0) {
3739 *start = 0;
3740 return;
3741 }
3742 } while (1);
3743 }
3744}
3745
3746/**
3747 * xmlRelaxNGCheckAttributes:
3748 * @ctxt: a Relax-NG parser context
3749 * @node: a Relax-NG node
3750 *
3751 * Check all the attributes on the given node
3752 */
3753static void
3754xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3755 xmlAttrPtr cur, next;
3756
3757 cur = node->properties;
3758 while (cur != NULL) {
3759 next = cur->next;
3760 if ((cur->ns == NULL) ||
3761 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
3762 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
3763 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
3764 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
3765 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
3766 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
3767 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
3768 if (ctxt->error != NULL)
3769 ctxt->error(ctxt->userData,
3770 "Attribute %s is not allowed on %s\n",
3771 cur->name, node->name);
3772 ctxt->nbErrors++;
3773 }
3774 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
3775 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
3776 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
3777 if (ctxt->error != NULL)
3778 ctxt->error(ctxt->userData,
3779 "Attribute %s is not allowed on %s\n",
3780 cur->name, node->name);
3781 ctxt->nbErrors++;
3782 }
3783 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
3784 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
3785 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
3786 if (ctxt->error != NULL)
3787 ctxt->error(ctxt->userData,
3788 "Attribute %s is not allowed on %s\n",
3789 cur->name, node->name);
3790 ctxt->nbErrors++;
3791 }
3792 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
3793 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
3794 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
3795 if (ctxt->error != NULL)
3796 ctxt->error(ctxt->userData,
3797 "Attribute %s is not allowed on %s\n",
3798 cur->name, node->name);
3799 ctxt->nbErrors++;
3800 }
3801 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
3802 xmlChar *val;
3803 xmlURIPtr uri;
3804
3805 val = xmlNodeListGetString(node->doc, cur->children, 1);
3806 if (val != NULL) {
3807 if (val[0] != 0) {
3808 uri = xmlParseURI((const char *) val);
3809 if (uri == NULL) {
3810 if (ctxt->error != NULL)
3811 ctxt->error(ctxt->userData,
3812 "Attribute %s contains invalid URI %s\n",
3813 cur->name, val);
3814 ctxt->nbErrors++;
3815 } else {
3816 if (uri->scheme == NULL) {
3817 if (ctxt->error != NULL)
3818 ctxt->error(ctxt->userData,
3819 "Attribute %s URI %s is not absolute\n",
3820 cur->name, val);
3821 ctxt->nbErrors++;
3822 }
3823 if (uri->fragment != NULL) {
3824 if (ctxt->error != NULL)
3825 ctxt->error(ctxt->userData,
3826 "Attribute %s URI %s has a fragment ID\n",
3827 cur->name, val);
3828 ctxt->nbErrors++;
3829 }
3830 xmlFreeURI(uri);
3831 }
3832 }
3833 xmlFree(val);
3834 }
3835 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
3836 if (ctxt->error != NULL)
3837 ctxt->error(ctxt->userData,
3838 "Unknown attribute %s on %s\n",
3839 cur->name, node->name);
3840 ctxt->nbErrors++;
3841 }
3842 }
3843 cur = next;
3844 }
3845}
3846
3847/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003848 * xmlRelaxNGCleanupDoc:
3849 * @ctxt: a Relax-NG parser context
3850 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00003851 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003852 * Cleanup the document from unwanted nodes for parsing, resolve
3853 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003854 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003855 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00003856 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003857static xmlDocPtr
3858xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003859 xmlNodePtr root, cur, delete;
3860
Daniel Veillard6eadf632003-01-23 18:29:16 +00003861 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003862 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00003863 */
3864 root = xmlDocGetRootElement(doc);
3865 if (root == NULL) {
3866 if (ctxt->error != NULL)
3867 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
3868 ctxt->URL);
3869 ctxt->nbErrors++;
3870 return (NULL);
3871 }
3872
3873 /*
3874 * Remove all the blank text nodes
3875 */
3876 delete = NULL;
3877 cur = root;
3878 while (cur != NULL) {
3879 if (delete != NULL) {
3880 xmlUnlinkNode(delete);
3881 xmlFreeNode(delete);
3882 delete = NULL;
3883 }
3884 if (cur->type == XML_ELEMENT_NODE) {
3885 /*
3886 * Simplification 4.1. Annotations
3887 */
3888 if ((cur->ns == NULL) ||
3889 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00003890 if ((cur->parent != NULL) &&
3891 (cur->parent->type == XML_ELEMENT_NODE) &&
3892 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
3893 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
3894 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
3895 if (ctxt->error != NULL)
3896 ctxt->error(ctxt->userData,
3897 "element %s doesn't allow foreign elements\n",
3898 cur->parent->name);
3899 ctxt->nbErrors++;
3900 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003901 delete = cur;
3902 goto skip_children;
3903 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003904 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003905 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003906 xmlChar *href, *ns, *base, *URL;
3907 xmlRelaxNGDocumentPtr docu;
3908
3909 ns = xmlGetProp(cur, BAD_CAST "ns");
3910 href = xmlGetProp(cur, BAD_CAST "href");
3911 if (href == NULL) {
3912 if (ctxt->error != NULL)
3913 ctxt->error(ctxt->userData,
3914 "xmlRelaxNGParse: externalRef has no href attribute\n");
3915 ctxt->nbErrors++;
3916 delete = cur;
3917 goto skip_children;
3918 }
3919 base = xmlNodeGetBase(cur->doc, cur);
3920 URL = xmlBuildURI(href, base);
3921 if (URL == NULL) {
3922 if (ctxt->error != NULL)
3923 ctxt->error(ctxt->userData,
3924 "Failed to compute URL for externalRef %s\n", href);
3925 ctxt->nbErrors++;
3926 if (href != NULL)
3927 xmlFree(href);
3928 if (base != NULL)
3929 xmlFree(base);
3930 delete = cur;
3931 goto skip_children;
3932 }
3933 if (href != NULL)
3934 xmlFree(href);
3935 if (base != NULL)
3936 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003937 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003938 if (docu == NULL) {
3939 if (ctxt->error != NULL)
3940 ctxt->error(ctxt->userData,
3941 "Failed to load externalRef %s\n", URL);
3942 ctxt->nbErrors++;
3943 xmlFree(URL);
3944 delete = cur;
3945 goto skip_children;
3946 }
3947 xmlFree(URL);
3948 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003949 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003950 xmlChar *href, *base, *URL;
3951 xmlRelaxNGIncludePtr incl;
3952
3953 href = xmlGetProp(cur, BAD_CAST "href");
3954 if (href == NULL) {
3955 if (ctxt->error != NULL)
3956 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003957 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003958 ctxt->nbErrors++;
3959 delete = cur;
3960 goto skip_children;
3961 }
3962 base = xmlNodeGetBase(cur->doc, cur);
3963 URL = xmlBuildURI(href, base);
3964 if (URL == NULL) {
3965 if (ctxt->error != NULL)
3966 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003967 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003968 ctxt->nbErrors++;
3969 if (href != NULL)
3970 xmlFree(href);
3971 if (base != NULL)
3972 xmlFree(base);
3973 delete = cur;
3974 goto skip_children;
3975 }
3976 if (href != NULL)
3977 xmlFree(href);
3978 if (base != NULL)
3979 xmlFree(base);
3980 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur);
3981 if (incl == NULL) {
3982 if (ctxt->error != NULL)
3983 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003984 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00003985 ctxt->nbErrors++;
3986 xmlFree(URL);
3987 delete = cur;
3988 goto skip_children;
3989 }
3990 xmlFree(URL);
3991 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003992 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
3993 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00003994 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003995 xmlNodePtr text = NULL;
3996
3997 /*
3998 * Simplification 4.8. name attribute of element
3999 * and attribute elements
4000 */
4001 name = xmlGetProp(cur, BAD_CAST "name");
4002 if (name != NULL) {
4003 if (cur->children == NULL) {
4004 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
4005 name);
4006 } else {
4007 xmlNodePtr node;
4008 node = xmlNewNode(cur->ns, BAD_CAST "name");
4009 if (node != NULL) {
4010 xmlAddPrevSibling(cur->children, node);
4011 text = xmlNewText(name);
4012 xmlAddChild(node, text);
4013 text = node;
4014 }
4015 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004016 if (text == NULL) {
4017 if (ctxt->error != NULL)
4018 ctxt->error(ctxt->userData,
4019 "Failed to create a name %s element\n", name);
4020 ctxt->nbErrors++;
4021 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004022 xmlUnsetProp(cur, BAD_CAST "name");
4023 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00004024 ns = xmlGetProp(cur, BAD_CAST "ns");
4025 if (ns != NULL) {
4026 if (text != NULL) {
4027 xmlSetProp(text, BAD_CAST "ns", ns);
4028 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00004029 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004030 xmlFree(ns);
4031 } else if (xmlStrEqual(cur->name,
4032 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004033 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
4034 }
4035 }
4036 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
4037 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
4038 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
4039 /*
4040 * Simplification 4.8. name attribute of element
4041 * and attribute elements
4042 */
4043 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
4044 xmlNodePtr node;
4045 xmlChar *ns = NULL;
4046
4047 node = cur->parent;
4048 while ((node != NULL) &&
4049 (node->type == XML_ELEMENT_NODE)) {
4050 ns = xmlGetProp(node, BAD_CAST "ns");
4051 if (ns != NULL) {
4052 break;
4053 }
4054 node = node->parent;
4055 }
4056 if (ns == NULL) {
4057 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
4058 } else {
4059 xmlSetProp(cur, BAD_CAST "ns", ns);
4060 xmlFree(ns);
4061 }
4062 }
4063 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4064 xmlChar *name, *local, *prefix;
4065
4066 /*
4067 * Simplification: 4.10. QNames
4068 */
4069 name = xmlNodeGetContent(cur);
4070 if (name != NULL) {
4071 local = xmlSplitQName2(name, &prefix);
4072 if (local != NULL) {
4073 xmlNsPtr ns;
4074
4075 ns = xmlSearchNs(cur->doc, cur, prefix);
4076 if (ns == NULL) {
4077 if (ctxt->error != NULL)
4078 ctxt->error(ctxt->userData,
4079 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
4080 ctxt->nbErrors++;
4081 } else {
4082 xmlSetProp(cur, BAD_CAST "ns", ns->href);
4083 xmlNodeSetContent(cur, local);
4084 }
4085 xmlFree(local);
4086 xmlFree(prefix);
4087 }
4088 xmlFree(name);
4089 }
4090 }
4091 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004092 /*
4093 * Thisd is not an else since "include" is transformed
4094 * into a div
4095 */
4096 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
4097 /*
4098 * implements rule 4.11
4099 */
4100 xmlNodePtr child, ins, tmp;
4101
4102 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004103 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004104 while (child != NULL) {
4105 tmp = child->next;
4106 xmlUnlinkNode(child);
4107 ins = xmlAddNextSibling(ins, child);
4108 child = tmp;
4109 }
4110 delete = cur;
4111 goto skip_children;
4112 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004113 }
4114 }
4115 /*
4116 * Simplification 4.2 whitespaces
4117 */
4118 else if (cur->type == XML_TEXT_NODE) {
4119 if (IS_BLANK_NODE(cur)) {
4120 if (cur->parent->type == XML_ELEMENT_NODE) {
4121 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
4122 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
4123 delete = cur;
4124 } else {
4125 delete = cur;
4126 goto skip_children;
4127 }
4128 }
4129 } else if (cur->type != XML_CDATA_SECTION_NODE) {
4130 delete = cur;
4131 goto skip_children;
4132 }
4133
4134 /*
4135 * Skip to next node
4136 */
4137 if (cur->children != NULL) {
4138 if ((cur->children->type != XML_ENTITY_DECL) &&
4139 (cur->children->type != XML_ENTITY_REF_NODE) &&
4140 (cur->children->type != XML_ENTITY_NODE)) {
4141 cur = cur->children;
4142 continue;
4143 }
4144 }
4145skip_children:
4146 if (cur->next != NULL) {
4147 cur = cur->next;
4148 continue;
4149 }
4150
4151 do {
4152 cur = cur->parent;
4153 if (cur == NULL)
4154 break;
4155 if (cur == root) {
4156 cur = NULL;
4157 break;
4158 }
4159 if (cur->next != NULL) {
4160 cur = cur->next;
4161 break;
4162 }
4163 } while (cur != NULL);
4164 }
4165 if (delete != NULL) {
4166 xmlUnlinkNode(delete);
4167 xmlFreeNode(delete);
4168 delete = NULL;
4169 }
4170
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004171 return(doc);
4172}
4173
4174/**
4175 * xmlRelaxNGParse:
4176 * @ctxt: a Relax-NG parser context
4177 *
4178 * parse a schema definition resource and build an internal
4179 * XML Shema struture which can be used to validate instances.
4180 * *WARNING* this interface is highly subject to change
4181 *
4182 * Returns the internal XML RelaxNG structure built from the resource or
4183 * NULL in case of error
4184 */
4185xmlRelaxNGPtr
4186xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
4187{
4188 xmlRelaxNGPtr ret = NULL;
4189 xmlDocPtr doc;
4190 xmlNodePtr root;
4191
4192 xmlRelaxNGInitTypes();
4193
4194 if (ctxt == NULL)
4195 return (NULL);
4196
4197 /*
4198 * First step is to parse the input document into an DOM/Infoset
4199 */
4200 if (ctxt->URL != NULL) {
4201 doc = xmlParseFile((const char *) ctxt->URL);
4202 if (doc == NULL) {
4203 if (ctxt->error != NULL)
4204 ctxt->error(ctxt->userData,
4205 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
4206 ctxt->nbErrors++;
4207 return (NULL);
4208 }
4209 } else if (ctxt->buffer != NULL) {
4210 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
4211 if (doc == NULL) {
4212 if (ctxt->error != NULL)
4213 ctxt->error(ctxt->userData,
4214 "xmlRelaxNGParse: could not parse schemas\n");
4215 ctxt->nbErrors++;
4216 return (NULL);
4217 }
4218 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4219 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
4220 } else {
4221 if (ctxt->error != NULL)
4222 ctxt->error(ctxt->userData,
4223 "xmlRelaxNGParse: nothing to parse\n");
4224 ctxt->nbErrors++;
4225 return (NULL);
4226 }
4227 ctxt->document = doc;
4228
4229 /*
4230 * Some preprocessing of the document content
4231 */
4232 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
4233 if (doc == NULL) {
4234 xmlFreeDoc(ctxt->document);
4235 ctxt->document = NULL;
4236 return(NULL);
4237 }
4238
Daniel Veillard6eadf632003-01-23 18:29:16 +00004239 /*
4240 * Then do the parsing for good
4241 */
4242 root = xmlDocGetRootElement(doc);
4243 if (root == NULL) {
4244 if (ctxt->error != NULL)
4245 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4246 ctxt->URL);
4247 ctxt->nbErrors++;
4248 return (NULL);
4249 }
4250 ret = xmlRelaxNGParseDocument(ctxt, root);
4251 if (ret == NULL)
4252 return(NULL);
4253
4254 /*
4255 * Check the ref/defines links
4256 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004257 /*
4258 * try to preprocess interleaves
4259 */
4260 if (ctxt->interleaves != NULL) {
4261 xmlHashScan(ctxt->interleaves,
4262 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
4263 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004264
4265 /*
4266 * if there was a parsing error return NULL
4267 */
4268 if (ctxt->nbErrors > 0) {
4269 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004270 ctxt->document = NULL;
4271 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004272 return(NULL);
4273 }
4274
4275 /*
4276 * Transfer the pointer for cleanup at the schema level.
4277 */
4278 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004279 ctxt->document = NULL;
4280 ret->documents = ctxt->documents;
4281 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004282 ret->includes = ctxt->includes;
4283 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00004284 ret->defNr = ctxt->defNr;
4285 ret->defTab = ctxt->defTab;
4286 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004287
4288 return (ret);
4289}
4290
4291/**
4292 * xmlRelaxNGSetParserErrors:
4293 * @ctxt: a Relax-NG validation context
4294 * @err: the error callback
4295 * @warn: the warning callback
4296 * @ctx: contextual data for the callbacks
4297 *
4298 * Set the callback functions used to handle errors for a validation context
4299 */
4300void
4301xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
4302 xmlRelaxNGValidityErrorFunc err,
4303 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
4304 if (ctxt == NULL)
4305 return;
4306 ctxt->error = err;
4307 ctxt->warning = warn;
4308 ctxt->userData = ctx;
4309}
4310/************************************************************************
4311 * *
4312 * Dump back a compiled form *
4313 * *
4314 ************************************************************************/
4315static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
4316
4317/**
4318 * xmlRelaxNGDumpDefines:
4319 * @output: the file output
4320 * @defines: a list of define structures
4321 *
4322 * Dump a RelaxNG structure back
4323 */
4324static void
4325xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
4326 while (defines != NULL) {
4327 xmlRelaxNGDumpDefine(output, defines);
4328 defines = defines->next;
4329 }
4330}
4331
4332/**
4333 * xmlRelaxNGDumpDefine:
4334 * @output: the file output
4335 * @define: a define structure
4336 *
4337 * Dump a RelaxNG structure back
4338 */
4339static void
4340xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
4341 if (define == NULL)
4342 return;
4343 switch(define->type) {
4344 case XML_RELAXNG_EMPTY:
4345 fprintf(output, "<empty/>\n");
4346 break;
4347 case XML_RELAXNG_NOT_ALLOWED:
4348 fprintf(output, "<notAllowed/>\n");
4349 break;
4350 case XML_RELAXNG_TEXT:
4351 fprintf(output, "<text/>\n");
4352 break;
4353 case XML_RELAXNG_ELEMENT:
4354 fprintf(output, "<element>\n");
4355 if (define->name != NULL) {
4356 fprintf(output, "<name");
4357 if (define->ns != NULL)
4358 fprintf(output, " ns=\"%s\"", define->ns);
4359 fprintf(output, ">%s</name>\n", define->name);
4360 }
4361 xmlRelaxNGDumpDefines(output, define->attrs);
4362 xmlRelaxNGDumpDefines(output, define->content);
4363 fprintf(output, "</element>\n");
4364 break;
4365 case XML_RELAXNG_LIST:
4366 fprintf(output, "<list>\n");
4367 xmlRelaxNGDumpDefines(output, define->content);
4368 fprintf(output, "</list>\n");
4369 break;
4370 case XML_RELAXNG_ONEORMORE:
4371 fprintf(output, "<oneOrMore>\n");
4372 xmlRelaxNGDumpDefines(output, define->content);
4373 fprintf(output, "</oneOrMore>\n");
4374 break;
4375 case XML_RELAXNG_ZEROORMORE:
4376 fprintf(output, "<zeroOrMore>\n");
4377 xmlRelaxNGDumpDefines(output, define->content);
4378 fprintf(output, "</zeroOrMore>\n");
4379 break;
4380 case XML_RELAXNG_CHOICE:
4381 fprintf(output, "<choice>\n");
4382 xmlRelaxNGDumpDefines(output, define->content);
4383 fprintf(output, "</choice>\n");
4384 break;
4385 case XML_RELAXNG_GROUP:
4386 fprintf(output, "<group>\n");
4387 xmlRelaxNGDumpDefines(output, define->content);
4388 fprintf(output, "</group>\n");
4389 break;
4390 case XML_RELAXNG_INTERLEAVE:
4391 fprintf(output, "<interleave>\n");
4392 xmlRelaxNGDumpDefines(output, define->content);
4393 fprintf(output, "</interleave>\n");
4394 break;
4395 case XML_RELAXNG_OPTIONAL:
4396 fprintf(output, "<optional>\n");
4397 xmlRelaxNGDumpDefines(output, define->content);
4398 fprintf(output, "</optional>\n");
4399 break;
4400 case XML_RELAXNG_ATTRIBUTE:
4401 fprintf(output, "<attribute>\n");
4402 xmlRelaxNGDumpDefines(output, define->content);
4403 fprintf(output, "</attribute>\n");
4404 break;
4405 case XML_RELAXNG_DEF:
4406 fprintf(output, "<define");
4407 if (define->name != NULL)
4408 fprintf(output, " name=\"%s\"", define->name);
4409 fprintf(output, ">\n");
4410 xmlRelaxNGDumpDefines(output, define->content);
4411 fprintf(output, "</define>\n");
4412 break;
4413 case XML_RELAXNG_REF:
4414 fprintf(output, "<ref");
4415 if (define->name != NULL)
4416 fprintf(output, " name=\"%s\"", define->name);
4417 fprintf(output, ">\n");
4418 xmlRelaxNGDumpDefines(output, define->content);
4419 fprintf(output, "</ref>\n");
4420 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00004421 case XML_RELAXNG_PARENTREF:
4422 fprintf(output, "<parentRef");
4423 if (define->name != NULL)
4424 fprintf(output, " name=\"%s\"", define->name);
4425 fprintf(output, ">\n");
4426 xmlRelaxNGDumpDefines(output, define->content);
4427 fprintf(output, "</parentRef>\n");
4428 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004429 case XML_RELAXNG_EXTERNALREF:
Daniel Veillarde431a272003-01-29 23:02:33 +00004430 fprintf(output, "<externalRef");
4431 xmlRelaxNGDumpDefines(output, define->content);
4432 fprintf(output, "</externalRef>\n");
4433 break;
4434 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004435 case XML_RELAXNG_VALUE:
4436 TODO
4437 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004438 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00004439 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004440 TODO
4441 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004442 }
4443}
4444
4445/**
4446 * xmlRelaxNGDumpGrammar:
4447 * @output: the file output
4448 * @grammar: a grammar structure
4449 * @top: is this a top grammar
4450 *
4451 * Dump a RelaxNG structure back
4452 */
4453static void
4454xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
4455{
4456 if (grammar == NULL)
4457 return;
4458
4459 fprintf(output, "<grammar");
4460 if (top)
4461 fprintf(output,
4462 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
4463 switch(grammar->combine) {
4464 case XML_RELAXNG_COMBINE_UNDEFINED:
4465 break;
4466 case XML_RELAXNG_COMBINE_CHOICE:
4467 fprintf(output, " combine=\"choice\"");
4468 break;
4469 case XML_RELAXNG_COMBINE_INTERLEAVE:
4470 fprintf(output, " combine=\"interleave\"");
4471 break;
4472 default:
4473 fprintf(output, " <!-- invalid combine value -->");
4474 }
4475 fprintf(output, ">\n");
4476 if (grammar->start == NULL) {
4477 fprintf(output, " <!-- grammar had no start -->");
4478 } else {
4479 fprintf(output, "<start>\n");
4480 xmlRelaxNGDumpDefine(output, grammar->start);
4481 fprintf(output, "</start>\n");
4482 }
4483 /* TODO ? Dump the defines ? */
4484 fprintf(output, "</grammar>\n");
4485}
4486
4487/**
4488 * xmlRelaxNGDump:
4489 * @output: the file output
4490 * @schema: a schema structure
4491 *
4492 * Dump a RelaxNG structure back
4493 */
4494void
4495xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
4496{
4497 if (schema == NULL) {
4498 fprintf(output, "RelaxNG empty or failed to compile\n");
4499 return;
4500 }
4501 fprintf(output, "RelaxNG: ");
4502 if (schema->doc == NULL) {
4503 fprintf(output, "no document\n");
4504 } else if (schema->doc->URL != NULL) {
4505 fprintf(output, "%s\n", schema->doc->URL);
4506 } else {
4507 fprintf(output, "\n");
4508 }
4509 if (schema->topgrammar == NULL) {
4510 fprintf(output, "RelaxNG has no top grammar\n");
4511 return;
4512 }
4513 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
4514}
4515
Daniel Veillardfebcca42003-02-16 15:44:18 +00004516/**
4517 * xmlRelaxNGDumpTree:
4518 * @output: the file output
4519 * @schema: a schema structure
4520 *
4521 * Dump the transformed RelaxNG tree.
4522 */
4523void
4524xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
4525{
4526 if (schema == NULL) {
4527 fprintf(output, "RelaxNG empty or failed to compile\n");
4528 return;
4529 }
4530 if (schema->doc == NULL) {
4531 fprintf(output, "no document\n");
4532 } else {
4533 xmlDocDump(output, schema->doc);
4534 }
4535}
4536
Daniel Veillard6eadf632003-01-23 18:29:16 +00004537/************************************************************************
4538 * *
4539 * Validation implementation *
4540 * *
4541 ************************************************************************/
4542static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
4543 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004544static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4545 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004546
4547/**
4548 * xmlRelaxNGSkipIgnored:
4549 * @ctxt: a schema validation context
4550 * @node: the top node.
4551 *
4552 * Skip ignorable nodes in that context
4553 *
4554 * Returns the new sibling or NULL in case of error.
4555 */
4556static xmlNodePtr
4557xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4558 xmlNodePtr node) {
4559 /*
4560 * TODO complete and handle entities
4561 */
4562 while ((node != NULL) &&
4563 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004564 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00004565 ((node->type == XML_TEXT_NODE) &&
4566 (IS_BLANK_NODE(node))))) {
4567 node = node->next;
4568 }
4569 return(node);
4570}
4571
4572/**
Daniel Veillardedc91922003-01-26 00:52:04 +00004573 * xmlRelaxNGNormalize:
4574 * @ctxt: a schema validation context
4575 * @str: the string to normalize
4576 *
4577 * Implements the normalizeWhiteSpace( s ) function from
4578 * section 6.2.9 of the spec
4579 *
4580 * Returns the new string or NULL in case of error.
4581 */
4582static xmlChar *
4583xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
4584 xmlChar *ret, *p;
4585 const xmlChar *tmp;
4586 int len;
4587
4588 if (str == NULL)
4589 return(NULL);
4590 tmp = str;
4591 while (*tmp != 0) tmp++;
4592 len = tmp - str;
4593
4594 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
4595 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004596 if (ctxt != NULL) {
4597 VALID_CTXT();
4598 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
4599 } else {
4600 xmlGenericError(xmlGenericErrorContext,
4601 "xmlRelaxNGNormalize: out of memory\n");
4602 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004603 return(NULL);
4604 }
4605 p = ret;
4606 while (IS_BLANK(*str)) str++;
4607 while (*str != 0) {
4608 if (IS_BLANK(*str)) {
4609 while (IS_BLANK(*str)) str++;
4610 if (*str == 0)
4611 break;
4612 *p++ = ' ';
4613 } else
4614 *p++ = *str++;
4615 }
4616 *p = 0;
4617 return(ret);
4618}
4619
4620/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004621 * xmlRelaxNGValidateDatatype:
4622 * @ctxt: a Relax-NG validation context
4623 * @value: the string value
4624 * @type: the datatype definition
4625 *
4626 * Validate the given value against the dataype
4627 *
4628 * Returns 0 if the validation succeeded or an error code.
4629 */
4630static int
4631xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
4632 xmlRelaxNGDefinePtr define) {
4633 int ret;
4634 xmlRelaxNGTypeLibraryPtr lib;
4635
4636 if ((define == NULL) || (define->data == NULL)) {
4637 return(-1);
4638 }
4639 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4640 if (lib->check != NULL)
4641 ret = lib->check(lib->data, define->name, value);
4642 else
4643 ret = -1;
4644 if (ret < 0) {
4645 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004646 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004647 return(-1);
4648 } else if (ret == 1) {
4649 ret = 0;
4650 } else {
4651 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004652 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004653 return(-1);
4654 ret = -1;
4655 }
4656 return(ret);
4657}
4658
4659/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004660 * xmlRelaxNGNextValue:
4661 * @ctxt: a Relax-NG validation context
4662 *
4663 * Skip to the next value when validating within a list
4664 *
4665 * Returns 0 if the operation succeeded or an error code.
4666 */
4667static int
4668xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
4669 xmlChar *cur;
4670
4671 cur = ctxt->state->value;
4672 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
4673 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00004674 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004675 return(0);
4676 }
4677 while (*cur != 0) cur++;
4678 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
4679 if (cur == ctxt->state->endvalue)
4680 ctxt->state->value = NULL;
4681 else
4682 ctxt->state->value = cur;
4683 return(0);
4684}
4685
4686/**
4687 * xmlRelaxNGValidateValueList:
4688 * @ctxt: a Relax-NG validation context
4689 * @defines: the list of definitions to verify
4690 *
4691 * Validate the given set of definitions for the current value
4692 *
4693 * Returns 0 if the validation succeeded or an error code.
4694 */
4695static int
4696xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
4697 xmlRelaxNGDefinePtr defines) {
4698 int ret = 0;
4699
4700 while (defines != NULL) {
4701 ret = xmlRelaxNGValidateValue(ctxt, defines);
4702 if (ret != 0)
4703 break;
4704 defines = defines->next;
4705 }
4706 return(ret);
4707}
4708
4709/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004710 * xmlRelaxNGValidateValue:
4711 * @ctxt: a Relax-NG validation context
4712 * @define: the definition to verify
4713 *
4714 * Validate the given definition for the current value
4715 *
4716 * Returns 0 if the validation succeeded or an error code.
4717 */
4718static int
4719xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
4720 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00004721 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004722 xmlChar *value;
4723
4724 value = ctxt->state->value;
4725 switch (define->type) {
4726 case XML_RELAXNG_EMPTY:
4727 if ((value != NULL) && (value[0] != '0'))
4728 ret = -1;
4729 break;
4730 case XML_RELAXNG_TEXT:
4731 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00004732 case XML_RELAXNG_VALUE: {
4733 if (!xmlStrEqual(value, define->value)) {
4734 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00004735 xmlRelaxNGTypeLibraryPtr lib;
4736
4737 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
4738 if ((lib != NULL) && (lib->comp != NULL))
4739 ret = lib->comp(lib->data, define->name, value,
4740 define->value);
4741 else
4742 ret = -1;
4743 if (ret < 0) {
4744 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004745 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00004746 define->name);
4747 return(-1);
4748 } else if (ret == 1) {
4749 ret = 0;
4750 } else {
4751 ret = -1;
4752 }
Daniel Veillardedc91922003-01-26 00:52:04 +00004753 } else {
4754 xmlChar *nval, *nvalue;
4755
4756 /*
4757 * TODO: trivial optimizations are possible by
4758 * computing at compile-time
4759 */
4760 nval = xmlRelaxNGNormalize(ctxt, define->value);
4761 nvalue = xmlRelaxNGNormalize(ctxt, value);
4762
Daniel Veillardea3f3982003-01-26 19:45:18 +00004763 if ((nval == NULL) || (nvalue == NULL) ||
4764 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00004765 ret = -1;
4766 if (nval != NULL)
4767 xmlFree(nval);
4768 if (nvalue != NULL)
4769 xmlFree(nvalue);
4770 }
4771 }
4772 break;
4773 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004774 case XML_RELAXNG_DATATYPE: {
4775 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
4776 if (ret == 0)
4777 xmlRelaxNGNextValue(ctxt);
4778
4779 break;
4780 }
4781 case XML_RELAXNG_CHOICE: {
4782 xmlRelaxNGDefinePtr list = define->content;
4783 xmlChar *oldvalue;
4784
4785 oldflags = ctxt->flags;
4786 ctxt->flags |= FLAGS_IGNORABLE;
4787
4788 oldvalue = ctxt->state->value;
4789 while (list != NULL) {
4790 ret = xmlRelaxNGValidateValue(ctxt, list);
4791 if (ret == 0) {
4792 break;
4793 }
4794 ctxt->state->value = oldvalue;
4795 list = list->next;
4796 }
4797 ctxt->flags = oldflags;
4798 break;
4799 }
4800 case XML_RELAXNG_LIST: {
4801 xmlRelaxNGDefinePtr list = define->content;
4802 xmlChar *oldvalue, *oldend, *val, *cur;
4803
4804 oldvalue = ctxt->state->value;
4805 oldend = ctxt->state->endvalue;
4806
4807 val = xmlStrdup(oldvalue);
4808 if (val == NULL) {
4809 VALID_CTXT();
4810 VALID_ERROR("Internal: no state\n");
4811 return(-1);
4812 }
4813 cur = val;
4814 while (*cur != 0) {
4815 if (IS_BLANK(*cur))
4816 *cur = 0;
4817 cur++;
4818 }
4819 ctxt->state->endvalue = cur;
4820 cur = val;
4821 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
4822
4823 ctxt->state->value = cur;
4824
4825 while (list != NULL) {
4826 ret = xmlRelaxNGValidateValue(ctxt, list);
4827 if (ret != 0) {
4828 break;
4829 }
4830 list = list->next;
4831 }
4832 if ((ret == 0) && (ctxt->state->value != NULL) &&
4833 (ctxt->state->value != ctxt->state->endvalue)) {
4834 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004835 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004836 ret = -1;
4837 }
4838 xmlFree(val);
4839 ctxt->state->value = oldvalue;
4840 ctxt->state->endvalue = oldend;
4841 break;
4842 }
4843 case XML_RELAXNG_ONEORMORE:
4844 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4845 if (ret != 0) {
4846 break;
4847 }
4848 /* no break on purpose */
4849 case XML_RELAXNG_ZEROORMORE: {
4850 xmlChar *cur, *temp;
4851
4852 oldflags = ctxt->flags;
4853 ctxt->flags |= FLAGS_IGNORABLE;
4854 cur = ctxt->state->value;
4855 temp = NULL;
4856 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
4857 (temp != cur)) {
4858 temp = cur;
4859 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
4860 if (ret != 0) {
4861 ctxt->state->value = temp;
4862 ret = 0;
4863 break;
4864 }
4865 cur = ctxt->state->value;
4866 }
4867 ctxt->flags = oldflags;
4868 break;
4869 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004870 default:
4871 TODO
4872 ret = -1;
4873 }
4874 return(ret);
4875}
4876
4877/**
4878 * xmlRelaxNGValidateValueContent:
4879 * @ctxt: a Relax-NG validation context
4880 * @defines: the list of definitions to verify
4881 *
4882 * Validate the given definitions for the current value
4883 *
4884 * Returns 0 if the validation succeeded or an error code.
4885 */
4886static int
4887xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
4888 xmlRelaxNGDefinePtr defines) {
4889 int ret = 0;
4890
4891 while (defines != NULL) {
4892 ret = xmlRelaxNGValidateValue(ctxt, defines);
4893 if (ret != 0)
4894 break;
4895 defines = defines->next;
4896 }
4897 return(ret);
4898}
4899
4900/**
Daniel Veillard144fae12003-02-03 13:17:57 +00004901 * xmlRelaxNGAttributeMatch:
4902 * @ctxt: a Relax-NG validation context
4903 * @define: the definition to check
4904 * @prop: the attribute
4905 *
4906 * Check if the attribute matches the definition nameClass
4907 *
4908 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
4909 */
4910static int
4911xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
4912 xmlRelaxNGDefinePtr define,
4913 xmlAttrPtr prop) {
4914 int ret;
4915
4916 if (define->name != NULL) {
4917 if (!xmlStrEqual(define->name, prop->name))
4918 return(0);
4919 }
4920 if (define->ns != NULL) {
4921 if (define->ns[0] == 0) {
4922 if (prop->ns != NULL)
4923 return(0);
4924 } else {
4925 if ((prop->ns == NULL) ||
4926 (!xmlStrEqual(define->ns, prop->ns->href)))
4927 return(0);
4928 }
4929 }
4930 if (define->nameClass == NULL)
4931 return(1);
4932 define = define->nameClass;
4933 if (define->type == XML_RELAXNG_EXCEPT) {
4934 xmlRelaxNGDefinePtr list;
4935
4936 list = define->content;
4937 while (list != NULL) {
4938 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
4939 if (ret == 1)
4940 return(0);
4941 if (ret < 0)
4942 return(ret);
4943 list = list->next;
4944 }
4945 } else {
4946 TODO
4947 }
4948 return(1);
4949}
4950
4951/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004952 * xmlRelaxNGValidateAttribute:
4953 * @ctxt: a Relax-NG validation context
4954 * @define: the definition to verify
4955 *
4956 * Validate the given attribute definition for that node
4957 *
4958 * Returns 0 if the validation succeeded or an error code.
4959 */
4960static int
4961xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
4962 xmlRelaxNGDefinePtr define) {
4963 int ret = 0, i;
4964 xmlChar *value, *oldvalue;
4965 xmlAttrPtr prop = NULL, tmp;
4966
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004967 if (ctxt->state->nbAttrLeft <= 0)
4968 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004969 if (define->name != NULL) {
4970 for (i = 0;i < ctxt->state->nbAttrs;i++) {
4971 tmp = ctxt->state->attrs[i];
4972 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
4973 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
4974 (tmp->ns == NULL)) ||
4975 ((tmp->ns != NULL) &&
4976 (xmlStrEqual(define->ns, tmp->ns->href)))) {
4977 prop = tmp;
4978 break;
4979 }
4980 }
4981 }
4982 if (prop != NULL) {
4983 value = xmlNodeListGetString(prop->doc, prop->children, 1);
4984 oldvalue = ctxt->state->value;
4985 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00004986 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004987 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00004988 if (ctxt->state->value != NULL)
4989 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004990 if (value != NULL)
4991 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00004992 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004993 if (ret == 0) {
4994 /*
4995 * flag the attribute as processed
4996 */
4997 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00004998 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004999 }
5000 } else {
5001 ret = -1;
5002 }
5003#ifdef DEBUG
5004 xmlGenericError(xmlGenericErrorContext,
5005 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
5006#endif
5007 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005008 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5009 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00005010 if ((tmp != NULL) &&
5011 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005012 prop = tmp;
5013 break;
5014 }
5015 }
5016 if (prop != NULL) {
5017 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5018 oldvalue = ctxt->state->value;
5019 ctxt->state->value = value;
5020 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005021 if (ctxt->state->value != NULL)
5022 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005023 if (value != NULL)
5024 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005025 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005026 if (ret == 0) {
5027 /*
5028 * flag the attribute as processed
5029 */
5030 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005031 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005032 }
5033 } else {
5034 ret = -1;
5035 }
5036#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00005037 if (define->ns != NULL) {
5038 xmlGenericError(xmlGenericErrorContext,
5039 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
5040 define->ns, ret);
5041 } else {
5042 xmlGenericError(xmlGenericErrorContext,
5043 "xmlRelaxNGValidateAttribute(anyName): %d\n",
5044 ret);
5045 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005046#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005047 }
5048
5049 return(ret);
5050}
5051
5052/**
5053 * xmlRelaxNGValidateAttributeList:
5054 * @ctxt: a Relax-NG validation context
5055 * @define: the list of definition to verify
5056 *
5057 * Validate the given node against the list of attribute definitions
5058 *
5059 * Returns 0 if the validation succeeded or an error code.
5060 */
5061static int
5062xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
5063 xmlRelaxNGDefinePtr defines) {
5064 int ret = 0;
5065 while (defines != NULL) {
5066 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
5067 ret = -1;
5068 defines = defines->next;
5069 }
5070 return(ret);
5071}
5072
5073/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005074 * xmlRelaxNGValidateTryPermutation:
5075 * @ctxt: a Relax-NG validation context
5076 * @groups: the array of groups
5077 * @nbgroups: the number of groups in the array
5078 * @array: the permutation to try
5079 * @len: the size of the set
5080 *
5081 * Try to validate a permutation for the group of definitions.
5082 *
5083 * Returns 0 if the validation succeeded or an error code.
5084 */
5085static int
5086xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
5087 xmlRelaxNGDefinePtr rule,
5088 xmlNodePtr *array, int len) {
5089 int i, ret;
5090
5091 if (len > 0) {
5092 /*
5093 * One only need the next pointer set-up to do the validation
5094 */
5095 for (i = 0;i < (len - 1);i++)
5096 array[i]->next = array[i + 1];
5097 array[i]->next = NULL;
5098
5099 /*
5100 * Now try to validate the sequence
5101 */
5102 ctxt->state->seq = array[0];
5103 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5104 } else {
5105 ctxt->state->seq = NULL;
5106 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
5107 }
5108
5109 /*
5110 * the sequence must be fully consumed
5111 */
5112 if (ctxt->state->seq != NULL)
5113 return(-1);
5114
5115 return(ret);
5116}
5117
5118/**
5119 * xmlRelaxNGValidateWalkPermutations:
5120 * @ctxt: a Relax-NG validation context
5121 * @groups: the array of groups
5122 * @nbgroups: the number of groups in the array
5123 * @nodes: the set of nodes
5124 * @array: the current state of the parmutation
5125 * @len: the size of the set
5126 * @level: a pointer to the level variable
5127 * @k: the index in the array to fill
5128 *
5129 * Validate a set of nodes for a groups of definitions, will try the
5130 * full set of permutations
5131 *
5132 * Returns 0 if the validation succeeded or an error code.
5133 */
5134static int
5135xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
5136 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
5137 xmlNodePtr *array, int len,
5138 int *level, int k) {
5139 int i, ret;
5140
5141 if ((k >= 0) && (k < len))
5142 array[k] = nodes[*level];
5143 *level = *level + 1;
5144 if (*level == len) {
5145 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
5146 if (ret == 0)
5147 return(0);
5148 } else {
5149 for (i = 0;i < len;i++) {
5150 if (array[i] == NULL) {
5151 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
5152 nodes, array, len, level, i);
5153 if (ret == 0)
5154 return(0);
5155 }
5156 }
5157 }
5158 *level = *level - 1;
5159 array[k] = NULL;
5160 return(-1);
5161}
5162
5163/**
5164 * xmlRelaxNGNodeMatchesList:
5165 * @node: the node
5166 * @list: a NULL terminated array of definitions
5167 *
5168 * Check if a node can be matched by one of the definitions
5169 *
5170 * Returns 1 if matches 0 otherwise
5171 */
5172static int
5173xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
5174 xmlRelaxNGDefinePtr cur;
5175 int i = 0;
5176
5177 if ((node == NULL) || (list == NULL))
5178 return(0);
5179
5180 cur = list[i++];
5181 while (cur != NULL) {
5182 if ((node->type == XML_ELEMENT_NODE) &&
5183 (cur->type == XML_RELAXNG_ELEMENT)) {
5184 if (cur->name == NULL) {
5185 if ((node->ns != NULL) &&
5186 (xmlStrEqual(node->ns->href, cur->ns)))
5187 return(1);
5188 } else if (xmlStrEqual(cur->name, node->name)) {
5189 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
5190 if (node->ns == NULL)
5191 return(1);
5192 } else {
5193 if ((node->ns != NULL) &&
5194 (xmlStrEqual(node->ns->href, cur->ns)))
5195 return(1);
5196 }
5197 }
5198 } else if ((node->type == XML_TEXT_NODE) &&
5199 (cur->type == XML_RELAXNG_TEXT)) {
5200 return(1);
5201 }
5202 cur = list[i++];
5203 }
5204 return(0);
5205}
5206
5207/**
5208 * xmlRelaxNGValidatePartGroup:
5209 * @ctxt: a Relax-NG validation context
5210 * @groups: the array of groups
5211 * @nbgroups: the number of groups in the array
5212 * @nodes: the set of nodes
5213 * @len: the size of the set of nodes
5214 *
5215 * Validate a set of nodes for a groups of definitions
5216 *
5217 * Returns 0 if the validation succeeded or an error code.
5218 */
5219static int
5220xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
5221 xmlRelaxNGInterleaveGroupPtr *groups,
5222 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005223 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005224 xmlNodePtr *array = NULL, *list, oldseq;
5225 xmlRelaxNGInterleaveGroupPtr group;
5226
5227 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5228 if (list == NULL) {
5229 return(-1);
5230 }
5231 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
5232 if (array == NULL) {
5233 xmlFree(list);
5234 return(-1);
5235 }
5236 memset(array, 0, len * sizeof(xmlNodePtr));
5237
5238 /*
5239 * Partition the elements and validate the subsets.
5240 */
5241 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005242 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005243 for (i = 0;i < nbgroups;i++) {
5244 group = groups[i];
5245 if (group == NULL)
5246 continue;
5247 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00005248 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005249 for (j = 0;j < len;j++) {
5250 if (nodes[j] == NULL)
5251 continue;
5252 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
5253 list[k++] = nodes[j];
5254 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005255 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005256 }
5257 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005258 if (top_j > max_j)
5259 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005260 ctxt->state->seq = oldseq;
5261 if (k > 1) {
5262 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00005263 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005264 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
5265 list, array, k, &level, -1);
5266 } else {
5267 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
5268 }
5269 if (ret != 0) {
5270 ctxt->state->seq = oldseq;
5271 break;
5272 }
5273 }
5274
Daniel Veillard231d7912003-02-09 14:22:17 +00005275 for (j = 0;j < max_j;j++) {
5276 if (nodes[j] != NULL) {
5277 TODO /* problem, one of the nodes didn't got a match */
5278 }
5279 }
5280 if (ret == 0) {
5281 if (max_j + 1 < len)
5282 ctxt->state->seq = nodes[max_j + 1];
5283 else
5284 ctxt->state->seq = NULL;
5285 }
5286
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005287 xmlFree(list);
5288 xmlFree(array);
5289 return(ret);
5290}
5291
5292/**
5293 * xmlRelaxNGValidateInterleave:
5294 * @ctxt: a Relax-NG validation context
5295 * @define: the definition to verify
5296 *
5297 * Validate an interleave definition for a node.
5298 *
5299 * Returns 0 if the validation succeeded or an error code.
5300 */
5301static int
5302xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
5303 xmlRelaxNGDefinePtr define) {
5304 int ret = 0, nbchildren, nbtot, i, j;
5305 xmlRelaxNGPartitionPtr partitions;
5306 xmlNodePtr *children = NULL;
5307 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00005308 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005309
5310 if (define->data != NULL) {
5311 partitions = (xmlRelaxNGPartitionPtr) define->data;
5312 } else {
5313 VALID_CTXT();
5314 VALID_ERROR("Internal: interleave block has no data\n");
5315 return(-1);
5316 }
5317
5318 /*
5319 * Build the sequence of child and an array preserving the children
5320 * initial order.
5321 */
5322 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00005323 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005324 nbchildren = 0;
5325 nbtot = 0;
5326 while (cur != NULL) {
5327 if ((cur->type == XML_COMMENT_NODE) ||
5328 (cur->type == XML_PI_NODE) ||
5329 ((cur->type == XML_TEXT_NODE) &&
5330 (IS_BLANK_NODE(cur)))) {
5331 nbtot++;
5332 } else {
5333 nbchildren++;
5334 nbtot++;
5335 }
5336 cur = cur->next;
5337 }
5338 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
5339 if (children == NULL)
5340 goto error;
5341 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
5342 if (order == NULL)
5343 goto error;
5344 cur = ctxt->state->seq;
5345 i = 0;
5346 j = 0;
5347 while (cur != NULL) {
5348 if ((cur->type == XML_COMMENT_NODE) ||
5349 (cur->type == XML_PI_NODE) ||
5350 ((cur->type == XML_TEXT_NODE) &&
5351 (IS_BLANK_NODE(cur)))) {
5352 order[j++] = cur;
5353 } else {
5354 order[j++] = cur;
5355 children[i++] = cur;
5356 }
5357 cur = cur->next;
5358 }
5359
5360 /* TODO: retry with a maller set of child if there is a next... */
5361 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
5362 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00005363 if (ret != 0)
5364 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005365
5366 /*
5367 * Cleanup: rebuid the child sequence and free the structure
5368 */
5369 if (order != NULL) {
5370 for (i = 0;i < nbtot;i++) {
5371 if (i == 0)
5372 order[i]->prev = NULL;
5373 else
5374 order[i]->prev = order[i - 1];
5375 if (i == nbtot - 1)
5376 order[i]->next = NULL;
5377 else
5378 order[i]->next = order[i + 1];
5379 }
5380 xmlFree(order);
5381 }
5382 if (children != NULL)
5383 xmlFree(children);
5384
5385 return(ret);
5386
5387error:
5388 if (order != NULL) {
5389 for (i = 0;i < nbtot;i++) {
5390 if (i == 0)
5391 order[i]->prev = NULL;
5392 else
5393 order[i]->prev = order[i - 1];
5394 if (i == nbtot - 1)
5395 order[i]->next = NULL;
5396 else
5397 order[i]->next = order[i + 1];
5398 }
5399 xmlFree(order);
5400 }
5401 if (children != NULL)
5402 xmlFree(children);
5403 return(-1);
5404}
5405
5406/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005407 * xmlRelaxNGValidateElementContent:
5408 * @ctxt: a Relax-NG validation context
5409 * @define: the list of definition to verify
5410 *
5411 * Validate the given node content against the (list) of definitions
5412 *
5413 * Returns 0 if the validation succeeded or an error code.
5414 */
5415static int
5416xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
5417 xmlRelaxNGDefinePtr defines) {
5418 int ret = 0, res;
5419
5420 if (ctxt->state == NULL) {
5421 VALID_CTXT();
5422 VALID_ERROR("Internal: no state\n");
5423 return(-1);
5424 }
5425 while (defines != NULL) {
5426 res = xmlRelaxNGValidateDefinition(ctxt, defines);
5427 if (res < 0)
5428 ret = -1;
5429 defines = defines->next;
5430 }
5431
5432 return(ret);
5433}
5434
5435/**
5436 * xmlRelaxNGValidateDefinition:
5437 * @ctxt: a Relax-NG validation context
5438 * @define: the definition to verify
5439 *
5440 * Validate the current node against the definition
5441 *
5442 * Returns 0 if the validation succeeded or an error code.
5443 */
5444static int
5445xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5446 xmlRelaxNGDefinePtr define) {
5447 xmlNodePtr node;
5448 int ret = 0, i, tmp, oldflags;
5449 xmlRelaxNGValidStatePtr oldstate, state;
5450
5451 if (define == NULL) {
5452 VALID_CTXT();
5453 VALID_ERROR("internal error: define == NULL\n");
5454 return(-1);
5455 }
5456 if (ctxt->state != NULL) {
5457 node = ctxt->state->seq;
5458 } else {
5459 node = NULL;
5460 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005461#ifdef DEBUG
5462 for (i = 0;i < ctxt->depth;i++)
5463 xmlGenericError(xmlGenericErrorContext, " ");
5464 xmlGenericError(xmlGenericErrorContext,
5465 "Start validating %s ", xmlRelaxNGDefName(define));
5466 if (define->name != NULL)
5467 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5468 if ((node != NULL) && (node->name != NULL))
5469 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
5470 else
5471 xmlGenericError(xmlGenericErrorContext, "\n");
5472#endif
5473 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005474 switch (define->type) {
5475 case XML_RELAXNG_EMPTY:
5476 if (node != NULL) {
5477 VALID_CTXT();
5478 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00005479 ret = -1;
5480 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005481 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005482 ret = 0;
5483 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005484 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00005485 ret = -1;
5486 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005487 case XML_RELAXNG_TEXT:
Daniel Veillard231d7912003-02-09 14:22:17 +00005488 if (node == NULL) {
5489 ret = 0;
5490 break;
5491 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005492 while ((node != NULL) &&
5493 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005494 (node->type == XML_COMMENT_NODE) ||
5495 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005496 (node->type == XML_CDATA_SECTION_NODE)))
5497 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005498 if (node == ctxt->state->seq) {
5499 VALID_CTXT();
5500 VALID_ERROR("Expecting text content\n");
5501 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005502 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005503 ctxt->state->seq = node;
5504 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005505 case XML_RELAXNG_ELEMENT:
5506 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00005507 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005508 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00005509 VALID_ERROR("Expecting an element, got empty\n");
5510 ret = -1;
5511 break;
5512 }
5513 if (node->type != XML_ELEMENT_NODE) {
5514 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005515 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00005516 ret = -1;
5517 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005518 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005519 /*
5520 * This node was already validated successfully against
5521 * this definition.
5522 */
5523 if (node->_private == define)
5524 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005525 if (define->name != NULL) {
5526 if (!xmlStrEqual(node->name, define->name)) {
5527 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005528 VALID_ERROR3("Expecting element %s, got %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005529 define->name, node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005530 ret = -1;
5531 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005532 }
5533 }
5534 if ((define->ns != NULL) && (define->ns[0] != 0)) {
5535 if (node->ns == NULL) {
5536 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005537 VALID_ERROR2("Expecting a namespace for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005538 node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005539 ret = -1;
5540 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005541 } else if (!xmlStrEqual(node->ns->href, define->ns)) {
5542 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005543 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005544 node->name, define->ns);
Daniel Veillard231d7912003-02-09 14:22:17 +00005545 ret = -1;
5546 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005547 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005548 } else if (define->name != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005549 if (node->ns != NULL) {
5550 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005551 VALID_ERROR2("Expecting no namespace for element %s\n",
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005552 define->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00005553 ret = -1;
5554 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005555 }
5556 }
5557
5558 state = xmlRelaxNGNewValidState(ctxt, node);
5559 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00005560 ret = -1;
5561 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005562 }
5563
5564 oldstate = ctxt->state;
5565 ctxt->state = state;
5566 if (define->attrs != NULL) {
5567 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00005568 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005569 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005570#ifdef DEBUG
5571 xmlGenericError(xmlGenericErrorContext,
5572 "E: Element %s failed to validate attributes\n",
5573 node->name);
5574#endif
5575 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005576 }
5577 if (define->content != NULL) {
5578 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005579 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005580 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005581#ifdef DEBUG
5582 xmlGenericError(xmlGenericErrorContext,
5583 "E: Element %s failed to validate element content\n",
5584 node->name);
5585#endif
5586 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005587 }
5588 state = ctxt->state;
5589 if (state->seq != NULL) {
5590 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
5591 if (state->seq != NULL) {
5592 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005593 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005594 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005595 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00005596#ifdef DEBUG
5597 xmlGenericError(xmlGenericErrorContext,
5598 "E: Element %s has extra content: %s\n",
5599 node->name, state->seq->name);
5600#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005601 }
5602 }
5603 for (i = 0;i < state->nbAttrs;i++) {
5604 if (state->attrs[i] != NULL) {
5605 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005606 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005607 state->attrs[i]->name, node->name);
5608 ret = -1;
5609 }
5610 }
5611 ctxt->state = oldstate;
5612 xmlRelaxNGFreeValidState(state);
5613 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005614 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
5615 if (ret == 0)
5616 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005617
5618
5619#ifdef DEBUG
5620 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00005621 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00005622 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00005623 if (oldstate == NULL)
5624 xmlGenericError(xmlGenericErrorContext, ": no state\n");
5625 else if (oldstate->seq == NULL)
5626 xmlGenericError(xmlGenericErrorContext, ": done\n");
5627 else if (oldstate->seq->type == XML_ELEMENT_NODE)
5628 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
5629 oldstate->seq->name);
5630 else
5631 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
5632 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005633#endif
5634 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005635 case XML_RELAXNG_OPTIONAL:
5636 oldflags = ctxt->flags;
5637 ctxt->flags |= FLAGS_IGNORABLE;
5638 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5639 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5640 if (ret != 0) {
5641 xmlRelaxNGFreeValidState(ctxt->state);
5642 ctxt->state = oldstate;
5643 ret = 0;
5644 break;
5645 }
5646 xmlRelaxNGFreeValidState(oldstate);
5647 ctxt->flags = oldflags;
5648 ret = 0;
5649 break;
5650 case XML_RELAXNG_ONEORMORE:
5651 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5652 if (ret != 0) {
5653 break;
5654 }
5655 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00005656 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005657 oldflags = ctxt->flags;
5658 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005659 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005660 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5661 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5662 if (ret != 0) {
5663 xmlRelaxNGFreeValidState(ctxt->state);
5664 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005665 break;
5666 }
5667 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005668 }
5669 if (ret == 0) {
5670 /*
5671 * There is no attribute left to be consumed,
5672 * we can check the closure by looking at ctxt->state->seq
5673 */
5674 xmlNodePtr cur, temp;
5675
Daniel Veillard276be4a2003-01-24 01:03:34 +00005676 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005677 temp = NULL;
5678 while ((cur != NULL) && (temp != cur)) {
5679 temp = cur;
5680 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5681 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5682 if (ret != 0) {
5683 xmlRelaxNGFreeValidState(ctxt->state);
5684 ctxt->state = oldstate;
5685 break;
5686 }
5687 xmlRelaxNGFreeValidState(oldstate);
5688 cur = ctxt->state->seq;
5689 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005690 }
5691 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005692 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005693 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005694 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005695 case XML_RELAXNG_CHOICE: {
5696 xmlRelaxNGDefinePtr list = define->content;
5697
5698 oldflags = ctxt->flags;
5699 ctxt->flags |= FLAGS_IGNORABLE;
5700
5701 while (list != NULL) {
5702 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
5703 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5704 if (ret == 0) {
5705 xmlRelaxNGFreeValidState(oldstate);
5706 break;
5707 }
5708 xmlRelaxNGFreeValidState(ctxt->state);
5709 ctxt->state = oldstate;
5710 list = list->next;
5711 }
5712 ctxt->flags = oldflags;
5713 break;
5714 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005715 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005716 case XML_RELAXNG_GROUP: {
5717 xmlRelaxNGDefinePtr list = define->content;
5718
5719 while (list != NULL) {
5720 ret = xmlRelaxNGValidateDefinition(ctxt, list);
5721 if (ret != 0)
5722 break;
5723 list = list->next;
5724 }
5725 break;
5726 }
5727 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005728 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005729 break;
5730 case XML_RELAXNG_ATTRIBUTE:
5731 ret = xmlRelaxNGValidateAttribute(ctxt, define);
5732 break;
5733 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00005734 case XML_RELAXNG_PARENTREF:
5735 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005736 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
5737 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005738 case XML_RELAXNG_DATATYPE: {
5739 xmlChar *content;
5740
5741 content = xmlNodeGetContent(node);
5742 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
5743 if (ret == -1) {
5744 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005745 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005746 } else if (ret == 0) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005747 if (node != NULL)
5748 ctxt->state->seq = node->next;
5749 else
5750 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005751 }
5752 /*
5753 * TODO cover the problems with
5754 * <p>12<!-- comment -->34</p>
5755 * TODO detect full element coverage at compilation time.
5756 */
5757 if ((node != NULL) && (node->next != NULL)) {
5758 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005759 VALID_ERROR2("The data does not cover the full element %s\n",
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005760 node->parent->name);
5761 ret = -1;
5762 }
5763 if (content != NULL)
5764 xmlFree(content);
5765 break;
5766 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00005767 case XML_RELAXNG_VALUE: {
5768 xmlChar *content;
5769 xmlChar *oldvalue;
5770
5771 content = xmlNodeGetContent(node);
5772 oldvalue = ctxt->state->value;
5773 ctxt->state->value = content;
5774 ret = xmlRelaxNGValidateValue(ctxt, define);
5775 ctxt->state->value = oldvalue;
5776 if (ret == -1) {
5777 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00005778 if (define->name != NULL) {
5779 VALID_ERROR2("error validating value %s\n", define->name);
5780 } else {
5781 VALID_ERROR("error validating value\n");
5782 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00005783 } else if (ret == 0) {
5784 ctxt->state->seq = node->next;
5785 }
5786 /*
5787 * TODO cover the problems with
5788 * <p>12<!-- comment -->34</p>
5789 * TODO detect full element coverage at compilation time.
5790 */
5791 if ((node != NULL) && (node->next != NULL)) {
5792 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005793 VALID_ERROR2("The value does not cover the full element %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005794 node->parent->name);
5795 ret = -1;
5796 }
5797 if (content != NULL)
5798 xmlFree(content);
5799 break;
5800 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005801 case XML_RELAXNG_LIST: {
5802 xmlChar *content;
5803 xmlChar *oldvalue, *oldendvalue;
5804 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00005805
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005806 content = xmlNodeGetContent(node);
5807 len = xmlStrlen(content);
5808 oldvalue = ctxt->state->value;
5809 oldendvalue = ctxt->state->endvalue;
5810 ctxt->state->value = content;
5811 ctxt->state->endvalue = content + len;
5812 ret = xmlRelaxNGValidateValue(ctxt, define);
5813 ctxt->state->value = oldvalue;
5814 ctxt->state->endvalue = oldendvalue;
5815 if (ret == -1) {
5816 VALID_CTXT();
5817 VALID_ERROR("internal error validating list\n");
5818 } else if (ret == 0) {
5819 ctxt->state->seq = node->next;
5820 }
5821 /*
5822 * TODO cover the problems with
5823 * <p>12<!-- comment -->34</p>
5824 * TODO detect full element coverage at compilation time.
5825 */
5826 if ((node != NULL) && (node->next != NULL)) {
5827 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005828 VALID_ERROR2("The list does not cover the full element %s\n",
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005829 node->parent->name);
5830 ret = -1;
5831 }
5832 if (content != NULL)
5833 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005834 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005835 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005836 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005837 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005838 TODO
5839 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005840 }
Daniel Veillard231d7912003-02-09 14:22:17 +00005841 ctxt->depth--;
5842#ifdef DEBUG
5843 for (i = 0;i < ctxt->depth;i++)
5844 xmlGenericError(xmlGenericErrorContext, " ");
5845 xmlGenericError(xmlGenericErrorContext,
5846 "Validating %s ", xmlRelaxNGDefName(define));
5847 if (define->name != NULL)
5848 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
5849 if (ret == 0)
5850 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
5851 else
5852 xmlGenericError(xmlGenericErrorContext, "failed\n");
5853#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005854 return(ret);
5855}
5856
5857/**
5858 * xmlRelaxNGValidateDocument:
5859 * @ctxt: a Relax-NG validation context
5860 * @doc: the document
5861 *
5862 * Validate the given document
5863 *
5864 * Returns 0 if the validation succeeded or an error code.
5865 */
5866static int
5867xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5868 int ret;
5869 xmlRelaxNGPtr schema;
5870 xmlRelaxNGGrammarPtr grammar;
5871 xmlRelaxNGValidStatePtr state;
5872
5873 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
5874 return(-1);
5875
5876 schema = ctxt->schema;
5877 grammar = schema->topgrammar;
5878 if (grammar == NULL) {
5879 VALID_CTXT();
5880 VALID_ERROR("No top grammar defined\n");
5881 return(-1);
5882 }
5883 state = xmlRelaxNGNewValidState(ctxt, NULL);
5884 ctxt->state = state;
5885 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
5886 state = ctxt->state;
5887 if ((state != NULL) && (state->seq != NULL)) {
5888 xmlNodePtr node;
5889
5890 node = state->seq;
5891 node = xmlRelaxNGSkipIgnored(ctxt, node);
5892 if (node != NULL) {
5893 VALID_CTXT();
5894 VALID_ERROR("extra data on the document\n");
5895 ret = -1;
5896 }
5897 }
5898 xmlRelaxNGFreeValidState(state);
5899
5900 return(ret);
5901}
5902
5903/************************************************************************
5904 * *
5905 * Validation interfaces *
5906 * *
5907 ************************************************************************/
5908/**
5909 * xmlRelaxNGNewValidCtxt:
5910 * @schema: a precompiled XML RelaxNGs
5911 *
5912 * Create an XML RelaxNGs validation context based on the given schema
5913 *
5914 * Returns the validation context or NULL in case of error
5915 */
5916xmlRelaxNGValidCtxtPtr
5917xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
5918 xmlRelaxNGValidCtxtPtr ret;
5919
5920 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
5921 if (ret == NULL) {
5922 xmlGenericError(xmlGenericErrorContext,
5923 "Failed to allocate new schama validation context\n");
5924 return (NULL);
5925 }
5926 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
5927 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005928 ret->error = xmlGenericError;
5929 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005930 return (ret);
5931}
5932
5933/**
5934 * xmlRelaxNGFreeValidCtxt:
5935 * @ctxt: the schema validation context
5936 *
5937 * Free the resources associated to the schema validation context
5938 */
5939void
5940xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
5941 if (ctxt == NULL)
5942 return;
5943 xmlFree(ctxt);
5944}
5945
5946/**
5947 * xmlRelaxNGSetValidErrors:
5948 * @ctxt: a Relax-NG validation context
5949 * @err: the error function
5950 * @warn: the warning function
5951 * @ctx: the functions context
5952 *
5953 * Set the error and warning callback informations
5954 */
5955void
5956xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
5957 xmlRelaxNGValidityErrorFunc err,
5958 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5959 if (ctxt == NULL)
5960 return;
5961 ctxt->error = err;
5962 ctxt->warning = warn;
5963 ctxt->userData = ctx;
5964}
5965
5966/**
5967 * xmlRelaxNGValidateDoc:
5968 * @ctxt: a Relax-NG validation context
5969 * @doc: a parsed document tree
5970 *
5971 * Validate a document tree in memory.
5972 *
5973 * Returns 0 if the document is valid, a positive error code
5974 * number otherwise and -1 in case of internal or API error.
5975 */
5976int
5977xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
5978 int ret;
5979
5980 if ((ctxt == NULL) || (doc == NULL))
5981 return(-1);
5982
5983 ctxt->doc = doc;
5984
5985 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00005986 /*
5987 * TODO: build error codes
5988 */
5989 if (ret == -1)
5990 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005991 return(ret);
5992}
5993
5994#endif /* LIBXML_SCHEMAS_ENABLED */
5995