blob: b5e50ed089f21404cc3642b91c0a684eda070646 [file] [log] [blame]
Daniel Veillard6eadf632003-01-23 18:29:16 +00001/*
2 * relaxng.c : implementation of the Relax-NG handling and validity checking
3 *
4 * See Copyright for the status of this software.
5 *
6 * Daniel Veillard <veillard@redhat.com>
7 */
8
Daniel Veillardd41f4f42003-01-29 21:07:52 +00009/**
10 * TODO:
Daniel Veillardd41f4f42003-01-29 21:07:52 +000011 * - error reporting
Daniel Veillarde2a5a082003-02-02 14:35:17 +000012 * - simplification of the resulting compiled trees:
13 * - NOT_ALLOWED
14 * - EMPTY
Daniel Veillard1ed7f362003-02-03 10:57:45 +000015 * - handle namespace declarations as attributes.
Daniel Veillardf4b4f982003-02-13 11:02:08 +000016 * - add support for DTD compatibility spec
17 * http://www.oasis-open.org/committees/relax-ng/compatibility-20011203.html
Daniel Veillardd41f4f42003-01-29 21:07:52 +000018 */
19
Daniel Veillard6eadf632003-01-23 18:29:16 +000020#define IN_LIBXML
21#include "libxml.h"
22
23#ifdef LIBXML_SCHEMAS_ENABLED
24
25#include <string.h>
26#include <stdio.h>
27#include <libxml/xmlmemory.h>
28#include <libxml/parser.h>
29#include <libxml/parserInternals.h>
30#include <libxml/hash.h>
31#include <libxml/uri.h>
32
33#include <libxml/relaxng.h>
34
35#include <libxml/xmlschemastypes.h>
36#include <libxml/xmlautomata.h>
37#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000038#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000039
40/*
41 * The Relax-NG namespace
42 */
43static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
44 "http://relaxng.org/ns/structure/1.0";
45
46#define IS_RELAXNG(node, type) \
47 ((node != NULL) && (node->ns != NULL) && \
48 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
49 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
50
51
Daniel Veillard71531f32003-02-05 13:19:53 +000052/* #define DEBUG 1 */ /* very verbose output */
53/* #define DEBUG_CONTENT 1 */
54/* #define DEBUG_TYPE 1 */
55/* #define DEBUG_VALID 1 */
Daniel Veillarde5b110b2003-02-04 14:43:39 +000056/* #define DEBUG_INTERLEAVE 1 */
Daniel Veillard416589a2003-02-17 17:25:42 +000057/* #define DEBUG_LIST 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000058
59#define UNBOUNDED (1 << 30)
60#define TODO \
61 xmlGenericError(xmlGenericErrorContext, \
62 "Unimplemented block at %s:%d\n", \
63 __FILE__, __LINE__);
64
65typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
66typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
67
68typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
69typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
70
Daniel Veillardd41f4f42003-01-29 21:07:52 +000071typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
72typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
73
Daniel Veillarda9d912d2003-02-01 17:43:10 +000074typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
75typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
76
Daniel Veillard6eadf632003-01-23 18:29:16 +000077typedef enum {
78 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
79 XML_RELAXNG_COMBINE_CHOICE, /* choice */
80 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
81} xmlRelaxNGCombine;
82
83typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
84typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
85
86struct _xmlRelaxNGGrammar {
87 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
88 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
89 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
90 xmlRelaxNGDefinePtr start; /* <start> content */
91 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000092 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000093 xmlHashTablePtr defs; /* define* */
94 xmlHashTablePtr refs; /* references */
95};
96
97
Daniel Veillard6eadf632003-01-23 18:29:16 +000098typedef enum {
Daniel Veillard77648bb2003-02-20 15:03:22 +000099 XML_RELAXNG_NOOP = -1, /* a no operation from simplification */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000100 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
101 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +0000102 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000103 XML_RELAXNG_TEXT, /* textual content */
104 XML_RELAXNG_ELEMENT, /* an element */
105 XML_RELAXNG_DATATYPE, /* extenal data type definition */
Daniel Veillard8fe98712003-02-19 00:19:14 +0000106 XML_RELAXNG_PARAM, /* extenal data type parameter */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000107 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
108 XML_RELAXNG_LIST, /* a list of patterns */
109 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
110 XML_RELAXNG_DEF, /* a definition */
111 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000112 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000113 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000114 XML_RELAXNG_OPTIONAL, /* optional patterns */
115 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
116 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
117 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
118 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000119 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
120 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000121} xmlRelaxNGType;
122
123struct _xmlRelaxNGDefine {
124 xmlRelaxNGType type; /* the type of definition */
125 xmlNodePtr node; /* the node in the source */
126 xmlChar *name; /* the element local name if present */
127 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000128 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000129 void *data; /* data lib or specific pointer */
Daniel Veillardd4310742003-02-18 21:12:46 +0000130 int depth; /* used for the cycle detection */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000131 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000132 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000133 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
134 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000135 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000136 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
137};
138
139/**
140 * _xmlRelaxNG:
141 *
142 * A RelaxNGs definition
143 */
144struct _xmlRelaxNG {
145 xmlRelaxNGGrammarPtr topgrammar;
146 xmlDocPtr doc;
147
148 xmlHashTablePtr defs; /* define */
149 xmlHashTablePtr refs; /* references */
Daniel Veillard419a7682003-02-03 23:22:49 +0000150 xmlHashTablePtr documents; /* all the documents loaded */
151 xmlHashTablePtr includes; /* all the includes loaded */
152 int defNr; /* number of defines used */
153 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000154 void *_private; /* unused by the library for users or bindings */
155};
156
157typedef enum {
158 XML_RELAXNG_ERR_OK = 0,
159 XML_RELAXNG_ERR_NOROOT = 1,
160 XML_RELAXNG_ERR_
161} xmlRelaxNGValidError;
162
Daniel Veillard77648bb2003-02-20 15:03:22 +0000163#define XML_RELAXNG_IN_ATTRIBUTE (1 << 0)
164#define XML_RELAXNG_IN_ONEORMORE (1 << 1)
165#define XML_RELAXNG_IN_LIST (1 << 2)
166#define XML_RELAXNG_IN_DATAEXCEPT (1 << 3)
167#define XML_RELAXNG_IN_START (1 << 4)
168#define XML_RELAXNG_IN_OOMGROUP (1 << 5)
169#define XML_RELAXNG_IN_OOMINTERLEAVE (1 << 6)
170#define XML_RELAXNG_IN_EXTERNALREF (1 << 7)
Daniel Veillard6eadf632003-01-23 18:29:16 +0000171
172struct _xmlRelaxNGParserCtxt {
173 void *userData; /* user specific data block */
174 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
175 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
176 xmlRelaxNGValidError err;
177
178 xmlRelaxNGPtr schema; /* The schema in use */
179 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000180 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000181 int flags; /* parser flags */
182 int nbErrors; /* number of errors at parse time */
183 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000184 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000185 xmlRelaxNGDefinePtr def; /* the current define */
186
187 int nbInterleaves;
188 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000189
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000190 xmlHashTablePtr documents; /* all the documents loaded */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000191 xmlHashTablePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000192 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000193 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000194
Daniel Veillard419a7682003-02-03 23:22:49 +0000195 int defNr; /* number of defines used */
196 int defMax; /* number of defines aloocated */
197 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
198
Daniel Veillard6eadf632003-01-23 18:29:16 +0000199 const char *buffer;
200 int size;
201
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000202 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000203 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000204 int docNr; /* Depth of the parsing stack */
205 int docMax; /* Max depth of the parsing stack */
206 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000207
208 /* the include stack */
Daniel Veillardd4310742003-02-18 21:12:46 +0000209 xmlRelaxNGIncludePtr inc; /* Current parsed include */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000210 int incNr; /* Depth of the include parsing stack */
211 int incMax; /* Max depth of the parsing stack */
212 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000213};
214
215#define FLAGS_IGNORABLE 1
216#define FLAGS_NEGATIVE 2
217
218/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000219 * xmlRelaxNGInterleaveGroup:
220 *
221 * A RelaxNGs partition set associated to lists of definitions
222 */
223typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
224typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
225struct _xmlRelaxNGInterleaveGroup {
226 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
227 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
228};
229
230/**
231 * xmlRelaxNGPartitions:
232 *
233 * A RelaxNGs partition associated to an interleave group
234 */
235typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
236typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
237struct _xmlRelaxNGPartition {
238 int nbgroups; /* number of groups in the partitions */
239 xmlRelaxNGInterleaveGroupPtr *groups;
240};
241
242/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000243 * xmlRelaxNGValidState:
244 *
245 * A RelaxNGs validation state
246 */
247#define MAX_ATTR 20
248typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
249typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
250struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000251 xmlNodePtr node; /* the current node */
252 xmlNodePtr seq; /* the sequence of children left to validate */
253 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000254 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000255 xmlChar *value; /* the value when operating on string */
256 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000257 xmlAttrPtr attrs[1]; /* the array of attributes */
258};
259
260/**
261 * xmlRelaxNGValidCtxt:
262 *
263 * A RelaxNGs validation context
264 */
265
266struct _xmlRelaxNGValidCtxt {
267 void *userData; /* user specific data block */
268 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
269 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
270
271 xmlRelaxNGPtr schema; /* The schema in use */
272 xmlDocPtr doc; /* the document being validated */
273 xmlRelaxNGValidStatePtr state; /* the current validation state */
274 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000275 int depth; /* validation depth */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000276};
277
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000278/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000279 * xmlRelaxNGInclude:
280 *
281 * Structure associated to a RelaxNGs document element
282 */
283struct _xmlRelaxNGInclude {
284 xmlChar *href; /* the normalized href value */
285 xmlDocPtr doc; /* the associated XML document */
286 xmlRelaxNGDefinePtr content;/* the definitions */
287 xmlRelaxNGPtr schema; /* the schema */
288};
289
290/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000291 * xmlRelaxNGDocument:
292 *
293 * Structure associated to a RelaxNGs document element
294 */
295struct _xmlRelaxNGDocument {
296 xmlChar *href; /* the normalized href value */
297 xmlDocPtr doc; /* the associated XML document */
298 xmlRelaxNGDefinePtr content;/* the definitions */
299 xmlRelaxNGPtr schema; /* the schema */
300};
301
Daniel Veillard6eadf632003-01-23 18:29:16 +0000302/************************************************************************
303 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000304 * Preliminary type checking interfaces *
305 * *
306 ************************************************************************/
307/**
308 * xmlRelaxNGTypeHave:
309 * @data: data needed for the library
310 * @type: the type name
311 * @value: the value to check
312 *
313 * Function provided by a type library to check if a type is exported
314 *
315 * Returns 1 if yes, 0 if no and -1 in case of error.
316 */
317typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
318
319/**
320 * xmlRelaxNGTypeCheck:
321 * @data: data needed for the library
322 * @type: the type name
323 * @value: the value to check
324 *
325 * Function provided by a type library to check if a value match a type
326 *
327 * Returns 1 if yes, 0 if no and -1 in case of error.
328 */
329typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
330 const xmlChar *value);
331
332/**
333 * xmlRelaxNGTypeCompare:
334 * @data: data needed for the library
335 * @type: the type name
336 * @value1: the first value
337 * @value2: the second value
338 *
339 * Function provided by a type library to compare two values accordingly
340 * to a type.
341 *
342 * Returns 1 if yes, 0 if no and -1 in case of error.
343 */
344typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
345 const xmlChar *value1,
346 const xmlChar *value2);
347typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
348typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
349struct _xmlRelaxNGTypeLibrary {
350 const xmlChar *namespace; /* the datatypeLibrary value */
351 void *data; /* data needed for the library */
352 xmlRelaxNGTypeHave have; /* the export function */
353 xmlRelaxNGTypeCheck check; /* the checking function */
354 xmlRelaxNGTypeCompare comp; /* the compare function */
355};
356
357/************************************************************************
358 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000359 * Allocation functions *
360 * *
361 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000362static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
363static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000364static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000365
366/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000367 * xmlRelaxNGFreeDocument:
368 * @docu: a document structure
369 *
370 * Deallocate a RelaxNG document structure.
371 */
372static void
373xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
374{
375 if (docu == NULL)
376 return;
377
378 if (docu->href != NULL)
379 xmlFree(docu->href);
380 if (docu->doc != NULL)
381 xmlFreeDoc(docu->doc);
382 if (docu->schema != NULL)
383 xmlRelaxNGFree(docu->schema);
384 xmlFree(docu);
385}
386
387/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000388 * xmlRelaxNGFreeInclude:
389 * @incl: a include structure
390 *
391 * Deallocate a RelaxNG include structure.
392 */
393static void
394xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
395{
396 if (incl == NULL)
397 return;
398
399 if (incl->href != NULL)
400 xmlFree(incl->href);
401 if (incl->doc != NULL)
402 xmlFreeDoc(incl->doc);
403 if (incl->schema != NULL)
404 xmlRelaxNGFree(incl->schema);
405 xmlFree(incl);
406}
407
408/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000409 * xmlRelaxNGNewRelaxNG:
410 * @ctxt: a Relax-NG validation context (optional)
411 *
412 * Allocate a new RelaxNG structure.
413 *
414 * Returns the newly allocated structure or NULL in case or error
415 */
416static xmlRelaxNGPtr
417xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
418{
419 xmlRelaxNGPtr ret;
420
421 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
422 if (ret == NULL) {
423 if ((ctxt != NULL) && (ctxt->error != NULL))
424 ctxt->error(ctxt->userData, "Out of memory\n");
425 ctxt->nbErrors++;
426 return (NULL);
427 }
428 memset(ret, 0, sizeof(xmlRelaxNG));
429
430 return (ret);
431}
432
433/**
434 * xmlRelaxNGFree:
435 * @schema: a schema structure
436 *
437 * Deallocate a RelaxNG structure.
438 */
439void
440xmlRelaxNGFree(xmlRelaxNGPtr schema)
441{
442 if (schema == NULL)
443 return;
444
Daniel Veillard6eadf632003-01-23 18:29:16 +0000445 if (schema->topgrammar != NULL)
446 xmlRelaxNGFreeGrammar(schema->topgrammar);
447 if (schema->doc != NULL)
448 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000449 if (schema->documents != NULL)
450 xmlHashFree(schema->documents, (xmlHashDeallocator)
451 xmlRelaxNGFreeDocument);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000452 if (schema->includes != NULL)
453 xmlHashFree(schema->includes, (xmlHashDeallocator)
454 xmlRelaxNGFreeInclude);
Daniel Veillard419a7682003-02-03 23:22:49 +0000455 if (schema->defTab != NULL) {
456 int i;
457
458 for (i = 0;i < schema->defNr;i++)
459 xmlRelaxNGFreeDefine(schema->defTab[i]);
460 xmlFree(schema->defTab);
461 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000462
463 xmlFree(schema);
464}
465
466/**
467 * xmlRelaxNGNewGrammar:
468 * @ctxt: a Relax-NG validation context (optional)
469 *
470 * Allocate a new RelaxNG grammar.
471 *
472 * Returns the newly allocated structure or NULL in case or error
473 */
474static xmlRelaxNGGrammarPtr
475xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
476{
477 xmlRelaxNGGrammarPtr ret;
478
479 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
480 if (ret == NULL) {
481 if ((ctxt != NULL) && (ctxt->error != NULL))
482 ctxt->error(ctxt->userData, "Out of memory\n");
483 ctxt->nbErrors++;
484 return (NULL);
485 }
486 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
487
488 return (ret);
489}
490
491/**
492 * xmlRelaxNGFreeGrammar:
493 * @grammar: a grammar structure
494 *
495 * Deallocate a RelaxNG grammar structure.
496 */
497static void
498xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
499{
500 if (grammar == NULL)
501 return;
502
Daniel Veillard419a7682003-02-03 23:22:49 +0000503 if (grammar->next != NULL) {
504 xmlRelaxNGFreeGrammar(grammar->next);
505 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000506 if (grammar->refs != NULL) {
507 xmlHashFree(grammar->refs, NULL);
508 }
509 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000510 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000511 }
512
513 xmlFree(grammar);
514}
515
516/**
517 * xmlRelaxNGNewDefine:
518 * @ctxt: a Relax-NG validation context
519 * @node: the node in the input document.
520 *
521 * Allocate a new RelaxNG define.
522 *
523 * Returns the newly allocated structure or NULL in case or error
524 */
525static xmlRelaxNGDefinePtr
526xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
527{
528 xmlRelaxNGDefinePtr ret;
529
Daniel Veillard419a7682003-02-03 23:22:49 +0000530 if (ctxt->defMax == 0) {
531 ctxt->defMax = 16;
532 ctxt->defNr = 0;
533 ctxt->defTab = (xmlRelaxNGDefinePtr *)
534 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
535 if (ctxt->defTab == 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 } else if (ctxt->defMax <= ctxt->defNr) {
542 xmlRelaxNGDefinePtr *tmp;
543 ctxt->defMax *= 2;
544 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
545 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
546 if (tmp == NULL) {
547 if ((ctxt != NULL) && (ctxt->error != NULL))
548 ctxt->error(ctxt->userData, "Out of memory\n");
549 ctxt->nbErrors++;
550 return (NULL);
551 }
552 ctxt->defTab = tmp;
553 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000554 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
555 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000556 if ((ctxt != NULL) && (ctxt->error != NULL))
557 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000558 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000559 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000560 }
561 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000562 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000563 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000564 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000565 return (ret);
566}
567
568/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000569 * xmlRelaxNGFreePartition:
570 * @partitions: a partition set structure
571 *
572 * Deallocate RelaxNG partition set structures.
573 */
574static void
575xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
576 xmlRelaxNGInterleaveGroupPtr group;
577 int j;
578
579 if (partitions != NULL) {
580 if (partitions->groups != NULL) {
581 for (j = 0;j < partitions->nbgroups;j++) {
582 group = partitions->groups[j];
583 if (group != NULL) {
584 if (group->defs != NULL)
585 xmlFree(group->defs);
586 xmlFree(group);
587 }
588 }
589 xmlFree(partitions->groups);
590 }
591 xmlFree(partitions);
592 }
593}
594/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000595 * xmlRelaxNGFreeDefine:
596 * @define: a define structure
597 *
598 * Deallocate a RelaxNG define structure.
599 */
600static void
601xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
602{
603 if (define == NULL)
604 return;
605
Daniel Veillard419a7682003-02-03 23:22:49 +0000606 if ((define->data != NULL) &&
607 (define->type == XML_RELAXNG_INTERLEAVE))
608 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000609 if (define->name != NULL)
610 xmlFree(define->name);
611 if (define->ns != NULL)
612 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000613 if (define->value != NULL)
614 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000615 xmlFree(define);
616}
617
618/**
619 * xmlRelaxNGNewValidState:
620 * @ctxt: a Relax-NG validation context
621 * @node: the current node or NULL for the document
622 *
623 * Allocate a new RelaxNG validation state
624 *
625 * Returns the newly allocated structure or NULL in case or error
626 */
627static xmlRelaxNGValidStatePtr
628xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
629{
630 xmlRelaxNGValidStatePtr ret;
631 xmlAttrPtr attr;
632 xmlAttrPtr attrs[MAX_ATTR];
633 int nbAttrs = 0;
634 xmlNodePtr root = NULL;
635
636 if (node == NULL) {
637 root = xmlDocGetRootElement(ctxt->doc);
638 if (root == NULL)
639 return(NULL);
640 } else {
641 attr = node->properties;
642 while (attr != NULL) {
643 if (nbAttrs < MAX_ATTR)
644 attrs[nbAttrs++] = attr;
645 else
646 nbAttrs++;
647 attr = attr->next;
648 }
649 }
650
651 if (nbAttrs < MAX_ATTR)
652 attrs[nbAttrs] = NULL;
653 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
654 nbAttrs * sizeof(xmlAttrPtr));
655 if (ret == NULL) {
656 if ((ctxt != NULL) && (ctxt->error != NULL))
657 ctxt->error(ctxt->userData, "Out of memory\n");
658 return (NULL);
659 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000660 ret->value = NULL;
661 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000662 if (node == NULL) {
663 ret->node = (xmlNodePtr) ctxt->doc;
664 ret->seq = root;
665 ret->nbAttrs = 0;
666 } else {
667 ret->node = node;
668 ret->seq = node->children;
669 ret->nbAttrs = nbAttrs;
670 if (nbAttrs > 0) {
671 if (nbAttrs < MAX_ATTR) {
672 memcpy(&(ret->attrs[0]), attrs,
673 sizeof(xmlAttrPtr) * (nbAttrs + 1));
674 } else {
675 attr = node->properties;
676 nbAttrs = 0;
677 while (attr != NULL) {
678 ret->attrs[nbAttrs++] = attr;
679 attr = attr->next;
680 }
681 ret->attrs[nbAttrs] = NULL;
682 }
683 }
684 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000685 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000686 return (ret);
687}
688
689/**
690 * xmlRelaxNGCopyValidState:
691 * @ctxt: a Relax-NG validation context
692 * @state: a validation state
693 *
694 * Copy the validation state
695 *
696 * Returns the newly allocated structure or NULL in case or error
697 */
698static xmlRelaxNGValidStatePtr
699xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
700 xmlRelaxNGValidStatePtr state)
701{
702 xmlRelaxNGValidStatePtr ret;
703 unsigned int size;
704
705 if (state == NULL)
706 return(NULL);
707
708 size = sizeof(xmlRelaxNGValidState) +
709 state->nbAttrs * sizeof(xmlAttrPtr);
710 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
711 if (ret == NULL) {
712 if ((ctxt != NULL) && (ctxt->error != NULL))
713 ctxt->error(ctxt->userData, "Out of memory\n");
714 return (NULL);
715 }
716 memcpy(ret, state, size);
717 return(ret);
718}
719
720/**
Daniel Veillardce14fa52003-02-19 17:32:48 +0000721 * xmlRelaxNGEqualValidState:
722 * @ctxt: a Relax-NG validation context
723 * @state1: a validation state
724 * @state2: a validation state
725 *
726 * Compare the validation states for equality
727 *
728 * Returns 1 if equald, 0 otherwise
729 */
730static int
731xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
732 xmlRelaxNGValidStatePtr state1,
733 xmlRelaxNGValidStatePtr state2)
734{
735 int i;
736
737 if ((state1 == NULL) || (state2 == NULL))
738 return(0);
739 if (state1 == state2)
740 return(1);
741 if (state1->node != state2->node)
742 return(0);
743 if (state1->seq != state2->seq)
744 return(0);
745 if (state1->nbAttrLeft != state2->nbAttrLeft)
746 return(0);
747 if (state1->nbAttrs != state2->nbAttrs)
748 return(0);
749 if (state1->endvalue != state2->endvalue)
750 return(0);
751 if ((state1->value != state2->value) &&
752 (!xmlStrEqual(state1->value, state2->value)))
753 return(0);
754 for (i = 0;i < state1->nbAttrs;i++) {
755 if (state1->attrs[i] != state2->attrs[i])
756 return(0);
757 }
758 return(1);
759}
760
761/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000762 * xmlRelaxNGFreeValidState:
763 * @state: a validation state structure
764 *
765 * Deallocate a RelaxNG validation state structure.
766 */
767static void
768xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
769{
770 if (state == NULL)
771 return;
772
773 xmlFree(state);
774}
775
776/************************************************************************
777 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000778 * Document functions *
779 * *
780 ************************************************************************/
781static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
782 xmlDocPtr doc);
783
784/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000785 * xmlRelaxNGIncludePush:
786 * @ctxt: the parser context
787 * @value: the element doc
788 *
789 * Pushes a new include on top of the include stack
790 *
791 * Returns 0 in case of error, the index in the stack otherwise
792 */
793static int
794xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
795 xmlRelaxNGIncludePtr value)
796{
797 if (ctxt->incTab == NULL) {
798 ctxt->incMax = 4;
799 ctxt->incNr = 0;
800 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
801 ctxt->incMax * sizeof(ctxt->incTab[0]));
802 if (ctxt->incTab == NULL) {
803 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
804 return (0);
805 }
806 }
807 if (ctxt->incNr >= ctxt->incMax) {
808 ctxt->incMax *= 2;
809 ctxt->incTab =
810 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
811 ctxt->incMax *
812 sizeof(ctxt->incTab[0]));
813 if (ctxt->incTab == NULL) {
814 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
815 return (0);
816 }
817 }
818 ctxt->incTab[ctxt->incNr] = value;
819 ctxt->inc = value;
820 return (ctxt->incNr++);
821}
822
823/**
824 * xmlRelaxNGIncludePop:
825 * @ctxt: the parser context
826 *
827 * Pops the top include from the include stack
828 *
829 * Returns the include just removed
830 */
831static xmlRelaxNGIncludePtr
832xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
833{
834 xmlRelaxNGIncludePtr ret;
835
836 if (ctxt->incNr <= 0)
837 return (0);
838 ctxt->incNr--;
839 if (ctxt->incNr > 0)
840 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
841 else
842 ctxt->inc = NULL;
843 ret = ctxt->incTab[ctxt->incNr];
844 ctxt->incTab[ctxt->incNr] = 0;
845 return (ret);
846}
847
848/**
849 * xmlRelaxNGLoadInclude:
850 * @ctxt: the parser context
851 * @URL: the normalized URL
852 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +0000853 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000854 *
855 * First lookup if the document is already loaded into the parser context,
856 * check against recursion. If not found the resource is loaded and
857 * the content is preprocessed before being returned back to the caller.
858 *
859 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
860 */
861static xmlRelaxNGIncludePtr
862xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +0000863 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000864 xmlRelaxNGIncludePtr ret = NULL;
865 xmlDocPtr doc;
866 int i;
867 xmlNodePtr root, tmp, tmp2, cur;
868
869 /*
870 * check against recursion in the stack
871 */
872 for (i = 0;i < ctxt->incNr;i++) {
873 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
874 if (ctxt->error != NULL)
875 ctxt->error(ctxt->userData,
876 "Detected an externalRef recursion for %s\n",
877 URL);
878 ctxt->nbErrors++;
879 return(NULL);
880 }
881 }
882
883 /*
884 * Lookup in the hash table
885 */
886 if (ctxt->includes == NULL) {
887 ctxt->includes = xmlHashCreate(10);
888 if (ctxt->includes == NULL) {
889 if (ctxt->error != NULL)
890 ctxt->error(ctxt->userData,
891 "Failed to allocate hash table for document\n");
892 ctxt->nbErrors++;
893 return(NULL);
894 }
895 } else {
Daniel Veillard416589a2003-02-17 17:25:42 +0000896 if (ns == NULL)
897 ret = xmlHashLookup2(ctxt->includes, BAD_CAST "", URL);
898 else
899 ret = xmlHashLookup2(ctxt->includes, ns, URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000900 if (ret != NULL)
901 return(ret);
902 }
903
904
905 /*
906 * load the document
907 */
908 doc = xmlParseFile((const char *) URL);
909 if (doc == NULL) {
910 if (ctxt->error != NULL)
911 ctxt->error(ctxt->userData,
912 "xmlRelaxNG: could not load %s\n", URL);
913 ctxt->nbErrors++;
914 return (NULL);
915 }
916
917 /*
918 * Allocate the document structures and register it first.
919 */
920 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
921 if (ret == NULL) {
922 if (ctxt->error != NULL)
923 ctxt->error(ctxt->userData,
924 "xmlRelaxNG: allocate memory for doc %s\n", URL);
925 ctxt->nbErrors++;
926 xmlFreeDoc(doc);
927 return (NULL);
928 }
929 memset(ret, 0, sizeof(xmlRelaxNGInclude));
930 ret->doc = doc;
931 ret->href = xmlStrdup(URL);
932
933 /*
Daniel Veillard416589a2003-02-17 17:25:42 +0000934 * transmit the ns if needed
935 */
936 if (ns != NULL) {
937 root = xmlDocGetRootElement(doc);
938 if (root != NULL) {
939 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
940 xmlSetProp(root, BAD_CAST"ns", ns);
941 }
942 }
943 }
944
945 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000946 * push it on the stack and register it in the hash table
947 */
Daniel Veillard416589a2003-02-17 17:25:42 +0000948 if (ns == NULL)
949 xmlHashAddEntry2(ctxt->includes, BAD_CAST "", URL, ret);
950 else
951 xmlHashAddEntry2(ctxt->includes, ns, URL, ret);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000952 xmlRelaxNGIncludePush(ctxt, ret);
953
954 /*
955 * Some preprocessing of the document content, this include recursing
956 * in the include stack.
957 */
958 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
959 if (doc == NULL) {
960 /* xmlFreeDoc(ctxt->include); */
961 ctxt->inc = NULL;
962 return(NULL);
963 }
964
965 /*
966 * Pop up the include from the stack
967 */
968 xmlRelaxNGIncludePop(ctxt);
969
970 /*
971 * Check that the top element is a grammar
972 */
973 root = xmlDocGetRootElement(doc);
974 if (root == NULL) {
975 if (ctxt->error != NULL)
976 ctxt->error(ctxt->userData,
977 "xmlRelaxNG: included document is empty %s\n", URL);
978 ctxt->nbErrors++;
979 xmlFreeDoc(doc);
980 return (NULL);
981 }
982 if (!IS_RELAXNG(root, "grammar")) {
983 if (ctxt->error != NULL)
984 ctxt->error(ctxt->userData,
985 "xmlRelaxNG: included document %s root is not a grammar\n",
986 URL);
987 ctxt->nbErrors++;
988 xmlFreeDoc(doc);
989 return (NULL);
990 }
991
992 /*
993 * Elimination of redefined rules in the include.
994 */
995 cur = node->children;
996 while (cur != NULL) {
997 if (IS_RELAXNG(cur, "start")) {
998 int found = 0;
999
1000 tmp = root->children;
1001 while (tmp != NULL) {
1002 tmp2 = tmp->next;
1003 if (IS_RELAXNG(tmp, "start")) {
1004 found = 1;
1005 xmlUnlinkNode(tmp);
1006 xmlFreeNode(tmp);
1007 }
1008 tmp = tmp2;
1009 }
1010 if (!found) {
1011 if (ctxt->error != NULL)
1012 ctxt->error(ctxt->userData,
1013 "xmlRelaxNG: include %s has a start but not the included grammar\n",
1014 URL);
1015 ctxt->nbErrors++;
1016 }
1017 } else if (IS_RELAXNG(cur, "define")) {
1018 xmlChar *name, *name2;
1019
1020 name = xmlGetProp(cur, BAD_CAST "name");
1021 if (name == NULL) {
1022 if (ctxt->error != NULL)
1023 ctxt->error(ctxt->userData,
1024 "xmlRelaxNG: include %s has define without name\n",
1025 URL);
1026 ctxt->nbErrors++;
1027 } else {
1028 int found = 0;
1029
Daniel Veillardd2298792003-02-14 16:54:11 +00001030 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001031 tmp = root->children;
1032 while (tmp != NULL) {
1033 tmp2 = tmp->next;
1034 if (IS_RELAXNG(tmp, "define")) {
1035 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +00001036 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001037 if (name2 != NULL) {
1038 if (xmlStrEqual(name, name2)) {
1039 found = 1;
1040 xmlUnlinkNode(tmp);
1041 xmlFreeNode(tmp);
1042 }
1043 xmlFree(name2);
1044 }
1045 }
1046 tmp = tmp2;
1047 }
1048 if (!found) {
1049 if (ctxt->error != NULL)
1050 ctxt->error(ctxt->userData,
1051 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1052 URL, name);
1053 ctxt->nbErrors++;
1054 }
1055 xmlFree(name);
1056 }
1057 }
1058 cur = cur->next;
1059 }
1060
1061
1062 return(ret);
1063}
1064
1065/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001066 * xmlRelaxNGDocumentPush:
1067 * @ctxt: the parser context
1068 * @value: the element doc
1069 *
1070 * Pushes a new doc on top of the doc stack
1071 *
1072 * Returns 0 in case of error, the index in the stack otherwise
1073 */
1074static int
1075xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1076 xmlRelaxNGDocumentPtr value)
1077{
1078 if (ctxt->docTab == NULL) {
1079 ctxt->docMax = 4;
1080 ctxt->docNr = 0;
1081 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1082 ctxt->docMax * sizeof(ctxt->docTab[0]));
1083 if (ctxt->docTab == NULL) {
1084 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1085 return (0);
1086 }
1087 }
1088 if (ctxt->docNr >= ctxt->docMax) {
1089 ctxt->docMax *= 2;
1090 ctxt->docTab =
1091 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1092 ctxt->docMax *
1093 sizeof(ctxt->docTab[0]));
1094 if (ctxt->docTab == NULL) {
1095 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1096 return (0);
1097 }
1098 }
1099 ctxt->docTab[ctxt->docNr] = value;
1100 ctxt->doc = value;
1101 return (ctxt->docNr++);
1102}
1103
1104/**
1105 * xmlRelaxNGDocumentPop:
1106 * @ctxt: the parser context
1107 *
1108 * Pops the top doc from the doc stack
1109 *
1110 * Returns the doc just removed
1111 */
1112static xmlRelaxNGDocumentPtr
1113xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1114{
1115 xmlRelaxNGDocumentPtr ret;
1116
1117 if (ctxt->docNr <= 0)
1118 return (0);
1119 ctxt->docNr--;
1120 if (ctxt->docNr > 0)
1121 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1122 else
1123 ctxt->doc = NULL;
1124 ret = ctxt->docTab[ctxt->docNr];
1125 ctxt->docTab[ctxt->docNr] = 0;
1126 return (ret);
1127}
1128
1129/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001130 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001131 * @ctxt: the parser context
1132 * @URL: the normalized URL
1133 * @ns: the inherited ns if any
1134 *
1135 * First lookup if the document is already loaded into the parser context,
1136 * check against recursion. If not found the resource is loaded and
1137 * the content is preprocessed before being returned back to the caller.
1138 *
1139 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1140 */
1141static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001142xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001143 const xmlChar *ns) {
1144 xmlRelaxNGDocumentPtr ret = NULL;
1145 xmlDocPtr doc;
1146 xmlNodePtr root;
1147 int i;
1148
1149 /*
1150 * check against recursion in the stack
1151 */
1152 for (i = 0;i < ctxt->docNr;i++) {
1153 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1154 if (ctxt->error != NULL)
1155 ctxt->error(ctxt->userData,
1156 "Detected an externalRef recursion for %s\n",
1157 URL);
1158 ctxt->nbErrors++;
1159 return(NULL);
1160 }
1161 }
1162
1163 /*
1164 * Lookup in the hash table
1165 */
1166 if (ctxt->documents == NULL) {
1167 ctxt->documents = xmlHashCreate(10);
1168 if (ctxt->documents == NULL) {
1169 if (ctxt->error != NULL)
1170 ctxt->error(ctxt->userData,
1171 "Failed to allocate hash table for document\n");
1172 ctxt->nbErrors++;
1173 return(NULL);
1174 }
1175 } else {
1176 if (ns == NULL)
1177 ret = xmlHashLookup2(ctxt->documents, BAD_CAST "", URL);
1178 else
1179 ret = xmlHashLookup2(ctxt->documents, ns, URL);
1180 if (ret != NULL)
1181 return(ret);
1182 }
1183
1184
1185 /*
1186 * load the document
1187 */
1188 doc = xmlParseFile((const char *) URL);
1189 if (doc == NULL) {
1190 if (ctxt->error != NULL)
1191 ctxt->error(ctxt->userData,
1192 "xmlRelaxNG: could not load %s\n", URL);
1193 ctxt->nbErrors++;
1194 return (NULL);
1195 }
1196
1197 /*
1198 * Allocate the document structures and register it first.
1199 */
1200 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1201 if (ret == NULL) {
1202 if (ctxt->error != NULL)
1203 ctxt->error(ctxt->userData,
1204 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1205 ctxt->nbErrors++;
1206 xmlFreeDoc(doc);
1207 return (NULL);
1208 }
1209 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1210 ret->doc = doc;
1211 ret->href = xmlStrdup(URL);
1212
1213 /*
1214 * transmit the ns if needed
1215 */
1216 if (ns != NULL) {
1217 root = xmlDocGetRootElement(doc);
1218 if (root != NULL) {
1219 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1220 xmlSetProp(root, BAD_CAST"ns", ns);
1221 }
1222 }
1223 }
1224
1225 /*
1226 * push it on the stack and register it in the hash table
1227 */
1228 if (ns == NULL)
1229 xmlHashAddEntry2(ctxt->documents, BAD_CAST "", URL, ret);
1230 else
1231 xmlHashAddEntry2(ctxt->documents, ns, URL, ret);
1232 xmlRelaxNGDocumentPush(ctxt, ret);
1233
1234 /*
1235 * Some preprocessing of the document content
1236 */
1237 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1238 if (doc == NULL) {
1239 xmlFreeDoc(ctxt->document);
1240 ctxt->doc = NULL;
1241 return(NULL);
1242 }
1243
1244 xmlRelaxNGDocumentPop(ctxt);
1245
1246 return(ret);
1247}
1248
1249/************************************************************************
1250 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001251 * Error functions *
1252 * *
1253 ************************************************************************/
1254
1255#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001256 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1257 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001258 "error detected at %s:%d\n", \
1259 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001260
1261#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001262 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001263 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1264#define VALID_ERROR2(a, b) \
1265 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1266 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1267#define VALID_ERROR3(a, b, c) \
1268 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1269 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001270
Daniel Veillardd2298792003-02-14 16:54:11 +00001271#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001272static const char *
1273xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1274 if (def == NULL)
1275 return("none");
1276 switch(def->type) {
1277 case XML_RELAXNG_EMPTY: return("empty");
1278 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1279 case XML_RELAXNG_EXCEPT: return("except");
1280 case XML_RELAXNG_TEXT: return("text");
1281 case XML_RELAXNG_ELEMENT: return("element");
1282 case XML_RELAXNG_DATATYPE: return("datatype");
1283 case XML_RELAXNG_VALUE: return("value");
1284 case XML_RELAXNG_LIST: return("list");
1285 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1286 case XML_RELAXNG_DEF: return("def");
1287 case XML_RELAXNG_REF: return("ref");
1288 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1289 case XML_RELAXNG_PARENTREF: return("parentRef");
1290 case XML_RELAXNG_OPTIONAL: return("optional");
1291 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1292 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1293 case XML_RELAXNG_CHOICE: return("choice");
1294 case XML_RELAXNG_GROUP: return("group");
1295 case XML_RELAXNG_INTERLEAVE: return("interleave");
1296 case XML_RELAXNG_START: return("start");
1297 }
1298 return("unknown");
1299}
Daniel Veillardd2298792003-02-14 16:54:11 +00001300#endif
1301
Daniel Veillard6eadf632003-01-23 18:29:16 +00001302#if 0
1303/**
1304 * xmlRelaxNGErrorContext:
1305 * @ctxt: the parsing context
1306 * @schema: the schema being built
1307 * @node: the node being processed
1308 * @child: the child being processed
1309 *
1310 * Dump a RelaxNGType structure
1311 */
1312static void
1313xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1314 xmlNodePtr node, xmlNodePtr child)
1315{
1316 int line = 0;
1317 const xmlChar *file = NULL;
1318 const xmlChar *name = NULL;
1319 const char *type = "error";
1320
1321 if ((ctxt == NULL) || (ctxt->error == NULL))
1322 return;
1323
1324 if (child != NULL)
1325 node = child;
1326
1327 if (node != NULL) {
1328 if ((node->type == XML_DOCUMENT_NODE) ||
1329 (node->type == XML_HTML_DOCUMENT_NODE)) {
1330 xmlDocPtr doc = (xmlDocPtr) node;
1331
1332 file = doc->URL;
1333 } else {
1334 /*
1335 * Try to find contextual informations to report
1336 */
1337 if (node->type == XML_ELEMENT_NODE) {
1338 line = (int) node->content;
1339 } else if ((node->prev != NULL) &&
1340 (node->prev->type == XML_ELEMENT_NODE)) {
1341 line = (int) node->prev->content;
1342 } else if ((node->parent != NULL) &&
1343 (node->parent->type == XML_ELEMENT_NODE)) {
1344 line = (int) node->parent->content;
1345 }
1346 if ((node->doc != NULL) && (node->doc->URL != NULL))
1347 file = node->doc->URL;
1348 if (node->name != NULL)
1349 name = node->name;
1350 }
1351 }
1352
1353 if (ctxt != NULL)
1354 type = "compilation error";
1355 else if (schema != NULL)
1356 type = "runtime error";
1357
1358 if ((file != NULL) && (line != 0) && (name != NULL))
1359 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1360 type, file, line, name);
1361 else if ((file != NULL) && (name != NULL))
1362 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1363 type, file, name);
1364 else if ((file != NULL) && (line != 0))
1365 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1366 else if (file != NULL)
1367 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1368 else if (name != NULL)
1369 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1370 else
1371 ctxt->error(ctxt->userData, "%s\n", type);
1372}
1373#endif
1374
1375/************************************************************************
1376 * *
1377 * Type library hooks *
1378 * *
1379 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001380static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1381 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001382
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001383/**
1384 * xmlRelaxNGSchemaTypeHave:
1385 * @data: data needed for the library
1386 * @type: the type name
1387 *
1388 * Check if the given type is provided by
1389 * the W3C XMLSchema Datatype library.
1390 *
1391 * Returns 1 if yes, 0 if no and -1 in case of error.
1392 */
1393static int
1394xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001395 const xmlChar *type) {
1396 xmlSchemaTypePtr typ;
1397
1398 if (type == NULL)
1399 return(-1);
1400 typ = xmlSchemaGetPredefinedType(type,
1401 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1402 if (typ == NULL)
1403 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001404 return(1);
1405}
1406
1407/**
1408 * xmlRelaxNGSchemaTypeCheck:
1409 * @data: data needed for the library
1410 * @type: the type name
1411 * @value: the value to check
1412 *
1413 * Check if the given type and value are validated by
1414 * the W3C XMLSchema Datatype library.
1415 *
1416 * Returns 1 if yes, 0 if no and -1 in case of error.
1417 */
1418static int
1419xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001420 const xmlChar *type,
1421 const xmlChar *value) {
1422 xmlSchemaTypePtr typ;
1423 int ret;
1424
1425 /*
1426 * TODO: the type should be cached ab provided back, interface subject
1427 * to changes.
1428 * TODO: handle facets, may require an additional interface and keep
1429 * the value returned from the validation.
1430 */
1431 if ((type == NULL) || (value == NULL))
1432 return(-1);
1433 typ = xmlSchemaGetPredefinedType(type,
1434 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1435 if (typ == NULL)
1436 return(-1);
1437 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1438 if (ret == 0)
1439 return(1);
1440 if (ret > 0)
1441 return(0);
1442 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001443}
1444
1445/**
1446 * xmlRelaxNGSchemaTypeCompare:
1447 * @data: data needed for the library
1448 * @type: the type name
1449 * @value1: the first value
1450 * @value2: the second value
1451 *
1452 * Compare two values accordingly a type from the W3C XMLSchema
1453 * Datatype library.
1454 *
1455 * Returns 1 if yes, 0 if no and -1 in case of error.
1456 */
1457static int
1458xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1459 const xmlChar *type ATTRIBUTE_UNUSED,
1460 const xmlChar *value1 ATTRIBUTE_UNUSED,
1461 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1462 TODO
1463 return(1);
1464}
1465
1466/**
1467 * xmlRelaxNGDefaultTypeHave:
1468 * @data: data needed for the library
1469 * @type: the type name
1470 *
1471 * Check if the given type is provided by
1472 * the default datatype library.
1473 *
1474 * Returns 1 if yes, 0 if no and -1 in case of error.
1475 */
1476static int
1477xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1478 if (type == NULL)
1479 return(-1);
1480 if (xmlStrEqual(type, BAD_CAST "string"))
1481 return(1);
1482 if (xmlStrEqual(type, BAD_CAST "token"))
1483 return(1);
1484 return(0);
1485}
1486
1487/**
1488 * xmlRelaxNGDefaultTypeCheck:
1489 * @data: data needed for the library
1490 * @type: the type name
1491 * @value: the value to check
1492 *
1493 * Check if the given type and value are validated by
1494 * the default datatype library.
1495 *
1496 * Returns 1 if yes, 0 if no and -1 in case of error.
1497 */
1498static int
1499xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1500 const xmlChar *type ATTRIBUTE_UNUSED,
1501 const xmlChar *value ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001502 if (value == NULL)
1503 return(-1);
1504 if (xmlStrEqual(type, BAD_CAST "string"))
1505 return(1);
1506 if (xmlStrEqual(type, BAD_CAST "token")) {
1507#if 0
1508 const xmlChar *cur = value;
1509
1510 while (*cur != 0) {
1511 if (!IS_BLANK(*cur))
1512 return(1);
1513 cur++;
1514 }
1515#endif
1516 return(1);
1517 }
1518
1519 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001520}
1521
1522/**
1523 * xmlRelaxNGDefaultTypeCompare:
1524 * @data: data needed for the library
1525 * @type: the type name
1526 * @value1: the first value
1527 * @value2: the second value
1528 *
1529 * Compare two values accordingly a type from the default
1530 * datatype library.
1531 *
1532 * Returns 1 if yes, 0 if no and -1 in case of error.
1533 */
1534static int
1535xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1536 const xmlChar *type ATTRIBUTE_UNUSED,
1537 const xmlChar *value1 ATTRIBUTE_UNUSED,
1538 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001539 int ret = -1;
1540
1541 if (xmlStrEqual(type, BAD_CAST "string")) {
1542 ret = xmlStrEqual(value1, value2);
1543 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1544 if (!xmlStrEqual(value1, value2)) {
1545 xmlChar *nval, *nvalue;
1546
1547 /*
1548 * TODO: trivial optimizations are possible by
1549 * computing at compile-time
1550 */
1551 nval = xmlRelaxNGNormalize(NULL, value1);
1552 nvalue = xmlRelaxNGNormalize(NULL, value2);
1553
Daniel Veillardd4310742003-02-18 21:12:46 +00001554 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00001555 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00001556 else if (xmlStrEqual(nval, nvalue))
1557 ret = 1;
1558 else
1559 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001560 if (nval != NULL)
1561 xmlFree(nval);
1562 if (nvalue != NULL)
1563 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00001564 } else
1565 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001566 }
1567 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001568}
1569
1570static int xmlRelaxNGTypeInitialized = 0;
1571static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1572
1573/**
1574 * xmlRelaxNGFreeTypeLibrary:
1575 * @lib: the type library structure
1576 * @namespace: the URI bound to the library
1577 *
1578 * Free the structure associated to the type library
1579 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001580static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001581xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1582 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1583 if (lib == NULL)
1584 return;
1585 if (lib->namespace != NULL)
1586 xmlFree((xmlChar *)lib->namespace);
1587 xmlFree(lib);
1588}
1589
1590/**
1591 * xmlRelaxNGRegisterTypeLibrary:
1592 * @namespace: the URI bound to the library
1593 * @data: data associated to the library
1594 * @have: the provide function
1595 * @check: the checking function
1596 * @comp: the comparison function
1597 *
1598 * Register a new type library
1599 *
1600 * Returns 0 in case of success and -1 in case of error.
1601 */
1602static int
1603xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1604 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1605 xmlRelaxNGTypeCompare comp) {
1606 xmlRelaxNGTypeLibraryPtr lib;
1607 int ret;
1608
1609 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1610 (check == NULL) || (comp == NULL))
1611 return(-1);
1612 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1613 xmlGenericError(xmlGenericErrorContext,
1614 "Relax-NG types library '%s' already registered\n",
1615 namespace);
1616 return(-1);
1617 }
1618 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1619 if (lib == NULL) {
1620 xmlGenericError(xmlGenericErrorContext,
1621 "Relax-NG types library '%s' malloc() failed\n",
1622 namespace);
1623 return (-1);
1624 }
1625 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1626 lib->namespace = xmlStrdup(namespace);
1627 lib->data = data;
1628 lib->have = have;
1629 lib->comp = comp;
1630 lib->check = check;
1631 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1632 if (ret < 0) {
1633 xmlGenericError(xmlGenericErrorContext,
1634 "Relax-NG types library failed to register '%s'\n",
1635 namespace);
1636 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1637 return(-1);
1638 }
1639 return(0);
1640}
1641
1642/**
1643 * xmlRelaxNGInitTypes:
1644 *
1645 * Initilize the default type libraries.
1646 *
1647 * Returns 0 in case of success and -1 in case of error.
1648 */
1649static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001650xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001651 if (xmlRelaxNGTypeInitialized != 0)
1652 return(0);
1653 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1654 if (xmlRelaxNGRegisteredTypes == NULL) {
1655 xmlGenericError(xmlGenericErrorContext,
1656 "Failed to allocate sh table for Relax-NG types\n");
1657 return(-1);
1658 }
1659 xmlRelaxNGRegisterTypeLibrary(
1660 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1661 NULL,
1662 xmlRelaxNGSchemaTypeHave,
1663 xmlRelaxNGSchemaTypeCheck,
1664 xmlRelaxNGSchemaTypeCompare);
1665 xmlRelaxNGRegisterTypeLibrary(
1666 xmlRelaxNGNs,
1667 NULL,
1668 xmlRelaxNGDefaultTypeHave,
1669 xmlRelaxNGDefaultTypeCheck,
1670 xmlRelaxNGDefaultTypeCompare);
1671 xmlRelaxNGTypeInitialized = 1;
1672 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001673}
1674
1675/**
1676 * xmlRelaxNGCleanupTypes:
1677 *
1678 * Cleanup the default Schemas type library associated to RelaxNG
1679 */
1680void
1681xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001682 if (xmlRelaxNGTypeInitialized == 0)
1683 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001684 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001685 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1686 xmlRelaxNGFreeTypeLibrary);
1687 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001688}
1689
1690/************************************************************************
1691 * *
1692 * Parsing functions *
1693 * *
1694 ************************************************************************/
1695
1696static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1697 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1698static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1699 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1700static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001701 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001702static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1703 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001704static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1705 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001706static int xmlRelaxNGParseGrammarContent(
1707 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001708static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1709 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1710 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001711static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1712 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001713
1714
1715#define IS_BLANK_NODE(n) \
1716 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1717
1718/**
1719 * xmlRelaxNGIsBlank:
1720 * @str: a string
1721 *
1722 * Check if a string is ignorable c.f. 4.2. Whitespace
1723 *
1724 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1725 */
1726static int
1727xmlRelaxNGIsBlank(xmlChar *str) {
1728 if (str == NULL)
1729 return(1);
1730 while (*str != 0) {
1731 if (!(IS_BLANK(*str))) return(0);
1732 str++;
1733 }
1734 return(1);
1735}
1736
Daniel Veillard6eadf632003-01-23 18:29:16 +00001737/**
1738 * xmlRelaxNGGetDataTypeLibrary:
1739 * @ctxt: a Relax-NG parser context
1740 * @node: the current data or value element
1741 *
1742 * Applies algorithm from 4.3. datatypeLibrary attribute
1743 *
1744 * Returns the datatypeLibary value or NULL if not found
1745 */
1746static xmlChar *
1747xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1748 xmlNodePtr node) {
1749 xmlChar *ret, *escape;
1750
Daniel Veillard6eadf632003-01-23 18:29:16 +00001751 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1752 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1753 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001754 if (ret[0] == 0) {
1755 xmlFree(ret);
1756 return(NULL);
1757 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001758 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001759 if (escape == NULL) {
1760 return(ret);
1761 }
1762 xmlFree(ret);
1763 return(escape);
1764 }
1765 }
1766 node = node->parent;
1767 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001768 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1769 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001770 if (ret[0] == 0) {
1771 xmlFree(ret);
1772 return(NULL);
1773 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001774 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1775 if (escape == NULL) {
1776 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001777 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001778 xmlFree(ret);
1779 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001780 }
1781 node = node->parent;
1782 }
1783 return(NULL);
1784}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001785
1786/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001787 * xmlRelaxNGParseValue:
1788 * @ctxt: a Relax-NG parser context
1789 * @node: the data node.
1790 *
1791 * parse the content of a RelaxNG value node.
1792 *
1793 * Returns the definition pointer or NULL in case of error
1794 */
1795static xmlRelaxNGDefinePtr
1796xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1797 xmlRelaxNGDefinePtr def = NULL;
1798 xmlRelaxNGTypeLibraryPtr lib;
1799 xmlChar *type;
1800 xmlChar *library;
1801 int tmp;
1802
1803 def = xmlRelaxNGNewDefine(ctxt, node);
1804 if (def == NULL)
1805 return(NULL);
1806 def->type = XML_RELAXNG_VALUE;
1807
1808 type = xmlGetProp(node, BAD_CAST "type");
1809 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001810 xmlRelaxNGNormExtSpace(type);
1811 if (xmlValidateNCName(type, 0)) {
1812 if (ctxt->error != NULL)
1813 ctxt->error(ctxt->userData,
1814 "value type '%s' is not an NCName\n",
1815 type);
1816 ctxt->nbErrors++;
1817 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001818 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1819 if (library == NULL)
1820 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1821
1822 def->name = type;
1823 def->ns = library;
1824
1825 lib = (xmlRelaxNGTypeLibraryPtr)
1826 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1827 if (lib == NULL) {
1828 if (ctxt->error != NULL)
1829 ctxt->error(ctxt->userData,
1830 "Use of unregistered type library '%s'\n",
1831 library);
1832 ctxt->nbErrors++;
1833 def->data = NULL;
1834 } else {
1835 def->data = lib;
1836 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001837 if (ctxt->error != NULL)
1838 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001839 "Internal error with type library '%s': no 'have'\n",
1840 library);
1841 ctxt->nbErrors++;
1842 } else {
1843 tmp = lib->have(lib->data, def->name);
1844 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001845 if (ctxt->error != NULL)
1846 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001847 "Error type '%s' is not exported by type library '%s'\n",
1848 def->name, library);
1849 ctxt->nbErrors++;
1850 }
1851 }
1852 }
1853 }
1854 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001855 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00001856 } else if ((node->children->type != XML_TEXT_NODE) ||
1857 (node->children->next != NULL)) {
1858 if (ctxt->error != NULL)
1859 ctxt->error(ctxt->userData,
1860 "Expecting a single text value for <value>content\n");
1861 ctxt->nbErrors++;
1862 } else {
1863 def->value = xmlNodeGetContent(node);
1864 if (def->value == NULL) {
1865 if (ctxt->error != NULL)
1866 ctxt->error(ctxt->userData,
1867 "Element <value> has no content\n");
1868 ctxt->nbErrors++;
1869 }
1870 }
1871 /* TODO check ahead of time that the value is okay per the type */
1872 return(def);
1873}
1874
1875/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001876 * xmlRelaxNGParseData:
1877 * @ctxt: a Relax-NG parser context
1878 * @node: the data node.
1879 *
1880 * parse the content of a RelaxNG data node.
1881 *
1882 * Returns the definition pointer or NULL in case of error
1883 */
1884static xmlRelaxNGDefinePtr
1885xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001886 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001887 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001888 xmlRelaxNGTypeLibraryPtr lib;
1889 xmlChar *type;
1890 xmlChar *library;
1891 xmlNodePtr content;
1892 int tmp;
1893
1894 type = xmlGetProp(node, BAD_CAST "type");
1895 if (type == NULL) {
1896 if (ctxt->error != NULL)
1897 ctxt->error(ctxt->userData,
1898 "data has no type\n");
1899 ctxt->nbErrors++;
1900 return(NULL);
1901 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001902 xmlRelaxNGNormExtSpace(type);
1903 if (xmlValidateNCName(type, 0)) {
1904 if (ctxt->error != NULL)
1905 ctxt->error(ctxt->userData,
1906 "data type '%s' is not an NCName\n",
1907 type);
1908 ctxt->nbErrors++;
1909 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001910 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1911 if (library == NULL)
1912 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1913
1914 def = xmlRelaxNGNewDefine(ctxt, node);
1915 if (def == NULL) {
1916 xmlFree(type);
1917 return(NULL);
1918 }
1919 def->type = XML_RELAXNG_DATATYPE;
1920 def->name = type;
1921 def->ns = library;
1922
1923 lib = (xmlRelaxNGTypeLibraryPtr)
1924 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1925 if (lib == NULL) {
1926 if (ctxt->error != NULL)
1927 ctxt->error(ctxt->userData,
1928 "Use of unregistered type library '%s'\n",
1929 library);
1930 ctxt->nbErrors++;
1931 def->data = NULL;
1932 } else {
1933 def->data = lib;
1934 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001935 if (ctxt->error != NULL)
1936 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001937 "Internal error with type library '%s': no 'have'\n",
1938 library);
1939 ctxt->nbErrors++;
1940 } else {
1941 tmp = lib->have(lib->data, def->name);
1942 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001943 if (ctxt->error != NULL)
1944 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001945 "Error type '%s' is not exported by type library '%s'\n",
1946 def->name, library);
1947 ctxt->nbErrors++;
1948 }
1949 }
1950 }
1951 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00001952
1953 /*
1954 * Handle optional params
1955 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001956 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001957 if (!xmlStrEqual(content->name, BAD_CAST "param"))
1958 break;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001959 param = xmlRelaxNGNewDefine(ctxt, node);
1960 if (param != NULL) {
1961 param->type = XML_RELAXNG_PARAM;
1962 param->name = xmlGetProp(content, BAD_CAST "name");
1963 if (param->name == NULL) {
1964 if (ctxt->error != NULL)
1965 ctxt->error(ctxt->userData,
1966 "param has no name\n");
1967 ctxt->nbErrors++;
1968 }
1969 param->value = xmlNodeGetContent(content);
1970 if (lastparam == NULL) {
1971 def->attrs = lastparam = param;
1972 } else {
1973 lastparam->next = param;
1974 lastparam = param;
1975 }
1976 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001977 content = content->next;
1978 }
Daniel Veillard416589a2003-02-17 17:25:42 +00001979 /*
1980 * Handle optional except
1981 */
1982 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
1983 xmlNodePtr child;
1984 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
1985
1986 except = xmlRelaxNGNewDefine(ctxt, node);
1987 if (except == NULL) {
1988 return(def);
1989 }
1990 except->type = XML_RELAXNG_EXCEPT;
1991 child = content->children;
1992 if (last == NULL) {
1993 def->content = except;
1994 } else {
1995 last->next = except;
1996 }
1997 if (child == NULL) {
1998 if (ctxt->error != NULL)
1999 ctxt->error(ctxt->userData,
2000 "except has no content\n");
2001 ctxt->nbErrors++;
2002 }
2003 while (child != NULL) {
2004 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
2005 if (tmp2 != NULL) {
2006 if (last2 == NULL) {
2007 except->content = last2 = tmp2;
2008 } else {
2009 last2->next = tmp2;
2010 last2 = tmp2;
2011 }
2012 }
2013 child = child->next;
2014 }
2015 content = content->next;
2016 }
2017 /*
2018 * Check there is no unhandled data
2019 */
2020 if (content != NULL) {
2021 if (ctxt->error != NULL)
2022 ctxt->error(ctxt->userData,
2023 "Element data has unexpected content %s\n", content->name);
2024 ctxt->nbErrors++;
2025 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002026
2027 return(def);
2028}
2029
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002030/**
2031 * xmlRelaxNGCompareElemDefLists:
2032 * @ctxt: a Relax-NG parser context
2033 * @defs1: the first list of element defs
2034 * @defs2: the second list of element defs
2035 *
2036 * Compare the 2 lists of element definitions. The comparison is
2037 * that if both lists do not accept the same QNames, it returns 1
2038 * If the 2 lists can accept the same QName the comparison returns 0
2039 *
2040 * Returns 1 disttinct, 0 if equal
2041 */
2042static int
2043xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2044 xmlRelaxNGDefinePtr *def1,
2045 xmlRelaxNGDefinePtr *def2) {
2046 xmlRelaxNGDefinePtr *basedef2 = def2;
2047
Daniel Veillard154877e2003-01-30 12:17:05 +00002048 if ((def1 == NULL) || (def2 == NULL))
2049 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002050 if ((*def1 == NULL) || (*def2 == NULL))
2051 return(1);
2052 while (*def1 != NULL) {
2053 while ((*def2) != NULL) {
2054 if ((*def1)->name == NULL) {
2055 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2056 return(0);
2057 } else if ((*def2)->name == NULL) {
2058 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2059 return(0);
2060 } else if (xmlStrEqual((*def1)->name, (*def2)->name)) {
2061 if (xmlStrEqual((*def2)->ns, (*def1)->ns))
2062 return(0);
2063 }
2064 def2++;
2065 }
2066 def2 = basedef2;
2067 def1++;
2068 }
2069 return(1);
2070}
2071
2072/**
2073 * xmlRelaxNGGetElements:
2074 * @ctxt: a Relax-NG parser context
2075 * @def: the interleave definition
2076 *
2077 * Compute the list of top elements a definition can generate
2078 *
2079 * Returns a list of elements or NULL if none was found.
2080 */
2081static xmlRelaxNGDefinePtr *
2082xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
2083 xmlRelaxNGDefinePtr def) {
2084 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2085 int len = 0;
2086 int max = 0;
2087
2088 parent = NULL;
2089 cur = def;
2090 while (cur != NULL) {
Daniel Veillardb08c9812003-01-28 23:09:49 +00002091 if ((cur->type == XML_RELAXNG_ELEMENT) ||
2092 (cur->type == XML_RELAXNG_TEXT)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002093 if (ret == NULL) {
2094 max = 10;
2095 ret = (xmlRelaxNGDefinePtr *)
2096 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2097 if (ret == NULL) {
2098 if (ctxt->error != NULL)
2099 ctxt->error(ctxt->userData,
2100 "Out of memory in element search\n");
2101 ctxt->nbErrors++;
2102 return(NULL);
2103 }
2104 } else if (max <= len) {
2105 max *= 2;
2106 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2107 if (ret == NULL) {
2108 if (ctxt->error != NULL)
2109 ctxt->error(ctxt->userData,
2110 "Out of memory in element search\n");
2111 ctxt->nbErrors++;
2112 return(NULL);
2113 }
2114 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002115 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002116 ret[len] = NULL;
2117 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2118 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2119 (cur->type == XML_RELAXNG_GROUP) ||
2120 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002121 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2122 (cur->type == XML_RELAXNG_OPTIONAL) ||
2123 (cur->type == XML_RELAXNG_REF) ||
2124 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002125 /*
2126 * Don't go within elements or attributes or string values.
2127 * Just gather the element top list
2128 */
2129 if (cur->content != NULL) {
2130 parent = cur;
2131 cur = cur->content;
2132 tmp = cur;
2133 while (tmp != NULL) {
2134 tmp->parent = parent;
2135 tmp = tmp->next;
2136 }
2137 continue;
2138 }
2139 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002140 if (cur == def)
2141 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002142 if (cur->next != NULL) {
2143 cur = cur->next;
2144 continue;
2145 }
2146 do {
2147 cur = cur->parent;
2148 if (cur == NULL) break;
2149 if (cur == def) return(ret);
2150 if (cur->next != NULL) {
2151 cur = cur->next;
2152 break;
2153 }
2154 } while (cur != NULL);
2155 }
2156 return(ret);
2157}
2158
2159/**
2160 * xmlRelaxNGComputeInterleaves:
2161 * @def: the interleave definition
2162 * @ctxt: a Relax-NG parser context
2163 * @node: the data node.
2164 *
2165 * A lot of work for preprocessing interleave definitions
2166 * is potentially needed to get a decent execution speed at runtime
2167 * - trying to get a total order on the element nodes generated
2168 * by the interleaves, order the list of interleave definitions
2169 * following that order.
2170 * - if <text/> is used to handle mixed content, it is better to
2171 * flag this in the define and simplify the runtime checking
2172 * algorithm
2173 */
2174static void
2175xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2176 xmlRelaxNGParserCtxtPtr ctxt,
2177 xmlChar *name ATTRIBUTE_UNUSED) {
2178 xmlRelaxNGDefinePtr cur;
2179
2180 xmlRelaxNGDefinePtr *list = NULL;
2181 xmlRelaxNGPartitionPtr partitions = NULL;
2182 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2183 xmlRelaxNGInterleaveGroupPtr group;
2184 int i,j,ret;
2185 int nbgroups = 0;
2186 int nbchild = 0;
2187
2188#ifdef DEBUG_INTERLEAVE
2189 xmlGenericError(xmlGenericErrorContext,
2190 "xmlRelaxNGComputeInterleaves(%s)\n",
2191 name);
2192#endif
2193 cur = def->content;
2194 while (cur != NULL) {
2195 nbchild++;
2196 cur = cur->next;
2197 }
2198
2199#ifdef DEBUG_INTERLEAVE
2200 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2201#endif
2202 groups = (xmlRelaxNGInterleaveGroupPtr *)
2203 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2204 if (groups == NULL)
2205 goto error;
2206 cur = def->content;
2207 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002208 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2209 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2210 if (groups[nbgroups] == NULL)
2211 goto error;
2212 groups[nbgroups]->rule = cur;
2213 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur);
2214 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002215 cur = cur->next;
2216 }
2217 list = NULL;
2218#ifdef DEBUG_INTERLEAVE
2219 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2220#endif
2221
2222 /*
2223 * Let's check that all rules makes a partitions according to 7.4
2224 */
2225 partitions = (xmlRelaxNGPartitionPtr)
2226 xmlMalloc(sizeof(xmlRelaxNGPartition));
2227 if (partitions == NULL)
2228 goto error;
2229 partitions->nbgroups = nbgroups;
2230 for (i = 0;i < nbgroups;i++) {
2231 group = groups[i];
2232 for (j = i+1;j < nbgroups;j++) {
2233 if (groups[j] == NULL)
2234 continue;
2235 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2236 groups[j]->defs);
2237 if (ret == 0) {
2238 if (ctxt->error != NULL)
2239 ctxt->error(ctxt->userData,
2240 "Element or text conflicts in interleave\n");
2241 ctxt->nbErrors++;
2242 }
2243 }
2244 }
2245 partitions->groups = groups;
2246
2247 /*
2248 * Free Up the child list, and save the partition list back in the def
2249 */
2250 def->data = partitions;
2251 return;
2252
2253error:
2254 if (ctxt->error != NULL)
2255 ctxt->error(ctxt->userData,
2256 "Out of memory in interleave computation\n");
2257 ctxt->nbErrors++;
2258 if (list == NULL)
2259 xmlFree(list);
2260 if (groups != NULL) {
2261 for (i = 0;i < nbgroups;i++)
2262 if (groups[i] != NULL) {
2263 if (groups[i]->defs != NULL)
2264 xmlFree(groups[i]->defs);
2265 xmlFree(groups[i]);
2266 }
2267 xmlFree(groups);
2268 }
2269 xmlRelaxNGFreePartition(partitions);
2270}
2271
2272/**
2273 * xmlRelaxNGParseInterleave:
2274 * @ctxt: a Relax-NG parser context
2275 * @node: the data node.
2276 *
2277 * parse the content of a RelaxNG interleave node.
2278 *
2279 * Returns the definition pointer or NULL in case of error
2280 */
2281static xmlRelaxNGDefinePtr
2282xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2283 xmlRelaxNGDefinePtr def = NULL;
2284 xmlRelaxNGDefinePtr last = NULL, cur;
2285 xmlNodePtr child;
2286
2287 def = xmlRelaxNGNewDefine(ctxt, node);
2288 if (def == NULL) {
2289 return(NULL);
2290 }
2291 def->type = XML_RELAXNG_INTERLEAVE;
2292
2293 if (ctxt->interleaves == NULL)
2294 ctxt->interleaves = xmlHashCreate(10);
2295 if (ctxt->interleaves == NULL) {
2296 if (ctxt->error != NULL)
2297 ctxt->error(ctxt->userData,
2298 "Failed to create interleaves hash table\n");
2299 ctxt->nbErrors++;
2300 } else {
2301 char name[32];
2302
2303 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2304 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2305 if (ctxt->error != NULL)
2306 ctxt->error(ctxt->userData,
2307 "Failed to add %s to hash table\n", name);
2308 ctxt->nbErrors++;
2309 }
2310 }
2311 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002312 if (child == NULL) {
2313 if (ctxt->error != NULL)
2314 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2315 ctxt->nbErrors++;
2316 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002317 while (child != NULL) {
2318 if (IS_RELAXNG(child, "element")) {
2319 cur = xmlRelaxNGParseElement(ctxt, child);
2320 } else {
2321 cur = xmlRelaxNGParsePattern(ctxt, child);
2322 }
2323 if (cur != NULL) {
2324 cur->parent = def;
2325 if (last == NULL) {
2326 def->content = last = cur;
2327 } else {
2328 last->next = cur;
2329 last = cur;
2330 }
2331 }
2332 child = child->next;
2333 }
2334
2335 return(def);
2336}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002337
2338/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002339 * xmlRelaxNGParseInclude:
2340 * @ctxt: a Relax-NG parser context
2341 * @node: the include node
2342 *
2343 * Integrate the content of an include node in the current grammar
2344 *
2345 * Returns 0 in case of success or -1 in case of error
2346 */
2347static int
2348xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2349 xmlRelaxNGIncludePtr incl;
2350 xmlNodePtr root;
2351 int ret = 0, tmp;
2352
2353 incl = node->_private;
2354 if (incl == NULL) {
2355 if (ctxt->error != NULL)
2356 ctxt->error(ctxt->userData,
2357 "Include node has no data\n");
2358 ctxt->nbErrors++;
2359 return(-1);
2360 }
2361 root = xmlDocGetRootElement(incl->doc);
2362 if (root == NULL) {
2363 if (ctxt->error != NULL)
2364 ctxt->error(ctxt->userData,
2365 "Include document is empty\n");
2366 ctxt->nbErrors++;
2367 return(-1);
2368 }
2369 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2370 if (ctxt->error != NULL)
2371 ctxt->error(ctxt->userData,
2372 "Include document root is not a grammar\n");
2373 ctxt->nbErrors++;
2374 return(-1);
2375 }
2376
2377 /*
2378 * Merge the definition from both the include and the internal list
2379 */
2380 if (root->children != NULL) {
2381 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2382 if (tmp != 0)
2383 ret = -1;
2384 }
2385 if (node->children != NULL) {
2386 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2387 if (tmp != 0)
2388 ret = -1;
2389 }
2390 return(ret);
2391}
2392
2393/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002394 * xmlRelaxNGParseDefine:
2395 * @ctxt: a Relax-NG parser context
2396 * @node: the define node
2397 *
2398 * parse the content of a RelaxNG define element node.
2399 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002400 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002401 */
2402static int
2403xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2404 xmlChar *name;
2405 int ret = 0, tmp;
2406 xmlRelaxNGDefinePtr def;
2407 const xmlChar *olddefine;
2408
2409 name = xmlGetProp(node, BAD_CAST "name");
2410 if (name == NULL) {
2411 if (ctxt->error != NULL)
2412 ctxt->error(ctxt->userData,
2413 "define has no name\n");
2414 ctxt->nbErrors++;
2415 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002416 xmlRelaxNGNormExtSpace(name);
2417 if (xmlValidateNCName(name, 0)) {
2418 if (ctxt->error != NULL)
2419 ctxt->error(ctxt->userData,
2420 "define name '%s' is not an NCName\n",
2421 name);
2422 ctxt->nbErrors++;
2423 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002424 def = xmlRelaxNGNewDefine(ctxt, node);
2425 if (def == NULL) {
2426 xmlFree(name);
2427 return(-1);
2428 }
2429 def->type = XML_RELAXNG_DEF;
2430 def->name = name;
2431 if (node->children == NULL) {
2432 if (ctxt->error != NULL)
2433 ctxt->error(ctxt->userData,
2434 "define has no children\n");
2435 ctxt->nbErrors++;
2436 } else {
2437 olddefine = ctxt->define;
2438 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002439 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002440 ctxt->define = olddefine;
2441 }
2442 if (ctxt->grammar->defs == NULL)
2443 ctxt->grammar->defs = xmlHashCreate(10);
2444 if (ctxt->grammar->defs == NULL) {
2445 if (ctxt->error != NULL)
2446 ctxt->error(ctxt->userData,
2447 "Could not create definition hash\n");
2448 ctxt->nbErrors++;
2449 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002450 } else {
2451 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2452 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002453 xmlRelaxNGDefinePtr prev;
2454
2455 prev = xmlHashLookup(ctxt->grammar->defs, name);
2456 if (prev == NULL) {
2457 if (ctxt->error != NULL)
2458 ctxt->error(ctxt->userData,
2459 "Internal error on define aggregation of %s\n",
2460 name);
2461 ctxt->nbErrors++;
2462 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002463 } else {
2464 while (prev->nextHash != NULL)
2465 prev = prev->nextHash;
2466 prev->nextHash = def;
2467 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002468 }
2469 }
2470 }
2471 return(ret);
2472}
2473
2474/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002475 * xmlRelaxNGProcessExternalRef:
2476 * @ctxt: the parser context
2477 * @node: the externlRef node
2478 *
2479 * Process and compile an externlRef node
2480 *
2481 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2482 */
2483static xmlRelaxNGDefinePtr
2484xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2485 xmlRelaxNGDocumentPtr docu;
2486 xmlNodePtr root, tmp;
2487 xmlChar *ns;
Daniel Veillard77648bb2003-02-20 15:03:22 +00002488 int newNs = 0, oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002489 xmlRelaxNGDefinePtr def;
2490
2491 docu = node->_private;
2492 if (docu != NULL) {
2493 def = xmlRelaxNGNewDefine(ctxt, node);
2494 if (def == NULL)
2495 return(NULL);
2496 def->type = XML_RELAXNG_EXTERNALREF;
2497
2498 if (docu->content == NULL) {
2499 /*
2500 * Then do the parsing for good
2501 */
2502 root = xmlDocGetRootElement(docu->doc);
2503 if (root == NULL) {
2504 if (ctxt->error != NULL)
2505 ctxt->error(ctxt->userData,
2506 "xmlRelaxNGParse: %s is empty\n",
2507 ctxt->URL);
2508 ctxt->nbErrors++;
2509 return (NULL);
2510 }
2511 /*
2512 * ns transmission rules
2513 */
2514 ns = xmlGetProp(root, BAD_CAST "ns");
2515 if (ns == NULL) {
2516 tmp = node;
2517 while ((tmp != NULL) &&
2518 (tmp->type == XML_ELEMENT_NODE)) {
2519 ns = xmlGetProp(tmp, BAD_CAST "ns");
2520 if (ns != NULL) {
2521 break;
2522 }
2523 tmp = tmp->parent;
2524 }
2525 if (ns != NULL) {
2526 xmlSetProp(root, BAD_CAST "ns", ns);
2527 newNs = 1;
2528 xmlFree(ns);
2529 }
2530 } else {
2531 xmlFree(ns);
2532 }
2533
2534 /*
2535 * Parsing to get a precompiled schemas.
2536 */
Daniel Veillard77648bb2003-02-20 15:03:22 +00002537 oldflags = ctxt->flags;
2538 ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002539 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard77648bb2003-02-20 15:03:22 +00002540 ctxt->flags = oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002541 if ((docu->schema != NULL) &&
2542 (docu->schema->topgrammar != NULL)) {
2543 docu->content = docu->schema->topgrammar->start;
2544 }
2545
2546 /*
2547 * the externalRef may be reused in a different ns context
2548 */
2549 if (newNs == 1) {
2550 xmlUnsetProp(root, BAD_CAST "ns");
2551 }
2552 }
2553 def->content = docu->content;
2554 } else {
2555 def = NULL;
2556 }
2557 return(def);
2558}
2559
2560/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002561 * xmlRelaxNGParsePattern:
2562 * @ctxt: a Relax-NG parser context
2563 * @node: the pattern node.
2564 *
2565 * parse the content of a RelaxNG pattern node.
2566 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002567 * Returns the definition pointer or NULL in case of error or if no
2568 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002569 */
2570static xmlRelaxNGDefinePtr
2571xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2572 xmlRelaxNGDefinePtr def = NULL;
2573
Daniel Veillardd2298792003-02-14 16:54:11 +00002574 if (node == NULL) {
2575 return(NULL);
2576 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002577 if (IS_RELAXNG(node, "element")) {
2578 def = xmlRelaxNGParseElement(ctxt, node);
2579 } else if (IS_RELAXNG(node, "attribute")) {
2580 def = xmlRelaxNGParseAttribute(ctxt, node);
2581 } else if (IS_RELAXNG(node, "empty")) {
2582 def = xmlRelaxNGNewDefine(ctxt, node);
2583 if (def == NULL)
2584 return(NULL);
2585 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002586 if (node->children != NULL) {
2587 if (ctxt->error != NULL)
2588 ctxt->error(ctxt->userData, "empty: had a child node\n");
2589 ctxt->nbErrors++;
2590 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002591 } else if (IS_RELAXNG(node, "text")) {
2592 def = xmlRelaxNGNewDefine(ctxt, node);
2593 if (def == NULL)
2594 return(NULL);
2595 def->type = XML_RELAXNG_TEXT;
2596 if (node->children != NULL) {
2597 if (ctxt->error != NULL)
2598 ctxt->error(ctxt->userData, "text: had a child node\n");
2599 ctxt->nbErrors++;
2600 }
2601 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2602 def = xmlRelaxNGNewDefine(ctxt, node);
2603 if (def == NULL)
2604 return(NULL);
2605 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002606 if (node->children == NULL) {
2607 if (ctxt->error != NULL)
2608 ctxt->error(ctxt->userData,
2609 "Element %s is empty\n", node->name);
2610 ctxt->nbErrors++;
2611 } else {
2612 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2613 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002614 } else if (IS_RELAXNG(node, "oneOrMore")) {
2615 def = xmlRelaxNGNewDefine(ctxt, node);
2616 if (def == NULL)
2617 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002618 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002619 if (node->children == NULL) {
2620 if (ctxt->error != NULL)
2621 ctxt->error(ctxt->userData,
2622 "Element %s is empty\n", node->name);
2623 ctxt->nbErrors++;
2624 } else {
2625 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2626 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002627 } else if (IS_RELAXNG(node, "optional")) {
2628 def = xmlRelaxNGNewDefine(ctxt, node);
2629 if (def == NULL)
2630 return(NULL);
2631 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002632 if (node->children == NULL) {
2633 if (ctxt->error != NULL)
2634 ctxt->error(ctxt->userData,
2635 "Element %s is empty\n", node->name);
2636 ctxt->nbErrors++;
2637 } else {
2638 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2639 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002640 } else if (IS_RELAXNG(node, "choice")) {
2641 def = xmlRelaxNGNewDefine(ctxt, node);
2642 if (def == NULL)
2643 return(NULL);
2644 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002645 if (node->children == NULL) {
2646 if (ctxt->error != NULL)
2647 ctxt->error(ctxt->userData,
2648 "Element %s is empty\n", node->name);
2649 ctxt->nbErrors++;
2650 } else {
2651 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2652 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002653 } else if (IS_RELAXNG(node, "group")) {
2654 def = xmlRelaxNGNewDefine(ctxt, node);
2655 if (def == NULL)
2656 return(NULL);
2657 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002658 if (node->children == NULL) {
2659 if (ctxt->error != NULL)
2660 ctxt->error(ctxt->userData,
2661 "Element %s is empty\n", node->name);
2662 ctxt->nbErrors++;
2663 } else {
2664 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2665 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002666 } else if (IS_RELAXNG(node, "ref")) {
2667 def = xmlRelaxNGNewDefine(ctxt, node);
2668 if (def == NULL)
2669 return(NULL);
2670 def->type = XML_RELAXNG_REF;
2671 def->name = xmlGetProp(node, BAD_CAST "name");
2672 if (def->name == NULL) {
2673 if (ctxt->error != NULL)
2674 ctxt->error(ctxt->userData,
2675 "ref has no name\n");
2676 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002677 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002678 xmlRelaxNGNormExtSpace(def->name);
2679 if (xmlValidateNCName(def->name, 0)) {
2680 if (ctxt->error != NULL)
2681 ctxt->error(ctxt->userData,
2682 "ref name '%s' is not an NCName\n",
2683 def->name);
2684 ctxt->nbErrors++;
2685 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002686 }
2687 if (node->children != NULL) {
2688 if (ctxt->error != NULL)
2689 ctxt->error(ctxt->userData,
2690 "ref is not empty\n");
2691 ctxt->nbErrors++;
2692 }
2693 if (ctxt->grammar->refs == NULL)
2694 ctxt->grammar->refs = xmlHashCreate(10);
2695 if (ctxt->grammar->refs == NULL) {
2696 if (ctxt->error != NULL)
2697 ctxt->error(ctxt->userData,
2698 "Could not create references hash\n");
2699 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002700 def = NULL;
2701 } else {
2702 int tmp;
2703
2704 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2705 if (tmp < 0) {
2706 xmlRelaxNGDefinePtr prev;
2707
2708 prev = (xmlRelaxNGDefinePtr)
2709 xmlHashLookup(ctxt->grammar->refs, def->name);
2710 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002711 if (def->name != NULL) {
2712 if (ctxt->error != NULL)
2713 ctxt->error(ctxt->userData,
2714 "Error refs definitions '%s'\n",
2715 def->name);
2716 } else {
2717 if (ctxt->error != NULL)
2718 ctxt->error(ctxt->userData,
2719 "Error refs definitions\n");
2720 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002721 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002722 def = NULL;
2723 } else {
2724 def->nextHash = prev->nextHash;
2725 prev->nextHash = def;
2726 }
2727 }
2728 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002729 } else if (IS_RELAXNG(node, "data")) {
2730 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002731#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002732 } else if (IS_RELAXNG(node, "define")) {
2733 xmlRelaxNGParseDefine(ctxt, node);
2734 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002735#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002736 } else if (IS_RELAXNG(node, "value")) {
2737 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002738 } else if (IS_RELAXNG(node, "list")) {
2739 def = xmlRelaxNGNewDefine(ctxt, node);
2740 if (def == NULL)
2741 return(NULL);
2742 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002743 if (node->children == NULL) {
2744 if (ctxt->error != NULL)
2745 ctxt->error(ctxt->userData,
2746 "Element %s is empty\n", node->name);
2747 ctxt->nbErrors++;
2748 } else {
2749 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2750 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002751 } else if (IS_RELAXNG(node, "interleave")) {
2752 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002753 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002754 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002755 } else if (IS_RELAXNG(node, "notAllowed")) {
2756 def = xmlRelaxNGNewDefine(ctxt, node);
2757 if (def == NULL)
2758 return(NULL);
2759 def->type = XML_RELAXNG_NOT_ALLOWED;
2760 if (node->children != NULL) {
2761 if (ctxt->error != NULL)
2762 ctxt->error(ctxt->userData,
2763 "xmlRelaxNGParse: notAllowed element is not empty\n");
2764 ctxt->nbErrors++;
2765 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002766 } else if (IS_RELAXNG(node, "grammar")) {
2767 xmlRelaxNGGrammarPtr grammar, old;
2768 xmlRelaxNGGrammarPtr oldparent;
2769
2770 oldparent = ctxt->parentgrammar;
2771 old = ctxt->grammar;
2772 ctxt->parentgrammar = old;
2773 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
2774 if (old != NULL) {
2775 ctxt->grammar = old;
2776 ctxt->parentgrammar = oldparent;
2777 if (grammar != NULL) {
2778 grammar->next = old->next;
2779 old->next = grammar;
2780 }
2781 }
2782 if (grammar != NULL)
2783 def = grammar->start;
2784 else
2785 def = NULL;
2786 } else if (IS_RELAXNG(node, "parentRef")) {
2787 if (ctxt->parentgrammar == NULL) {
2788 if (ctxt->error != NULL)
2789 ctxt->error(ctxt->userData,
2790 "Use of parentRef without a parent grammar\n");
2791 ctxt->nbErrors++;
2792 return(NULL);
2793 }
2794 def = xmlRelaxNGNewDefine(ctxt, node);
2795 if (def == NULL)
2796 return(NULL);
2797 def->type = XML_RELAXNG_PARENTREF;
2798 def->name = xmlGetProp(node, BAD_CAST "name");
2799 if (def->name == NULL) {
2800 if (ctxt->error != NULL)
2801 ctxt->error(ctxt->userData,
2802 "parentRef has no name\n");
2803 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00002804 } else {
2805 xmlRelaxNGNormExtSpace(def->name);
2806 if (xmlValidateNCName(def->name, 0)) {
2807 if (ctxt->error != NULL)
2808 ctxt->error(ctxt->userData,
2809 "parentRef name '%s' is not an NCName\n",
2810 def->name);
2811 ctxt->nbErrors++;
2812 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002813 }
2814 if (node->children != NULL) {
2815 if (ctxt->error != NULL)
2816 ctxt->error(ctxt->userData,
2817 "parentRef is not empty\n");
2818 ctxt->nbErrors++;
2819 }
2820 if (ctxt->parentgrammar->refs == NULL)
2821 ctxt->parentgrammar->refs = xmlHashCreate(10);
2822 if (ctxt->parentgrammar->refs == NULL) {
2823 if (ctxt->error != NULL)
2824 ctxt->error(ctxt->userData,
2825 "Could not create references hash\n");
2826 ctxt->nbErrors++;
2827 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002828 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00002829 int tmp;
2830
2831 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
2832 if (tmp < 0) {
2833 xmlRelaxNGDefinePtr prev;
2834
2835 prev = (xmlRelaxNGDefinePtr)
2836 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
2837 if (prev == NULL) {
2838 if (ctxt->error != NULL)
2839 ctxt->error(ctxt->userData,
2840 "Internal error parentRef definitions '%s'\n",
2841 def->name);
2842 ctxt->nbErrors++;
2843 def = NULL;
2844 } else {
2845 def->nextHash = prev->nextHash;
2846 prev->nextHash = def;
2847 }
2848 }
2849 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002850 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002851 if (node->children == NULL) {
2852 if (ctxt->error != NULL)
2853 ctxt->error(ctxt->userData,
2854 "Mixed is empty\n");
2855 ctxt->nbErrors++;
2856 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00002857 } else {
2858 def = xmlRelaxNGParseInterleave(ctxt, node);
2859 if (def != NULL) {
2860 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00002861
2862 if ((def->content != NULL) && (def->content->next != NULL)) {
2863 tmp = xmlRelaxNGNewDefine(ctxt, node);
2864 if (tmp != NULL) {
2865 tmp->type = XML_RELAXNG_GROUP;
2866 tmp->content = def->content;
2867 def->content = tmp;
2868 }
2869 }
2870
Daniel Veillard416589a2003-02-17 17:25:42 +00002871 tmp = xmlRelaxNGNewDefine(ctxt, node);
2872 if (tmp == NULL)
2873 return(def);
2874 tmp->type = XML_RELAXNG_TEXT;
2875 tmp->next = def->content;
2876 def->content = tmp;
2877 }
2878 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002879 } else {
2880 if (ctxt->error != NULL)
2881 ctxt->error(ctxt->userData,
2882 "Unexpected node %s is not a pattern\n",
2883 node->name);
2884 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002885 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002886 }
2887 return(def);
2888}
2889
2890/**
2891 * xmlRelaxNGParseAttribute:
2892 * @ctxt: a Relax-NG parser context
2893 * @node: the element node
2894 *
2895 * parse the content of a RelaxNG attribute node.
2896 *
2897 * Returns the definition pointer or NULL in case of error.
2898 */
2899static xmlRelaxNGDefinePtr
2900xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002901 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002902 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002903 int old_flags;
2904
2905 ret = xmlRelaxNGNewDefine(ctxt, node);
2906 if (ret == NULL)
2907 return(NULL);
2908 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002909 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002910 child = node->children;
2911 if (child == NULL) {
2912 if (ctxt->error != NULL)
2913 ctxt->error(ctxt->userData,
2914 "xmlRelaxNGParseattribute: attribute has no children\n");
2915 ctxt->nbErrors++;
2916 return(ret);
2917 }
2918 old_flags = ctxt->flags;
2919 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002920 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
2921 if (cur != NULL)
2922 child = child->next;
2923
Daniel Veillardd2298792003-02-14 16:54:11 +00002924 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00002925 cur = xmlRelaxNGParsePattern(ctxt, child);
2926 if (cur != NULL) {
2927 switch (cur->type) {
2928 case XML_RELAXNG_EMPTY:
2929 case XML_RELAXNG_NOT_ALLOWED:
2930 case XML_RELAXNG_TEXT:
2931 case XML_RELAXNG_ELEMENT:
2932 case XML_RELAXNG_DATATYPE:
2933 case XML_RELAXNG_VALUE:
2934 case XML_RELAXNG_LIST:
2935 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00002936 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002937 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00002938 case XML_RELAXNG_DEF:
2939 case XML_RELAXNG_ONEORMORE:
2940 case XML_RELAXNG_ZEROORMORE:
2941 case XML_RELAXNG_OPTIONAL:
2942 case XML_RELAXNG_CHOICE:
2943 case XML_RELAXNG_GROUP:
2944 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard77648bb2003-02-20 15:03:22 +00002945 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00002946 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002947 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002948 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002949 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00002950 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00002951 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00002952 if (ctxt->error != NULL)
2953 ctxt->error(ctxt->userData,
2954 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002955 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002956 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00002957 case XML_RELAXNG_NOOP:
2958 TODO
2959 if (ctxt->error != NULL)
2960 ctxt->error(ctxt->userData,
2961 "Internal error, noop found\n");
2962 ctxt->nbErrors++;
2963 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002964 }
2965 }
2966 child = child->next;
2967 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002968 if (child != NULL) {
2969 if (ctxt->error != NULL)
2970 ctxt->error(ctxt->userData, "attribute has multiple children\n");
2971 ctxt->nbErrors++;
2972 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002973 ctxt->flags = old_flags;
2974 return(ret);
2975}
2976
2977/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002978 * xmlRelaxNGParseExceptNameClass:
2979 * @ctxt: a Relax-NG parser context
2980 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00002981 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002982 *
2983 * parse the content of a RelaxNG nameClass node.
2984 *
2985 * Returns the definition pointer or NULL in case of error.
2986 */
2987static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00002988xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
2989 xmlNodePtr node, int attr) {
2990 xmlRelaxNGDefinePtr ret, cur, last = NULL;
2991 xmlNodePtr child;
2992
Daniel Veillardd2298792003-02-14 16:54:11 +00002993 if (!IS_RELAXNG(node, "except")) {
2994 if (ctxt->error != NULL)
2995 ctxt->error(ctxt->userData,
2996 "Expecting an except node\n");
2997 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00002998 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00002999 }
3000 if (node->next != NULL) {
3001 if (ctxt->error != NULL)
3002 ctxt->error(ctxt->userData,
3003 "exceptNameClass allows only a single except node\n");
3004 ctxt->nbErrors++;
3005 }
Daniel Veillard144fae12003-02-03 13:17:57 +00003006 if (node->children == NULL) {
3007 if (ctxt->error != NULL)
3008 ctxt->error(ctxt->userData,
3009 "except has no content\n");
3010 ctxt->nbErrors++;
3011 return(NULL);
3012 }
3013
3014 ret = xmlRelaxNGNewDefine(ctxt, node);
3015 if (ret == NULL)
3016 return(NULL);
3017 ret->type = XML_RELAXNG_EXCEPT;
3018 child = node->children;
3019 while (child != NULL) {
3020 cur = xmlRelaxNGNewDefine(ctxt, child);
3021 if (cur == NULL)
3022 break;
3023 if (attr)
3024 cur->type = XML_RELAXNG_ATTRIBUTE;
3025 else
3026 cur->type = XML_RELAXNG_ELEMENT;
3027
Daniel Veillard419a7682003-02-03 23:22:49 +00003028 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00003029 if (last == NULL) {
3030 ret->content = cur;
3031 } else {
3032 last->next = cur;
3033 }
3034 last = cur;
3035 }
3036 child = child->next;
3037 }
3038
3039 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003040}
3041
3042/**
3043 * xmlRelaxNGParseNameClass:
3044 * @ctxt: a Relax-NG parser context
3045 * @node: the nameClass node
3046 * @def: the current definition
3047 *
3048 * parse the content of a RelaxNG nameClass node.
3049 *
3050 * Returns the definition pointer or NULL in case of error.
3051 */
3052static xmlRelaxNGDefinePtr
3053xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
3054 xmlRelaxNGDefinePtr def) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003055 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003056 xmlChar *val;
3057
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003058 ret = def;
3059 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
3060 (IS_RELAXNG(node, "nsName"))) {
3061 if ((def->type != XML_RELAXNG_ELEMENT) &&
3062 (def->type != XML_RELAXNG_ATTRIBUTE)) {
3063 ret = xmlRelaxNGNewDefine(ctxt, node);
3064 if (ret == NULL)
3065 return(NULL);
3066 ret->parent = def;
3067 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
3068 ret->type = XML_RELAXNG_ATTRIBUTE;
3069 else
3070 ret->type = XML_RELAXNG_ELEMENT;
3071 }
3072 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003073 if (IS_RELAXNG(node, "name")) {
3074 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003075 xmlRelaxNGNormExtSpace(val);
3076 if (xmlValidateNCName(val, 0)) {
3077 if (ctxt->error != NULL) {
3078 if (node->parent != NULL)
3079 ctxt->error(ctxt->userData,
3080 "Element %s name '%s' is not an NCName\n",
3081 node->parent->name, val);
3082 else
3083 ctxt->error(ctxt->userData,
3084 "name '%s' is not an NCName\n",
3085 val);
3086 }
3087 ctxt->nbErrors++;
3088 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003089 ret->name = val;
3090 val = xmlGetProp(node, BAD_CAST "ns");
3091 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003092 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3093 (val != NULL) &&
3094 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3095 ctxt->error(ctxt->userData,
3096 "Attribute with namespace '%s' is not allowed\n",
3097 val);
3098 ctxt->nbErrors++;
3099 }
3100 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3101 (val != NULL) &&
3102 (val[0] == 0) &&
3103 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3104 ctxt->error(ctxt->userData,
3105 "Attribute with QName 'xmlns' is not allowed\n",
3106 val);
3107 ctxt->nbErrors++;
3108 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003109 } else if (IS_RELAXNG(node, "anyName")) {
3110 ret->name = NULL;
3111 ret->ns = NULL;
3112 if (node->children != NULL) {
3113 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003114 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3115 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003116 }
3117 } else if (IS_RELAXNG(node, "nsName")) {
3118 ret->name = NULL;
3119 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3120 if (ret->ns == NULL) {
3121 if (ctxt->error != NULL)
3122 ctxt->error(ctxt->userData,
3123 "nsName has no ns attribute\n");
3124 ctxt->nbErrors++;
3125 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003126 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3127 (ret->ns != NULL) &&
3128 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3129 ctxt->error(ctxt->userData,
3130 "Attribute with namespace '%s' is not allowed\n",
3131 ret->ns);
3132 ctxt->nbErrors++;
3133 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003134 if (node->children != NULL) {
3135 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003136 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3137 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003138 }
3139 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003140 xmlNodePtr child;
3141 xmlRelaxNGDefinePtr last = NULL;
3142
Daniel Veillardd4310742003-02-18 21:12:46 +00003143 ret = xmlRelaxNGNewDefine(ctxt, node);
3144 if (ret == NULL)
3145 return(NULL);
3146 ret->parent = def;
3147 ret->type = XML_RELAXNG_CHOICE;
3148
Daniel Veillardd2298792003-02-14 16:54:11 +00003149 if (node->children == NULL) {
3150 if (ctxt->error != NULL)
3151 ctxt->error(ctxt->userData,
3152 "Element choice is empty\n");
3153 ctxt->nbErrors++;
3154 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003155
3156 child = node->children;
3157 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003158 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003159 if (tmp != NULL) {
3160 if (last == NULL) {
3161 last = ret->nameClass = tmp;
3162 } else {
3163 last->next = tmp;
3164 last = tmp;
3165 }
3166 }
3167 child = child->next;
3168 }
3169 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003170 } else {
3171 if (ctxt->error != NULL)
3172 ctxt->error(ctxt->userData,
3173 "expecting name, anyName, nsName or choice : got %s\n",
3174 node->name);
3175 ctxt->nbErrors++;
3176 return(NULL);
3177 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003178 if (ret != def) {
3179 if (def->nameClass == NULL) {
3180 def->nameClass = ret;
3181 } else {
3182 tmp = def->nameClass;
3183 while (tmp->next != NULL) {
3184 tmp = tmp->next;
3185 }
3186 tmp->next = ret;
3187 }
3188 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003189 return(ret);
3190}
3191
3192/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003193 * xmlRelaxNGParseElement:
3194 * @ctxt: a Relax-NG parser context
3195 * @node: the element node
3196 *
3197 * parse the content of a RelaxNG element node.
3198 *
3199 * Returns the definition pointer or NULL in case of error.
3200 */
3201static xmlRelaxNGDefinePtr
3202xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3203 xmlRelaxNGDefinePtr ret, cur, last;
3204 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003205 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003206
3207 ret = xmlRelaxNGNewDefine(ctxt, node);
3208 if (ret == NULL)
3209 return(NULL);
3210 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003211 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003212 child = node->children;
3213 if (child == NULL) {
3214 if (ctxt->error != NULL)
3215 ctxt->error(ctxt->userData,
3216 "xmlRelaxNGParseElement: element has no children\n");
3217 ctxt->nbErrors++;
3218 return(ret);
3219 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003220 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3221 if (cur != NULL)
3222 child = child->next;
3223
Daniel Veillard6eadf632003-01-23 18:29:16 +00003224 if (child == NULL) {
3225 if (ctxt->error != NULL)
3226 ctxt->error(ctxt->userData,
3227 "xmlRelaxNGParseElement: element has no content\n");
3228 ctxt->nbErrors++;
3229 return(ret);
3230 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003231 olddefine = ctxt->define;
3232 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003233 last = NULL;
3234 while (child != NULL) {
3235 cur = xmlRelaxNGParsePattern(ctxt, child);
3236 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003237 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003238 switch (cur->type) {
3239 case XML_RELAXNG_EMPTY:
3240 case XML_RELAXNG_NOT_ALLOWED:
3241 case XML_RELAXNG_TEXT:
3242 case XML_RELAXNG_ELEMENT:
3243 case XML_RELAXNG_DATATYPE:
3244 case XML_RELAXNG_VALUE:
3245 case XML_RELAXNG_LIST:
3246 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003247 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003248 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003249 case XML_RELAXNG_DEF:
3250 case XML_RELAXNG_ZEROORMORE:
3251 case XML_RELAXNG_ONEORMORE:
3252 case XML_RELAXNG_OPTIONAL:
3253 case XML_RELAXNG_CHOICE:
3254 case XML_RELAXNG_GROUP:
3255 case XML_RELAXNG_INTERLEAVE:
3256 if (last == NULL) {
3257 ret->content = last = cur;
3258 } else {
3259 if ((last->type == XML_RELAXNG_ELEMENT) &&
3260 (ret->content == last)) {
3261 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3262 if (ret->content != NULL) {
3263 ret->content->type = XML_RELAXNG_GROUP;
3264 ret->content->content = last;
3265 } else {
3266 ret->content = last;
3267 }
3268 }
3269 last->next = cur;
3270 last = cur;
3271 }
3272 break;
3273 case XML_RELAXNG_ATTRIBUTE:
3274 cur->next = ret->attrs;
3275 ret->attrs = cur;
3276 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003277 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003278 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003279 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003280 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003281 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003282 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003283 case XML_RELAXNG_NOOP:
3284 TODO
3285 if (ctxt->error != NULL)
3286 ctxt->error(ctxt->userData,
3287 "Internal error, noop found\n");
3288 ctxt->nbErrors++;
3289 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003290 }
3291 }
3292 child = child->next;
3293 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003294 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003295 return(ret);
3296}
3297
3298/**
3299 * xmlRelaxNGParsePatterns:
3300 * @ctxt: a Relax-NG parser context
3301 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003302 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003303 *
3304 * parse the content of a RelaxNG start node.
3305 *
3306 * Returns the definition pointer or NULL in case of error.
3307 */
3308static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003309xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3310 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003311 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003312
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003313 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003314 while (nodes != NULL) {
3315 if (IS_RELAXNG(nodes, "element")) {
3316 cur = xmlRelaxNGParseElement(ctxt, nodes);
3317 if (def == NULL) {
3318 def = last = cur;
3319 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003320 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3321 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003322 def = xmlRelaxNGNewDefine(ctxt, nodes);
3323 def->type = XML_RELAXNG_GROUP;
3324 def->content = last;
3325 }
3326 last->next = cur;
3327 last = cur;
3328 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003329 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003330 } else {
3331 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003332 if (cur != NULL) {
3333 if (def == NULL) {
3334 def = last = cur;
3335 } else {
3336 last->next = cur;
3337 last = cur;
3338 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003339 }
3340 }
3341 nodes = nodes->next;
3342 }
3343 return(def);
3344}
3345
3346/**
3347 * xmlRelaxNGParseStart:
3348 * @ctxt: a Relax-NG parser context
3349 * @nodes: start children nodes
3350 *
3351 * parse the content of a RelaxNG start node.
3352 *
3353 * Returns 0 in case of success, -1 in case of error
3354 */
3355static int
3356xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3357 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003358 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003359
Daniel Veillardd2298792003-02-14 16:54:11 +00003360 if (nodes == NULL) {
3361 if (ctxt->error != NULL)
3362 ctxt->error(ctxt->userData,
3363 "start has no children\n");
3364 ctxt->nbErrors++;
3365 return(-1);
3366 }
3367 if (IS_RELAXNG(nodes, "empty")) {
3368 def = xmlRelaxNGNewDefine(ctxt, nodes);
3369 if (def == NULL)
3370 return(-1);
3371 def->type = XML_RELAXNG_EMPTY;
3372 if (nodes->children != NULL) {
3373 if (ctxt->error != NULL)
3374 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003375 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003376 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003377 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3378 def = xmlRelaxNGNewDefine(ctxt, nodes);
3379 if (def == NULL)
3380 return(-1);
3381 def->type = XML_RELAXNG_NOT_ALLOWED;
3382 if (nodes->children != NULL) {
3383 if (ctxt->error != NULL)
3384 ctxt->error(ctxt->userData,
3385 "element notAllowed is not empty\n");
3386 ctxt->nbErrors++;
3387 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003388 } else {
3389 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003390 }
3391 if (ctxt->grammar->start != NULL) {
3392 last = ctxt->grammar->start;
3393 while (last->next != NULL)
3394 last = last->next;
3395 last->next = def;
3396 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003397 ctxt->grammar->start = def;
3398 }
3399 nodes = nodes->next;
3400 if (nodes != NULL) {
3401 if (ctxt->error != NULL)
3402 ctxt->error(ctxt->userData,
3403 "start more than one children\n");
3404 ctxt->nbErrors++;
3405 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003406 }
3407 return(ret);
3408}
3409
3410/**
3411 * xmlRelaxNGParseGrammarContent:
3412 * @ctxt: a Relax-NG parser context
3413 * @nodes: grammar children nodes
3414 *
3415 * parse the content of a RelaxNG grammar node.
3416 *
3417 * Returns 0 in case of success, -1 in case of error
3418 */
3419static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003420xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003421{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003422 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003423
3424 if (nodes == NULL) {
3425 if (ctxt->error != NULL)
3426 ctxt->error(ctxt->userData,
3427 "grammar has no children\n");
3428 ctxt->nbErrors++;
3429 return(-1);
3430 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003431 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003432 if (IS_RELAXNG(nodes, "start")) {
3433 if (nodes->children == NULL) {
3434 if (ctxt->error != NULL)
3435 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003436 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003437 ctxt->nbErrors++;
3438 } else {
3439 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3440 if (tmp != 0)
3441 ret = -1;
3442 }
3443 } else if (IS_RELAXNG(nodes, "define")) {
3444 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3445 if (tmp != 0)
3446 ret = -1;
3447 } else if (IS_RELAXNG(nodes, "include")) {
3448 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3449 if (tmp != 0)
3450 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003451 } else {
3452 if (ctxt->error != NULL)
3453 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003454 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003455 ctxt->nbErrors++;
3456 ret = -1;
3457 }
3458 nodes = nodes->next;
3459 }
3460 return (ret);
3461}
3462
3463/**
3464 * xmlRelaxNGCheckReference:
3465 * @ref: the ref
3466 * @ctxt: a Relax-NG parser context
3467 * @name: the name associated to the defines
3468 *
3469 * Applies the 4.17. combine attribute rule for all the define
3470 * element of a given grammar using the same name.
3471 */
3472static void
3473xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3474 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3475 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003476 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003477
3478 grammar = ctxt->grammar;
3479 if (grammar == NULL) {
3480 if (ctxt->error != NULL)
3481 ctxt->error(ctxt->userData,
3482 "Internal error: no grammar in CheckReference %s\n",
3483 name);
3484 ctxt->nbErrors++;
3485 return;
3486 }
3487 if (ref->content != NULL) {
3488 if (ctxt->error != NULL)
3489 ctxt->error(ctxt->userData,
3490 "Internal error: reference has content in CheckReference %s\n",
3491 name);
3492 ctxt->nbErrors++;
3493 return;
3494 }
3495 if (grammar->defs != NULL) {
3496 def = xmlHashLookup(grammar->defs, name);
3497 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003498 cur = ref;
3499 while (cur != NULL) {
3500 cur->content = def;
3501 cur = cur->nextHash;
3502 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003503 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00003504 if (ctxt->error != NULL)
3505 ctxt->error(ctxt->userData,
3506 "Reference %s has no matching definition\n",
3507 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003508 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003509 }
Daniel Veillardd4310742003-02-18 21:12:46 +00003510 } else {
3511 if (ctxt->error != NULL)
3512 ctxt->error(ctxt->userData,
3513 "Reference %s has no matching definition\n",
3514 name);
3515 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003516 }
3517 /*
3518 * TODO: make a closure and verify there is no loop !
3519 */
3520}
3521
3522/**
3523 * xmlRelaxNGCheckCombine:
3524 * @define: the define(s) list
3525 * @ctxt: a Relax-NG parser context
3526 * @name: the name associated to the defines
3527 *
3528 * Applies the 4.17. combine attribute rule for all the define
3529 * element of a given grammar using the same name.
3530 */
3531static void
3532xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3533 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3534 xmlChar *combine;
3535 int choiceOrInterleave = -1;
3536 int missing = 0;
3537 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3538
3539 if (define->nextHash == NULL)
3540 return;
3541 cur = define;
3542 while (cur != NULL) {
3543 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3544 if (combine != NULL) {
3545 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3546 if (choiceOrInterleave == -1)
3547 choiceOrInterleave = 1;
3548 else if (choiceOrInterleave == 0) {
3549 if (ctxt->error != NULL)
3550 ctxt->error(ctxt->userData,
3551 "Defines for %s use both 'choice' and 'interleave'\n",
3552 name);
3553 ctxt->nbErrors++;
3554 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003555 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003556 if (choiceOrInterleave == -1)
3557 choiceOrInterleave = 0;
3558 else if (choiceOrInterleave == 1) {
3559 if (ctxt->error != NULL)
3560 ctxt->error(ctxt->userData,
3561 "Defines for %s use both 'choice' and 'interleave'\n",
3562 name);
3563 ctxt->nbErrors++;
3564 }
3565 } else {
3566 if (ctxt->error != NULL)
3567 ctxt->error(ctxt->userData,
3568 "Defines for %s use unknown combine value '%s''\n",
3569 name, combine);
3570 ctxt->nbErrors++;
3571 }
3572 xmlFree(combine);
3573 } else {
3574 if (missing == 0)
3575 missing = 1;
3576 else {
3577 if (ctxt->error != NULL)
3578 ctxt->error(ctxt->userData,
3579 "Some defines for %s lacks the combine attribute\n",
3580 name);
3581 ctxt->nbErrors++;
3582 }
3583 }
3584
3585 cur = cur->nextHash;
3586 }
3587#ifdef DEBUG
3588 xmlGenericError(xmlGenericErrorContext,
3589 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3590 name, choiceOrInterleave);
3591#endif
3592 if (choiceOrInterleave == -1)
3593 choiceOrInterleave = 0;
3594 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3595 if (cur == NULL)
3596 return;
3597 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003598 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003599 else
3600 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003601 tmp = define;
3602 last = NULL;
3603 while (tmp != NULL) {
3604 if (tmp->content != NULL) {
3605 if (tmp->content->next != NULL) {
3606 /*
3607 * we need first to create a wrapper.
3608 */
3609 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3610 if (tmp2 == NULL)
3611 break;
3612 tmp2->type = XML_RELAXNG_GROUP;
3613 tmp2->content = tmp->content;
3614 } else {
3615 tmp2 = tmp->content;
3616 }
3617 if (last == NULL) {
3618 cur->content = tmp2;
3619 } else {
3620 last->next = tmp2;
3621 }
3622 last = tmp2;
3623 tmp->content = NULL;
3624 }
3625 tmp = tmp->nextHash;
3626 }
3627 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003628 if (choiceOrInterleave == 0) {
3629 if (ctxt->interleaves == NULL)
3630 ctxt->interleaves = xmlHashCreate(10);
3631 if (ctxt->interleaves == NULL) {
3632 if (ctxt->error != NULL)
3633 ctxt->error(ctxt->userData,
3634 "Failed to create interleaves hash table\n");
3635 ctxt->nbErrors++;
3636 } else {
3637 char tmpname[32];
3638
3639 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3640 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3641 if (ctxt->error != NULL)
3642 ctxt->error(ctxt->userData,
3643 "Failed to add %s to hash table\n", tmpname);
3644 ctxt->nbErrors++;
3645 }
3646 }
3647 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003648}
3649
3650/**
3651 * xmlRelaxNGCombineStart:
3652 * @ctxt: a Relax-NG parser context
3653 * @grammar: the grammar
3654 *
3655 * Applies the 4.17. combine rule for all the start
3656 * element of a given grammar.
3657 */
3658static void
3659xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3660 xmlRelaxNGGrammarPtr grammar) {
3661 xmlRelaxNGDefinePtr starts;
3662 xmlChar *combine;
3663 int choiceOrInterleave = -1;
3664 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003665 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003666
Daniel Veillard2df2de22003-02-17 23:34:33 +00003667 starts = grammar->start;
3668 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003669 return;
3670 cur = starts;
3671 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00003672 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
3673 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
3674 combine = NULL;
3675 if (ctxt->error != NULL)
3676 ctxt->error(ctxt->userData,
3677 "Internal error: start element not found\n");
3678 ctxt->nbErrors++;
3679 } else {
3680 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
3681 }
3682
Daniel Veillard6eadf632003-01-23 18:29:16 +00003683 if (combine != NULL) {
3684 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3685 if (choiceOrInterleave == -1)
3686 choiceOrInterleave = 1;
3687 else if (choiceOrInterleave == 0) {
3688 if (ctxt->error != NULL)
3689 ctxt->error(ctxt->userData,
3690 "<start> use both 'choice' and 'interleave'\n");
3691 ctxt->nbErrors++;
3692 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00003693 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003694 if (choiceOrInterleave == -1)
3695 choiceOrInterleave = 0;
3696 else if (choiceOrInterleave == 1) {
3697 if (ctxt->error != NULL)
3698 ctxt->error(ctxt->userData,
3699 "<start> use both 'choice' and 'interleave'\n");
3700 ctxt->nbErrors++;
3701 }
3702 } else {
3703 if (ctxt->error != NULL)
3704 ctxt->error(ctxt->userData,
3705 "<start> uses unknown combine value '%s''\n", combine);
3706 ctxt->nbErrors++;
3707 }
3708 xmlFree(combine);
3709 } else {
3710 if (missing == 0)
3711 missing = 1;
3712 else {
3713 if (ctxt->error != NULL)
3714 ctxt->error(ctxt->userData,
3715 "Some <start> elements lacks the combine attribute\n");
3716 ctxt->nbErrors++;
3717 }
3718 }
3719
Daniel Veillard2df2de22003-02-17 23:34:33 +00003720 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003721 }
3722#ifdef DEBUG
3723 xmlGenericError(xmlGenericErrorContext,
3724 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3725 choiceOrInterleave);
3726#endif
3727 if (choiceOrInterleave == -1)
3728 choiceOrInterleave = 0;
3729 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3730 if (cur == NULL)
3731 return;
3732 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003733 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003734 else
3735 cur->type = XML_RELAXNG_CHOICE;
3736 cur->content = grammar->start;
3737 grammar->start = cur;
3738 if (choiceOrInterleave == 0) {
3739 if (ctxt->interleaves == NULL)
3740 ctxt->interleaves = xmlHashCreate(10);
3741 if (ctxt->interleaves == NULL) {
3742 if (ctxt->error != NULL)
3743 ctxt->error(ctxt->userData,
3744 "Failed to create interleaves hash table\n");
3745 ctxt->nbErrors++;
3746 } else {
3747 char tmpname[32];
3748
3749 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3750 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3751 if (ctxt->error != NULL)
3752 ctxt->error(ctxt->userData,
3753 "Failed to add %s to hash table\n", tmpname);
3754 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003755 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003756 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003757 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003758}
3759
3760/**
Daniel Veillardd4310742003-02-18 21:12:46 +00003761 * xmlRelaxNGCheckCycles:
3762 * @ctxt: a Relax-NG parser context
3763 * @nodes: grammar children nodes
3764 * @depth: the counter
3765 *
3766 * Check for cycles.
3767 *
3768 * Returns 0 if check passed, and -1 in case of error
3769 */
3770static int
3771xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
3772 xmlRelaxNGDefinePtr cur, int depth) {
3773 int ret = 0;
3774
3775 while ((ret == 0) && (cur != NULL)) {
3776 if ((cur->type == XML_RELAXNG_REF) ||
3777 (cur->type == XML_RELAXNG_PARENTREF)) {
3778 if (cur->depth == -1) {
3779 cur->depth = depth;
3780 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3781 cur->depth = -2;
3782 } else if (depth == cur->depth) {
3783 if (ctxt->error != NULL)
3784 ctxt->error(ctxt->userData,
3785 "Detected a cycle in %s references\n", cur->name);
3786 ctxt->nbErrors++;
3787 return(-1);
3788 }
3789 } else if (cur->type == XML_RELAXNG_ELEMENT) {
3790 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
3791 } else {
3792 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
3793 }
3794 cur = cur->next;
3795 }
3796 return(ret);
3797}
3798
3799/**
Daniel Veillard77648bb2003-02-20 15:03:22 +00003800 * xmlRelaxNGTryUnlink:
3801 * @ctxt: a Relax-NG parser context
3802 * @cur: the definition to unlink
3803 * @parent: the parent definition
3804 * @prev: the previous sibling definition
3805 *
3806 * Try to unlink a definition. If not possble make it a NOOP
3807 *
3808 * Returns the new prev definition
3809 */
3810static xmlRelaxNGDefinePtr
3811xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
3812 xmlRelaxNGDefinePtr cur,
3813 xmlRelaxNGDefinePtr parent,
3814 xmlRelaxNGDefinePtr prev) {
3815 if (prev != NULL) {
3816 prev->next = cur->next;
3817 } else {
3818 if (parent != NULL) {
3819 if (parent->content == cur)
3820 parent->content = cur->next;
3821 else if (parent->attrs == cur)
3822 parent->attrs = cur->next;
3823 else if (parent->nameClass == cur)
3824 parent->nameClass = cur->next;
3825 } else {
3826 cur->type = XML_RELAXNG_NOOP;
3827 prev = cur;
3828 }
3829 }
3830 return(prev);
3831}
3832
3833/**
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003834 * xmlRelaxNGCheckRules:
3835 * @ctxt: a Relax-NG parser context
3836 * @nodes: grammar children nodes
3837 *
3838 * Check for simplification of empty and notAllowed
3839 */
3840static void
3841xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
3842 xmlRelaxNGDefinePtr cur,
3843 xmlRelaxNGDefinePtr parent) {
3844 xmlRelaxNGDefinePtr prev = NULL;
3845
3846 while (cur != NULL) {
3847 if ((cur->type == XML_RELAXNG_REF) ||
3848 (cur->type == XML_RELAXNG_PARENTREF)) {
3849 if (cur->depth != -3) {
3850 cur->depth = -3;
3851 xmlRelaxNGSimplify(ctxt, cur->content, cur);
3852 }
3853 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003854 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003855 if ((parent != NULL) &&
3856 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
3857 (parent->type == XML_RELAXNG_LIST) ||
3858 (parent->type == XML_RELAXNG_GROUP) ||
3859 (parent->type == XML_RELAXNG_INTERLEAVE) ||
3860 (parent->type == XML_RELAXNG_ONEORMORE) ||
3861 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3862 parent->type = XML_RELAXNG_NOT_ALLOWED;
3863 break;
3864 }
3865 if ((parent != NULL) &&
3866 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003867 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003868 } else
3869 prev = cur;
3870 } else if (cur->type == XML_RELAXNG_EMPTY){
Daniel Veillard77648bb2003-02-20 15:03:22 +00003871 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003872 if ((parent != NULL) &&
3873 ((parent->type == XML_RELAXNG_ONEORMORE) ||
3874 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3875 parent->type = XML_RELAXNG_EMPTY;
3876 break;
3877 }
3878 if ((parent != NULL) &&
3879 ((parent->type == XML_RELAXNG_GROUP) ||
3880 (parent->type == XML_RELAXNG_INTERLEAVE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003881 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003882 } else
3883 prev = cur;
3884 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003885 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003886 if (cur->content != NULL)
3887 xmlRelaxNGSimplify(ctxt, cur->content, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00003888 if (cur->attrs != NULL)
3889 xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
3890 if (cur->nameClass != NULL)
3891 xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003892 /*
3893 * This may result in a simplification
3894 */
3895 if ((cur->type == XML_RELAXNG_GROUP) ||
3896 (cur->type == XML_RELAXNG_INTERLEAVE)) {
3897 if (cur->content == NULL)
3898 cur->type = XML_RELAXNG_EMPTY;
3899 else if (cur->content->next == NULL) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003900 if ((parent == NULL) && (prev == NULL)) {
3901 cur->type = XML_RELAXNG_NOOP;
3902 } else if (prev == NULL) {
3903 parent->content = cur->content;
3904 cur->content->next = cur->next;
3905 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003906 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003907 cur->content->next = cur->next;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003908 prev->next = cur->content;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003909 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003910 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003911 }
3912 }
3913 /*
3914 * the current node may have been transformed back
3915 */
3916 if ((cur->type == XML_RELAXNG_EXCEPT) &&
3917 (cur->content != NULL) &&
3918 (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003919 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003920 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
3921 if ((parent != NULL) &&
3922 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
3923 (parent->type == XML_RELAXNG_LIST) ||
3924 (parent->type == XML_RELAXNG_GROUP) ||
3925 (parent->type == XML_RELAXNG_INTERLEAVE) ||
3926 (parent->type == XML_RELAXNG_ONEORMORE) ||
3927 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3928 parent->type = XML_RELAXNG_NOT_ALLOWED;
3929 break;
3930 }
3931 if ((parent != NULL) &&
3932 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003933 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003934 } else
3935 prev = cur;
3936 } else if (cur->type == XML_RELAXNG_EMPTY){
3937 if ((parent != NULL) &&
3938 ((parent->type == XML_RELAXNG_ONEORMORE) ||
3939 (parent->type == XML_RELAXNG_ZEROORMORE))) {
3940 parent->type = XML_RELAXNG_EMPTY;
3941 break;
3942 }
3943 if ((parent != NULL) &&
3944 ((parent->type == XML_RELAXNG_GROUP) ||
3945 (parent->type == XML_RELAXNG_INTERLEAVE) ||
3946 (parent->type == XML_RELAXNG_CHOICE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003947 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003948 } else
3949 prev = cur;
3950 } else {
3951 prev = cur;
3952 }
3953 }
3954 cur = cur->next;
3955 }
3956}
3957
Daniel Veillard77648bb2003-02-20 15:03:22 +00003958
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003959/**
3960 * xmlRelaxNGCheckRules:
3961 * @ctxt: a Relax-NG parser context
3962 * @nodes: grammar children nodes
Daniel Veillard77648bb2003-02-20 15:03:22 +00003963 * @flags: some accumulated flags
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003964 *
Daniel Veillard77648bb2003-02-20 15:03:22 +00003965 * Check for rules in section 7
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003966 *
3967 * Returns 0 if check passed, and -1 in case of error
3968 */
3969static int
3970xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard77648bb2003-02-20 15:03:22 +00003971 xmlRelaxNGDefinePtr cur, int flags) {
3972 int ret = 0, nflags = flags;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003973
Daniel Veillard77648bb2003-02-20 15:03:22 +00003974 while (cur != NULL) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003975 if ((cur->type == XML_RELAXNG_REF) ||
3976 (cur->type == XML_RELAXNG_PARENTREF)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00003977 if (flags & XML_RELAXNG_IN_LIST) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003978 if (ctxt->error != NULL)
3979 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00003980 "Found forbidden pattern list//ref\n");
Daniel Veillard1c745ad2003-02-20 00:11:02 +00003981 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003982 ret = -1;
3983 }
3984 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
3985 if (ctxt->error != NULL)
3986 ctxt->error(ctxt->userData,
3987 "Found forbidden pattern attribute//ref\n");
3988 ctxt->nbErrors++;
3989 ret = -1;
3990 }
3991 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
3992 if (ctxt->error != NULL)
3993 ctxt->error(ctxt->userData,
3994 "Found forbidden pattern data/except//ref\n");
3995 ctxt->nbErrors++;
3996 ret = -1;
3997 }
3998 if (cur->depth != -4) {
3999 cur->depth = -4;
4000 if (xmlRelaxNGCheckRules(ctxt, cur->content, flags) != 0)
4001 ret = -1;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004002 }
4003 } else if (cur->type == XML_RELAXNG_ELEMENT) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004004 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4005 if (ctxt->error != NULL)
4006 ctxt->error(ctxt->userData,
4007 "Found forbidden pattern data/except//element(ref)\n");
4008 ctxt->nbErrors++;
4009 ret = -1;
4010 }
4011 if (flags & XML_RELAXNG_IN_LIST) {
4012 if (ctxt->error != NULL)
4013 ctxt->error(ctxt->userData,
4014 "Found forbidden pattern list//element(ref)\n");
4015 ctxt->nbErrors++;
4016 ret = -1;
4017 }
4018 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4019 if (ctxt->error != NULL)
4020 ctxt->error(ctxt->userData,
4021 "Found forbidden pattern attribute//element(ref)\n");
4022 ctxt->nbErrors++;
4023 ret = -1;
4024 }
4025 /*
4026 * reset since in the simple form elements are only child
4027 * of grammar/define
4028 */
4029 nflags = 0;
4030 if (xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags) != 0)
4031 ret = -1;
4032 if (xmlRelaxNGCheckRules(ctxt, cur->content, nflags) != 0)
4033 ret = -1;
4034 } else if (cur->type == XML_RELAXNG_ATTRIBUTE) {
4035 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4036 if (ctxt->error != NULL)
4037 ctxt->error(ctxt->userData,
4038 "Found forbidden pattern attribute//attribute\n");
4039 ctxt->nbErrors++;
4040 ret = -1;
4041 }
4042 if (flags & XML_RELAXNG_IN_LIST) {
4043 if (ctxt->error != NULL)
4044 ctxt->error(ctxt->userData,
4045 "Found forbidden pattern list//attribute\n");
4046 ctxt->nbErrors++;
4047 ret = -1;
4048 }
4049 if (flags & XML_RELAXNG_IN_OOMGROUP) {
4050 if (ctxt->error != NULL)
4051 ctxt->error(ctxt->userData,
4052 "Found forbidden pattern oneOrMore//group//attribute\n");
4053 ctxt->nbErrors++;
4054 ret = -1;
4055 }
4056 if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) {
4057 if (ctxt->error != NULL)
4058 ctxt->error(ctxt->userData,
4059 "Found forbidden pattern oneOrMore//interleave//attribute\n");
4060 ctxt->nbErrors++;
4061 ret = -1;
4062 }
4063 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4064 if (ctxt->error != NULL)
4065 ctxt->error(ctxt->userData,
4066 "Found forbidden pattern data/except//attribute\n");
4067 ctxt->nbErrors++;
4068 ret = -1;
4069 }
4070 if (flags & XML_RELAXNG_IN_START) {
4071 if (ctxt->error != NULL)
4072 ctxt->error(ctxt->userData,
4073 "Found forbidden pattern start//attribute\n");
4074 ctxt->nbErrors++;
4075 ret = -1;
4076 }
4077 nflags = flags | XML_RELAXNG_IN_ATTRIBUTE;
4078 if (xmlRelaxNGCheckRules(ctxt, cur->content, nflags) != 0)
4079 ret = -1;
4080 } else if ((cur->type == XML_RELAXNG_ONEORMORE) ||
4081 (cur->type == XML_RELAXNG_ZEROORMORE)) {
4082 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4083 if (ctxt->error != NULL)
4084 ctxt->error(ctxt->userData,
4085 "Found forbidden pattern data/except//oneOrMore\n");
4086 ctxt->nbErrors++;
4087 ret = -1;
4088 }
4089 if (flags & XML_RELAXNG_IN_START) {
4090 if (ctxt->error != NULL)
4091 ctxt->error(ctxt->userData,
4092 "Found forbidden pattern start//oneOrMore\n");
4093 ctxt->nbErrors++;
4094 ret = -1;
4095 }
4096 nflags = flags | XML_RELAXNG_IN_ONEORMORE;
4097 if (xmlRelaxNGCheckRules(ctxt, cur->content, nflags) != 0)
4098 ret = -1;
4099 } else if (cur->type == XML_RELAXNG_LIST) {
4100 if (flags & XML_RELAXNG_IN_LIST) {
4101 if (ctxt->error != NULL)
4102 ctxt->error(ctxt->userData,
4103 "Found forbidden pattern list//list\n");
4104 ctxt->nbErrors++;
4105 ret = -1;
4106 }
4107 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4108 if (ctxt->error != NULL)
4109 ctxt->error(ctxt->userData,
4110 "Found forbidden pattern data/except//list\n");
4111 ctxt->nbErrors++;
4112 ret = -1;
4113 }
4114 if (flags & XML_RELAXNG_IN_START) {
4115 if (ctxt->error != NULL)
4116 ctxt->error(ctxt->userData,
4117 "Found forbidden pattern start//list\n");
4118 ctxt->nbErrors++;
4119 ret = -1;
4120 }
4121 nflags = flags | XML_RELAXNG_IN_LIST;
4122 if (xmlRelaxNGCheckRules(ctxt, cur->content, nflags) != 0)
4123 ret = -1;
4124 } else if (cur->type == XML_RELAXNG_GROUP) {
4125 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4126 if (ctxt->error != NULL)
4127 ctxt->error(ctxt->userData,
4128 "Found forbidden pattern data/except//group\n");
4129 ctxt->nbErrors++;
4130 ret = -1;
4131 }
4132 if (flags & XML_RELAXNG_IN_START) {
4133 if (ctxt->error != NULL)
4134 ctxt->error(ctxt->userData,
4135 "Found forbidden pattern start//group\n");
4136 ctxt->nbErrors++;
4137 ret = -1;
4138 }
4139 if (flags & XML_RELAXNG_IN_ONEORMORE)
4140 nflags = flags | XML_RELAXNG_IN_OOMGROUP;
4141 else
4142 nflags = flags;
4143 if (xmlRelaxNGCheckRules(ctxt, cur->content, nflags) != 0)
4144 ret = -1;
4145 } else if (cur->type == XML_RELAXNG_INTERLEAVE) {
4146 if (flags & XML_RELAXNG_IN_LIST) {
4147 if (ctxt->error != NULL)
4148 ctxt->error(ctxt->userData,
4149 "Found forbidden pattern list//interleave\n");
4150 ctxt->nbErrors++;
4151 ret = -1;
4152 }
4153 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4154 if (ctxt->error != NULL)
4155 ctxt->error(ctxt->userData,
4156 "Found forbidden pattern data/except//interleave\n");
4157 ctxt->nbErrors++;
4158 ret = -1;
4159 }
4160 if (flags & XML_RELAXNG_IN_START) {
4161 if (ctxt->error != NULL)
4162 ctxt->error(ctxt->userData,
4163 "Found forbidden pattern start//interleave\n");
4164 ctxt->nbErrors++;
4165 ret = -1;
4166 }
4167 if (flags & XML_RELAXNG_IN_ONEORMORE)
4168 nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE;
4169 else
4170 nflags = flags;
4171 if (xmlRelaxNGCheckRules(ctxt, cur->content, nflags) != 0)
4172 ret = -1;
4173 } else if (cur->type == XML_RELAXNG_EXCEPT) {
4174 if ((cur->parent != NULL) &&
4175 (cur->parent->type == XML_RELAXNG_DATATYPE))
4176 nflags = flags | XML_RELAXNG_IN_DATAEXCEPT;
4177 else
4178 nflags = flags;
4179 if (xmlRelaxNGCheckRules(ctxt, cur->content, nflags) != 0)
4180 ret = -1;
4181 } else if (cur->type == XML_RELAXNG_DATATYPE) {
4182 if (flags & XML_RELAXNG_IN_START) {
4183 if (ctxt->error != NULL)
4184 ctxt->error(ctxt->userData,
4185 "Found forbidden pattern start//data\n");
4186 ctxt->nbErrors++;
4187 ret = -1;
4188 }
4189 if (xmlRelaxNGCheckRules(ctxt, cur->content, flags) != 0)
4190 ret = -1;
4191 } else if (cur->type == XML_RELAXNG_VALUE) {
4192 if (flags & XML_RELAXNG_IN_START) {
4193 if (ctxt->error != NULL)
4194 ctxt->error(ctxt->userData,
4195 "Found forbidden pattern start//value\n");
4196 ctxt->nbErrors++;
4197 ret = -1;
4198 }
4199 if (xmlRelaxNGCheckRules(ctxt, cur->content, flags) != 0)
4200 ret = -1;
4201 } else if (cur->type == XML_RELAXNG_TEXT) {
4202 if (flags & XML_RELAXNG_IN_LIST) {
4203 if (ctxt->error != NULL)
4204 ctxt->error(ctxt->userData,
4205 "Found forbidden pattern list//text\n");
4206 ctxt->nbErrors++;
4207 ret = -1;
4208 }
4209 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4210 if (ctxt->error != NULL)
4211 ctxt->error(ctxt->userData,
4212 "Found forbidden pattern data/except//text\n");
4213 ctxt->nbErrors++;
4214 ret = -1;
4215 }
4216 if (flags & XML_RELAXNG_IN_START) {
4217 if (ctxt->error != NULL)
4218 ctxt->error(ctxt->userData,
4219 "Found forbidden pattern start//text\n");
4220 ctxt->nbErrors++;
4221 ret = -1;
4222 }
4223 } else if (cur->type == XML_RELAXNG_EMPTY) {
4224 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4225 if (ctxt->error != NULL)
4226 ctxt->error(ctxt->userData,
4227 "Found forbidden pattern data/except//empty\n");
4228 ctxt->nbErrors++;
4229 ret = -1;
4230 }
4231 if (flags & XML_RELAXNG_IN_START) {
4232 if (ctxt->error != NULL)
4233 ctxt->error(ctxt->userData,
4234 "Found forbidden pattern start//empty\n");
4235 ctxt->nbErrors++;
4236 ret = -1;
4237 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004238 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004239 if (xmlRelaxNGCheckRules(ctxt, cur->content, flags) != 0)
4240 ret = -1;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004241 }
4242 cur = cur->next;
4243 }
4244 return(ret);
4245}
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004246
4247/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004248 * xmlRelaxNGParseGrammar:
4249 * @ctxt: a Relax-NG parser context
4250 * @nodes: grammar children nodes
4251 *
4252 * parse a Relax-NG <grammar> node
4253 *
4254 * Returns the internal xmlRelaxNGGrammarPtr built or
4255 * NULL in case of error
4256 */
4257static xmlRelaxNGGrammarPtr
4258xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
4259 xmlRelaxNGGrammarPtr ret, tmp, old;
4260
Daniel Veillard6eadf632003-01-23 18:29:16 +00004261 ret = xmlRelaxNGNewGrammar(ctxt);
4262 if (ret == NULL)
4263 return(NULL);
4264
4265 /*
4266 * Link the new grammar in the tree
4267 */
4268 ret->parent = ctxt->grammar;
4269 if (ctxt->grammar != NULL) {
4270 tmp = ctxt->grammar->children;
4271 if (tmp == NULL) {
4272 ctxt->grammar->children = ret;
4273 } else {
4274 while (tmp->next != NULL)
4275 tmp = tmp->next;
4276 tmp->next = ret;
4277 }
4278 }
4279
4280 old = ctxt->grammar;
4281 ctxt->grammar = ret;
4282 xmlRelaxNGParseGrammarContent(ctxt, nodes);
4283 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004284 if (ctxt->grammar == NULL) {
4285 if (ctxt->error != NULL)
4286 ctxt->error(ctxt->userData,
4287 "Failed to parse <grammar> content\n");
4288 ctxt->nbErrors++;
4289 } else if (ctxt->grammar->start == NULL) {
4290 if (ctxt->error != NULL)
4291 ctxt->error(ctxt->userData,
4292 "Element <grammar> has no <start>\n");
4293 ctxt->nbErrors++;
4294 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004295
4296 /*
4297 * Apply 4.17 mergingd rules to defines and starts
4298 */
4299 xmlRelaxNGCombineStart(ctxt, ret);
4300 if (ret->defs != NULL) {
4301 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
4302 ctxt);
4303 }
4304
4305 /*
4306 * link together defines and refs in this grammar
4307 */
4308 if (ret->refs != NULL) {
4309 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
4310 ctxt);
4311 }
4312 ctxt->grammar = old;
4313 return(ret);
4314}
4315
4316/**
4317 * xmlRelaxNGParseDocument:
4318 * @ctxt: a Relax-NG parser context
4319 * @node: the root node of the RelaxNG schema
4320 *
4321 * parse a Relax-NG definition resource and build an internal
4322 * xmlRelaxNG struture which can be used to validate instances.
4323 *
4324 * Returns the internal XML RelaxNG structure built or
4325 * NULL in case of error
4326 */
4327static xmlRelaxNGPtr
4328xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4329 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004330 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00004331 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004332
4333 if ((ctxt == NULL) || (node == NULL))
4334 return (NULL);
4335
4336 schema = xmlRelaxNGNewRelaxNG(ctxt);
4337 if (schema == NULL)
4338 return(NULL);
4339
Daniel Veillard276be4a2003-01-24 01:03:34 +00004340 olddefine = ctxt->define;
4341 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004342 if (IS_RELAXNG(node, "grammar")) {
4343 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
4344 } else {
4345 schema->topgrammar = xmlRelaxNGNewGrammar(ctxt);
4346 if (schema->topgrammar == NULL) {
4347 return(schema);
4348 }
4349 schema->topgrammar->parent = NULL;
Daniel Veillarde431a272003-01-29 23:02:33 +00004350 old = ctxt->grammar;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004351 ctxt->grammar = schema->topgrammar;
4352 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00004353 if (old != NULL)
4354 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004355 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004356 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00004357 if (schema->topgrammar->start != NULL) {
4358 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004359 if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) {
4360 xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
4361 while ((schema->topgrammar->start != NULL) &&
4362 (schema->topgrammar->start->type == XML_RELAXNG_NOOP) &&
4363 (schema->topgrammar->start->next != NULL))
4364 schema->topgrammar->start = schema->topgrammar->start->content;
4365 xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start,
4366 XML_RELAXNG_IN_START);
4367 }
Daniel Veillardd4310742003-02-18 21:12:46 +00004368 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004369
4370#ifdef DEBUG
4371 if (schema == NULL)
4372 xmlGenericError(xmlGenericErrorContext,
4373 "xmlRelaxNGParseDocument() failed\n");
4374#endif
4375
4376 return (schema);
4377}
4378
4379/************************************************************************
4380 * *
4381 * Reading RelaxNGs *
4382 * *
4383 ************************************************************************/
4384
4385/**
4386 * xmlRelaxNGNewParserCtxt:
4387 * @URL: the location of the schema
4388 *
4389 * Create an XML RelaxNGs parse context for that file/resource expected
4390 * to contain an XML RelaxNGs file.
4391 *
4392 * Returns the parser context or NULL in case of error
4393 */
4394xmlRelaxNGParserCtxtPtr
4395xmlRelaxNGNewParserCtxt(const char *URL) {
4396 xmlRelaxNGParserCtxtPtr ret;
4397
4398 if (URL == NULL)
4399 return(NULL);
4400
4401 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4402 if (ret == NULL) {
4403 xmlGenericError(xmlGenericErrorContext,
4404 "Failed to allocate new schama parser context for %s\n", URL);
4405 return (NULL);
4406 }
4407 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4408 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004409 ret->error = xmlGenericError;
4410 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004411 return (ret);
4412}
4413
4414/**
4415 * xmlRelaxNGNewMemParserCtxt:
4416 * @buffer: a pointer to a char array containing the schemas
4417 * @size: the size of the array
4418 *
4419 * Create an XML RelaxNGs parse context for that memory buffer expected
4420 * to contain an XML RelaxNGs file.
4421 *
4422 * Returns the parser context or NULL in case of error
4423 */
4424xmlRelaxNGParserCtxtPtr
4425xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
4426 xmlRelaxNGParserCtxtPtr ret;
4427
4428 if ((buffer == NULL) || (size <= 0))
4429 return(NULL);
4430
4431 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4432 if (ret == NULL) {
4433 xmlGenericError(xmlGenericErrorContext,
4434 "Failed to allocate new schama parser context\n");
4435 return (NULL);
4436 }
4437 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4438 ret->buffer = buffer;
4439 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004440 ret->error = xmlGenericError;
4441 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004442 return (ret);
4443}
4444
4445/**
4446 * xmlRelaxNGFreeParserCtxt:
4447 * @ctxt: the schema parser context
4448 *
4449 * Free the resources associated to the schema parser context
4450 */
4451void
4452xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
4453 if (ctxt == NULL)
4454 return;
4455 if (ctxt->URL != NULL)
4456 xmlFree(ctxt->URL);
4457 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004458 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004459 if (ctxt->interleaves != NULL)
4460 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004461 if (ctxt->documents != NULL)
4462 xmlHashFree(ctxt->documents, (xmlHashDeallocator)
4463 xmlRelaxNGFreeDocument);
4464 if (ctxt->docTab != NULL)
4465 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004466 if (ctxt->incTab != NULL)
4467 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00004468 if (ctxt->defTab != NULL) {
4469 int i;
4470
4471 for (i = 0;i < ctxt->defNr;i++)
4472 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
4473 xmlFree(ctxt->defTab);
4474 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004475 xmlFree(ctxt);
4476}
4477
Daniel Veillard6eadf632003-01-23 18:29:16 +00004478/**
Daniel Veillardd2298792003-02-14 16:54:11 +00004479 * xmlRelaxNGNormExtSpace:
4480 * @value: a value
4481 *
4482 * Removes the leading and ending spaces of the value
4483 * The string is modified "in situ"
4484 */
4485static void
4486xmlRelaxNGNormExtSpace(xmlChar *value) {
4487 xmlChar *start = value;
4488 xmlChar *cur = value;
4489 if (value == NULL)
4490 return;
4491
4492 while (IS_BLANK(*cur)) cur++;
4493 if (cur == start) {
4494 do {
4495 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4496 if (*cur == 0)
4497 return;
4498 start = cur;
4499 while (IS_BLANK(*cur)) cur++;
4500 if (*cur == 0) {
4501 *start = 0;
4502 return;
4503 }
4504 } while (1);
4505 } else {
4506 do {
4507 while ((*cur != 0) && (!IS_BLANK(*cur)))
4508 *start++ = *cur++;
4509 if (*cur == 0) {
4510 *start = 0;
4511 return;
4512 }
4513 /* don't try to normalize the inner spaces */
4514 while (IS_BLANK(*cur)) cur++;
4515 *start++ = *cur++;
4516 if (*cur == 0) {
4517 *start = 0;
4518 return;
4519 }
4520 } while (1);
4521 }
4522}
4523
4524/**
4525 * xmlRelaxNGCheckAttributes:
4526 * @ctxt: a Relax-NG parser context
4527 * @node: a Relax-NG node
4528 *
4529 * Check all the attributes on the given node
4530 */
4531static void
4532xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4533 xmlAttrPtr cur, next;
4534
4535 cur = node->properties;
4536 while (cur != NULL) {
4537 next = cur->next;
4538 if ((cur->ns == NULL) ||
4539 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
4540 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4541 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
4542 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
4543 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
4544 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00004545 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00004546 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4547 if (ctxt->error != NULL)
4548 ctxt->error(ctxt->userData,
4549 "Attribute %s is not allowed on %s\n",
4550 cur->name, node->name);
4551 ctxt->nbErrors++;
4552 }
4553 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
4554 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
4555 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
4556 if (ctxt->error != NULL)
4557 ctxt->error(ctxt->userData,
4558 "Attribute %s is not allowed on %s\n",
4559 cur->name, node->name);
4560 ctxt->nbErrors++;
4561 }
4562 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
4563 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
4564 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
4565 if (ctxt->error != NULL)
4566 ctxt->error(ctxt->userData,
4567 "Attribute %s is not allowed on %s\n",
4568 cur->name, node->name);
4569 ctxt->nbErrors++;
4570 }
4571 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
4572 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
4573 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4574 if (ctxt->error != NULL)
4575 ctxt->error(ctxt->userData,
4576 "Attribute %s is not allowed on %s\n",
4577 cur->name, node->name);
4578 ctxt->nbErrors++;
4579 }
4580 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
4581 xmlChar *val;
4582 xmlURIPtr uri;
4583
4584 val = xmlNodeListGetString(node->doc, cur->children, 1);
4585 if (val != NULL) {
4586 if (val[0] != 0) {
4587 uri = xmlParseURI((const char *) val);
4588 if (uri == NULL) {
4589 if (ctxt->error != NULL)
4590 ctxt->error(ctxt->userData,
4591 "Attribute %s contains invalid URI %s\n",
4592 cur->name, val);
4593 ctxt->nbErrors++;
4594 } else {
4595 if (uri->scheme == NULL) {
4596 if (ctxt->error != NULL)
4597 ctxt->error(ctxt->userData,
4598 "Attribute %s URI %s is not absolute\n",
4599 cur->name, val);
4600 ctxt->nbErrors++;
4601 }
4602 if (uri->fragment != NULL) {
4603 if (ctxt->error != NULL)
4604 ctxt->error(ctxt->userData,
4605 "Attribute %s URI %s has a fragment ID\n",
4606 cur->name, val);
4607 ctxt->nbErrors++;
4608 }
4609 xmlFreeURI(uri);
4610 }
4611 }
4612 xmlFree(val);
4613 }
4614 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
4615 if (ctxt->error != NULL)
4616 ctxt->error(ctxt->userData,
4617 "Unknown attribute %s on %s\n",
4618 cur->name, node->name);
4619 ctxt->nbErrors++;
4620 }
4621 }
4622 cur = next;
4623 }
4624}
4625
4626/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004627 * xmlRelaxNGCleanupDoc:
4628 * @ctxt: a Relax-NG parser context
4629 * @doc: an xmldocPtr document pointer
Daniel Veillard6eadf632003-01-23 18:29:16 +00004630 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004631 * Cleanup the document from unwanted nodes for parsing, resolve
4632 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00004633 *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004634 * Returns the cleaned up document or NULL in case of error
Daniel Veillard6eadf632003-01-23 18:29:16 +00004635 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004636static xmlDocPtr
4637xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004638 xmlNodePtr root, cur, delete;
4639
Daniel Veillard6eadf632003-01-23 18:29:16 +00004640 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004641 * Extract the root
Daniel Veillard6eadf632003-01-23 18:29:16 +00004642 */
4643 root = xmlDocGetRootElement(doc);
4644 if (root == NULL) {
4645 if (ctxt->error != NULL)
4646 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
4647 ctxt->URL);
4648 ctxt->nbErrors++;
4649 return (NULL);
4650 }
4651
4652 /*
4653 * Remove all the blank text nodes
4654 */
4655 delete = NULL;
4656 cur = root;
4657 while (cur != NULL) {
4658 if (delete != NULL) {
4659 xmlUnlinkNode(delete);
4660 xmlFreeNode(delete);
4661 delete = NULL;
4662 }
4663 if (cur->type == XML_ELEMENT_NODE) {
4664 /*
4665 * Simplification 4.1. Annotations
4666 */
4667 if ((cur->ns == NULL) ||
4668 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004669 if ((cur->parent != NULL) &&
4670 (cur->parent->type == XML_ELEMENT_NODE) &&
4671 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4672 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4673 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4674 if (ctxt->error != NULL)
4675 ctxt->error(ctxt->userData,
4676 "element %s doesn't allow foreign elements\n",
4677 cur->parent->name);
4678 ctxt->nbErrors++;
4679 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004680 delete = cur;
4681 goto skip_children;
4682 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004683 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004684 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004685 xmlChar *href, *ns, *base, *URL;
4686 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004687 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004688
4689 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004690 if (ns == NULL) {
4691 tmp = cur->parent;
4692 while ((tmp != NULL) &&
4693 (tmp->type == XML_ELEMENT_NODE)) {
4694 ns = xmlGetProp(tmp, BAD_CAST "ns");
4695 if (ns != NULL)
4696 break;
4697 tmp = tmp->parent;
4698 }
4699 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004700 href = xmlGetProp(cur, BAD_CAST "href");
4701 if (href == NULL) {
4702 if (ctxt->error != NULL)
4703 ctxt->error(ctxt->userData,
4704 "xmlRelaxNGParse: externalRef has no href attribute\n");
4705 ctxt->nbErrors++;
4706 delete = cur;
4707 goto skip_children;
4708 }
4709 base = xmlNodeGetBase(cur->doc, cur);
4710 URL = xmlBuildURI(href, base);
4711 if (URL == NULL) {
4712 if (ctxt->error != NULL)
4713 ctxt->error(ctxt->userData,
4714 "Failed to compute URL for externalRef %s\n", href);
4715 ctxt->nbErrors++;
4716 if (href != NULL)
4717 xmlFree(href);
4718 if (base != NULL)
4719 xmlFree(base);
4720 delete = cur;
4721 goto skip_children;
4722 }
4723 if (href != NULL)
4724 xmlFree(href);
4725 if (base != NULL)
4726 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004727 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004728 if (docu == NULL) {
4729 if (ctxt->error != NULL)
4730 ctxt->error(ctxt->userData,
4731 "Failed to load externalRef %s\n", URL);
4732 ctxt->nbErrors++;
4733 xmlFree(URL);
4734 delete = cur;
4735 goto skip_children;
4736 }
4737 xmlFree(URL);
4738 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004739 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004740 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004741 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00004742 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004743
4744 href = xmlGetProp(cur, BAD_CAST "href");
4745 if (href == NULL) {
4746 if (ctxt->error != NULL)
4747 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004748 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004749 ctxt->nbErrors++;
4750 delete = cur;
4751 goto skip_children;
4752 }
4753 base = xmlNodeGetBase(cur->doc, cur);
4754 URL = xmlBuildURI(href, base);
4755 if (URL == NULL) {
4756 if (ctxt->error != NULL)
4757 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004758 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004759 ctxt->nbErrors++;
4760 if (href != NULL)
4761 xmlFree(href);
4762 if (base != NULL)
4763 xmlFree(base);
4764 delete = cur;
4765 goto skip_children;
4766 }
4767 if (href != NULL)
4768 xmlFree(href);
4769 if (base != NULL)
4770 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00004771 ns = xmlGetProp(cur, BAD_CAST "ns");
4772 if (ns == NULL) {
4773 tmp = cur->parent;
4774 while ((tmp != NULL) &&
4775 (tmp->type == XML_ELEMENT_NODE)) {
4776 ns = xmlGetProp(tmp, BAD_CAST "ns");
4777 if (ns != NULL)
4778 break;
4779 tmp = tmp->parent;
4780 }
4781 }
4782 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
4783 if (ns != NULL)
4784 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004785 if (incl == NULL) {
4786 if (ctxt->error != NULL)
4787 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004788 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004789 ctxt->nbErrors++;
4790 xmlFree(URL);
4791 delete = cur;
4792 goto skip_children;
4793 }
4794 xmlFree(URL);
4795 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004796 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
4797 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00004798 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004799 xmlNodePtr text = NULL;
4800
4801 /*
4802 * Simplification 4.8. name attribute of element
4803 * and attribute elements
4804 */
4805 name = xmlGetProp(cur, BAD_CAST "name");
4806 if (name != NULL) {
4807 if (cur->children == NULL) {
4808 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
4809 name);
4810 } else {
4811 xmlNodePtr node;
4812 node = xmlNewNode(cur->ns, BAD_CAST "name");
4813 if (node != NULL) {
4814 xmlAddPrevSibling(cur->children, node);
4815 text = xmlNewText(name);
4816 xmlAddChild(node, text);
4817 text = node;
4818 }
4819 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004820 if (text == NULL) {
4821 if (ctxt->error != NULL)
4822 ctxt->error(ctxt->userData,
4823 "Failed to create a name %s element\n", name);
4824 ctxt->nbErrors++;
4825 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004826 xmlUnsetProp(cur, BAD_CAST "name");
4827 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00004828 ns = xmlGetProp(cur, BAD_CAST "ns");
4829 if (ns != NULL) {
4830 if (text != NULL) {
4831 xmlSetProp(text, BAD_CAST "ns", ns);
4832 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00004833 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00004834 xmlFree(ns);
4835 } else if (xmlStrEqual(cur->name,
4836 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004837 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
4838 }
4839 }
4840 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
4841 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
4842 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
4843 /*
4844 * Simplification 4.8. name attribute of element
4845 * and attribute elements
4846 */
4847 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
4848 xmlNodePtr node;
4849 xmlChar *ns = NULL;
4850
4851 node = cur->parent;
4852 while ((node != NULL) &&
4853 (node->type == XML_ELEMENT_NODE)) {
4854 ns = xmlGetProp(node, BAD_CAST "ns");
4855 if (ns != NULL) {
4856 break;
4857 }
4858 node = node->parent;
4859 }
4860 if (ns == NULL) {
4861 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
4862 } else {
4863 xmlSetProp(cur, BAD_CAST "ns", ns);
4864 xmlFree(ns);
4865 }
4866 }
4867 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4868 xmlChar *name, *local, *prefix;
4869
4870 /*
4871 * Simplification: 4.10. QNames
4872 */
4873 name = xmlNodeGetContent(cur);
4874 if (name != NULL) {
4875 local = xmlSplitQName2(name, &prefix);
4876 if (local != NULL) {
4877 xmlNsPtr ns;
4878
4879 ns = xmlSearchNs(cur->doc, cur, prefix);
4880 if (ns == NULL) {
4881 if (ctxt->error != NULL)
4882 ctxt->error(ctxt->userData,
4883 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
4884 ctxt->nbErrors++;
4885 } else {
4886 xmlSetProp(cur, BAD_CAST "ns", ns->href);
4887 xmlNodeSetContent(cur, local);
4888 }
4889 xmlFree(local);
4890 xmlFree(prefix);
4891 }
4892 xmlFree(name);
4893 }
4894 }
4895 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004896 /*
4897 * Thisd is not an else since "include" is transformed
4898 * into a div
4899 */
4900 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004901 xmlChar *ns;
4902 xmlNodePtr child, ins, tmp;
4903
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004904 /*
4905 * implements rule 4.11
4906 */
Daniel Veillard416589a2003-02-17 17:25:42 +00004907
4908 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004909
4910 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004911 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004912 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00004913 if (ns != NULL) {
4914 if (!xmlHasProp(child, BAD_CAST "ns")) {
4915 xmlSetProp(child, BAD_CAST "ns", ns);
4916 }
4917 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004918 tmp = child->next;
4919 xmlUnlinkNode(child);
4920 ins = xmlAddNextSibling(ins, child);
4921 child = tmp;
4922 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004923 if (ns != NULL)
4924 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004925 delete = cur;
4926 goto skip_children;
4927 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004928 }
4929 }
4930 /*
4931 * Simplification 4.2 whitespaces
4932 */
4933 else if (cur->type == XML_TEXT_NODE) {
4934 if (IS_BLANK_NODE(cur)) {
4935 if (cur->parent->type == XML_ELEMENT_NODE) {
4936 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
4937 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
4938 delete = cur;
4939 } else {
4940 delete = cur;
4941 goto skip_children;
4942 }
4943 }
4944 } else if (cur->type != XML_CDATA_SECTION_NODE) {
4945 delete = cur;
4946 goto skip_children;
4947 }
4948
4949 /*
4950 * Skip to next node
4951 */
4952 if (cur->children != NULL) {
4953 if ((cur->children->type != XML_ENTITY_DECL) &&
4954 (cur->children->type != XML_ENTITY_REF_NODE) &&
4955 (cur->children->type != XML_ENTITY_NODE)) {
4956 cur = cur->children;
4957 continue;
4958 }
4959 }
4960skip_children:
4961 if (cur->next != NULL) {
4962 cur = cur->next;
4963 continue;
4964 }
4965
4966 do {
4967 cur = cur->parent;
4968 if (cur == NULL)
4969 break;
4970 if (cur == root) {
4971 cur = NULL;
4972 break;
4973 }
4974 if (cur->next != NULL) {
4975 cur = cur->next;
4976 break;
4977 }
4978 } while (cur != NULL);
4979 }
4980 if (delete != NULL) {
4981 xmlUnlinkNode(delete);
4982 xmlFreeNode(delete);
4983 delete = NULL;
4984 }
4985
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004986 return(doc);
4987}
4988
4989/**
4990 * xmlRelaxNGParse:
4991 * @ctxt: a Relax-NG parser context
4992 *
4993 * parse a schema definition resource and build an internal
4994 * XML Shema struture which can be used to validate instances.
4995 * *WARNING* this interface is highly subject to change
4996 *
4997 * Returns the internal XML RelaxNG structure built from the resource or
4998 * NULL in case of error
4999 */
5000xmlRelaxNGPtr
5001xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
5002{
5003 xmlRelaxNGPtr ret = NULL;
5004 xmlDocPtr doc;
5005 xmlNodePtr root;
5006
5007 xmlRelaxNGInitTypes();
5008
5009 if (ctxt == NULL)
5010 return (NULL);
5011
5012 /*
5013 * First step is to parse the input document into an DOM/Infoset
5014 */
5015 if (ctxt->URL != NULL) {
5016 doc = xmlParseFile((const char *) ctxt->URL);
5017 if (doc == NULL) {
5018 if (ctxt->error != NULL)
5019 ctxt->error(ctxt->userData,
5020 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
5021 ctxt->nbErrors++;
5022 return (NULL);
5023 }
5024 } else if (ctxt->buffer != NULL) {
5025 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
5026 if (doc == NULL) {
5027 if (ctxt->error != NULL)
5028 ctxt->error(ctxt->userData,
5029 "xmlRelaxNGParse: could not parse schemas\n");
5030 ctxt->nbErrors++;
5031 return (NULL);
5032 }
5033 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5034 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5035 } else {
5036 if (ctxt->error != NULL)
5037 ctxt->error(ctxt->userData,
5038 "xmlRelaxNGParse: nothing to parse\n");
5039 ctxt->nbErrors++;
5040 return (NULL);
5041 }
5042 ctxt->document = doc;
5043
5044 /*
5045 * Some preprocessing of the document content
5046 */
5047 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
5048 if (doc == NULL) {
5049 xmlFreeDoc(ctxt->document);
5050 ctxt->document = NULL;
5051 return(NULL);
5052 }
5053
Daniel Veillard6eadf632003-01-23 18:29:16 +00005054 /*
5055 * Then do the parsing for good
5056 */
5057 root = xmlDocGetRootElement(doc);
5058 if (root == NULL) {
5059 if (ctxt->error != NULL)
5060 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5061 ctxt->URL);
5062 ctxt->nbErrors++;
5063 return (NULL);
5064 }
5065 ret = xmlRelaxNGParseDocument(ctxt, root);
5066 if (ret == NULL)
5067 return(NULL);
5068
5069 /*
5070 * Check the ref/defines links
5071 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005072 /*
5073 * try to preprocess interleaves
5074 */
5075 if (ctxt->interleaves != NULL) {
5076 xmlHashScan(ctxt->interleaves,
5077 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
5078 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005079
5080 /*
5081 * if there was a parsing error return NULL
5082 */
5083 if (ctxt->nbErrors > 0) {
5084 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005085 ctxt->document = NULL;
5086 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005087 return(NULL);
5088 }
5089
5090 /*
5091 * Transfer the pointer for cleanup at the schema level.
5092 */
5093 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005094 ctxt->document = NULL;
5095 ret->documents = ctxt->documents;
5096 ctxt->documents = NULL;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005097 ret->includes = ctxt->includes;
5098 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00005099 ret->defNr = ctxt->defNr;
5100 ret->defTab = ctxt->defTab;
5101 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005102
5103 return (ret);
5104}
5105
5106/**
5107 * xmlRelaxNGSetParserErrors:
5108 * @ctxt: a Relax-NG validation context
5109 * @err: the error callback
5110 * @warn: the warning callback
5111 * @ctx: contextual data for the callbacks
5112 *
5113 * Set the callback functions used to handle errors for a validation context
5114 */
5115void
5116xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
5117 xmlRelaxNGValidityErrorFunc err,
5118 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5119 if (ctxt == NULL)
5120 return;
5121 ctxt->error = err;
5122 ctxt->warning = warn;
5123 ctxt->userData = ctx;
5124}
5125/************************************************************************
5126 * *
5127 * Dump back a compiled form *
5128 * *
5129 ************************************************************************/
5130static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
5131
5132/**
5133 * xmlRelaxNGDumpDefines:
5134 * @output: the file output
5135 * @defines: a list of define structures
5136 *
5137 * Dump a RelaxNG structure back
5138 */
5139static void
5140xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
5141 while (defines != NULL) {
5142 xmlRelaxNGDumpDefine(output, defines);
5143 defines = defines->next;
5144 }
5145}
5146
5147/**
5148 * xmlRelaxNGDumpDefine:
5149 * @output: the file output
5150 * @define: a define structure
5151 *
5152 * Dump a RelaxNG structure back
5153 */
5154static void
5155xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
5156 if (define == NULL)
5157 return;
5158 switch(define->type) {
5159 case XML_RELAXNG_EMPTY:
5160 fprintf(output, "<empty/>\n");
5161 break;
5162 case XML_RELAXNG_NOT_ALLOWED:
5163 fprintf(output, "<notAllowed/>\n");
5164 break;
5165 case XML_RELAXNG_TEXT:
5166 fprintf(output, "<text/>\n");
5167 break;
5168 case XML_RELAXNG_ELEMENT:
5169 fprintf(output, "<element>\n");
5170 if (define->name != NULL) {
5171 fprintf(output, "<name");
5172 if (define->ns != NULL)
5173 fprintf(output, " ns=\"%s\"", define->ns);
5174 fprintf(output, ">%s</name>\n", define->name);
5175 }
5176 xmlRelaxNGDumpDefines(output, define->attrs);
5177 xmlRelaxNGDumpDefines(output, define->content);
5178 fprintf(output, "</element>\n");
5179 break;
5180 case XML_RELAXNG_LIST:
5181 fprintf(output, "<list>\n");
5182 xmlRelaxNGDumpDefines(output, define->content);
5183 fprintf(output, "</list>\n");
5184 break;
5185 case XML_RELAXNG_ONEORMORE:
5186 fprintf(output, "<oneOrMore>\n");
5187 xmlRelaxNGDumpDefines(output, define->content);
5188 fprintf(output, "</oneOrMore>\n");
5189 break;
5190 case XML_RELAXNG_ZEROORMORE:
5191 fprintf(output, "<zeroOrMore>\n");
5192 xmlRelaxNGDumpDefines(output, define->content);
5193 fprintf(output, "</zeroOrMore>\n");
5194 break;
5195 case XML_RELAXNG_CHOICE:
5196 fprintf(output, "<choice>\n");
5197 xmlRelaxNGDumpDefines(output, define->content);
5198 fprintf(output, "</choice>\n");
5199 break;
5200 case XML_RELAXNG_GROUP:
5201 fprintf(output, "<group>\n");
5202 xmlRelaxNGDumpDefines(output, define->content);
5203 fprintf(output, "</group>\n");
5204 break;
5205 case XML_RELAXNG_INTERLEAVE:
5206 fprintf(output, "<interleave>\n");
5207 xmlRelaxNGDumpDefines(output, define->content);
5208 fprintf(output, "</interleave>\n");
5209 break;
5210 case XML_RELAXNG_OPTIONAL:
5211 fprintf(output, "<optional>\n");
5212 xmlRelaxNGDumpDefines(output, define->content);
5213 fprintf(output, "</optional>\n");
5214 break;
5215 case XML_RELAXNG_ATTRIBUTE:
5216 fprintf(output, "<attribute>\n");
5217 xmlRelaxNGDumpDefines(output, define->content);
5218 fprintf(output, "</attribute>\n");
5219 break;
5220 case XML_RELAXNG_DEF:
5221 fprintf(output, "<define");
5222 if (define->name != NULL)
5223 fprintf(output, " name=\"%s\"", define->name);
5224 fprintf(output, ">\n");
5225 xmlRelaxNGDumpDefines(output, define->content);
5226 fprintf(output, "</define>\n");
5227 break;
5228 case XML_RELAXNG_REF:
5229 fprintf(output, "<ref");
5230 if (define->name != NULL)
5231 fprintf(output, " name=\"%s\"", define->name);
5232 fprintf(output, ">\n");
5233 xmlRelaxNGDumpDefines(output, define->content);
5234 fprintf(output, "</ref>\n");
5235 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00005236 case XML_RELAXNG_PARENTREF:
5237 fprintf(output, "<parentRef");
5238 if (define->name != NULL)
5239 fprintf(output, " name=\"%s\"", define->name);
5240 fprintf(output, ">\n");
5241 xmlRelaxNGDumpDefines(output, define->content);
5242 fprintf(output, "</parentRef>\n");
5243 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005244 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00005245 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00005246 xmlRelaxNGDumpDefines(output, define->content);
5247 fprintf(output, "</externalRef>\n");
5248 break;
5249 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005250 case XML_RELAXNG_VALUE:
5251 TODO
5252 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005253 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005254 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00005255 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005256 TODO
5257 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00005258 case XML_RELAXNG_NOOP:
5259 xmlRelaxNGDumpDefines(output, define->content);
5260 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005261 }
5262}
5263
5264/**
5265 * xmlRelaxNGDumpGrammar:
5266 * @output: the file output
5267 * @grammar: a grammar structure
5268 * @top: is this a top grammar
5269 *
5270 * Dump a RelaxNG structure back
5271 */
5272static void
5273xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
5274{
5275 if (grammar == NULL)
5276 return;
5277
5278 fprintf(output, "<grammar");
5279 if (top)
5280 fprintf(output,
5281 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
5282 switch(grammar->combine) {
5283 case XML_RELAXNG_COMBINE_UNDEFINED:
5284 break;
5285 case XML_RELAXNG_COMBINE_CHOICE:
5286 fprintf(output, " combine=\"choice\"");
5287 break;
5288 case XML_RELAXNG_COMBINE_INTERLEAVE:
5289 fprintf(output, " combine=\"interleave\"");
5290 break;
5291 default:
5292 fprintf(output, " <!-- invalid combine value -->");
5293 }
5294 fprintf(output, ">\n");
5295 if (grammar->start == NULL) {
5296 fprintf(output, " <!-- grammar had no start -->");
5297 } else {
5298 fprintf(output, "<start>\n");
5299 xmlRelaxNGDumpDefine(output, grammar->start);
5300 fprintf(output, "</start>\n");
5301 }
5302 /* TODO ? Dump the defines ? */
5303 fprintf(output, "</grammar>\n");
5304}
5305
5306/**
5307 * xmlRelaxNGDump:
5308 * @output: the file output
5309 * @schema: a schema structure
5310 *
5311 * Dump a RelaxNG structure back
5312 */
5313void
5314xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
5315{
5316 if (schema == NULL) {
5317 fprintf(output, "RelaxNG empty or failed to compile\n");
5318 return;
5319 }
5320 fprintf(output, "RelaxNG: ");
5321 if (schema->doc == NULL) {
5322 fprintf(output, "no document\n");
5323 } else if (schema->doc->URL != NULL) {
5324 fprintf(output, "%s\n", schema->doc->URL);
5325 } else {
5326 fprintf(output, "\n");
5327 }
5328 if (schema->topgrammar == NULL) {
5329 fprintf(output, "RelaxNG has no top grammar\n");
5330 return;
5331 }
5332 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
5333}
5334
Daniel Veillardfebcca42003-02-16 15:44:18 +00005335/**
5336 * xmlRelaxNGDumpTree:
5337 * @output: the file output
5338 * @schema: a schema structure
5339 *
5340 * Dump the transformed RelaxNG tree.
5341 */
5342void
5343xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
5344{
5345 if (schema == NULL) {
5346 fprintf(output, "RelaxNG empty or failed to compile\n");
5347 return;
5348 }
5349 if (schema->doc == NULL) {
5350 fprintf(output, "no document\n");
5351 } else {
5352 xmlDocDump(output, schema->doc);
5353 }
5354}
5355
Daniel Veillard6eadf632003-01-23 18:29:16 +00005356/************************************************************************
5357 * *
5358 * Validation implementation *
5359 * *
5360 ************************************************************************/
5361static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5362 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005363static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5364 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005365
5366/**
5367 * xmlRelaxNGSkipIgnored:
5368 * @ctxt: a schema validation context
5369 * @node: the top node.
5370 *
5371 * Skip ignorable nodes in that context
5372 *
5373 * Returns the new sibling or NULL in case of error.
5374 */
5375static xmlNodePtr
5376xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5377 xmlNodePtr node) {
5378 /*
5379 * TODO complete and handle entities
5380 */
5381 while ((node != NULL) &&
5382 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005383 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005384 ((node->type == XML_TEXT_NODE) &&
5385 (IS_BLANK_NODE(node))))) {
5386 node = node->next;
5387 }
5388 return(node);
5389}
5390
5391/**
Daniel Veillardedc91922003-01-26 00:52:04 +00005392 * xmlRelaxNGNormalize:
5393 * @ctxt: a schema validation context
5394 * @str: the string to normalize
5395 *
5396 * Implements the normalizeWhiteSpace( s ) function from
5397 * section 6.2.9 of the spec
5398 *
5399 * Returns the new string or NULL in case of error.
5400 */
5401static xmlChar *
5402xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
5403 xmlChar *ret, *p;
5404 const xmlChar *tmp;
5405 int len;
5406
5407 if (str == NULL)
5408 return(NULL);
5409 tmp = str;
5410 while (*tmp != 0) tmp++;
5411 len = tmp - str;
5412
5413 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
5414 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005415 if (ctxt != NULL) {
5416 VALID_CTXT();
5417 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
5418 } else {
5419 xmlGenericError(xmlGenericErrorContext,
5420 "xmlRelaxNGNormalize: out of memory\n");
5421 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005422 return(NULL);
5423 }
5424 p = ret;
5425 while (IS_BLANK(*str)) str++;
5426 while (*str != 0) {
5427 if (IS_BLANK(*str)) {
5428 while (IS_BLANK(*str)) str++;
5429 if (*str == 0)
5430 break;
5431 *p++ = ' ';
5432 } else
5433 *p++ = *str++;
5434 }
5435 *p = 0;
5436 return(ret);
5437}
5438
5439/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005440 * xmlRelaxNGValidateDatatype:
5441 * @ctxt: a Relax-NG validation context
5442 * @value: the string value
5443 * @type: the datatype definition
5444 *
5445 * Validate the given value against the dataype
5446 *
5447 * Returns 0 if the validation succeeded or an error code.
5448 */
5449static int
5450xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
5451 xmlRelaxNGDefinePtr define) {
5452 int ret;
5453 xmlRelaxNGTypeLibraryPtr lib;
5454
5455 if ((define == NULL) || (define->data == NULL)) {
5456 return(-1);
5457 }
5458 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5459 if (lib->check != NULL)
5460 ret = lib->check(lib->data, define->name, value);
5461 else
5462 ret = -1;
5463 if (ret < 0) {
5464 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005465 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005466 return(-1);
5467 } else if (ret == 1) {
5468 ret = 0;
5469 } else {
5470 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005471 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005472 return(-1);
5473 ret = -1;
5474 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005475 if ((ret == 0) && (define->content != NULL)) {
5476 const xmlChar *oldvalue, *oldendvalue;
5477
5478 oldvalue = ctxt->state->value;
5479 oldendvalue = ctxt->state->endvalue;
5480 ctxt->state->value = (xmlChar *) value;
5481 ctxt->state->endvalue = NULL;
5482 ret = xmlRelaxNGValidateValue(ctxt, define->content);
5483 ctxt->state->value = (xmlChar *) oldvalue;
5484 ctxt->state->endvalue = (xmlChar *) oldendvalue;
5485 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005486 return(ret);
5487}
5488
5489/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005490 * xmlRelaxNGNextValue:
5491 * @ctxt: a Relax-NG validation context
5492 *
5493 * Skip to the next value when validating within a list
5494 *
5495 * Returns 0 if the operation succeeded or an error code.
5496 */
5497static int
5498xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
5499 xmlChar *cur;
5500
5501 cur = ctxt->state->value;
5502 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
5503 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005504 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005505 return(0);
5506 }
5507 while (*cur != 0) cur++;
5508 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
5509 if (cur == ctxt->state->endvalue)
5510 ctxt->state->value = NULL;
5511 else
5512 ctxt->state->value = cur;
5513 return(0);
5514}
5515
5516/**
5517 * xmlRelaxNGValidateValueList:
5518 * @ctxt: a Relax-NG validation context
5519 * @defines: the list of definitions to verify
5520 *
5521 * Validate the given set of definitions for the current value
5522 *
5523 * Returns 0 if the validation succeeded or an error code.
5524 */
5525static int
5526xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
5527 xmlRelaxNGDefinePtr defines) {
5528 int ret = 0;
5529
5530 while (defines != NULL) {
5531 ret = xmlRelaxNGValidateValue(ctxt, defines);
5532 if (ret != 0)
5533 break;
5534 defines = defines->next;
5535 }
5536 return(ret);
5537}
5538
5539/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005540 * xmlRelaxNGValidateValue:
5541 * @ctxt: a Relax-NG validation context
5542 * @define: the definition to verify
5543 *
5544 * Validate the given definition for the current value
5545 *
5546 * Returns 0 if the validation succeeded or an error code.
5547 */
5548static int
5549xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5550 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00005551 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005552 xmlChar *value;
5553
5554 value = ctxt->state->value;
5555 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005556 case XML_RELAXNG_EMPTY: {
5557 if ((value != NULL) && (value[0] != 0)) {
5558 int idx = 0;
5559
5560 while (IS_BLANK(value[idx]))
5561 idx++;
5562 if (value[idx] != 0)
5563 ret = -1;
5564 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005565 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00005566 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005567 case XML_RELAXNG_TEXT:
5568 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00005569 case XML_RELAXNG_VALUE: {
5570 if (!xmlStrEqual(value, define->value)) {
5571 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005572 xmlRelaxNGTypeLibraryPtr lib;
5573
5574 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5575 if ((lib != NULL) && (lib->comp != NULL))
5576 ret = lib->comp(lib->data, define->name, value,
5577 define->value);
5578 else
5579 ret = -1;
5580 if (ret < 0) {
5581 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005582 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005583 define->name);
5584 return(-1);
5585 } else if (ret == 1) {
5586 ret = 0;
5587 } else {
5588 ret = -1;
5589 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005590 } else {
5591 xmlChar *nval, *nvalue;
5592
5593 /*
5594 * TODO: trivial optimizations are possible by
5595 * computing at compile-time
5596 */
5597 nval = xmlRelaxNGNormalize(ctxt, define->value);
5598 nvalue = xmlRelaxNGNormalize(ctxt, value);
5599
Daniel Veillardea3f3982003-01-26 19:45:18 +00005600 if ((nval == NULL) || (nvalue == NULL) ||
5601 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00005602 ret = -1;
5603 if (nval != NULL)
5604 xmlFree(nval);
5605 if (nvalue != NULL)
5606 xmlFree(nvalue);
5607 }
5608 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005609 if (ret == 0)
5610 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00005611 break;
5612 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005613 case XML_RELAXNG_DATATYPE: {
5614 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
5615 if (ret == 0)
5616 xmlRelaxNGNextValue(ctxt);
5617
5618 break;
5619 }
5620 case XML_RELAXNG_CHOICE: {
5621 xmlRelaxNGDefinePtr list = define->content;
5622 xmlChar *oldvalue;
5623
5624 oldflags = ctxt->flags;
5625 ctxt->flags |= FLAGS_IGNORABLE;
5626
5627 oldvalue = ctxt->state->value;
5628 while (list != NULL) {
5629 ret = xmlRelaxNGValidateValue(ctxt, list);
5630 if (ret == 0) {
5631 break;
5632 }
5633 ctxt->state->value = oldvalue;
5634 list = list->next;
5635 }
5636 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00005637 if (ret == 0)
5638 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005639 break;
5640 }
5641 case XML_RELAXNG_LIST: {
5642 xmlRelaxNGDefinePtr list = define->content;
5643 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00005644#ifdef DEBUG_LIST
5645 int nb_values = 0;
5646#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005647
5648 oldvalue = ctxt->state->value;
5649 oldend = ctxt->state->endvalue;
5650
5651 val = xmlStrdup(oldvalue);
5652 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005653 val = xmlStrdup(BAD_CAST "");
5654 }
5655 if (val == NULL) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005656 VALID_CTXT();
5657 VALID_ERROR("Internal: no state\n");
5658 return(-1);
5659 }
5660 cur = val;
5661 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005662 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005663 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00005664 cur++;
5665#ifdef DEBUG_LIST
5666 nb_values++;
5667#endif
5668 while (IS_BLANK(*cur))
5669 *cur++ = 0;
5670 } else
5671 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005672 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005673#ifdef DEBUG_LIST
5674 xmlGenericError(xmlGenericErrorContext,
5675 "list value: '%s' found %d items\n", oldvalue, nb_values);
5676 nb_values = 0;
5677#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005678 ctxt->state->endvalue = cur;
5679 cur = val;
5680 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
5681
5682 ctxt->state->value = cur;
5683
5684 while (list != NULL) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005685 if (ctxt->state->value == ctxt->state->endvalue)
5686 ctxt->state->value = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005687 ret = xmlRelaxNGValidateValue(ctxt, list);
5688 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005689#ifdef DEBUG_LIST
5690 xmlGenericError(xmlGenericErrorContext,
5691 "Failed to validate value: '%s' with %d rule\n",
5692 ctxt->state->value, nb_values);
5693#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005694 break;
5695 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005696#ifdef DEBUG_LIST
5697 nb_values++;
5698#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005699 list = list->next;
5700 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00005701
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005702 if ((ret == 0) && (ctxt->state->value != NULL) &&
5703 (ctxt->state->value != ctxt->state->endvalue)) {
5704 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005705 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005706 ret = -1;
5707 }
5708 xmlFree(val);
5709 ctxt->state->value = oldvalue;
5710 ctxt->state->endvalue = oldend;
5711 break;
5712 }
5713 case XML_RELAXNG_ONEORMORE:
5714 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5715 if (ret != 0) {
5716 break;
5717 }
5718 /* no break on purpose */
5719 case XML_RELAXNG_ZEROORMORE: {
5720 xmlChar *cur, *temp;
5721
5722 oldflags = ctxt->flags;
5723 ctxt->flags |= FLAGS_IGNORABLE;
5724 cur = ctxt->state->value;
5725 temp = NULL;
5726 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
5727 (temp != cur)) {
5728 temp = cur;
5729 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
5730 if (ret != 0) {
5731 ctxt->state->value = temp;
5732 ret = 0;
5733 break;
5734 }
5735 cur = ctxt->state->value;
5736 }
5737 ctxt->flags = oldflags;
5738 break;
5739 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005740 case XML_RELAXNG_EXCEPT: {
5741 xmlRelaxNGDefinePtr list;
5742
5743 list = define->content;
5744 while (list != NULL) {
5745 ret = xmlRelaxNGValidateValue(ctxt, list);
5746 if (ret == 0) {
5747 ret = -1;
5748 break;
5749 } else
5750 ret = 0;
5751 list = list->next;
5752 }
5753 break;
5754 }
Daniel Veillardd4310742003-02-18 21:12:46 +00005755 case XML_RELAXNG_GROUP: {
5756 xmlRelaxNGDefinePtr list;
5757
5758 list = define->content;
5759 while (list != NULL) {
5760 ret = xmlRelaxNGValidateValue(ctxt, list);
5761 if (ret != 0) {
5762 ret = -1;
5763 break;
5764 } else
5765 ret = 0;
5766 list = list->next;
5767 }
5768 break;
5769 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005770 default:
5771 TODO
5772 ret = -1;
5773 }
5774 return(ret);
5775}
5776
5777/**
5778 * xmlRelaxNGValidateValueContent:
5779 * @ctxt: a Relax-NG validation context
5780 * @defines: the list of definitions to verify
5781 *
5782 * Validate the given definitions for the current value
5783 *
5784 * Returns 0 if the validation succeeded or an error code.
5785 */
5786static int
5787xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
5788 xmlRelaxNGDefinePtr defines) {
5789 int ret = 0;
5790
5791 while (defines != NULL) {
5792 ret = xmlRelaxNGValidateValue(ctxt, defines);
5793 if (ret != 0)
5794 break;
5795 defines = defines->next;
5796 }
5797 return(ret);
5798}
5799
5800/**
Daniel Veillard144fae12003-02-03 13:17:57 +00005801 * xmlRelaxNGAttributeMatch:
5802 * @ctxt: a Relax-NG validation context
5803 * @define: the definition to check
5804 * @prop: the attribute
5805 *
5806 * Check if the attribute matches the definition nameClass
5807 *
5808 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
5809 */
5810static int
5811xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
5812 xmlRelaxNGDefinePtr define,
5813 xmlAttrPtr prop) {
5814 int ret;
5815
5816 if (define->name != NULL) {
5817 if (!xmlStrEqual(define->name, prop->name))
5818 return(0);
5819 }
5820 if (define->ns != NULL) {
5821 if (define->ns[0] == 0) {
5822 if (prop->ns != NULL)
5823 return(0);
5824 } else {
5825 if ((prop->ns == NULL) ||
5826 (!xmlStrEqual(define->ns, prop->ns->href)))
5827 return(0);
5828 }
5829 }
5830 if (define->nameClass == NULL)
5831 return(1);
5832 define = define->nameClass;
5833 if (define->type == XML_RELAXNG_EXCEPT) {
5834 xmlRelaxNGDefinePtr list;
5835
5836 list = define->content;
5837 while (list != NULL) {
5838 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
5839 if (ret == 1)
5840 return(0);
5841 if (ret < 0)
5842 return(ret);
5843 list = list->next;
5844 }
5845 } else {
5846 TODO
5847 }
5848 return(1);
5849}
5850
5851/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005852 * xmlRelaxNGValidateAttribute:
5853 * @ctxt: a Relax-NG validation context
5854 * @define: the definition to verify
5855 *
5856 * Validate the given attribute definition for that node
5857 *
5858 * Returns 0 if the validation succeeded or an error code.
5859 */
5860static int
5861xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
5862 xmlRelaxNGDefinePtr define) {
5863 int ret = 0, i;
5864 xmlChar *value, *oldvalue;
5865 xmlAttrPtr prop = NULL, tmp;
5866
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005867 if (ctxt->state->nbAttrLeft <= 0)
5868 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005869 if (define->name != NULL) {
5870 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5871 tmp = ctxt->state->attrs[i];
5872 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
5873 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
5874 (tmp->ns == NULL)) ||
5875 ((tmp->ns != NULL) &&
5876 (xmlStrEqual(define->ns, tmp->ns->href)))) {
5877 prop = tmp;
5878 break;
5879 }
5880 }
5881 }
5882 if (prop != NULL) {
5883 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5884 oldvalue = ctxt->state->value;
5885 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00005886 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005887 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005888 if (ctxt->state->value != NULL)
5889 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005890 if (value != NULL)
5891 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005892 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005893 if (ret == 0) {
5894 /*
5895 * flag the attribute as processed
5896 */
5897 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005898 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005899 }
5900 } else {
5901 ret = -1;
5902 }
5903#ifdef DEBUG
5904 xmlGenericError(xmlGenericErrorContext,
5905 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
5906#endif
5907 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005908 for (i = 0;i < ctxt->state->nbAttrs;i++) {
5909 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00005910 if ((tmp != NULL) &&
5911 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005912 prop = tmp;
5913 break;
5914 }
5915 }
5916 if (prop != NULL) {
5917 value = xmlNodeListGetString(prop->doc, prop->children, 1);
5918 oldvalue = ctxt->state->value;
5919 ctxt->state->value = value;
5920 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00005921 if (ctxt->state->value != NULL)
5922 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005923 if (value != NULL)
5924 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00005925 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005926 if (ret == 0) {
5927 /*
5928 * flag the attribute as processed
5929 */
5930 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005931 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005932 }
5933 } else {
5934 ret = -1;
5935 }
5936#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00005937 if (define->ns != NULL) {
5938 xmlGenericError(xmlGenericErrorContext,
5939 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
5940 define->ns, ret);
5941 } else {
5942 xmlGenericError(xmlGenericErrorContext,
5943 "xmlRelaxNGValidateAttribute(anyName): %d\n",
5944 ret);
5945 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00005946#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00005947 }
5948
5949 return(ret);
5950}
5951
5952/**
5953 * xmlRelaxNGValidateAttributeList:
5954 * @ctxt: a Relax-NG validation context
5955 * @define: the list of definition to verify
5956 *
5957 * Validate the given node against the list of attribute definitions
5958 *
5959 * Returns 0 if the validation succeeded or an error code.
5960 */
5961static int
5962xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
5963 xmlRelaxNGDefinePtr defines) {
5964 int ret = 0;
5965 while (defines != NULL) {
5966 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
5967 ret = -1;
5968 defines = defines->next;
5969 }
5970 return(ret);
5971}
5972
5973/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005974 * xmlRelaxNGValidateTryPermutation:
5975 * @ctxt: a Relax-NG validation context
5976 * @groups: the array of groups
5977 * @nbgroups: the number of groups in the array
5978 * @array: the permutation to try
5979 * @len: the size of the set
5980 *
5981 * Try to validate a permutation for the group of definitions.
5982 *
5983 * Returns 0 if the validation succeeded or an error code.
5984 */
5985static int
5986xmlRelaxNGValidateTryPermutation(xmlRelaxNGValidCtxtPtr ctxt,
5987 xmlRelaxNGDefinePtr rule,
5988 xmlNodePtr *array, int len) {
5989 int i, ret;
5990
5991 if (len > 0) {
5992 /*
5993 * One only need the next pointer set-up to do the validation
5994 */
5995 for (i = 0;i < (len - 1);i++)
5996 array[i]->next = array[i + 1];
5997 array[i]->next = NULL;
5998
5999 /*
6000 * Now try to validate the sequence
6001 */
6002 ctxt->state->seq = array[0];
6003 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
6004 } else {
6005 ctxt->state->seq = NULL;
6006 ret = xmlRelaxNGValidateDefinition(ctxt, rule);
6007 }
6008
6009 /*
6010 * the sequence must be fully consumed
6011 */
6012 if (ctxt->state->seq != NULL)
6013 return(-1);
6014
6015 return(ret);
6016}
6017
6018/**
6019 * xmlRelaxNGValidateWalkPermutations:
6020 * @ctxt: a Relax-NG validation context
6021 * @groups: the array of groups
6022 * @nbgroups: the number of groups in the array
6023 * @nodes: the set of nodes
6024 * @array: the current state of the parmutation
6025 * @len: the size of the set
6026 * @level: a pointer to the level variable
6027 * @k: the index in the array to fill
6028 *
6029 * Validate a set of nodes for a groups of definitions, will try the
6030 * full set of permutations
6031 *
6032 * Returns 0 if the validation succeeded or an error code.
6033 */
6034static int
6035xmlRelaxNGValidateWalkPermutations(xmlRelaxNGValidCtxtPtr ctxt,
6036 xmlRelaxNGDefinePtr rule, xmlNodePtr *nodes,
6037 xmlNodePtr *array, int len,
6038 int *level, int k) {
6039 int i, ret;
6040
6041 if ((k >= 0) && (k < len))
6042 array[k] = nodes[*level];
6043 *level = *level + 1;
6044 if (*level == len) {
6045 ret = xmlRelaxNGValidateTryPermutation(ctxt, rule, array, len);
6046 if (ret == 0)
6047 return(0);
6048 } else {
6049 for (i = 0;i < len;i++) {
6050 if (array[i] == NULL) {
6051 ret = xmlRelaxNGValidateWalkPermutations(ctxt, rule,
6052 nodes, array, len, level, i);
6053 if (ret == 0)
6054 return(0);
6055 }
6056 }
6057 }
6058 *level = *level - 1;
6059 array[k] = NULL;
6060 return(-1);
6061}
6062
6063/**
6064 * xmlRelaxNGNodeMatchesList:
6065 * @node: the node
6066 * @list: a NULL terminated array of definitions
6067 *
6068 * Check if a node can be matched by one of the definitions
6069 *
6070 * Returns 1 if matches 0 otherwise
6071 */
6072static int
6073xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
6074 xmlRelaxNGDefinePtr cur;
6075 int i = 0;
6076
6077 if ((node == NULL) || (list == NULL))
6078 return(0);
6079
6080 cur = list[i++];
6081 while (cur != NULL) {
6082 if ((node->type == XML_ELEMENT_NODE) &&
6083 (cur->type == XML_RELAXNG_ELEMENT)) {
6084 if (cur->name == NULL) {
6085 if ((node->ns != NULL) &&
6086 (xmlStrEqual(node->ns->href, cur->ns)))
6087 return(1);
6088 } else if (xmlStrEqual(cur->name, node->name)) {
6089 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
6090 if (node->ns == NULL)
6091 return(1);
6092 } else {
6093 if ((node->ns != NULL) &&
6094 (xmlStrEqual(node->ns->href, cur->ns)))
6095 return(1);
6096 }
6097 }
6098 } else if ((node->type == XML_TEXT_NODE) &&
6099 (cur->type == XML_RELAXNG_TEXT)) {
6100 return(1);
6101 }
6102 cur = list[i++];
6103 }
6104 return(0);
6105}
6106
6107/**
6108 * xmlRelaxNGValidatePartGroup:
6109 * @ctxt: a Relax-NG validation context
6110 * @groups: the array of groups
6111 * @nbgroups: the number of groups in the array
6112 * @nodes: the set of nodes
6113 * @len: the size of the set of nodes
6114 *
6115 * Validate a set of nodes for a groups of definitions
6116 *
6117 * Returns 0 if the validation succeeded or an error code.
6118 */
6119static int
6120xmlRelaxNGValidatePartGroup(xmlRelaxNGValidCtxtPtr ctxt,
6121 xmlRelaxNGInterleaveGroupPtr *groups,
6122 int nbgroups, xmlNodePtr *nodes, int len) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006123 int level, ret = -1, i, j, k, top_j, max_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006124 xmlNodePtr *array = NULL, *list, oldseq;
6125 xmlRelaxNGInterleaveGroupPtr group;
6126
6127 list = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
6128 if (list == NULL) {
6129 return(-1);
6130 }
6131 array = (xmlNodePtr *) xmlMalloc(len * sizeof(xmlNodePtr));
6132 if (array == NULL) {
6133 xmlFree(list);
6134 return(-1);
6135 }
6136 memset(array, 0, len * sizeof(xmlNodePtr));
6137
6138 /*
6139 * Partition the elements and validate the subsets.
6140 */
6141 oldseq = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00006142 max_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006143 for (i = 0;i < nbgroups;i++) {
6144 group = groups[i];
6145 if (group == NULL)
6146 continue;
6147 k = 0;
Daniel Veillard231d7912003-02-09 14:22:17 +00006148 top_j = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006149 for (j = 0;j < len;j++) {
6150 if (nodes[j] == NULL)
6151 continue;
6152 if (xmlRelaxNGNodeMatchesList(nodes[j], group->defs)) {
6153 list[k++] = nodes[j];
6154 nodes[j] = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00006155 top_j = j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006156 }
6157 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006158 if (top_j > max_j)
6159 max_j = top_j;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006160 ctxt->state->seq = oldseq;
6161 if (k > 1) {
6162 memset(array, 0, k * sizeof(xmlNodePtr));
Daniel Veillardb08c9812003-01-28 23:09:49 +00006163 level = -1;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006164 ret = xmlRelaxNGValidateWalkPermutations(ctxt, group->rule,
6165 list, array, k, &level, -1);
6166 } else {
6167 ret = xmlRelaxNGValidateTryPermutation(ctxt, group->rule, list, k);
6168 }
6169 if (ret != 0) {
6170 ctxt->state->seq = oldseq;
6171 break;
6172 }
6173 }
6174
Daniel Veillard231d7912003-02-09 14:22:17 +00006175 for (j = 0;j < max_j;j++) {
6176 if (nodes[j] != NULL) {
6177 TODO /* problem, one of the nodes didn't got a match */
6178 }
6179 }
6180 if (ret == 0) {
6181 if (max_j + 1 < len)
6182 ctxt->state->seq = nodes[max_j + 1];
6183 else
6184 ctxt->state->seq = NULL;
6185 }
6186
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006187 xmlFree(list);
6188 xmlFree(array);
6189 return(ret);
6190}
6191
6192/**
6193 * xmlRelaxNGValidateInterleave:
6194 * @ctxt: a Relax-NG validation context
6195 * @define: the definition to verify
6196 *
6197 * Validate an interleave definition for a node.
6198 *
6199 * Returns 0 if the validation succeeded or an error code.
6200 */
6201static int
6202xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
6203 xmlRelaxNGDefinePtr define) {
6204 int ret = 0, nbchildren, nbtot, i, j;
6205 xmlRelaxNGPartitionPtr partitions;
6206 xmlNodePtr *children = NULL;
6207 xmlNodePtr *order = NULL;
Daniel Veillard231d7912003-02-09 14:22:17 +00006208 xmlNodePtr cur, oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006209
6210 if (define->data != NULL) {
6211 partitions = (xmlRelaxNGPartitionPtr) define->data;
6212 } else {
6213 VALID_CTXT();
6214 VALID_ERROR("Internal: interleave block has no data\n");
6215 return(-1);
6216 }
6217
6218 /*
6219 * Build the sequence of child and an array preserving the children
6220 * initial order.
6221 */
6222 cur = ctxt->state->seq;
Daniel Veillard231d7912003-02-09 14:22:17 +00006223 oldseq = ctxt->state->seq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006224 nbchildren = 0;
6225 nbtot = 0;
6226 while (cur != NULL) {
6227 if ((cur->type == XML_COMMENT_NODE) ||
6228 (cur->type == XML_PI_NODE) ||
6229 ((cur->type == XML_TEXT_NODE) &&
6230 (IS_BLANK_NODE(cur)))) {
6231 nbtot++;
6232 } else {
6233 nbchildren++;
6234 nbtot++;
6235 }
6236 cur = cur->next;
6237 }
6238 children = (xmlNodePtr *) xmlMalloc(nbchildren * sizeof(xmlNodePtr));
6239 if (children == NULL)
6240 goto error;
6241 order = (xmlNodePtr *) xmlMalloc(nbtot * sizeof(xmlNodePtr));
6242 if (order == NULL)
6243 goto error;
6244 cur = ctxt->state->seq;
6245 i = 0;
6246 j = 0;
6247 while (cur != NULL) {
6248 if ((cur->type == XML_COMMENT_NODE) ||
6249 (cur->type == XML_PI_NODE) ||
6250 ((cur->type == XML_TEXT_NODE) &&
6251 (IS_BLANK_NODE(cur)))) {
6252 order[j++] = cur;
6253 } else {
6254 order[j++] = cur;
6255 children[i++] = cur;
6256 }
6257 cur = cur->next;
6258 }
6259
6260 /* TODO: retry with a maller set of child if there is a next... */
6261 ret = xmlRelaxNGValidatePartGroup(ctxt, partitions->groups,
6262 partitions->nbgroups, children, nbchildren);
Daniel Veillard231d7912003-02-09 14:22:17 +00006263 if (ret != 0)
6264 ctxt->state->seq = oldseq;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006265
6266 /*
6267 * Cleanup: rebuid the child sequence and free the structure
6268 */
6269 if (order != NULL) {
6270 for (i = 0;i < nbtot;i++) {
6271 if (i == 0)
6272 order[i]->prev = NULL;
6273 else
6274 order[i]->prev = order[i - 1];
6275 if (i == nbtot - 1)
6276 order[i]->next = NULL;
6277 else
6278 order[i]->next = order[i + 1];
6279 }
6280 xmlFree(order);
6281 }
6282 if (children != NULL)
6283 xmlFree(children);
6284
6285 return(ret);
6286
6287error:
6288 if (order != NULL) {
6289 for (i = 0;i < nbtot;i++) {
6290 if (i == 0)
6291 order[i]->prev = NULL;
6292 else
6293 order[i]->prev = order[i - 1];
6294 if (i == nbtot - 1)
6295 order[i]->next = NULL;
6296 else
6297 order[i]->next = order[i + 1];
6298 }
6299 xmlFree(order);
6300 }
6301 if (children != NULL)
6302 xmlFree(children);
6303 return(-1);
6304}
6305
6306/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006307 * xmlRelaxNGValidateElementContent:
6308 * @ctxt: a Relax-NG validation context
6309 * @define: the list of definition to verify
6310 *
6311 * Validate the given node content against the (list) of definitions
6312 *
6313 * Returns 0 if the validation succeeded or an error code.
6314 */
6315static int
6316xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
6317 xmlRelaxNGDefinePtr defines) {
6318 int ret = 0, res;
6319
6320 if (ctxt->state == NULL) {
6321 VALID_CTXT();
6322 VALID_ERROR("Internal: no state\n");
6323 return(-1);
6324 }
6325 while (defines != NULL) {
6326 res = xmlRelaxNGValidateDefinition(ctxt, defines);
6327 if (res < 0)
6328 ret = -1;
6329 defines = defines->next;
6330 }
6331
6332 return(ret);
6333}
6334
6335/**
Daniel Veillard416589a2003-02-17 17:25:42 +00006336 * xmlRelaxNGElementMatch:
6337 * @ctxt: a Relax-NG validation context
6338 * @define: the definition to check
6339 * @elem: the element
6340 *
6341 * Check if the element matches the definition nameClass
6342 *
6343 * Returns 1 if the element matches, 0 if no, or -1 in case of error
6344 */
6345static int
6346xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
6347 xmlRelaxNGDefinePtr define,
6348 xmlNodePtr elem) {
6349 int ret, oldflags;
6350
6351 if (define->name != NULL) {
6352 if (!xmlStrEqual(elem->name, define->name)) {
6353 VALID_CTXT();
6354 VALID_ERROR3("Expecting element %s, got %s\n",
6355 define->name, elem->name);
6356 return(0);
6357 }
6358 }
6359 if ((define->ns != NULL) && (define->ns[0] != 0)) {
6360 if (elem->ns == NULL) {
6361 VALID_CTXT();
6362 VALID_ERROR2("Expecting a namespace for element %s\n",
6363 elem->name);
6364 return(0);
6365 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
6366 VALID_CTXT();
6367 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
6368 elem->name, define->ns);
6369 return(0);
6370 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00006371 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
6372 (define->name == NULL)) {
6373 VALID_CTXT();
6374 VALID_ERROR2("Expecting no namespace for element %s\n",
6375 define->name);
6376 return(0);
6377 } else if ((elem->ns != NULL) && (define->name != NULL)) {
6378 VALID_CTXT();
6379 VALID_ERROR2("Expecting no namespace for element %s\n",
6380 define->name);
6381 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00006382 }
6383
6384 if (define->nameClass == NULL)
6385 return(1);
6386
6387 define = define->nameClass;
6388 if (define->type == XML_RELAXNG_EXCEPT) {
6389 xmlRelaxNGDefinePtr list;
6390 oldflags = ctxt->flags;
6391 ctxt->flags |= FLAGS_IGNORABLE;
6392
6393 list = define->content;
6394 while (list != NULL) {
6395 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6396 if (ret == 1) {
6397 ctxt->flags = oldflags;
6398 return(0);
6399 }
6400 if (ret < 0) {
6401 ctxt->flags = oldflags;
6402 return(ret);
6403 }
6404 list = list->next;
6405 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006406 ret = 1;
6407 ctxt->flags = oldflags;
6408 } else if (define->type == XML_RELAXNG_CHOICE) {
6409 xmlRelaxNGDefinePtr list;
6410 oldflags = ctxt->flags;
6411 ctxt->flags |= FLAGS_IGNORABLE;
6412
6413 list = define->nameClass;
6414 while (list != NULL) {
6415 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6416 if (ret == 1) {
6417 ctxt->flags = oldflags;
6418 return(1);
6419 }
6420 if (ret < 0) {
6421 ctxt->flags = oldflags;
6422 return(ret);
6423 }
6424 list = list->next;
6425 }
6426 ret = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006427 ctxt->flags = oldflags;
6428 } else {
6429 TODO
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006430 ret = -1;
Daniel Veillard416589a2003-02-17 17:25:42 +00006431 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006432 return(ret);
Daniel Veillard416589a2003-02-17 17:25:42 +00006433}
6434
6435/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006436 * xmlRelaxNGValidateDefinition:
6437 * @ctxt: a Relax-NG validation context
6438 * @define: the definition to verify
6439 *
6440 * Validate the current node against the definition
6441 *
6442 * Returns 0 if the validation succeeded or an error code.
6443 */
6444static int
6445xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
6446 xmlRelaxNGDefinePtr define) {
6447 xmlNodePtr node;
6448 int ret = 0, i, tmp, oldflags;
6449 xmlRelaxNGValidStatePtr oldstate, state;
6450
6451 if (define == NULL) {
6452 VALID_CTXT();
6453 VALID_ERROR("internal error: define == NULL\n");
6454 return(-1);
6455 }
6456 if (ctxt->state != NULL) {
6457 node = ctxt->state->seq;
6458 } else {
6459 node = NULL;
6460 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006461#ifdef DEBUG
6462 for (i = 0;i < ctxt->depth;i++)
6463 xmlGenericError(xmlGenericErrorContext, " ");
6464 xmlGenericError(xmlGenericErrorContext,
6465 "Start validating %s ", xmlRelaxNGDefName(define));
6466 if (define->name != NULL)
6467 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6468 if ((node != NULL) && (node->name != NULL))
6469 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
6470 else
6471 xmlGenericError(xmlGenericErrorContext, "\n");
6472#endif
6473 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006474 switch (define->type) {
6475 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00006476 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006477 if (node != NULL) {
6478 VALID_CTXT();
6479 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00006480 ret = -1;
6481 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006482 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006483 ret = 0;
6484 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006485 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00006486 ret = -1;
6487 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006488 case XML_RELAXNG_TEXT:
Daniel Veillardce14fa52003-02-19 17:32:48 +00006489#if 0
Daniel Veillard231d7912003-02-09 14:22:17 +00006490 if (node == NULL) {
6491 ret = 0;
6492 break;
6493 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006494#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006495 while ((node != NULL) &&
6496 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006497 (node->type == XML_COMMENT_NODE) ||
6498 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00006499 (node->type == XML_CDATA_SECTION_NODE)))
6500 node = node->next;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006501#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00006502 if (node == ctxt->state->seq) {
6503 VALID_CTXT();
6504 VALID_ERROR("Expecting text content\n");
6505 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006506 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006507#endif
Daniel Veillard276be4a2003-01-24 01:03:34 +00006508 ctxt->state->seq = node;
6509 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006510 case XML_RELAXNG_ELEMENT:
6511 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00006512 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006513 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00006514 VALID_ERROR("Expecting an element, got empty\n");
6515 ret = -1;
6516 break;
6517 }
6518 if (node->type != XML_ELEMENT_NODE) {
6519 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006520 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00006521 ret = -1;
6522 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006523 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006524 /*
6525 * This node was already validated successfully against
6526 * this definition.
6527 */
6528 if (node->_private == define)
6529 break;
Daniel Veillard416589a2003-02-17 17:25:42 +00006530
6531 ret = xmlRelaxNGElementMatch(ctxt, define, node);
6532 if (ret <= 0) {
6533 ret = -1;
6534 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006535 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006536 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006537
6538 state = xmlRelaxNGNewValidState(ctxt, node);
6539 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006540 ret = -1;
6541 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006542 }
6543
6544 oldstate = ctxt->state;
6545 ctxt->state = state;
6546 if (define->attrs != NULL) {
6547 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00006548 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006549 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006550#ifdef DEBUG
6551 xmlGenericError(xmlGenericErrorContext,
6552 "E: Element %s failed to validate attributes\n",
6553 node->name);
6554#endif
6555 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006556 }
6557 if (define->content != NULL) {
6558 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006559 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006560 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006561#ifdef DEBUG
6562 xmlGenericError(xmlGenericErrorContext,
6563 "E: Element %s failed to validate element content\n",
6564 node->name);
6565#endif
6566 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006567 }
6568 state = ctxt->state;
6569 if (state->seq != NULL) {
6570 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
6571 if (state->seq != NULL) {
6572 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006573 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006574 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006575 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006576#ifdef DEBUG
6577 xmlGenericError(xmlGenericErrorContext,
6578 "E: Element %s has extra content: %s\n",
6579 node->name, state->seq->name);
6580#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006581 }
6582 }
6583 for (i = 0;i < state->nbAttrs;i++) {
6584 if (state->attrs[i] != NULL) {
6585 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006586 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006587 state->attrs[i]->name, node->name);
6588 ret = -1;
6589 }
6590 }
6591 ctxt->state = oldstate;
6592 xmlRelaxNGFreeValidState(state);
6593 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006594 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
6595 if (ret == 0)
6596 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006597
6598
6599#ifdef DEBUG
6600 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00006601 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006602 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00006603 if (oldstate == NULL)
6604 xmlGenericError(xmlGenericErrorContext, ": no state\n");
6605 else if (oldstate->seq == NULL)
6606 xmlGenericError(xmlGenericErrorContext, ": done\n");
6607 else if (oldstate->seq->type == XML_ELEMENT_NODE)
6608 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
6609 oldstate->seq->name);
6610 else
6611 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
6612 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006613#endif
6614 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006615 case XML_RELAXNG_OPTIONAL:
6616 oldflags = ctxt->flags;
6617 ctxt->flags |= FLAGS_IGNORABLE;
6618 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6619 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6620 if (ret != 0) {
6621 xmlRelaxNGFreeValidState(ctxt->state);
6622 ctxt->state = oldstate;
6623 ret = 0;
6624 break;
6625 }
6626 xmlRelaxNGFreeValidState(oldstate);
6627 ctxt->flags = oldflags;
6628 ret = 0;
6629 break;
6630 case XML_RELAXNG_ONEORMORE:
6631 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6632 if (ret != 0) {
6633 break;
6634 }
6635 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00006636 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006637 oldflags = ctxt->flags;
6638 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006639 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006640 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6641 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6642 if (ret != 0) {
6643 xmlRelaxNGFreeValidState(ctxt->state);
6644 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006645 break;
6646 }
6647 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006648 }
6649 if (ret == 0) {
6650 /*
6651 * There is no attribute left to be consumed,
6652 * we can check the closure by looking at ctxt->state->seq
6653 */
6654 xmlNodePtr cur, temp;
6655
Daniel Veillard276be4a2003-01-24 01:03:34 +00006656 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006657 temp = NULL;
6658 while ((cur != NULL) && (temp != cur)) {
6659 temp = cur;
6660 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6661 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6662 if (ret != 0) {
6663 xmlRelaxNGFreeValidState(ctxt->state);
6664 ctxt->state = oldstate;
6665 break;
6666 }
6667 xmlRelaxNGFreeValidState(oldstate);
6668 cur = ctxt->state->seq;
6669 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006670 }
6671 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006672 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006673 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00006674 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006675 case XML_RELAXNG_CHOICE: {
6676 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006677 int success = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006678
6679 oldflags = ctxt->flags;
6680 ctxt->flags |= FLAGS_IGNORABLE;
6681
6682 while (list != NULL) {
6683 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6684 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6685 if (ret == 0) {
Daniel Veillardce14fa52003-02-19 17:32:48 +00006686 if (xmlRelaxNGEqualValidState(ctxt, ctxt->state, oldstate)){
6687 /*
6688 * if that pattern was nullable flag it but try
6689 * to make more progresses
6690 */
6691 success = 1;
6692 } else {
6693 xmlRelaxNGFreeValidState(oldstate);
6694 break;
6695 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006696 }
6697 xmlRelaxNGFreeValidState(ctxt->state);
6698 ctxt->state = oldstate;
6699 list = list->next;
6700 }
6701 ctxt->flags = oldflags;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006702 if (success == 1)
6703 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006704 break;
6705 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006706 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006707 case XML_RELAXNG_GROUP: {
6708 xmlRelaxNGDefinePtr list = define->content;
6709
6710 while (list != NULL) {
6711 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6712 if (ret != 0)
6713 break;
6714 list = list->next;
6715 }
6716 break;
6717 }
6718 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006719 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006720 break;
6721 case XML_RELAXNG_ATTRIBUTE:
6722 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6723 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00006724 case XML_RELAXNG_NOOP:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006725 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006726 case XML_RELAXNG_PARENTREF:
6727 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006728 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6729 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006730 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006731 xmlNodePtr child;
6732 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006733
Daniel Veillardd4310742003-02-18 21:12:46 +00006734 child = node;
6735 while (child != NULL) {
6736 if (child->type == XML_ELEMENT_NODE) {
6737 VALID_CTXT();
6738 VALID_ERROR2("Element %s has child elements\n",
6739 node->parent->name);
6740 ret = -1;
6741 break;
6742 } else if ((child->type == XML_TEXT_NODE) ||
6743 (child->type == XML_CDATA_SECTION_NODE)) {
6744 content = xmlStrcat(content, child->content);
6745 }
6746 /* TODO: handle entities ... */
6747 child = child->next;
6748 }
6749 if (ret == -1) {
6750 if (content != NULL)
6751 xmlFree(content);
6752 break;
6753 }
6754 if (content == NULL) {
6755 content = xmlStrdup(BAD_CAST "");
6756 if (content == NULL) {
6757 VALID_CTXT();
6758 VALID_ERROR("Allocation failure\n");
6759 ret = -1;
6760 break;
6761 }
6762 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006763 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
6764 if (ret == -1) {
6765 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006766 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006767 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006768 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006769 }
6770 if (content != NULL)
6771 xmlFree(content);
6772 break;
6773 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006774 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006775 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006776 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00006777 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006778
Daniel Veillardd4310742003-02-18 21:12:46 +00006779 child = node;
6780 while (child != NULL) {
6781 if (child->type == XML_ELEMENT_NODE) {
6782 VALID_CTXT();
6783 VALID_ERROR2("Element %s has child elements\n",
6784 node->parent->name);
6785 ret = -1;
6786 break;
6787 } else if ((child->type == XML_TEXT_NODE) ||
6788 (child->type == XML_CDATA_SECTION_NODE)) {
6789 content = xmlStrcat(content, child->content);
6790 }
6791 /* TODO: handle entities ... */
6792 child = child->next;
6793 }
6794 if (ret == -1) {
6795 if (content != NULL)
6796 xmlFree(content);
6797 break;
6798 }
6799 if (content == NULL) {
6800 content = xmlStrdup(BAD_CAST "");
6801 if (content == NULL) {
6802 VALID_CTXT();
6803 VALID_ERROR("Allocation failure\n");
6804 ret = -1;
6805 break;
6806 }
6807 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006808 oldvalue = ctxt->state->value;
6809 ctxt->state->value = content;
6810 ret = xmlRelaxNGValidateValue(ctxt, define);
6811 ctxt->state->value = oldvalue;
6812 if (ret == -1) {
6813 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00006814 if (define->name != NULL) {
6815 VALID_ERROR2("error validating value %s\n", define->name);
6816 } else {
6817 VALID_ERROR("error validating value\n");
6818 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00006819 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006820 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006821 }
6822 if (content != NULL)
6823 xmlFree(content);
6824 break;
6825 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006826 case XML_RELAXNG_LIST: {
6827 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00006828 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006829 xmlChar *oldvalue, *oldendvalue;
6830 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00006831
Daniel Veillardd4310742003-02-18 21:12:46 +00006832 /*
6833 * Make sure it's only text nodes
6834 */
6835
6836 content = NULL;
6837 child = node;
6838 while (child != NULL) {
6839 if (child->type == XML_ELEMENT_NODE) {
6840 VALID_CTXT();
6841 VALID_ERROR2("Element %s has child elements\n",
6842 node->parent->name);
6843 ret = -1;
6844 break;
6845 } else if ((child->type == XML_TEXT_NODE) ||
6846 (child->type == XML_CDATA_SECTION_NODE)) {
6847 content = xmlStrcat(content, child->content);
6848 }
6849 /* TODO: handle entities ... */
6850 child = child->next;
6851 }
6852 if (ret == -1) {
6853 if (content != NULL)
6854 xmlFree(content);
6855 break;
6856 }
6857 if (content == NULL) {
6858 content = xmlStrdup(BAD_CAST "");
6859 if (content == NULL) {
6860 VALID_CTXT();
6861 VALID_ERROR("Allocation failure\n");
6862 ret = -1;
6863 break;
6864 }
6865 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006866 len = xmlStrlen(content);
6867 oldvalue = ctxt->state->value;
6868 oldendvalue = ctxt->state->endvalue;
6869 ctxt->state->value = content;
6870 ctxt->state->endvalue = content + len;
6871 ret = xmlRelaxNGValidateValue(ctxt, define);
6872 ctxt->state->value = oldvalue;
6873 ctxt->state->endvalue = oldendvalue;
6874 if (ret == -1) {
6875 VALID_CTXT();
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006876 VALID_ERROR("error validating list\n");
Daniel Veillardd4310742003-02-18 21:12:46 +00006877 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006878 ctxt->state->seq = node->next;
6879 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006880 if (content != NULL)
6881 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006882 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006883 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006884 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00006885 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00006886 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006887 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00006888 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006889 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006890 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006891 ctxt->depth--;
6892#ifdef DEBUG
6893 for (i = 0;i < ctxt->depth;i++)
6894 xmlGenericError(xmlGenericErrorContext, " ");
6895 xmlGenericError(xmlGenericErrorContext,
6896 "Validating %s ", xmlRelaxNGDefName(define));
6897 if (define->name != NULL)
6898 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6899 if (ret == 0)
6900 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
6901 else
6902 xmlGenericError(xmlGenericErrorContext, "failed\n");
6903#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006904 return(ret);
6905}
6906
6907/**
6908 * xmlRelaxNGValidateDocument:
6909 * @ctxt: a Relax-NG validation context
6910 * @doc: the document
6911 *
6912 * Validate the given document
6913 *
6914 * Returns 0 if the validation succeeded or an error code.
6915 */
6916static int
6917xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
6918 int ret;
6919 xmlRelaxNGPtr schema;
6920 xmlRelaxNGGrammarPtr grammar;
6921 xmlRelaxNGValidStatePtr state;
6922
6923 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
6924 return(-1);
6925
6926 schema = ctxt->schema;
6927 grammar = schema->topgrammar;
6928 if (grammar == NULL) {
6929 VALID_CTXT();
6930 VALID_ERROR("No top grammar defined\n");
6931 return(-1);
6932 }
6933 state = xmlRelaxNGNewValidState(ctxt, NULL);
6934 ctxt->state = state;
6935 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
6936 state = ctxt->state;
6937 if ((state != NULL) && (state->seq != NULL)) {
6938 xmlNodePtr node;
6939
6940 node = state->seq;
6941 node = xmlRelaxNGSkipIgnored(ctxt, node);
6942 if (node != NULL) {
6943 VALID_CTXT();
6944 VALID_ERROR("extra data on the document\n");
6945 ret = -1;
6946 }
6947 }
6948 xmlRelaxNGFreeValidState(state);
6949
6950 return(ret);
6951}
6952
6953/************************************************************************
6954 * *
6955 * Validation interfaces *
6956 * *
6957 ************************************************************************/
6958/**
6959 * xmlRelaxNGNewValidCtxt:
6960 * @schema: a precompiled XML RelaxNGs
6961 *
6962 * Create an XML RelaxNGs validation context based on the given schema
6963 *
6964 * Returns the validation context or NULL in case of error
6965 */
6966xmlRelaxNGValidCtxtPtr
6967xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
6968 xmlRelaxNGValidCtxtPtr ret;
6969
6970 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
6971 if (ret == NULL) {
6972 xmlGenericError(xmlGenericErrorContext,
6973 "Failed to allocate new schama validation context\n");
6974 return (NULL);
6975 }
6976 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
6977 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006978 ret->error = xmlGenericError;
6979 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006980 return (ret);
6981}
6982
6983/**
6984 * xmlRelaxNGFreeValidCtxt:
6985 * @ctxt: the schema validation context
6986 *
6987 * Free the resources associated to the schema validation context
6988 */
6989void
6990xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
6991 if (ctxt == NULL)
6992 return;
6993 xmlFree(ctxt);
6994}
6995
6996/**
6997 * xmlRelaxNGSetValidErrors:
6998 * @ctxt: a Relax-NG validation context
6999 * @err: the error function
7000 * @warn: the warning function
7001 * @ctx: the functions context
7002 *
7003 * Set the error and warning callback informations
7004 */
7005void
7006xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
7007 xmlRelaxNGValidityErrorFunc err,
7008 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
7009 if (ctxt == NULL)
7010 return;
7011 ctxt->error = err;
7012 ctxt->warning = warn;
7013 ctxt->userData = ctx;
7014}
7015
7016/**
7017 * xmlRelaxNGValidateDoc:
7018 * @ctxt: a Relax-NG validation context
7019 * @doc: a parsed document tree
7020 *
7021 * Validate a document tree in memory.
7022 *
7023 * Returns 0 if the document is valid, a positive error code
7024 * number otherwise and -1 in case of internal or API error.
7025 */
7026int
7027xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7028 int ret;
7029
7030 if ((ctxt == NULL) || (doc == NULL))
7031 return(-1);
7032
7033 ctxt->doc = doc;
7034
7035 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00007036 /*
7037 * TODO: build error codes
7038 */
7039 if (ret == -1)
7040 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007041 return(ret);
7042}
7043
7044#endif /* LIBXML_SCHEMAS_ENABLED */
7045