blob: b1d4d475da0fe3a756850f713dffd8499e806b85 [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 Veillard1ed7f362003-02-03 10:57:45 +000012 * - handle namespace declarations as attributes.
Daniel Veillardf4b4f982003-02-13 11:02:08 +000013 * - add support for DTD compatibility spec
14 * http://www.oasis-open.org/committees/relax-ng/compatibility-20011203.html
Daniel Veillardd41f4f42003-01-29 21:07:52 +000015 */
16
Daniel Veillard6eadf632003-01-23 18:29:16 +000017#define IN_LIBXML
18#include "libxml.h"
19
20#ifdef LIBXML_SCHEMAS_ENABLED
21
22#include <string.h>
23#include <stdio.h>
24#include <libxml/xmlmemory.h>
25#include <libxml/parser.h>
26#include <libxml/parserInternals.h>
27#include <libxml/hash.h>
28#include <libxml/uri.h>
29
30#include <libxml/relaxng.h>
31
32#include <libxml/xmlschemastypes.h>
33#include <libxml/xmlautomata.h>
34#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000035#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000036
37/*
38 * The Relax-NG namespace
39 */
40static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
41 "http://relaxng.org/ns/structure/1.0";
42
43#define IS_RELAXNG(node, type) \
44 ((node != NULL) && (node->ns != NULL) && \
45 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
46 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
47
48
Daniel Veillard42f12e92003-03-07 18:32:59 +000049/* #define DEBUG 1 */ /* very verbose output */
Daniel Veillardc482e262003-02-26 14:48:48 +000050/* #define DEBUG_GRAMMAR 1 */
Daniel Veillard71531f32003-02-05 13:19:53 +000051/* #define DEBUG_CONTENT 1 */
52/* #define DEBUG_TYPE 1 */
53/* #define DEBUG_VALID 1 */
Daniel Veillarde5b110b2003-02-04 14:43:39 +000054/* #define DEBUG_INTERLEAVE 1 */
Daniel Veillard416589a2003-02-17 17:25:42 +000055/* #define DEBUG_LIST 1 */
Daniel Veillard5add8682003-03-10 13:13:58 +000056/* #define DEBUG_INCLUDE */
Daniel Veillard6eadf632003-01-23 18:29:16 +000057
58#define UNBOUNDED (1 << 30)
59#define TODO \
60 xmlGenericError(xmlGenericErrorContext, \
61 "Unimplemented block at %s:%d\n", \
62 __FILE__, __LINE__);
63
64typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
65typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
66
67typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
68typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
69
Daniel Veillardd41f4f42003-01-29 21:07:52 +000070typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
71typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
72
Daniel Veillarda9d912d2003-02-01 17:43:10 +000073typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
74typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
75
Daniel Veillard6eadf632003-01-23 18:29:16 +000076typedef enum {
77 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
78 XML_RELAXNG_COMBINE_CHOICE, /* choice */
79 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
80} xmlRelaxNGCombine;
81
Daniel Veillard4c5cf702003-02-21 15:40:34 +000082typedef enum {
83 XML_RELAXNG_CONTENT_ERROR = -1,
84 XML_RELAXNG_CONTENT_EMPTY = 0,
85 XML_RELAXNG_CONTENT_SIMPLE,
86 XML_RELAXNG_CONTENT_COMPLEX
87} xmlRelaxNGContentType;
88
Daniel Veillard6eadf632003-01-23 18:29:16 +000089typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
90typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
91
92struct _xmlRelaxNGGrammar {
93 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
94 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
95 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
96 xmlRelaxNGDefinePtr start; /* <start> content */
97 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000098 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000099 xmlHashTablePtr defs; /* define* */
100 xmlHashTablePtr refs; /* references */
101};
102
103
Daniel Veillard6eadf632003-01-23 18:29:16 +0000104typedef enum {
Daniel Veillard77648bb2003-02-20 15:03:22 +0000105 XML_RELAXNG_NOOP = -1, /* a no operation from simplification */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000106 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
107 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +0000108 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000109 XML_RELAXNG_TEXT, /* textual content */
110 XML_RELAXNG_ELEMENT, /* an element */
111 XML_RELAXNG_DATATYPE, /* extenal data type definition */
Daniel Veillard8fe98712003-02-19 00:19:14 +0000112 XML_RELAXNG_PARAM, /* extenal data type parameter */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000113 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
114 XML_RELAXNG_LIST, /* a list of patterns */
115 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
116 XML_RELAXNG_DEF, /* a definition */
117 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000118 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000119 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000120 XML_RELAXNG_OPTIONAL, /* optional patterns */
121 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
122 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
123 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
124 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000125 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
126 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000127} xmlRelaxNGType;
128
129struct _xmlRelaxNGDefine {
130 xmlRelaxNGType type; /* the type of definition */
131 xmlNodePtr node; /* the node in the source */
132 xmlChar *name; /* the element local name if present */
133 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000134 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000135 void *data; /* data lib or specific pointer */
Daniel Veillardd4310742003-02-18 21:12:46 +0000136 int depth; /* used for the cycle detection */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000137 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000138 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000139 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
140 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000141 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000142 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
143};
144
145/**
146 * _xmlRelaxNG:
147 *
148 * A RelaxNGs definition
149 */
150struct _xmlRelaxNG {
151 xmlRelaxNGGrammarPtr topgrammar;
152 xmlDocPtr doc;
153
154 xmlHashTablePtr defs; /* define */
155 xmlHashTablePtr refs; /* references */
Daniel Veillardc482e262003-02-26 14:48:48 +0000156 xmlRelaxNGDocumentPtr documents; /* all the documents loaded */
157 xmlRelaxNGIncludePtr includes; /* all the includes loaded */
Daniel Veillard419a7682003-02-03 23:22:49 +0000158 int defNr; /* number of defines used */
159 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000160 void *_private; /* unused by the library for users or bindings */
161};
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 Veillardc5312d72003-02-21 17:14:10 +0000171#define XML_RELAXNG_IN_ANYEXCEPT (1 << 8)
172#define XML_RELAXNG_IN_NSEXCEPT (1 << 9)
Daniel Veillard6eadf632003-01-23 18:29:16 +0000173
174struct _xmlRelaxNGParserCtxt {
175 void *userData; /* user specific data block */
176 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
177 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
Daniel Veillard42f12e92003-03-07 18:32:59 +0000178 xmlRelaxNGValidErr err;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000179
180 xmlRelaxNGPtr schema; /* The schema in use */
181 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000182 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000183 int flags; /* parser flags */
184 int nbErrors; /* number of errors at parse time */
185 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000186 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000187 xmlRelaxNGDefinePtr def; /* the current define */
188
189 int nbInterleaves;
190 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000191
Daniel Veillardc482e262003-02-26 14:48:48 +0000192 xmlRelaxNGDocumentPtr documents; /* all the documents loaded */
193 xmlRelaxNGIncludePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000194 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000195 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000196
Daniel Veillard419a7682003-02-03 23:22:49 +0000197 int defNr; /* number of defines used */
198 int defMax; /* number of defines aloocated */
199 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
200
Daniel Veillard6eadf632003-01-23 18:29:16 +0000201 const char *buffer;
202 int size;
203
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000204 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000205 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000206 int docNr; /* Depth of the parsing stack */
207 int docMax; /* Max depth of the parsing stack */
208 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000209
210 /* the include stack */
Daniel Veillardd4310742003-02-18 21:12:46 +0000211 xmlRelaxNGIncludePtr inc; /* Current parsed include */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000212 int incNr; /* Depth of the include parsing stack */
213 int incMax; /* Max depth of the parsing stack */
214 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000215};
216
217#define FLAGS_IGNORABLE 1
218#define FLAGS_NEGATIVE 2
219
220/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000221 * xmlRelaxNGInterleaveGroup:
222 *
223 * A RelaxNGs partition set associated to lists of definitions
224 */
225typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
226typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
227struct _xmlRelaxNGInterleaveGroup {
228 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
229 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000230 xmlRelaxNGDefinePtr *attrs; /* the array of attributes definitions */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000231};
232
233/**
234 * xmlRelaxNGPartitions:
235 *
236 * A RelaxNGs partition associated to an interleave group
237 */
238typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
239typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
240struct _xmlRelaxNGPartition {
241 int nbgroups; /* number of groups in the partitions */
242 xmlRelaxNGInterleaveGroupPtr *groups;
243};
244
245/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000246 * xmlRelaxNGValidState:
247 *
248 * A RelaxNGs validation state
249 */
250#define MAX_ATTR 20
251typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
252typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
253struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000254 xmlNodePtr node; /* the current node */
255 xmlNodePtr seq; /* the sequence of children left to validate */
256 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000257 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000258 xmlChar *value; /* the value when operating on string */
259 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000260 xmlAttrPtr attrs[1]; /* the array of attributes */
261};
262
263/**
Daniel Veillard42f12e92003-03-07 18:32:59 +0000264 * xmlRelaxNGValidError:
265 *
266 * A RelaxNGs validation error
267 */
268typedef struct _xmlRelaxNGValidError xmlRelaxNGValidError;
269typedef xmlRelaxNGValidError *xmlRelaxNGValidErrorPtr;
270struct _xmlRelaxNGValidError {
271 xmlRelaxNGValidErr err; /* the error number */
272 xmlNodePtr node; /* the current node */
273 xmlNodePtr seq; /* the current child */
274 const xmlChar * arg1; /* first arg */
275 const xmlChar * arg2; /* second arg */
276};
277
278/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000279 * xmlRelaxNGValidCtxt:
280 *
281 * A RelaxNGs validation context
282 */
283
284struct _xmlRelaxNGValidCtxt {
285 void *userData; /* user specific data block */
286 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
287 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
288
289 xmlRelaxNGPtr schema; /* The schema in use */
290 xmlDocPtr doc; /* the document being validated */
291 xmlRelaxNGValidStatePtr state; /* the current validation state */
292 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000293 int depth; /* validation depth */
Daniel Veillard42f12e92003-03-07 18:32:59 +0000294
295 /*
296 * Errors accumulated in branches may have to be stacked to be
297 * provided back when it's sure they affect validation.
298 */
299 xmlRelaxNGValidErrorPtr err; /* Last error */
300 int errNr; /* Depth of the error stack */
301 int errMax; /* Max depth of the error stack */
302 xmlRelaxNGValidErrorPtr errTab; /* stack of errors */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000303};
304
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000305/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000306 * xmlRelaxNGInclude:
307 *
308 * Structure associated to a RelaxNGs document element
309 */
310struct _xmlRelaxNGInclude {
Daniel Veillardc482e262003-02-26 14:48:48 +0000311 xmlRelaxNGIncludePtr next; /* keep a chain of includes */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000312 xmlChar *href; /* the normalized href value */
313 xmlDocPtr doc; /* the associated XML document */
314 xmlRelaxNGDefinePtr content;/* the definitions */
315 xmlRelaxNGPtr schema; /* the schema */
316};
317
318/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000319 * xmlRelaxNGDocument:
320 *
321 * Structure associated to a RelaxNGs document element
322 */
323struct _xmlRelaxNGDocument {
Daniel Veillardc482e262003-02-26 14:48:48 +0000324 xmlRelaxNGDocumentPtr next; /* keep a chain of documents */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000325 xmlChar *href; /* the normalized href value */
326 xmlDocPtr doc; /* the associated XML document */
327 xmlRelaxNGDefinePtr content;/* the definitions */
328 xmlRelaxNGPtr schema; /* the schema */
329};
330
Daniel Veillard3ebc7d42003-02-24 17:17:58 +0000331
Daniel Veillard6eadf632003-01-23 18:29:16 +0000332/************************************************************************
333 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000334 * Preliminary type checking interfaces *
335 * *
336 ************************************************************************/
337/**
338 * xmlRelaxNGTypeHave:
339 * @data: data needed for the library
340 * @type: the type name
341 * @value: the value to check
342 *
343 * Function provided by a type library to check if a type is exported
344 *
345 * Returns 1 if yes, 0 if no and -1 in case of error.
346 */
347typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
348
349/**
350 * xmlRelaxNGTypeCheck:
351 * @data: data needed for the library
352 * @type: the type name
353 * @value: the value to check
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000354 * @result: place to store the result if needed
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000355 *
356 * Function provided by a type library to check if a value match a type
357 *
358 * Returns 1 if yes, 0 if no and -1 in case of error.
359 */
360typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000361 const xmlChar *value, void **result);
362
363/**
364 * xmlRelaxNGFacetCheck:
365 * @data: data needed for the library
366 * @type: the type name
367 * @facet: the facet name
368 * @val: the facet value
369 * @strval: the string value
370 * @value: the value to check
371 *
372 * Function provided by a type library to check a value facet
373 *
374 * Returns 1 if yes, 0 if no and -1 in case of error.
375 */
376typedef int (*xmlRelaxNGFacetCheck) (void *data, const xmlChar *type,
377 const xmlChar *facet, const xmlChar *val,
378 const xmlChar *strval, void *value);
379
380/**
381 * xmlRelaxNGTypeFree:
382 * @data: data needed for the library
383 * @result: the value to free
384 *
385 * Function provided by a type library to free a returned result
386 */
387typedef void (*xmlRelaxNGTypeFree) (void *data, void *result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000388
389/**
390 * xmlRelaxNGTypeCompare:
391 * @data: data needed for the library
392 * @type: the type name
393 * @value1: the first value
394 * @value2: the second value
395 *
396 * Function provided by a type library to compare two values accordingly
397 * to a type.
398 *
399 * Returns 1 if yes, 0 if no and -1 in case of error.
400 */
401typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
402 const xmlChar *value1,
403 const xmlChar *value2);
404typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
405typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
406struct _xmlRelaxNGTypeLibrary {
407 const xmlChar *namespace; /* the datatypeLibrary value */
408 void *data; /* data needed for the library */
409 xmlRelaxNGTypeHave have; /* the export function */
410 xmlRelaxNGTypeCheck check; /* the checking function */
411 xmlRelaxNGTypeCompare comp; /* the compare function */
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000412 xmlRelaxNGFacetCheck facet; /* the facet check function */
413 xmlRelaxNGTypeFree freef; /* the freeing function */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000414};
415
416/************************************************************************
417 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000418 * Allocation functions *
419 * *
420 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000421static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
422static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000423static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillardc482e262003-02-26 14:48:48 +0000424static void xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000425
426/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000427 * xmlRelaxNGFreeDocument:
428 * @docu: a document structure
429 *
430 * Deallocate a RelaxNG document structure.
431 */
432static void
433xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
434{
435 if (docu == NULL)
436 return;
437
438 if (docu->href != NULL)
439 xmlFree(docu->href);
440 if (docu->doc != NULL)
441 xmlFreeDoc(docu->doc);
442 if (docu->schema != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000443 xmlRelaxNGFreeInnerSchema(docu->schema);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000444 xmlFree(docu);
445}
446
447/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000448 * xmlRelaxNGFreeDocumentList:
449 * @docu: a list of document structure
450 *
451 * Deallocate a RelaxNG document structures.
452 */
453static void
454xmlRelaxNGFreeDocumentList(xmlRelaxNGDocumentPtr docu)
455{
456 xmlRelaxNGDocumentPtr next;
457 while (docu != NULL) {
458 next = docu->next;
459 xmlRelaxNGFreeDocument(docu);
460 docu = next;
461 }
462}
463
464/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000465 * xmlRelaxNGFreeInclude:
466 * @incl: a include structure
467 *
468 * Deallocate a RelaxNG include structure.
469 */
470static void
471xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
472{
473 if (incl == NULL)
474 return;
475
476 if (incl->href != NULL)
477 xmlFree(incl->href);
478 if (incl->doc != NULL)
479 xmlFreeDoc(incl->doc);
480 if (incl->schema != NULL)
481 xmlRelaxNGFree(incl->schema);
482 xmlFree(incl);
483}
484
485/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000486 * xmlRelaxNGFreeIncludeList:
487 * @incl: a include structure list
488 *
489 * Deallocate a RelaxNG include structure.
490 */
491static void
492xmlRelaxNGFreeIncludeList(xmlRelaxNGIncludePtr incl)
493{
494 xmlRelaxNGIncludePtr next;
495 while (incl != NULL) {
496 next = incl->next;
497 xmlRelaxNGFreeInclude(incl);
498 incl = next;
499 }
500}
501
502/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000503 * xmlRelaxNGNewRelaxNG:
504 * @ctxt: a Relax-NG validation context (optional)
505 *
506 * Allocate a new RelaxNG structure.
507 *
508 * Returns the newly allocated structure or NULL in case or error
509 */
510static xmlRelaxNGPtr
511xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
512{
513 xmlRelaxNGPtr ret;
514
515 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
516 if (ret == NULL) {
517 if ((ctxt != NULL) && (ctxt->error != NULL))
518 ctxt->error(ctxt->userData, "Out of memory\n");
519 ctxt->nbErrors++;
520 return (NULL);
521 }
522 memset(ret, 0, sizeof(xmlRelaxNG));
523
524 return (ret);
525}
526
527/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000528 * xmlRelaxNGFreeInnerSchema:
529 * @schema: a schema structure
530 *
531 * Deallocate a RelaxNG schema structure.
532 */
533static void
534xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema)
535{
536 if (schema == NULL)
537 return;
538
539 if (schema->doc != NULL)
540 xmlFreeDoc(schema->doc);
541 if (schema->defTab != NULL) {
542 int i;
543
544 for (i = 0;i < schema->defNr;i++)
545 xmlRelaxNGFreeDefine(schema->defTab[i]);
546 xmlFree(schema->defTab);
547 }
548
549 xmlFree(schema);
550}
551
552/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000553 * xmlRelaxNGFree:
554 * @schema: a schema structure
555 *
556 * Deallocate a RelaxNG structure.
557 */
558void
559xmlRelaxNGFree(xmlRelaxNGPtr schema)
560{
561 if (schema == NULL)
562 return;
563
Daniel Veillard6eadf632003-01-23 18:29:16 +0000564 if (schema->topgrammar != NULL)
565 xmlRelaxNGFreeGrammar(schema->topgrammar);
566 if (schema->doc != NULL)
567 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000568 if (schema->documents != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000569 xmlRelaxNGFreeDocumentList(schema->documents);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000570 if (schema->includes != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000571 xmlRelaxNGFreeIncludeList(schema->includes);
Daniel Veillard419a7682003-02-03 23:22:49 +0000572 if (schema->defTab != NULL) {
573 int i;
574
575 for (i = 0;i < schema->defNr;i++)
576 xmlRelaxNGFreeDefine(schema->defTab[i]);
577 xmlFree(schema->defTab);
578 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000579
580 xmlFree(schema);
581}
582
583/**
584 * xmlRelaxNGNewGrammar:
585 * @ctxt: a Relax-NG validation context (optional)
586 *
587 * Allocate a new RelaxNG grammar.
588 *
589 * Returns the newly allocated structure or NULL in case or error
590 */
591static xmlRelaxNGGrammarPtr
592xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
593{
594 xmlRelaxNGGrammarPtr ret;
595
596 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
597 if (ret == NULL) {
598 if ((ctxt != NULL) && (ctxt->error != NULL))
599 ctxt->error(ctxt->userData, "Out of memory\n");
600 ctxt->nbErrors++;
601 return (NULL);
602 }
603 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
604
605 return (ret);
606}
607
608/**
609 * xmlRelaxNGFreeGrammar:
610 * @grammar: a grammar structure
611 *
612 * Deallocate a RelaxNG grammar structure.
613 */
614static void
615xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
616{
617 if (grammar == NULL)
618 return;
619
Daniel Veillardc482e262003-02-26 14:48:48 +0000620 if (grammar->children != NULL) {
621 xmlRelaxNGFreeGrammar(grammar->children);
622 }
Daniel Veillard419a7682003-02-03 23:22:49 +0000623 if (grammar->next != NULL) {
624 xmlRelaxNGFreeGrammar(grammar->next);
625 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000626 if (grammar->refs != NULL) {
627 xmlHashFree(grammar->refs, NULL);
628 }
629 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000630 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000631 }
632
633 xmlFree(grammar);
634}
635
636/**
637 * xmlRelaxNGNewDefine:
638 * @ctxt: a Relax-NG validation context
639 * @node: the node in the input document.
640 *
641 * Allocate a new RelaxNG define.
642 *
643 * Returns the newly allocated structure or NULL in case or error
644 */
645static xmlRelaxNGDefinePtr
646xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
647{
648 xmlRelaxNGDefinePtr ret;
649
Daniel Veillard419a7682003-02-03 23:22:49 +0000650 if (ctxt->defMax == 0) {
651 ctxt->defMax = 16;
652 ctxt->defNr = 0;
653 ctxt->defTab = (xmlRelaxNGDefinePtr *)
654 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
655 if (ctxt->defTab == NULL) {
656 if ((ctxt != NULL) && (ctxt->error != NULL))
657 ctxt->error(ctxt->userData, "Out of memory\n");
658 ctxt->nbErrors++;
659 return (NULL);
660 }
661 } else if (ctxt->defMax <= ctxt->defNr) {
662 xmlRelaxNGDefinePtr *tmp;
663 ctxt->defMax *= 2;
664 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
665 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
666 if (tmp == NULL) {
667 if ((ctxt != NULL) && (ctxt->error != NULL))
668 ctxt->error(ctxt->userData, "Out of memory\n");
669 ctxt->nbErrors++;
670 return (NULL);
671 }
672 ctxt->defTab = tmp;
673 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000674 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
675 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000676 if ((ctxt != NULL) && (ctxt->error != NULL))
677 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000678 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000679 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000680 }
681 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000682 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000683 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000684 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000685 return (ret);
686}
687
688/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000689 * xmlRelaxNGFreePartition:
690 * @partitions: a partition set structure
691 *
692 * Deallocate RelaxNG partition set structures.
693 */
694static void
695xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
696 xmlRelaxNGInterleaveGroupPtr group;
697 int j;
698
699 if (partitions != NULL) {
700 if (partitions->groups != NULL) {
701 for (j = 0;j < partitions->nbgroups;j++) {
702 group = partitions->groups[j];
703 if (group != NULL) {
704 if (group->defs != NULL)
705 xmlFree(group->defs);
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000706 if (group->attrs != NULL)
707 xmlFree(group->attrs);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000708 xmlFree(group);
709 }
710 }
711 xmlFree(partitions->groups);
712 }
713 xmlFree(partitions);
714 }
715}
716/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000717 * xmlRelaxNGFreeDefine:
718 * @define: a define structure
719 *
720 * Deallocate a RelaxNG define structure.
721 */
722static void
723xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
724{
725 if (define == NULL)
726 return;
727
Daniel Veillard419a7682003-02-03 23:22:49 +0000728 if ((define->data != NULL) &&
729 (define->type == XML_RELAXNG_INTERLEAVE))
730 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000731 if (define->name != NULL)
732 xmlFree(define->name);
733 if (define->ns != NULL)
734 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000735 if (define->value != NULL)
736 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000737 xmlFree(define);
738}
739
740/**
741 * xmlRelaxNGNewValidState:
742 * @ctxt: a Relax-NG validation context
743 * @node: the current node or NULL for the document
744 *
745 * Allocate a new RelaxNG validation state
746 *
747 * Returns the newly allocated structure or NULL in case or error
748 */
749static xmlRelaxNGValidStatePtr
750xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
751{
752 xmlRelaxNGValidStatePtr ret;
753 xmlAttrPtr attr;
754 xmlAttrPtr attrs[MAX_ATTR];
755 int nbAttrs = 0;
756 xmlNodePtr root = NULL;
757
758 if (node == NULL) {
759 root = xmlDocGetRootElement(ctxt->doc);
760 if (root == NULL)
761 return(NULL);
762 } else {
763 attr = node->properties;
764 while (attr != NULL) {
765 if (nbAttrs < MAX_ATTR)
766 attrs[nbAttrs++] = attr;
767 else
768 nbAttrs++;
769 attr = attr->next;
770 }
771 }
772
773 if (nbAttrs < MAX_ATTR)
774 attrs[nbAttrs] = NULL;
775 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
776 nbAttrs * sizeof(xmlAttrPtr));
777 if (ret == NULL) {
778 if ((ctxt != NULL) && (ctxt->error != NULL))
779 ctxt->error(ctxt->userData, "Out of memory\n");
780 return (NULL);
781 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000782 ret->value = NULL;
783 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000784 if (node == NULL) {
785 ret->node = (xmlNodePtr) ctxt->doc;
786 ret->seq = root;
787 ret->nbAttrs = 0;
788 } else {
789 ret->node = node;
790 ret->seq = node->children;
791 ret->nbAttrs = nbAttrs;
792 if (nbAttrs > 0) {
793 if (nbAttrs < MAX_ATTR) {
794 memcpy(&(ret->attrs[0]), attrs,
795 sizeof(xmlAttrPtr) * (nbAttrs + 1));
796 } else {
797 attr = node->properties;
798 nbAttrs = 0;
799 while (attr != NULL) {
800 ret->attrs[nbAttrs++] = attr;
801 attr = attr->next;
802 }
803 ret->attrs[nbAttrs] = NULL;
804 }
805 }
806 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000807 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000808 return (ret);
809}
810
811/**
812 * xmlRelaxNGCopyValidState:
813 * @ctxt: a Relax-NG validation context
814 * @state: a validation state
815 *
816 * Copy the validation state
817 *
818 * Returns the newly allocated structure or NULL in case or error
819 */
820static xmlRelaxNGValidStatePtr
821xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
822 xmlRelaxNGValidStatePtr state)
823{
824 xmlRelaxNGValidStatePtr ret;
825 unsigned int size;
826
827 if (state == NULL)
828 return(NULL);
829
830 size = sizeof(xmlRelaxNGValidState) +
831 state->nbAttrs * sizeof(xmlAttrPtr);
832 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
833 if (ret == NULL) {
834 if ((ctxt != NULL) && (ctxt->error != NULL))
835 ctxt->error(ctxt->userData, "Out of memory\n");
836 return (NULL);
837 }
838 memcpy(ret, state, size);
839 return(ret);
840}
841
842/**
Daniel Veillardce14fa52003-02-19 17:32:48 +0000843 * xmlRelaxNGEqualValidState:
844 * @ctxt: a Relax-NG validation context
845 * @state1: a validation state
846 * @state2: a validation state
847 *
848 * Compare the validation states for equality
849 *
850 * Returns 1 if equald, 0 otherwise
851 */
852static int
853xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
854 xmlRelaxNGValidStatePtr state1,
855 xmlRelaxNGValidStatePtr state2)
856{
857 int i;
858
859 if ((state1 == NULL) || (state2 == NULL))
860 return(0);
861 if (state1 == state2)
862 return(1);
863 if (state1->node != state2->node)
864 return(0);
865 if (state1->seq != state2->seq)
866 return(0);
867 if (state1->nbAttrLeft != state2->nbAttrLeft)
868 return(0);
869 if (state1->nbAttrs != state2->nbAttrs)
870 return(0);
871 if (state1->endvalue != state2->endvalue)
872 return(0);
873 if ((state1->value != state2->value) &&
874 (!xmlStrEqual(state1->value, state2->value)))
875 return(0);
876 for (i = 0;i < state1->nbAttrs;i++) {
877 if (state1->attrs[i] != state2->attrs[i])
878 return(0);
879 }
880 return(1);
881}
882
883/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000884 * xmlRelaxNGFreeValidState:
885 * @state: a validation state structure
886 *
887 * Deallocate a RelaxNG validation state structure.
888 */
889static void
890xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
891{
892 if (state == NULL)
893 return;
894
895 xmlFree(state);
896}
897
898/************************************************************************
899 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000900 * Document functions *
901 * *
902 ************************************************************************/
903static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
904 xmlDocPtr doc);
905
906/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000907 * xmlRelaxNGIncludePush:
908 * @ctxt: the parser context
909 * @value: the element doc
910 *
911 * Pushes a new include on top of the include stack
912 *
913 * Returns 0 in case of error, the index in the stack otherwise
914 */
915static int
916xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
917 xmlRelaxNGIncludePtr value)
918{
919 if (ctxt->incTab == NULL) {
920 ctxt->incMax = 4;
921 ctxt->incNr = 0;
922 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
923 ctxt->incMax * sizeof(ctxt->incTab[0]));
924 if (ctxt->incTab == NULL) {
925 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
926 return (0);
927 }
928 }
929 if (ctxt->incNr >= ctxt->incMax) {
930 ctxt->incMax *= 2;
931 ctxt->incTab =
932 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
933 ctxt->incMax *
934 sizeof(ctxt->incTab[0]));
935 if (ctxt->incTab == NULL) {
936 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
937 return (0);
938 }
939 }
940 ctxt->incTab[ctxt->incNr] = value;
941 ctxt->inc = value;
942 return (ctxt->incNr++);
943}
944
945/**
946 * xmlRelaxNGIncludePop:
947 * @ctxt: the parser context
948 *
949 * Pops the top include from the include stack
950 *
951 * Returns the include just removed
952 */
953static xmlRelaxNGIncludePtr
954xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
955{
956 xmlRelaxNGIncludePtr ret;
957
958 if (ctxt->incNr <= 0)
959 return (0);
960 ctxt->incNr--;
961 if (ctxt->incNr > 0)
962 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
963 else
964 ctxt->inc = NULL;
965 ret = ctxt->incTab[ctxt->incNr];
966 ctxt->incTab[ctxt->incNr] = 0;
967 return (ret);
968}
969
970/**
Daniel Veillard5add8682003-03-10 13:13:58 +0000971 * xmlRelaxNGRemoveRedefine:
972 * @ctxt: the parser context
973 * @URL: the normalized URL
974 * @target: the included target
975 * @name: the define name to eliminate
976 *
977 * Applies the elimination algorithm of 4.7
978 *
979 * Returns 0 in case of error, 1 in case of success.
980 */
981static int
982xmlRelaxNGRemoveRedefine(xmlRelaxNGParserCtxtPtr ctxt,
983 const xmlChar *URL ATTRIBUTE_UNUSED,
984 xmlNodePtr target, const xmlChar *name) {
985 int found = 0;
986 xmlNodePtr tmp, tmp2;
987 xmlChar *name2;
988
989#ifdef DEBUG_INCLUDE
990 xmlGenericError(xmlGenericErrorContext,
991 "Elimination of <include> %s from %s\n", name, URL);
992#endif
993 tmp = target;
994 while (tmp != NULL) {
995 tmp2 = tmp->next;
996 if ((name == NULL) && (IS_RELAXNG(tmp, "start"))) {
997 found = 1;
998 xmlUnlinkNode(tmp);
999 xmlFreeNode(tmp);
1000 } else if ((name != NULL) && (IS_RELAXNG(tmp, "define"))) {
1001 name2 = xmlGetProp(tmp, BAD_CAST "name");
1002 xmlRelaxNGNormExtSpace(name2);
1003 if (name2 != NULL) {
1004 if (xmlStrEqual(name, name2)) {
1005 found = 1;
1006 xmlUnlinkNode(tmp);
1007 xmlFreeNode(tmp);
1008 }
1009 xmlFree(name2);
1010 }
1011 } else if (IS_RELAXNG(tmp, "include")) {
1012 xmlChar *href = NULL;
1013 xmlRelaxNGDocumentPtr inc = tmp->_private;
1014
1015 if ((inc != NULL) && (inc->doc != NULL) &&
1016 (inc->doc->children != NULL)) {
1017
1018 if (xmlStrEqual(inc->doc->children->name, BAD_CAST "grammar")) {
1019#ifdef DEBUG_INCLUDE
1020 href = xmlGetProp(tmp, BAD_CAST "href");
1021#endif
1022 if (xmlRelaxNGRemoveRedefine(ctxt, href,
1023 inc->doc->children->children, name) == 1) {
1024 found = 1;
1025 }
1026 if (href != NULL)
1027 xmlFree(href);
1028 }
1029 }
1030 }
1031 tmp = tmp2;
1032 }
1033 return(found);
1034}
1035
1036/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001037 * xmlRelaxNGLoadInclude:
1038 * @ctxt: the parser context
1039 * @URL: the normalized URL
1040 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +00001041 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001042 *
1043 * First lookup if the document is already loaded into the parser context,
1044 * check against recursion. If not found the resource is loaded and
1045 * the content is preprocessed before being returned back to the caller.
1046 *
1047 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
1048 */
1049static xmlRelaxNGIncludePtr
1050xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +00001051 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001052 xmlRelaxNGIncludePtr ret = NULL;
1053 xmlDocPtr doc;
1054 int i;
Daniel Veillard5add8682003-03-10 13:13:58 +00001055 xmlNodePtr root, cur;
1056
1057#ifdef DEBUG_INCLUDE
1058 xmlGenericError(xmlGenericErrorContext,
1059 "xmlRelaxNGLoadInclude(%s)\n", URL);
1060#endif
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001061
1062 /*
1063 * check against recursion in the stack
1064 */
1065 for (i = 0;i < ctxt->incNr;i++) {
1066 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
1067 if (ctxt->error != NULL)
1068 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00001069 "Detected an Include recursion for %s\n",
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001070 URL);
1071 ctxt->nbErrors++;
1072 return(NULL);
1073 }
1074 }
1075
1076 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001077 * load the document
1078 */
1079 doc = xmlParseFile((const char *) URL);
1080 if (doc == NULL) {
1081 if (ctxt->error != NULL)
1082 ctxt->error(ctxt->userData,
1083 "xmlRelaxNG: could not load %s\n", URL);
1084 ctxt->nbErrors++;
1085 return (NULL);
1086 }
1087
Daniel Veillard5add8682003-03-10 13:13:58 +00001088#ifdef DEBUG_INCLUDE
1089 xmlGenericError(xmlGenericErrorContext,
1090 "Parsed %s Okay\n", URL);
1091#endif
1092
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001093 /*
1094 * Allocate the document structures and register it first.
1095 */
1096 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
1097 if (ret == NULL) {
1098 if (ctxt->error != NULL)
1099 ctxt->error(ctxt->userData,
1100 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1101 ctxt->nbErrors++;
1102 xmlFreeDoc(doc);
1103 return (NULL);
1104 }
1105 memset(ret, 0, sizeof(xmlRelaxNGInclude));
1106 ret->doc = doc;
1107 ret->href = xmlStrdup(URL);
Daniel Veillardc482e262003-02-26 14:48:48 +00001108 ret->next = ctxt->includes;
1109 ctxt->includes = ret;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001110
1111 /*
Daniel Veillard416589a2003-02-17 17:25:42 +00001112 * transmit the ns if needed
1113 */
1114 if (ns != NULL) {
1115 root = xmlDocGetRootElement(doc);
1116 if (root != NULL) {
1117 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1118 xmlSetProp(root, BAD_CAST"ns", ns);
1119 }
1120 }
1121 }
1122
1123 /*
Daniel Veillardc482e262003-02-26 14:48:48 +00001124 * push it on the stack
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001125 */
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001126 xmlRelaxNGIncludePush(ctxt, ret);
1127
1128 /*
1129 * Some preprocessing of the document content, this include recursing
1130 * in the include stack.
1131 */
Daniel Veillard5add8682003-03-10 13:13:58 +00001132#ifdef DEBUG_INCLUDE
1133 xmlGenericError(xmlGenericErrorContext,
1134 "cleanup of %s\n", URL);
1135#endif
1136
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001137 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1138 if (doc == NULL) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001139 ctxt->inc = NULL;
1140 return(NULL);
1141 }
1142
1143 /*
1144 * Pop up the include from the stack
1145 */
1146 xmlRelaxNGIncludePop(ctxt);
1147
Daniel Veillard5add8682003-03-10 13:13:58 +00001148#ifdef DEBUG_INCLUDE
1149 xmlGenericError(xmlGenericErrorContext,
1150 "Checking of %s\n", URL);
1151#endif
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001152 /*
1153 * Check that the top element is a grammar
1154 */
1155 root = xmlDocGetRootElement(doc);
1156 if (root == NULL) {
1157 if (ctxt->error != NULL)
1158 ctxt->error(ctxt->userData,
1159 "xmlRelaxNG: included document is empty %s\n", URL);
1160 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001161 return (NULL);
1162 }
1163 if (!IS_RELAXNG(root, "grammar")) {
1164 if (ctxt->error != NULL)
1165 ctxt->error(ctxt->userData,
1166 "xmlRelaxNG: included document %s root is not a grammar\n",
1167 URL);
1168 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001169 return (NULL);
1170 }
1171
1172 /*
1173 * Elimination of redefined rules in the include.
1174 */
1175 cur = node->children;
1176 while (cur != NULL) {
1177 if (IS_RELAXNG(cur, "start")) {
1178 int found = 0;
1179
Daniel Veillard5add8682003-03-10 13:13:58 +00001180 found = xmlRelaxNGRemoveRedefine(ctxt, URL, root->children, NULL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001181 if (!found) {
1182 if (ctxt->error != NULL)
1183 ctxt->error(ctxt->userData,
1184 "xmlRelaxNG: include %s has a start but not the included grammar\n",
1185 URL);
1186 ctxt->nbErrors++;
1187 }
1188 } else if (IS_RELAXNG(cur, "define")) {
Daniel Veillard5add8682003-03-10 13:13:58 +00001189 xmlChar *name;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001190
1191 name = xmlGetProp(cur, BAD_CAST "name");
1192 if (name == NULL) {
1193 if (ctxt->error != NULL)
1194 ctxt->error(ctxt->userData,
1195 "xmlRelaxNG: include %s has define without name\n",
1196 URL);
1197 ctxt->nbErrors++;
1198 } else {
Daniel Veillard5add8682003-03-10 13:13:58 +00001199 int found;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001200
Daniel Veillardd2298792003-02-14 16:54:11 +00001201 xmlRelaxNGNormExtSpace(name);
Daniel Veillard5add8682003-03-10 13:13:58 +00001202 found = xmlRelaxNGRemoveRedefine(ctxt, URL,
1203 root->children, name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001204 if (!found) {
1205 if (ctxt->error != NULL)
1206 ctxt->error(ctxt->userData,
1207 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1208 URL, name);
1209 ctxt->nbErrors++;
1210 }
1211 xmlFree(name);
1212 }
1213 }
1214 cur = cur->next;
1215 }
1216
1217
1218 return(ret);
1219}
1220
1221/**
Daniel Veillard42f12e92003-03-07 18:32:59 +00001222 * xmlRelaxNGValidErrorPush:
1223 * @ctxt: the validation context
1224 * @err: the error code
1225 * @arg1: the first string argument
1226 * @arg2: the second string argument
1227 *
1228 * Pushes a new error on top of the error stack
1229 *
1230 * Returns 0 in case of error, the index in the stack otherwise
1231 */
1232static int
1233xmlRelaxNGValidErrorPush(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err,
1234 const xmlChar *arg1, const xmlChar *arg2)
1235{
1236 xmlRelaxNGValidErrorPtr cur;
1237 if (ctxt->errTab == NULL) {
1238 ctxt->errMax = 8;
1239 ctxt->errNr = 0;
1240 ctxt->errTab = (xmlRelaxNGValidErrorPtr) xmlMalloc(
1241 ctxt->errMax * sizeof(xmlRelaxNGValidError));
1242 if (ctxt->errTab == NULL) {
1243 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1244 return (0);
1245 }
1246 }
1247 if (ctxt->errNr >= ctxt->errMax) {
1248 ctxt->errMax *= 2;
1249 ctxt->errTab =
1250 (xmlRelaxNGValidErrorPtr) xmlRealloc(ctxt->errTab,
1251 ctxt->errMax * sizeof(xmlRelaxNGValidError));
1252 if (ctxt->errTab == NULL) {
1253 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1254 return (0);
1255 }
1256 }
1257 cur = &ctxt->errTab[ctxt->errNr];
1258 cur->err = err;
1259 cur->arg1 = arg1;
1260 cur->arg2 = arg2;
1261 if (ctxt->state != NULL) {
1262 cur->node = ctxt->state->node;
1263 cur->seq = ctxt->state->seq;
1264 } else {
1265 cur->node = NULL;
1266 cur->seq = NULL;
1267 }
1268 ctxt->err = cur;
1269 return (ctxt->errNr++);
1270}
1271
1272/**
1273 * xmlRelaxNGValidErrorPop:
1274 * @ctxt: the validation context
1275 *
1276 * Pops the top error from the error stack
1277 *
1278 * Returns the error just removed
1279 */
1280static xmlRelaxNGValidErrorPtr
1281xmlRelaxNGValidErrorPop(xmlRelaxNGValidCtxtPtr ctxt)
1282{
1283 xmlRelaxNGValidErrorPtr ret;
1284
1285 if (ctxt->errNr <= 0)
1286 return (NULL);
1287 ctxt->errNr--;
1288 if (ctxt->errNr > 0)
1289 ctxt->err = &ctxt->errTab[ctxt->errNr - 1];
1290 else
1291 ctxt->err = NULL;
1292 ret = &ctxt->errTab[ctxt->errNr];
1293 return (ret);
1294}
1295
1296
1297/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001298 * xmlRelaxNGDocumentPush:
1299 * @ctxt: the parser context
1300 * @value: the element doc
1301 *
1302 * Pushes a new doc on top of the doc stack
1303 *
1304 * Returns 0 in case of error, the index in the stack otherwise
1305 */
1306static int
1307xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1308 xmlRelaxNGDocumentPtr value)
1309{
1310 if (ctxt->docTab == NULL) {
1311 ctxt->docMax = 4;
1312 ctxt->docNr = 0;
1313 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1314 ctxt->docMax * sizeof(ctxt->docTab[0]));
1315 if (ctxt->docTab == NULL) {
1316 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1317 return (0);
1318 }
1319 }
1320 if (ctxt->docNr >= ctxt->docMax) {
1321 ctxt->docMax *= 2;
1322 ctxt->docTab =
1323 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1324 ctxt->docMax *
1325 sizeof(ctxt->docTab[0]));
1326 if (ctxt->docTab == NULL) {
1327 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1328 return (0);
1329 }
1330 }
1331 ctxt->docTab[ctxt->docNr] = value;
1332 ctxt->doc = value;
1333 return (ctxt->docNr++);
1334}
1335
1336/**
1337 * xmlRelaxNGDocumentPop:
1338 * @ctxt: the parser context
1339 *
1340 * Pops the top doc from the doc stack
1341 *
1342 * Returns the doc just removed
1343 */
1344static xmlRelaxNGDocumentPtr
1345xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1346{
1347 xmlRelaxNGDocumentPtr ret;
1348
1349 if (ctxt->docNr <= 0)
1350 return (0);
1351 ctxt->docNr--;
1352 if (ctxt->docNr > 0)
1353 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1354 else
1355 ctxt->doc = NULL;
1356 ret = ctxt->docTab[ctxt->docNr];
1357 ctxt->docTab[ctxt->docNr] = 0;
1358 return (ret);
1359}
1360
1361/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001362 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001363 * @ctxt: the parser context
1364 * @URL: the normalized URL
1365 * @ns: the inherited ns if any
1366 *
1367 * First lookup if the document is already loaded into the parser context,
1368 * check against recursion. If not found the resource is loaded and
1369 * the content is preprocessed before being returned back to the caller.
1370 *
1371 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1372 */
1373static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001374xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001375 const xmlChar *ns) {
1376 xmlRelaxNGDocumentPtr ret = NULL;
1377 xmlDocPtr doc;
1378 xmlNodePtr root;
1379 int i;
1380
1381 /*
1382 * check against recursion in the stack
1383 */
1384 for (i = 0;i < ctxt->docNr;i++) {
1385 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1386 if (ctxt->error != NULL)
1387 ctxt->error(ctxt->userData,
1388 "Detected an externalRef recursion for %s\n",
1389 URL);
1390 ctxt->nbErrors++;
1391 return(NULL);
1392 }
1393 }
1394
1395 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001396 * load the document
1397 */
1398 doc = xmlParseFile((const char *) URL);
1399 if (doc == NULL) {
1400 if (ctxt->error != NULL)
1401 ctxt->error(ctxt->userData,
1402 "xmlRelaxNG: could not load %s\n", URL);
1403 ctxt->nbErrors++;
1404 return (NULL);
1405 }
1406
1407 /*
1408 * Allocate the document structures and register it first.
1409 */
1410 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1411 if (ret == NULL) {
1412 if (ctxt->error != NULL)
1413 ctxt->error(ctxt->userData,
1414 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1415 ctxt->nbErrors++;
1416 xmlFreeDoc(doc);
1417 return (NULL);
1418 }
1419 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1420 ret->doc = doc;
1421 ret->href = xmlStrdup(URL);
Daniel Veillardc482e262003-02-26 14:48:48 +00001422 ret->next = ctxt->documents;
1423 ctxt->documents = ret;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001424
1425 /*
1426 * transmit the ns if needed
1427 */
1428 if (ns != NULL) {
1429 root = xmlDocGetRootElement(doc);
1430 if (root != NULL) {
1431 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1432 xmlSetProp(root, BAD_CAST"ns", ns);
1433 }
1434 }
1435 }
1436
1437 /*
1438 * push it on the stack and register it in the hash table
1439 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001440 xmlRelaxNGDocumentPush(ctxt, ret);
1441
1442 /*
1443 * Some preprocessing of the document content
1444 */
1445 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1446 if (doc == NULL) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001447 ctxt->doc = NULL;
1448 return(NULL);
1449 }
1450
1451 xmlRelaxNGDocumentPop(ctxt);
1452
1453 return(ret);
1454}
1455
1456/************************************************************************
1457 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001458 * Error functions *
1459 * *
1460 ************************************************************************/
1461
Daniel Veillard42f12e92003-03-07 18:32:59 +00001462#define VALID_ERR(a) xmlRelaxNGAddValidError(ctxt, a, NULL, NULL);
1463#define VALID_ERR2(a, b) xmlRelaxNGAddValidError(ctxt, a, b, NULL);
1464#define VALID_ERR3(a, b, c) xmlRelaxNGAddValidError(ctxt, a, b, c);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001465
Daniel Veillardd2298792003-02-14 16:54:11 +00001466#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001467static const char *
1468xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1469 if (def == NULL)
1470 return("none");
1471 switch(def->type) {
1472 case XML_RELAXNG_EMPTY: return("empty");
1473 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1474 case XML_RELAXNG_EXCEPT: return("except");
1475 case XML_RELAXNG_TEXT: return("text");
1476 case XML_RELAXNG_ELEMENT: return("element");
1477 case XML_RELAXNG_DATATYPE: return("datatype");
1478 case XML_RELAXNG_VALUE: return("value");
1479 case XML_RELAXNG_LIST: return("list");
1480 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1481 case XML_RELAXNG_DEF: return("def");
1482 case XML_RELAXNG_REF: return("ref");
1483 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1484 case XML_RELAXNG_PARENTREF: return("parentRef");
1485 case XML_RELAXNG_OPTIONAL: return("optional");
1486 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1487 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1488 case XML_RELAXNG_CHOICE: return("choice");
1489 case XML_RELAXNG_GROUP: return("group");
1490 case XML_RELAXNG_INTERLEAVE: return("interleave");
1491 case XML_RELAXNG_START: return("start");
1492 }
1493 return("unknown");
1494}
Daniel Veillardd2298792003-02-14 16:54:11 +00001495#endif
1496
Daniel Veillard6eadf632003-01-23 18:29:16 +00001497/**
Daniel Veillard42f12e92003-03-07 18:32:59 +00001498 * xmlRelaxNGGetErrorString:
1499 * @err: the error code
1500 * @arg1: the first string argument
1501 * @arg2: the second string argument
Daniel Veillard6eadf632003-01-23 18:29:16 +00001502 *
Daniel Veillard42f12e92003-03-07 18:32:59 +00001503 * computes a formatted error string for the given error code and args
1504 *
1505 * Returns the error string, it must be deallocated by the caller
1506 */
1507static xmlChar *
1508xmlRelaxNGGetErrorString(xmlRelaxNGValidErr err, const xmlChar *arg1,
1509 const xmlChar *arg2) {
1510 char msg[1000];
1511
1512 if (arg1 == NULL)
1513 arg1 = BAD_CAST "";
1514 if (arg2 == NULL)
1515 arg2 = BAD_CAST "";
1516
1517 msg[0] = 0;
1518 switch (err) {
1519 case XML_RELAXNG_OK:
1520 return(NULL);
1521 case XML_RELAXNG_ERR_MEMORY:
1522 return(xmlCharStrdup("out of memory"));
1523 case XML_RELAXNG_ERR_TYPE:
1524 snprintf(msg, 1000, "failed to validate type %s", arg1);
1525 break;
1526 case XML_RELAXNG_ERR_TYPEVAL:
1527 snprintf(msg, 1000, "Type %s doesn't allow value %s", arg1, arg2);
1528 break;
1529 case XML_RELAXNG_ERR_TYPECMP:
1530 snprintf(msg, 1000, "failed to compare type %s", arg1);
1531 break;
1532 case XML_RELAXNG_ERR_NOSTATE:
1533 return(xmlCharStrdup("Internal error: no state"));
1534 case XML_RELAXNG_ERR_NODEFINE:
1535 return(xmlCharStrdup("Internal error: no define"));
1536 case XML_RELAXNG_ERR_LISTEXTRA:
1537 snprintf(msg, 1000, "Extra data in list: %s", arg1);
1538 break;
1539 case XML_RELAXNG_ERR_INTERNODATA:
1540 return(xmlCharStrdup("Internal: interleave block has no data"));
1541 case XML_RELAXNG_ERR_INTERSEQ:
1542 return(xmlCharStrdup("Invalid sequence in interleave"));
1543 case XML_RELAXNG_ERR_INTEREXTRA:
1544 snprintf(msg, 1000, "Extra element %s in interleave", arg1);
1545 break;
1546 case XML_RELAXNG_ERR_ELEMNAME:
1547 snprintf(msg, 1000, "Expecting element %s, got %s", arg1, arg2);
1548 break;
1549 case XML_RELAXNG_ERR_ELEMNONS:
1550 snprintf(msg, 1000, "Expecting a namespace for element %s", arg1);
1551 break;
1552 case XML_RELAXNG_ERR_ELEMWRONGNS:
1553 snprintf(msg, 1000, "Element %s has wrong namespace: expecting %s",
1554 arg1, arg2);
1555 break;
1556 case XML_RELAXNG_ERR_ELEMEXTRANS:
1557 snprintf(msg, 1000, "Expecting no namespace for element %s", arg1);
1558 break;
1559 case XML_RELAXNG_ERR_ELEMNOTEMPTY:
1560 snprintf(msg, 1000, "Expecting element %s to be empty", arg1);
1561 break;
1562 case XML_RELAXNG_ERR_NOELEM:
1563 snprintf(msg, 1000, "Expecting an element %s, got nothing", arg1);
1564 break;
1565 case XML_RELAXNG_ERR_NOTELEM:
1566 return(xmlCharStrdup("Expecting an element got text"));
1567 case XML_RELAXNG_ERR_ATTRVALID:
1568 snprintf(msg, 1000, "Element %s failed to validate attributes",
1569 arg1);
1570 break;
1571 case XML_RELAXNG_ERR_CONTENTVALID:
1572 snprintf(msg, 1000, "Element %s failed to validate content",
1573 arg1);
1574 break;
1575 case XML_RELAXNG_ERR_EXTRACONTENT:
1576 snprintf(msg, 1000, "Element %s has extra content: %s",
1577 arg1, arg2);
1578 break;
1579 case XML_RELAXNG_ERR_INVALIDATTR:
1580 snprintf(msg, 1000, "Invalid attribute %s for element %s",
1581 arg1, arg2);
1582 break;
1583 case XML_RELAXNG_ERR_DATAELEM:
1584 snprintf(msg, 1000, "Datatype element %s has child elements",
1585 arg1);
1586 break;
1587 case XML_RELAXNG_ERR_VALELEM:
1588 snprintf(msg, 1000, "Value element %s has child elements",
1589 arg1);
1590 break;
1591 case XML_RELAXNG_ERR_LISTELEM:
1592 snprintf(msg, 1000, "List element %s has child elements",
1593 arg1);
1594 break;
1595 case XML_RELAXNG_ERR_DATATYPE:
1596 snprintf(msg, 1000, "Error validating datatype %s",
1597 arg1);
1598 break;
1599 case XML_RELAXNG_ERR_VALUE:
1600 snprintf(msg, 1000, "Error validating value %s",
1601 arg1);
1602 break;
1603 case XML_RELAXNG_ERR_LIST:
1604 return(xmlCharStrdup("Error validating list"));
1605 case XML_RELAXNG_ERR_NOGRAMMAR:
1606 return(xmlCharStrdup("No top grammar defined"));
1607 case XML_RELAXNG_ERR_EXTRADATA:
1608 return(xmlCharStrdup("Extra data in the document"));
1609 }
1610 if (msg[0] == 0) {
1611 snprintf(msg, 1000, "Unknown error code %d", err);
1612 }
1613 msg[1000] = 0;
1614 return(xmlStrdup((xmlChar *) msg));
1615}
1616
1617/**
1618 * xmlRelaxNGValidErrorContext:
1619 * @ctxt: the validation context
1620 * @node: the node
1621 * @child: the node child generating the problem.
1622 *
1623 * Dump informations about the kocation of the error in the instance
Daniel Veillard6eadf632003-01-23 18:29:16 +00001624 */
1625static void
Daniel Veillard42f12e92003-03-07 18:32:59 +00001626xmlRelaxNGValidErrorContext(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node,
1627 xmlNodePtr child)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001628{
1629 int line = 0;
1630 const xmlChar *file = NULL;
1631 const xmlChar *name = NULL;
1632 const char *type = "error";
1633
1634 if ((ctxt == NULL) || (ctxt->error == NULL))
1635 return;
1636
1637 if (child != NULL)
1638 node = child;
1639
1640 if (node != NULL) {
1641 if ((node->type == XML_DOCUMENT_NODE) ||
1642 (node->type == XML_HTML_DOCUMENT_NODE)) {
1643 xmlDocPtr doc = (xmlDocPtr) node;
1644
1645 file = doc->URL;
1646 } else {
1647 /*
1648 * Try to find contextual informations to report
1649 */
1650 if (node->type == XML_ELEMENT_NODE) {
1651 line = (int) node->content;
1652 } else if ((node->prev != NULL) &&
1653 (node->prev->type == XML_ELEMENT_NODE)) {
1654 line = (int) node->prev->content;
1655 } else if ((node->parent != NULL) &&
1656 (node->parent->type == XML_ELEMENT_NODE)) {
1657 line = (int) node->parent->content;
1658 }
1659 if ((node->doc != NULL) && (node->doc->URL != NULL))
1660 file = node->doc->URL;
1661 if (node->name != NULL)
1662 name = node->name;
1663 }
1664 }
1665
Daniel Veillard42f12e92003-03-07 18:32:59 +00001666 type = "RNG validity error";
Daniel Veillard6eadf632003-01-23 18:29:16 +00001667
1668 if ((file != NULL) && (line != 0) && (name != NULL))
1669 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1670 type, file, line, name);
1671 else if ((file != NULL) && (name != NULL))
1672 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1673 type, file, name);
1674 else if ((file != NULL) && (line != 0))
1675 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1676 else if (file != NULL)
1677 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1678 else if (name != NULL)
1679 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1680 else
1681 ctxt->error(ctxt->userData, "%s\n", type);
1682}
Daniel Veillard42f12e92003-03-07 18:32:59 +00001683
1684/**
1685 * xmlRelaxNGShowValidError:
1686 * @ctxt: the validation context
1687 * @err: the error number
1688 * @node: the node
1689 * @child: the node child generating the problem.
1690 * @arg1: the first argument
1691 * @arg2: the second argument
1692 *
1693 * Show a validation error.
1694 */
1695static void
1696xmlRelaxNGShowValidError(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err,
1697 xmlNodePtr node, xmlNodePtr child,
1698 const xmlChar *arg1, const xmlChar *arg2)
1699{
1700 xmlChar *msg;
1701
1702 if (ctxt->error == NULL)
1703 return;
1704
1705 msg = xmlRelaxNGGetErrorString(err, arg1, arg2);
1706 if (msg == NULL)
1707 return;
1708
1709 xmlRelaxNGValidErrorContext(ctxt, node, child);
1710 ctxt->error(ctxt->userData, "%s\n", msg);
1711 xmlFree(msg);
1712}
1713
1714/**
1715 * xmlRelaxNGDumpValidError:
1716 * @ctxt: the validation context
1717 *
1718 * Show all validation error over a given index.
1719 */
1720static void
1721xmlRelaxNGDumpValidError(xmlRelaxNGValidCtxtPtr ctxt) {
1722 int i;
1723 xmlRelaxNGValidErrorPtr err;
1724
1725 for (i = 0;i < ctxt->errNr;i++) {
1726 err = &ctxt->errTab[i];
1727 xmlRelaxNGShowValidError(ctxt, err->err, err->node, err->seq,
1728 err->arg1, err->arg2);
1729 }
1730 ctxt->errNr = 0;
1731}
1732/**
1733 * xmlRelaxNGAddValidError:
1734 * @ctxt: the validation context
1735 * @err: the error number
1736 * @arg1: the first argument
1737 * @arg2: the second argument
1738 *
1739 * Register a validation error, either generating it if it's sure
1740 * or stacking it for later handling if unsure.
1741 */
1742static void
1743xmlRelaxNGAddValidError(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err,
1744 const xmlChar *arg1, const xmlChar *arg2)
1745{
1746 if ((ctxt == NULL) || (ctxt->error == NULL))
1747 return;
1748
1749 /*
1750 * generate the error directly
1751 */
1752 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) {
1753 xmlNodePtr node, seq;
1754 /*
1755 * Flush first any stacked error which might be the
1756 * real cause of the problem.
1757 */
1758 if (ctxt->errNr != 0)
1759 xmlRelaxNGDumpValidError(ctxt);
1760 if (ctxt->state != NULL) {
1761 node = ctxt->state->node;
1762 seq = ctxt->state->seq;
1763 } else {
1764 node = seq = NULL;
1765 }
1766 xmlRelaxNGShowValidError(ctxt, err, node, seq, arg1, arg2);
1767 }
1768 /*
1769 * Stack the error for later processing if needed
1770 */
1771 else {
1772 xmlRelaxNGValidErrorPush(ctxt, err, arg1, arg2);
1773 }
1774}
1775
Daniel Veillard6eadf632003-01-23 18:29:16 +00001776
1777/************************************************************************
1778 * *
1779 * Type library hooks *
1780 * *
1781 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001782static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1783 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001784
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001785/**
1786 * xmlRelaxNGSchemaTypeHave:
1787 * @data: data needed for the library
1788 * @type: the type name
1789 *
1790 * Check if the given type is provided by
1791 * the W3C XMLSchema Datatype library.
1792 *
1793 * Returns 1 if yes, 0 if no and -1 in case of error.
1794 */
1795static int
1796xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001797 const xmlChar *type) {
1798 xmlSchemaTypePtr typ;
1799
1800 if (type == NULL)
1801 return(-1);
1802 typ = xmlSchemaGetPredefinedType(type,
1803 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1804 if (typ == NULL)
1805 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001806 return(1);
1807}
1808
1809/**
1810 * xmlRelaxNGSchemaTypeCheck:
1811 * @data: data needed for the library
1812 * @type: the type name
1813 * @value: the value to check
1814 *
1815 * Check if the given type and value are validated by
1816 * the W3C XMLSchema Datatype library.
1817 *
1818 * Returns 1 if yes, 0 if no and -1 in case of error.
1819 */
1820static int
1821xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001822 const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001823 const xmlChar *value,
1824 void **result) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001825 xmlSchemaTypePtr typ;
1826 int ret;
1827
1828 /*
1829 * TODO: the type should be cached ab provided back, interface subject
1830 * to changes.
1831 * TODO: handle facets, may require an additional interface and keep
1832 * the value returned from the validation.
1833 */
1834 if ((type == NULL) || (value == NULL))
1835 return(-1);
1836 typ = xmlSchemaGetPredefinedType(type,
1837 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1838 if (typ == NULL)
1839 return(-1);
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001840 ret = xmlSchemaValidatePredefinedType(typ, value,
1841 (xmlSchemaValPtr *) result);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001842 if (ret == 0)
1843 return(1);
1844 if (ret > 0)
1845 return(0);
1846 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001847}
1848
1849/**
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001850 * xmlRelaxNGSchemaFacetCheck:
1851 * @data: data needed for the library
1852 * @type: the type name
1853 * @facet: the facet name
1854 * @val: the facet value
1855 * @strval: the string value
1856 * @value: the value to check
1857 *
1858 * Function provided by a type library to check a value facet
1859 *
1860 * Returns 1 if yes, 0 if no and -1 in case of error.
1861 */
1862static int
Daniel Veillard42f12e92003-03-07 18:32:59 +00001863xmlRelaxNGSchemaFacetCheck (void *data ATTRIBUTE_UNUSED, const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001864 const xmlChar *facetname, const xmlChar *val,
1865 const xmlChar *strval, void *value) {
1866 xmlSchemaFacetPtr facet;
1867 xmlSchemaTypePtr typ;
1868 int ret;
1869
1870 if ((type == NULL) || (strval == NULL))
1871 return(-1);
1872 typ = xmlSchemaGetPredefinedType(type,
1873 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1874 if (typ == NULL)
1875 return(-1);
1876
1877 facet = xmlSchemaNewFacet();
1878 if (facet == NULL)
1879 return(-1);
1880
1881 if (xmlStrEqual(facetname, BAD_CAST "minInclusive")) {
1882 facet->type = XML_SCHEMA_FACET_MININCLUSIVE;
1883 } else if (xmlStrEqual(facetname, BAD_CAST "minExclusive")) {
1884 facet->type = XML_SCHEMA_FACET_MINEXCLUSIVE;
1885 } else if (xmlStrEqual(facetname, BAD_CAST "maxInclusive")) {
1886 facet->type = XML_SCHEMA_FACET_MAXINCLUSIVE;
1887 } else if (xmlStrEqual(facetname, BAD_CAST "maxExclusive")) {
1888 facet->type = XML_SCHEMA_FACET_MAXEXCLUSIVE;
1889 } else if (xmlStrEqual(facetname, BAD_CAST "totalDigits")) {
1890 facet->type = XML_SCHEMA_FACET_TOTALDIGITS;
1891 } else if (xmlStrEqual(facetname, BAD_CAST "fractionDigits")) {
1892 facet->type = XML_SCHEMA_FACET_FRACTIONDIGITS;
1893 } else if (xmlStrEqual(facetname, BAD_CAST "pattern")) {
1894 facet->type = XML_SCHEMA_FACET_PATTERN;
1895 } else if (xmlStrEqual(facetname, BAD_CAST "enumeration")) {
1896 facet->type = XML_SCHEMA_FACET_ENUMERATION;
1897 } else if (xmlStrEqual(facetname, BAD_CAST "whiteSpace")) {
1898 facet->type = XML_SCHEMA_FACET_WHITESPACE;
1899 } else if (xmlStrEqual(facetname, BAD_CAST "length")) {
1900 facet->type = XML_SCHEMA_FACET_LENGTH;
1901 } else if (xmlStrEqual(facetname, BAD_CAST "maxLength")) {
1902 facet->type = XML_SCHEMA_FACET_MAXLENGTH;
1903 } else if (xmlStrEqual(facetname, BAD_CAST "minLength")) {
1904 facet->type = XML_SCHEMA_FACET_MINLENGTH;
1905 } else {
1906 xmlSchemaFreeFacet(facet);
1907 return(-1);
1908 }
1909 facet->value = xmlStrdup(val);
1910 ret = xmlSchemaCheckFacet(facet, typ, NULL, type);
1911 if (ret != 0) {
1912 xmlSchemaFreeFacet(facet);
1913 return(-1);
1914 }
1915 ret = xmlSchemaValidateFacet(typ, facet, strval, value);
1916 xmlSchemaFreeFacet(facet);
1917 if (ret != 0)
1918 return(-1);
1919 return(0);
1920}
1921
1922/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001923 * xmlRelaxNGSchemaTypeCompare:
1924 * @data: data needed for the library
1925 * @type: the type name
1926 * @value1: the first value
1927 * @value2: the second value
1928 *
1929 * Compare two values accordingly a type from the W3C XMLSchema
1930 * Datatype library.
1931 *
1932 * Returns 1 if yes, 0 if no and -1 in case of error.
1933 */
1934static int
1935xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1936 const xmlChar *type ATTRIBUTE_UNUSED,
1937 const xmlChar *value1 ATTRIBUTE_UNUSED,
1938 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1939 TODO
1940 return(1);
1941}
1942
1943/**
1944 * xmlRelaxNGDefaultTypeHave:
1945 * @data: data needed for the library
1946 * @type: the type name
1947 *
1948 * Check if the given type is provided by
1949 * the default datatype library.
1950 *
1951 * Returns 1 if yes, 0 if no and -1 in case of error.
1952 */
1953static int
1954xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1955 if (type == NULL)
1956 return(-1);
1957 if (xmlStrEqual(type, BAD_CAST "string"))
1958 return(1);
1959 if (xmlStrEqual(type, BAD_CAST "token"))
1960 return(1);
1961 return(0);
1962}
1963
1964/**
1965 * xmlRelaxNGDefaultTypeCheck:
1966 * @data: data needed for the library
1967 * @type: the type name
1968 * @value: the value to check
1969 *
1970 * Check if the given type and value are validated by
1971 * the default datatype library.
1972 *
1973 * Returns 1 if yes, 0 if no and -1 in case of error.
1974 */
1975static int
1976xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1977 const xmlChar *type ATTRIBUTE_UNUSED,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001978 const xmlChar *value ATTRIBUTE_UNUSED,
1979 void **result ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001980 if (value == NULL)
1981 return(-1);
1982 if (xmlStrEqual(type, BAD_CAST "string"))
1983 return(1);
1984 if (xmlStrEqual(type, BAD_CAST "token")) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001985 return(1);
1986 }
1987
1988 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001989}
1990
1991/**
1992 * xmlRelaxNGDefaultTypeCompare:
1993 * @data: data needed for the library
1994 * @type: the type name
1995 * @value1: the first value
1996 * @value2: the second value
1997 *
1998 * Compare two values accordingly a type from the default
1999 * datatype library.
2000 *
2001 * Returns 1 if yes, 0 if no and -1 in case of error.
2002 */
2003static int
2004xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
2005 const xmlChar *type ATTRIBUTE_UNUSED,
2006 const xmlChar *value1 ATTRIBUTE_UNUSED,
2007 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00002008 int ret = -1;
2009
2010 if (xmlStrEqual(type, BAD_CAST "string")) {
2011 ret = xmlStrEqual(value1, value2);
2012 } else if (xmlStrEqual(type, BAD_CAST "token")) {
2013 if (!xmlStrEqual(value1, value2)) {
2014 xmlChar *nval, *nvalue;
2015
2016 /*
2017 * TODO: trivial optimizations are possible by
2018 * computing at compile-time
2019 */
2020 nval = xmlRelaxNGNormalize(NULL, value1);
2021 nvalue = xmlRelaxNGNormalize(NULL, value2);
2022
Daniel Veillardd4310742003-02-18 21:12:46 +00002023 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00002024 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00002025 else if (xmlStrEqual(nval, nvalue))
2026 ret = 1;
2027 else
2028 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00002029 if (nval != NULL)
2030 xmlFree(nval);
2031 if (nvalue != NULL)
2032 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00002033 } else
2034 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00002035 }
2036 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002037}
2038
2039static int xmlRelaxNGTypeInitialized = 0;
2040static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
2041
2042/**
2043 * xmlRelaxNGFreeTypeLibrary:
2044 * @lib: the type library structure
2045 * @namespace: the URI bound to the library
2046 *
2047 * Free the structure associated to the type library
2048 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00002049static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002050xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
2051 const xmlChar *namespace ATTRIBUTE_UNUSED) {
2052 if (lib == NULL)
2053 return;
2054 if (lib->namespace != NULL)
2055 xmlFree((xmlChar *)lib->namespace);
2056 xmlFree(lib);
2057}
2058
2059/**
2060 * xmlRelaxNGRegisterTypeLibrary:
2061 * @namespace: the URI bound to the library
2062 * @data: data associated to the library
2063 * @have: the provide function
2064 * @check: the checking function
2065 * @comp: the comparison function
2066 *
2067 * Register a new type library
2068 *
2069 * Returns 0 in case of success and -1 in case of error.
2070 */
2071static int
2072xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
2073 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002074 xmlRelaxNGTypeCompare comp, xmlRelaxNGFacetCheck facet,
2075 xmlRelaxNGTypeFree freef) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002076 xmlRelaxNGTypeLibraryPtr lib;
2077 int ret;
2078
2079 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
2080 (check == NULL) || (comp == NULL))
2081 return(-1);
2082 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
2083 xmlGenericError(xmlGenericErrorContext,
2084 "Relax-NG types library '%s' already registered\n",
2085 namespace);
2086 return(-1);
2087 }
2088 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
2089 if (lib == NULL) {
2090 xmlGenericError(xmlGenericErrorContext,
2091 "Relax-NG types library '%s' malloc() failed\n",
2092 namespace);
2093 return (-1);
2094 }
2095 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
2096 lib->namespace = xmlStrdup(namespace);
2097 lib->data = data;
2098 lib->have = have;
2099 lib->comp = comp;
2100 lib->check = check;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002101 lib->facet = facet;
2102 lib->freef = freef;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002103 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
2104 if (ret < 0) {
2105 xmlGenericError(xmlGenericErrorContext,
2106 "Relax-NG types library failed to register '%s'\n",
2107 namespace);
2108 xmlRelaxNGFreeTypeLibrary(lib, namespace);
2109 return(-1);
2110 }
2111 return(0);
2112}
2113
2114/**
2115 * xmlRelaxNGInitTypes:
2116 *
2117 * Initilize the default type libraries.
2118 *
2119 * Returns 0 in case of success and -1 in case of error.
2120 */
2121static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00002122xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002123 if (xmlRelaxNGTypeInitialized != 0)
2124 return(0);
2125 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
2126 if (xmlRelaxNGRegisteredTypes == NULL) {
2127 xmlGenericError(xmlGenericErrorContext,
2128 "Failed to allocate sh table for Relax-NG types\n");
2129 return(-1);
2130 }
2131 xmlRelaxNGRegisterTypeLibrary(
2132 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
2133 NULL,
2134 xmlRelaxNGSchemaTypeHave,
2135 xmlRelaxNGSchemaTypeCheck,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002136 xmlRelaxNGSchemaTypeCompare,
2137 xmlRelaxNGSchemaFacetCheck,
2138 (xmlRelaxNGTypeFree) xmlSchemaFreeValue);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002139 xmlRelaxNGRegisterTypeLibrary(
2140 xmlRelaxNGNs,
2141 NULL,
2142 xmlRelaxNGDefaultTypeHave,
2143 xmlRelaxNGDefaultTypeCheck,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002144 xmlRelaxNGDefaultTypeCompare,
2145 NULL,
2146 NULL);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002147 xmlRelaxNGTypeInitialized = 1;
2148 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002149}
2150
2151/**
2152 * xmlRelaxNGCleanupTypes:
2153 *
2154 * Cleanup the default Schemas type library associated to RelaxNG
2155 */
2156void
2157xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002158 if (xmlRelaxNGTypeInitialized == 0)
2159 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002160 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002161 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
2162 xmlRelaxNGFreeTypeLibrary);
2163 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002164}
2165
2166/************************************************************************
2167 * *
2168 * Parsing functions *
2169 * *
2170 ************************************************************************/
2171
2172static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
2173 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
2174static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
2175 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
2176static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00002177 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002178static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
2179 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002180static xmlRelaxNGPtr xmlRelaxNGParseDocument(
2181 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002182static int xmlRelaxNGParseGrammarContent(
2183 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002184static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
2185 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2186 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00002187static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
2188 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard17bed982003-02-24 20:11:43 +00002189static int xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
2190 xmlRelaxNGDefinePtr define, xmlNodePtr elem);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002191
2192
2193#define IS_BLANK_NODE(n) \
2194 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
2195
2196/**
2197 * xmlRelaxNGIsBlank:
2198 * @str: a string
2199 *
2200 * Check if a string is ignorable c.f. 4.2. Whitespace
2201 *
2202 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
2203 */
2204static int
2205xmlRelaxNGIsBlank(xmlChar *str) {
2206 if (str == NULL)
2207 return(1);
2208 while (*str != 0) {
2209 if (!(IS_BLANK(*str))) return(0);
2210 str++;
2211 }
2212 return(1);
2213}
2214
Daniel Veillard6eadf632003-01-23 18:29:16 +00002215/**
2216 * xmlRelaxNGGetDataTypeLibrary:
2217 * @ctxt: a Relax-NG parser context
2218 * @node: the current data or value element
2219 *
2220 * Applies algorithm from 4.3. datatypeLibrary attribute
2221 *
2222 * Returns the datatypeLibary value or NULL if not found
2223 */
2224static xmlChar *
2225xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2226 xmlNodePtr node) {
2227 xmlChar *ret, *escape;
2228
Daniel Veillard6eadf632003-01-23 18:29:16 +00002229 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
2230 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
2231 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002232 if (ret[0] == 0) {
2233 xmlFree(ret);
2234 return(NULL);
2235 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002236 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00002237 if (escape == NULL) {
2238 return(ret);
2239 }
2240 xmlFree(ret);
2241 return(escape);
2242 }
2243 }
2244 node = node->parent;
2245 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00002246 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
2247 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002248 if (ret[0] == 0) {
2249 xmlFree(ret);
2250 return(NULL);
2251 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00002252 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
2253 if (escape == NULL) {
2254 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002255 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00002256 xmlFree(ret);
2257 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002258 }
2259 node = node->parent;
2260 }
2261 return(NULL);
2262}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002263
2264/**
Daniel Veillardedc91922003-01-26 00:52:04 +00002265 * xmlRelaxNGParseValue:
2266 * @ctxt: a Relax-NG parser context
2267 * @node: the data node.
2268 *
2269 * parse the content of a RelaxNG value node.
2270 *
2271 * Returns the definition pointer or NULL in case of error
2272 */
2273static xmlRelaxNGDefinePtr
2274xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2275 xmlRelaxNGDefinePtr def = NULL;
2276 xmlRelaxNGTypeLibraryPtr lib;
2277 xmlChar *type;
2278 xmlChar *library;
2279 int tmp;
2280
2281 def = xmlRelaxNGNewDefine(ctxt, node);
2282 if (def == NULL)
2283 return(NULL);
2284 def->type = XML_RELAXNG_VALUE;
2285
2286 type = xmlGetProp(node, BAD_CAST "type");
2287 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002288 xmlRelaxNGNormExtSpace(type);
2289 if (xmlValidateNCName(type, 0)) {
2290 if (ctxt->error != NULL)
2291 ctxt->error(ctxt->userData,
2292 "value type '%s' is not an NCName\n",
2293 type);
2294 ctxt->nbErrors++;
2295 }
Daniel Veillardedc91922003-01-26 00:52:04 +00002296 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
2297 if (library == NULL)
2298 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
2299
2300 def->name = type;
2301 def->ns = library;
2302
2303 lib = (xmlRelaxNGTypeLibraryPtr)
2304 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
2305 if (lib == NULL) {
2306 if (ctxt->error != NULL)
2307 ctxt->error(ctxt->userData,
2308 "Use of unregistered type library '%s'\n",
2309 library);
2310 ctxt->nbErrors++;
2311 def->data = NULL;
2312 } else {
2313 def->data = lib;
2314 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002315 if (ctxt->error != NULL)
2316 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00002317 "Internal error with type library '%s': no 'have'\n",
2318 library);
2319 ctxt->nbErrors++;
2320 } else {
2321 tmp = lib->have(lib->data, def->name);
2322 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002323 if (ctxt->error != NULL)
2324 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00002325 "Error type '%s' is not exported by type library '%s'\n",
2326 def->name, library);
2327 ctxt->nbErrors++;
2328 }
2329 }
2330 }
2331 }
2332 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00002333 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00002334 } else if ((node->children->type != XML_TEXT_NODE) ||
2335 (node->children->next != NULL)) {
2336 if (ctxt->error != NULL)
2337 ctxt->error(ctxt->userData,
2338 "Expecting a single text value for <value>content\n");
2339 ctxt->nbErrors++;
2340 } else {
2341 def->value = xmlNodeGetContent(node);
2342 if (def->value == NULL) {
2343 if (ctxt->error != NULL)
2344 ctxt->error(ctxt->userData,
2345 "Element <value> has no content\n");
2346 ctxt->nbErrors++;
2347 }
2348 }
2349 /* TODO check ahead of time that the value is okay per the type */
2350 return(def);
2351}
2352
2353/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002354 * xmlRelaxNGParseData:
2355 * @ctxt: a Relax-NG parser context
2356 * @node: the data node.
2357 *
2358 * parse the content of a RelaxNG data node.
2359 *
2360 * Returns the definition pointer or NULL in case of error
2361 */
2362static xmlRelaxNGDefinePtr
2363xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002364 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002365 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002366 xmlRelaxNGTypeLibraryPtr lib;
2367 xmlChar *type;
2368 xmlChar *library;
2369 xmlNodePtr content;
2370 int tmp;
2371
2372 type = xmlGetProp(node, BAD_CAST "type");
2373 if (type == NULL) {
2374 if (ctxt->error != NULL)
2375 ctxt->error(ctxt->userData,
2376 "data has no type\n");
2377 ctxt->nbErrors++;
2378 return(NULL);
2379 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002380 xmlRelaxNGNormExtSpace(type);
2381 if (xmlValidateNCName(type, 0)) {
2382 if (ctxt->error != NULL)
2383 ctxt->error(ctxt->userData,
2384 "data type '%s' is not an NCName\n",
2385 type);
2386 ctxt->nbErrors++;
2387 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002388 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
2389 if (library == NULL)
2390 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
2391
2392 def = xmlRelaxNGNewDefine(ctxt, node);
2393 if (def == NULL) {
2394 xmlFree(type);
2395 return(NULL);
2396 }
2397 def->type = XML_RELAXNG_DATATYPE;
2398 def->name = type;
2399 def->ns = library;
2400
2401 lib = (xmlRelaxNGTypeLibraryPtr)
2402 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
2403 if (lib == NULL) {
2404 if (ctxt->error != NULL)
2405 ctxt->error(ctxt->userData,
2406 "Use of unregistered type library '%s'\n",
2407 library);
2408 ctxt->nbErrors++;
2409 def->data = NULL;
2410 } else {
2411 def->data = lib;
2412 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002413 if (ctxt->error != NULL)
2414 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002415 "Internal error with type library '%s': no 'have'\n",
2416 library);
2417 ctxt->nbErrors++;
2418 } else {
2419 tmp = lib->have(lib->data, def->name);
2420 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002421 if (ctxt->error != NULL)
2422 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002423 "Error type '%s' is not exported by type library '%s'\n",
2424 def->name, library);
2425 ctxt->nbErrors++;
2426 }
2427 }
2428 }
2429 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00002430
2431 /*
2432 * Handle optional params
2433 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002434 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002435 if (!xmlStrEqual(content->name, BAD_CAST "param"))
2436 break;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002437 if (xmlStrEqual(library,
2438 BAD_CAST"http://relaxng.org/ns/structure/1.0")) {
2439 if (ctxt->error != NULL)
2440 ctxt->error(ctxt->userData,
2441 "Type library '%s' does not allow type parameters\n",
2442 library);
2443 ctxt->nbErrors++;
2444 content = content->next;
2445 while ((content != NULL) &&
2446 (xmlStrEqual(content->name, BAD_CAST "param")))
2447 content = content->next;
2448 } else {
2449 param = xmlRelaxNGNewDefine(ctxt, node);
2450 if (param != NULL) {
2451 param->type = XML_RELAXNG_PARAM;
2452 param->name = xmlGetProp(content, BAD_CAST "name");
2453 if (param->name == NULL) {
2454 if (ctxt->error != NULL)
2455 ctxt->error(ctxt->userData,
2456 "param has no name\n");
2457 ctxt->nbErrors++;
2458 }
2459 param->value = xmlNodeGetContent(content);
2460 if (lastparam == NULL) {
2461 def->attrs = lastparam = param;
2462 } else {
2463 lastparam->next = param;
2464 lastparam = param;
2465 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002466 if (lib != NULL) {
2467 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00002468 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002469 content = content->next;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002470 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002471 }
Daniel Veillard416589a2003-02-17 17:25:42 +00002472 /*
2473 * Handle optional except
2474 */
2475 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
2476 xmlNodePtr child;
2477 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
2478
2479 except = xmlRelaxNGNewDefine(ctxt, node);
2480 if (except == NULL) {
2481 return(def);
2482 }
2483 except->type = XML_RELAXNG_EXCEPT;
2484 child = content->children;
2485 if (last == NULL) {
2486 def->content = except;
2487 } else {
2488 last->next = except;
2489 }
2490 if (child == NULL) {
2491 if (ctxt->error != NULL)
2492 ctxt->error(ctxt->userData,
2493 "except has no content\n");
2494 ctxt->nbErrors++;
2495 }
2496 while (child != NULL) {
2497 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
2498 if (tmp2 != NULL) {
2499 if (last2 == NULL) {
2500 except->content = last2 = tmp2;
2501 } else {
2502 last2->next = tmp2;
2503 last2 = tmp2;
2504 }
2505 }
2506 child = child->next;
2507 }
2508 content = content->next;
2509 }
2510 /*
2511 * Check there is no unhandled data
2512 */
2513 if (content != NULL) {
2514 if (ctxt->error != NULL)
2515 ctxt->error(ctxt->userData,
2516 "Element data has unexpected content %s\n", content->name);
2517 ctxt->nbErrors++;
2518 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002519
2520 return(def);
2521}
2522
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002523static const xmlChar *invalidName = BAD_CAST "\1";
2524
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002525/**
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002526 * xmlRelaxNGCompareNameClasses:
2527 * @defs1: the first element/attribute defs
2528 * @defs2: the second element/attribute defs
2529 * @name: the restriction on the name
2530 * @ns: the restriction on the namespace
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002531 *
2532 * Compare the 2 lists of element definitions. The comparison is
2533 * that if both lists do not accept the same QNames, it returns 1
2534 * If the 2 lists can accept the same QName the comparison returns 0
2535 *
2536 * Returns 1 disttinct, 0 if equal
2537 */
2538static int
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002539xmlRelaxNGCompareNameClasses(xmlRelaxNGDefinePtr def1,
2540 xmlRelaxNGDefinePtr def2) {
2541 int ret = 1;
2542 xmlNode node;
2543 xmlNs ns;
2544 xmlRelaxNGValidCtxt ctxt;
2545 ctxt.flags = FLAGS_IGNORABLE;
2546
Daniel Veillard42f12e92003-03-07 18:32:59 +00002547 memset(&ctxt, 0, sizeof(xmlRelaxNGValidCtxt));
2548
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002549 if ((def1->type == XML_RELAXNG_ELEMENT) ||
2550 (def1->type == XML_RELAXNG_ATTRIBUTE)) {
2551 if (def2->type == XML_RELAXNG_TEXT)
2552 return(1);
2553 if (def1->name != NULL) {
2554 node.name = def1->name;
2555 } else {
2556 node.name = invalidName;
2557 }
2558 node.ns = &ns;
2559 if (def1->ns != NULL) {
2560 if (def1->ns[0] == 0) {
2561 node.ns = NULL;
2562 } else {
2563 ns.href = def1->ns;
2564 }
2565 } else {
2566 ns.href = invalidName;
2567 }
2568 if (xmlRelaxNGElementMatch(&ctxt, def2, &node)) {
2569 if (def1->nameClass != NULL) {
2570 ret = xmlRelaxNGCompareNameClasses(def1->nameClass, def2);
2571 } else {
2572 ret = 0;
2573 }
2574 } else {
2575 ret = 1;
2576 }
2577 } else if (def1->type == XML_RELAXNG_TEXT) {
2578 if (def2->type == XML_RELAXNG_TEXT)
2579 return(0);
2580 return(1);
2581 } else if (def1->type == XML_RELAXNG_EXCEPT) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002582 TODO
2583 ret = 0;
2584 } else {
2585 TODO
2586 ret = 0;
2587 }
2588 if (ret == 0)
2589 return(ret);
2590 if ((def2->type == XML_RELAXNG_ELEMENT) ||
2591 (def2->type == XML_RELAXNG_ATTRIBUTE)) {
2592 if (def2->name != NULL) {
2593 node.name = def2->name;
2594 } else {
2595 node.name = invalidName;
2596 }
2597 node.ns = &ns;
2598 if (def2->ns != NULL) {
2599 if (def2->ns[0] == 0) {
2600 node.ns = NULL;
2601 } else {
2602 ns.href = def2->ns;
2603 }
2604 } else {
2605 ns.href = invalidName;
2606 }
2607 if (xmlRelaxNGElementMatch(&ctxt, def1, &node)) {
2608 if (def2->nameClass != NULL) {
2609 ret = xmlRelaxNGCompareNameClasses(def2->nameClass, def1);
2610 } else {
2611 ret = 0;
2612 }
2613 } else {
2614 ret = 1;
2615 }
2616 } else {
2617 TODO
2618 ret = 0;
2619 }
2620
2621 return(ret);
2622}
2623
2624/**
2625 * xmlRelaxNGCompareElemDefLists:
2626 * @ctxt: a Relax-NG parser context
2627 * @defs1: the first list of element/attribute defs
2628 * @defs2: the second list of element/attribute defs
2629 *
2630 * Compare the 2 lists of element or attribute definitions. The comparison
2631 * is that if both lists do not accept the same QNames, it returns 1
2632 * If the 2 lists can accept the same QName the comparison returns 0
2633 *
2634 * Returns 1 disttinct, 0 if equal
2635 */
2636static int
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002637xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2638 xmlRelaxNGDefinePtr *def1,
2639 xmlRelaxNGDefinePtr *def2) {
2640 xmlRelaxNGDefinePtr *basedef2 = def2;
2641
Daniel Veillard154877e2003-01-30 12:17:05 +00002642 if ((def1 == NULL) || (def2 == NULL))
2643 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002644 if ((*def1 == NULL) || (*def2 == NULL))
2645 return(1);
2646 while (*def1 != NULL) {
2647 while ((*def2) != NULL) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002648 if (xmlRelaxNGCompareNameClasses(*def1, *def2) == 0)
2649 return(0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002650 def2++;
2651 }
2652 def2 = basedef2;
2653 def1++;
2654 }
2655 return(1);
2656}
2657
2658/**
2659 * xmlRelaxNGGetElements:
2660 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002661 * @def: the definition definition
2662 * @eora: gather elements (0) or attributes (1)
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002663 *
2664 * Compute the list of top elements a definition can generate
2665 *
2666 * Returns a list of elements or NULL if none was found.
2667 */
2668static xmlRelaxNGDefinePtr *
2669xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002670 xmlRelaxNGDefinePtr def,
2671 int eora) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002672 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2673 int len = 0;
2674 int max = 0;
2675
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002676 /*
2677 * Don't run that check in case of error. Infinite recursion
2678 * becomes possible.
2679 */
2680 if (ctxt->nbErrors != 0)
2681 return(NULL);
2682
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002683 parent = NULL;
2684 cur = def;
2685 while (cur != NULL) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002686 if (((eora == 0) && ((cur->type == XML_RELAXNG_ELEMENT) ||
2687 (cur->type == XML_RELAXNG_TEXT))) ||
2688 ((eora == 1) && (cur->type == XML_RELAXNG_ATTRIBUTE))) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002689 if (ret == NULL) {
2690 max = 10;
2691 ret = (xmlRelaxNGDefinePtr *)
2692 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2693 if (ret == NULL) {
2694 if (ctxt->error != NULL)
2695 ctxt->error(ctxt->userData,
2696 "Out of memory in element search\n");
2697 ctxt->nbErrors++;
2698 return(NULL);
2699 }
2700 } else if (max <= len) {
2701 max *= 2;
2702 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2703 if (ret == NULL) {
2704 if (ctxt->error != NULL)
2705 ctxt->error(ctxt->userData,
2706 "Out of memory in element search\n");
2707 ctxt->nbErrors++;
2708 return(NULL);
2709 }
2710 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002711 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002712 ret[len] = NULL;
2713 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2714 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2715 (cur->type == XML_RELAXNG_GROUP) ||
2716 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002717 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2718 (cur->type == XML_RELAXNG_OPTIONAL) ||
2719 (cur->type == XML_RELAXNG_REF) ||
2720 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002721 /*
2722 * Don't go within elements or attributes or string values.
2723 * Just gather the element top list
2724 */
2725 if (cur->content != NULL) {
2726 parent = cur;
2727 cur = cur->content;
2728 tmp = cur;
2729 while (tmp != NULL) {
2730 tmp->parent = parent;
2731 tmp = tmp->next;
2732 }
2733 continue;
2734 }
2735 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002736 if (cur == def)
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002737 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002738 if (cur->next != NULL) {
2739 cur = cur->next;
2740 continue;
2741 }
2742 do {
2743 cur = cur->parent;
2744 if (cur == NULL) break;
2745 if (cur == def) return(ret);
2746 if (cur->next != NULL) {
2747 cur = cur->next;
2748 break;
2749 }
2750 } while (cur != NULL);
2751 }
2752 return(ret);
2753}
2754
2755/**
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002756 * xmlRelaxNGCheckGroupAttrs:
2757 * @ctxt: a Relax-NG parser context
2758 * @def: the group definition
2759 *
2760 * Detects violations of rule 7.3
2761 */
2762static void
2763xmlRelaxNGCheckGroupAttrs(xmlRelaxNGParserCtxtPtr ctxt,
2764 xmlRelaxNGDefinePtr def) {
2765 xmlRelaxNGDefinePtr **list;
2766 xmlRelaxNGDefinePtr cur;
2767 int nbchild = 0, i, j, ret;
2768
2769 if ((def == NULL) ||
2770 ((def->type != XML_RELAXNG_GROUP) &&
2771 (def->type != XML_RELAXNG_ELEMENT)))
2772 return;
2773
2774 /*
2775 * Don't run that check in case of error. Infinite recursion
2776 * becomes possible.
2777 */
2778 if (ctxt->nbErrors != 0)
2779 return;
2780
2781 cur = def->attrs;
2782 while (cur != NULL) {
2783 nbchild++;
2784 cur = cur->next;
2785 }
2786 cur = def->content;
2787 while (cur != NULL) {
2788 nbchild++;
2789 cur = cur->next;
2790 }
2791
2792 list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
2793 sizeof(xmlRelaxNGDefinePtr *));
2794 if (list == NULL) {
2795 if (ctxt->error != NULL)
2796 ctxt->error(ctxt->userData,
2797 "Out of memory in group computation\n");
2798 ctxt->nbErrors++;
2799 return;
2800 }
2801 i = 0;
2802 cur = def->attrs;
2803 while (cur != NULL) {
2804 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2805 i++;
2806 cur = cur->next;
2807 }
2808 cur = def->content;
2809 while (cur != NULL) {
2810 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2811 i++;
2812 cur = cur->next;
2813 }
2814
2815 for (i = 0;i < nbchild;i++) {
2816 if (list[i] == NULL)
2817 continue;
2818 for (j = 0;j < i;j++) {
2819 if (list[j] == NULL)
2820 continue;
2821 ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
2822 if (ret == 0) {
2823 if (ctxt->error != NULL)
2824 ctxt->error(ctxt->userData,
2825 "Attributes conflicts in group\n");
2826 ctxt->nbErrors++;
2827 }
2828 }
2829 }
2830 for (i = 0;i < nbchild;i++) {
2831 if (list[i] != NULL)
2832 xmlFree(list[i]);
2833 }
2834 xmlFree(list);
2835}
2836
2837/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002838 * xmlRelaxNGComputeInterleaves:
2839 * @def: the interleave definition
2840 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002841 * @name: the definition name
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002842 *
2843 * A lot of work for preprocessing interleave definitions
2844 * is potentially needed to get a decent execution speed at runtime
2845 * - trying to get a total order on the element nodes generated
2846 * by the interleaves, order the list of interleave definitions
2847 * following that order.
2848 * - if <text/> is used to handle mixed content, it is better to
2849 * flag this in the define and simplify the runtime checking
2850 * algorithm
2851 */
2852static void
2853xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2854 xmlRelaxNGParserCtxtPtr ctxt,
2855 xmlChar *name ATTRIBUTE_UNUSED) {
2856 xmlRelaxNGDefinePtr cur;
2857
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002858 xmlRelaxNGPartitionPtr partitions = NULL;
2859 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2860 xmlRelaxNGInterleaveGroupPtr group;
2861 int i,j,ret;
2862 int nbgroups = 0;
2863 int nbchild = 0;
2864
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002865 /*
2866 * Don't run that check in case of error. Infinite recursion
2867 * becomes possible.
2868 */
2869 if (ctxt->nbErrors != 0)
2870 return;
2871
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002872#ifdef DEBUG_INTERLEAVE
2873 xmlGenericError(xmlGenericErrorContext,
2874 "xmlRelaxNGComputeInterleaves(%s)\n",
2875 name);
2876#endif
2877 cur = def->content;
2878 while (cur != NULL) {
2879 nbchild++;
2880 cur = cur->next;
2881 }
2882
2883#ifdef DEBUG_INTERLEAVE
2884 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2885#endif
2886 groups = (xmlRelaxNGInterleaveGroupPtr *)
2887 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2888 if (groups == NULL)
2889 goto error;
2890 cur = def->content;
2891 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002892 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2893 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2894 if (groups[nbgroups] == NULL)
2895 goto error;
2896 groups[nbgroups]->rule = cur;
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002897 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur, 0);
2898 groups[nbgroups]->attrs = xmlRelaxNGGetElements(ctxt, cur, 1);
Daniel Veillard154877e2003-01-30 12:17:05 +00002899 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002900 cur = cur->next;
2901 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002902#ifdef DEBUG_INTERLEAVE
2903 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2904#endif
2905
2906 /*
2907 * Let's check that all rules makes a partitions according to 7.4
2908 */
2909 partitions = (xmlRelaxNGPartitionPtr)
2910 xmlMalloc(sizeof(xmlRelaxNGPartition));
2911 if (partitions == NULL)
2912 goto error;
2913 partitions->nbgroups = nbgroups;
2914 for (i = 0;i < nbgroups;i++) {
2915 group = groups[i];
2916 for (j = i+1;j < nbgroups;j++) {
2917 if (groups[j] == NULL)
2918 continue;
2919 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2920 groups[j]->defs);
2921 if (ret == 0) {
2922 if (ctxt->error != NULL)
2923 ctxt->error(ctxt->userData,
2924 "Element or text conflicts in interleave\n");
2925 ctxt->nbErrors++;
2926 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002927 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->attrs,
2928 groups[j]->attrs);
2929 if (ret == 0) {
2930 if (ctxt->error != NULL)
2931 ctxt->error(ctxt->userData,
2932 "Attributes conflicts in interleave\n");
2933 ctxt->nbErrors++;
2934 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002935 }
2936 }
2937 partitions->groups = groups;
2938
2939 /*
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002940 * and save the partition list back in the def
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002941 */
2942 def->data = partitions;
2943 return;
2944
2945error:
2946 if (ctxt->error != NULL)
2947 ctxt->error(ctxt->userData,
2948 "Out of memory in interleave computation\n");
2949 ctxt->nbErrors++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002950 if (groups != NULL) {
2951 for (i = 0;i < nbgroups;i++)
2952 if (groups[i] != NULL) {
2953 if (groups[i]->defs != NULL)
2954 xmlFree(groups[i]->defs);
2955 xmlFree(groups[i]);
2956 }
2957 xmlFree(groups);
2958 }
2959 xmlRelaxNGFreePartition(partitions);
2960}
2961
2962/**
2963 * xmlRelaxNGParseInterleave:
2964 * @ctxt: a Relax-NG parser context
2965 * @node: the data node.
2966 *
2967 * parse the content of a RelaxNG interleave node.
2968 *
2969 * Returns the definition pointer or NULL in case of error
2970 */
2971static xmlRelaxNGDefinePtr
2972xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2973 xmlRelaxNGDefinePtr def = NULL;
2974 xmlRelaxNGDefinePtr last = NULL, cur;
2975 xmlNodePtr child;
2976
2977 def = xmlRelaxNGNewDefine(ctxt, node);
2978 if (def == NULL) {
2979 return(NULL);
2980 }
2981 def->type = XML_RELAXNG_INTERLEAVE;
2982
2983 if (ctxt->interleaves == NULL)
2984 ctxt->interleaves = xmlHashCreate(10);
2985 if (ctxt->interleaves == NULL) {
2986 if (ctxt->error != NULL)
2987 ctxt->error(ctxt->userData,
2988 "Failed to create interleaves hash table\n");
2989 ctxt->nbErrors++;
2990 } else {
2991 char name[32];
2992
2993 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2994 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2995 if (ctxt->error != NULL)
2996 ctxt->error(ctxt->userData,
2997 "Failed to add %s to hash table\n", name);
2998 ctxt->nbErrors++;
2999 }
3000 }
3001 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00003002 if (child == NULL) {
3003 if (ctxt->error != NULL)
3004 ctxt->error(ctxt->userData, "Element interleave is empty\n");
3005 ctxt->nbErrors++;
3006 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003007 while (child != NULL) {
3008 if (IS_RELAXNG(child, "element")) {
3009 cur = xmlRelaxNGParseElement(ctxt, child);
3010 } else {
3011 cur = xmlRelaxNGParsePattern(ctxt, child);
3012 }
3013 if (cur != NULL) {
3014 cur->parent = def;
3015 if (last == NULL) {
3016 def->content = last = cur;
3017 } else {
3018 last->next = cur;
3019 last = cur;
3020 }
3021 }
3022 child = child->next;
3023 }
3024
3025 return(def);
3026}
Daniel Veillard6eadf632003-01-23 18:29:16 +00003027
3028/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003029 * xmlRelaxNGParseInclude:
3030 * @ctxt: a Relax-NG parser context
3031 * @node: the include node
3032 *
3033 * Integrate the content of an include node in the current grammar
3034 *
3035 * Returns 0 in case of success or -1 in case of error
3036 */
3037static int
3038xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3039 xmlRelaxNGIncludePtr incl;
3040 xmlNodePtr root;
3041 int ret = 0, tmp;
3042
3043 incl = node->_private;
3044 if (incl == NULL) {
3045 if (ctxt->error != NULL)
3046 ctxt->error(ctxt->userData,
3047 "Include node has no data\n");
3048 ctxt->nbErrors++;
3049 return(-1);
3050 }
3051 root = xmlDocGetRootElement(incl->doc);
3052 if (root == NULL) {
3053 if (ctxt->error != NULL)
3054 ctxt->error(ctxt->userData,
3055 "Include document is empty\n");
3056 ctxt->nbErrors++;
3057 return(-1);
3058 }
3059 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
3060 if (ctxt->error != NULL)
3061 ctxt->error(ctxt->userData,
3062 "Include document root is not a grammar\n");
3063 ctxt->nbErrors++;
3064 return(-1);
3065 }
3066
3067 /*
3068 * Merge the definition from both the include and the internal list
3069 */
3070 if (root->children != NULL) {
3071 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
3072 if (tmp != 0)
3073 ret = -1;
3074 }
3075 if (node->children != NULL) {
3076 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
3077 if (tmp != 0)
3078 ret = -1;
3079 }
3080 return(ret);
3081}
3082
3083/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00003084 * xmlRelaxNGParseDefine:
3085 * @ctxt: a Relax-NG parser context
3086 * @node: the define node
3087 *
3088 * parse the content of a RelaxNG define element node.
3089 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003090 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00003091 */
3092static int
3093xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3094 xmlChar *name;
3095 int ret = 0, tmp;
3096 xmlRelaxNGDefinePtr def;
3097 const xmlChar *olddefine;
3098
3099 name = xmlGetProp(node, BAD_CAST "name");
3100 if (name == NULL) {
3101 if (ctxt->error != NULL)
3102 ctxt->error(ctxt->userData,
3103 "define has no name\n");
3104 ctxt->nbErrors++;
3105 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003106 xmlRelaxNGNormExtSpace(name);
3107 if (xmlValidateNCName(name, 0)) {
3108 if (ctxt->error != NULL)
3109 ctxt->error(ctxt->userData,
3110 "define name '%s' is not an NCName\n",
3111 name);
3112 ctxt->nbErrors++;
3113 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003114 def = xmlRelaxNGNewDefine(ctxt, node);
3115 if (def == NULL) {
3116 xmlFree(name);
3117 return(-1);
3118 }
3119 def->type = XML_RELAXNG_DEF;
3120 def->name = name;
3121 if (node->children == NULL) {
3122 if (ctxt->error != NULL)
3123 ctxt->error(ctxt->userData,
3124 "define has no children\n");
3125 ctxt->nbErrors++;
3126 } else {
3127 olddefine = ctxt->define;
3128 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00003129 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00003130 ctxt->define = olddefine;
3131 }
3132 if (ctxt->grammar->defs == NULL)
3133 ctxt->grammar->defs = xmlHashCreate(10);
3134 if (ctxt->grammar->defs == NULL) {
3135 if (ctxt->error != NULL)
3136 ctxt->error(ctxt->userData,
3137 "Could not create definition hash\n");
3138 ctxt->nbErrors++;
3139 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003140 } else {
3141 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
3142 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00003143 xmlRelaxNGDefinePtr prev;
3144
3145 prev = xmlHashLookup(ctxt->grammar->defs, name);
3146 if (prev == NULL) {
3147 if (ctxt->error != NULL)
3148 ctxt->error(ctxt->userData,
3149 "Internal error on define aggregation of %s\n",
3150 name);
3151 ctxt->nbErrors++;
3152 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00003153 } else {
3154 while (prev->nextHash != NULL)
3155 prev = prev->nextHash;
3156 prev->nextHash = def;
3157 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003158 }
3159 }
3160 }
3161 return(ret);
3162}
3163
3164/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00003165 * xmlRelaxNGProcessExternalRef:
3166 * @ctxt: the parser context
3167 * @node: the externlRef node
3168 *
3169 * Process and compile an externlRef node
3170 *
3171 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
3172 */
3173static xmlRelaxNGDefinePtr
3174xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3175 xmlRelaxNGDocumentPtr docu;
3176 xmlNodePtr root, tmp;
3177 xmlChar *ns;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003178 int newNs = 0, oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003179 xmlRelaxNGDefinePtr def;
3180
3181 docu = node->_private;
3182 if (docu != NULL) {
3183 def = xmlRelaxNGNewDefine(ctxt, node);
3184 if (def == NULL)
3185 return(NULL);
3186 def->type = XML_RELAXNG_EXTERNALREF;
3187
3188 if (docu->content == NULL) {
3189 /*
3190 * Then do the parsing for good
3191 */
3192 root = xmlDocGetRootElement(docu->doc);
3193 if (root == NULL) {
3194 if (ctxt->error != NULL)
3195 ctxt->error(ctxt->userData,
3196 "xmlRelaxNGParse: %s is empty\n",
3197 ctxt->URL);
3198 ctxt->nbErrors++;
3199 return (NULL);
3200 }
3201 /*
3202 * ns transmission rules
3203 */
3204 ns = xmlGetProp(root, BAD_CAST "ns");
3205 if (ns == NULL) {
3206 tmp = node;
3207 while ((tmp != NULL) &&
3208 (tmp->type == XML_ELEMENT_NODE)) {
3209 ns = xmlGetProp(tmp, BAD_CAST "ns");
3210 if (ns != NULL) {
3211 break;
3212 }
3213 tmp = tmp->parent;
3214 }
3215 if (ns != NULL) {
3216 xmlSetProp(root, BAD_CAST "ns", ns);
3217 newNs = 1;
3218 xmlFree(ns);
3219 }
3220 } else {
3221 xmlFree(ns);
3222 }
3223
3224 /*
3225 * Parsing to get a precompiled schemas.
3226 */
Daniel Veillard77648bb2003-02-20 15:03:22 +00003227 oldflags = ctxt->flags;
3228 ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003229 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard77648bb2003-02-20 15:03:22 +00003230 ctxt->flags = oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003231 if ((docu->schema != NULL) &&
3232 (docu->schema->topgrammar != NULL)) {
3233 docu->content = docu->schema->topgrammar->start;
3234 }
3235
3236 /*
3237 * the externalRef may be reused in a different ns context
3238 */
3239 if (newNs == 1) {
3240 xmlUnsetProp(root, BAD_CAST "ns");
3241 }
3242 }
3243 def->content = docu->content;
3244 } else {
3245 def = NULL;
3246 }
3247 return(def);
3248}
3249
3250/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003251 * xmlRelaxNGParsePattern:
3252 * @ctxt: a Relax-NG parser context
3253 * @node: the pattern node.
3254 *
3255 * parse the content of a RelaxNG pattern node.
3256 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00003257 * Returns the definition pointer or NULL in case of error or if no
3258 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003259 */
3260static xmlRelaxNGDefinePtr
3261xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3262 xmlRelaxNGDefinePtr def = NULL;
3263
Daniel Veillardd2298792003-02-14 16:54:11 +00003264 if (node == NULL) {
3265 return(NULL);
3266 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003267 if (IS_RELAXNG(node, "element")) {
3268 def = xmlRelaxNGParseElement(ctxt, node);
3269 } else if (IS_RELAXNG(node, "attribute")) {
3270 def = xmlRelaxNGParseAttribute(ctxt, node);
3271 } else if (IS_RELAXNG(node, "empty")) {
3272 def = xmlRelaxNGNewDefine(ctxt, node);
3273 if (def == NULL)
3274 return(NULL);
3275 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00003276 if (node->children != NULL) {
3277 if (ctxt->error != NULL)
3278 ctxt->error(ctxt->userData, "empty: had a child node\n");
3279 ctxt->nbErrors++;
3280 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003281 } else if (IS_RELAXNG(node, "text")) {
3282 def = xmlRelaxNGNewDefine(ctxt, node);
3283 if (def == NULL)
3284 return(NULL);
3285 def->type = XML_RELAXNG_TEXT;
3286 if (node->children != NULL) {
3287 if (ctxt->error != NULL)
3288 ctxt->error(ctxt->userData, "text: had a child node\n");
3289 ctxt->nbErrors++;
3290 }
3291 } else if (IS_RELAXNG(node, "zeroOrMore")) {
3292 def = xmlRelaxNGNewDefine(ctxt, node);
3293 if (def == NULL)
3294 return(NULL);
3295 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00003296 if (node->children == NULL) {
3297 if (ctxt->error != NULL)
3298 ctxt->error(ctxt->userData,
3299 "Element %s is empty\n", node->name);
3300 ctxt->nbErrors++;
3301 } else {
3302 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
3303 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003304 } else if (IS_RELAXNG(node, "oneOrMore")) {
3305 def = xmlRelaxNGNewDefine(ctxt, node);
3306 if (def == NULL)
3307 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003308 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00003309 if (node->children == NULL) {
3310 if (ctxt->error != NULL)
3311 ctxt->error(ctxt->userData,
3312 "Element %s is empty\n", node->name);
3313 ctxt->nbErrors++;
3314 } else {
3315 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
3316 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003317 } else if (IS_RELAXNG(node, "optional")) {
3318 def = xmlRelaxNGNewDefine(ctxt, node);
3319 if (def == NULL)
3320 return(NULL);
3321 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00003322 if (node->children == NULL) {
3323 if (ctxt->error != NULL)
3324 ctxt->error(ctxt->userData,
3325 "Element %s is empty\n", node->name);
3326 ctxt->nbErrors++;
3327 } else {
3328 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
3329 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003330 } else if (IS_RELAXNG(node, "choice")) {
3331 def = xmlRelaxNGNewDefine(ctxt, node);
3332 if (def == NULL)
3333 return(NULL);
3334 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00003335 if (node->children == NULL) {
3336 if (ctxt->error != NULL)
3337 ctxt->error(ctxt->userData,
3338 "Element %s is empty\n", node->name);
3339 ctxt->nbErrors++;
3340 } else {
3341 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3342 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003343 } else if (IS_RELAXNG(node, "group")) {
3344 def = xmlRelaxNGNewDefine(ctxt, node);
3345 if (def == NULL)
3346 return(NULL);
3347 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00003348 if (node->children == NULL) {
3349 if (ctxt->error != NULL)
3350 ctxt->error(ctxt->userData,
3351 "Element %s is empty\n", node->name);
3352 ctxt->nbErrors++;
3353 } else {
3354 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3355 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003356 } else if (IS_RELAXNG(node, "ref")) {
3357 def = xmlRelaxNGNewDefine(ctxt, node);
3358 if (def == NULL)
3359 return(NULL);
3360 def->type = XML_RELAXNG_REF;
3361 def->name = xmlGetProp(node, BAD_CAST "name");
3362 if (def->name == NULL) {
3363 if (ctxt->error != NULL)
3364 ctxt->error(ctxt->userData,
3365 "ref has no name\n");
3366 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003367 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003368 xmlRelaxNGNormExtSpace(def->name);
3369 if (xmlValidateNCName(def->name, 0)) {
3370 if (ctxt->error != NULL)
3371 ctxt->error(ctxt->userData,
3372 "ref name '%s' is not an NCName\n",
3373 def->name);
3374 ctxt->nbErrors++;
3375 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003376 }
3377 if (node->children != NULL) {
3378 if (ctxt->error != NULL)
3379 ctxt->error(ctxt->userData,
3380 "ref is not empty\n");
3381 ctxt->nbErrors++;
3382 }
3383 if (ctxt->grammar->refs == NULL)
3384 ctxt->grammar->refs = xmlHashCreate(10);
3385 if (ctxt->grammar->refs == NULL) {
3386 if (ctxt->error != NULL)
3387 ctxt->error(ctxt->userData,
3388 "Could not create references hash\n");
3389 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003390 def = NULL;
3391 } else {
3392 int tmp;
3393
3394 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
3395 if (tmp < 0) {
3396 xmlRelaxNGDefinePtr prev;
3397
3398 prev = (xmlRelaxNGDefinePtr)
3399 xmlHashLookup(ctxt->grammar->refs, def->name);
3400 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00003401 if (def->name != NULL) {
3402 if (ctxt->error != NULL)
3403 ctxt->error(ctxt->userData,
3404 "Error refs definitions '%s'\n",
3405 def->name);
3406 } else {
3407 if (ctxt->error != NULL)
3408 ctxt->error(ctxt->userData,
3409 "Error refs definitions\n");
3410 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003411 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003412 def = NULL;
3413 } else {
3414 def->nextHash = prev->nextHash;
3415 prev->nextHash = def;
3416 }
3417 }
3418 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00003419 } else if (IS_RELAXNG(node, "data")) {
3420 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardedc91922003-01-26 00:52:04 +00003421 } else if (IS_RELAXNG(node, "value")) {
3422 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003423 } else if (IS_RELAXNG(node, "list")) {
3424 def = xmlRelaxNGNewDefine(ctxt, node);
3425 if (def == NULL)
3426 return(NULL);
3427 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00003428 if (node->children == NULL) {
3429 if (ctxt->error != NULL)
3430 ctxt->error(ctxt->userData,
3431 "Element %s is empty\n", node->name);
3432 ctxt->nbErrors++;
3433 } else {
3434 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3435 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003436 } else if (IS_RELAXNG(node, "interleave")) {
3437 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003438 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00003439 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003440 } else if (IS_RELAXNG(node, "notAllowed")) {
3441 def = xmlRelaxNGNewDefine(ctxt, node);
3442 if (def == NULL)
3443 return(NULL);
3444 def->type = XML_RELAXNG_NOT_ALLOWED;
3445 if (node->children != NULL) {
3446 if (ctxt->error != NULL)
3447 ctxt->error(ctxt->userData,
3448 "xmlRelaxNGParse: notAllowed element is not empty\n");
3449 ctxt->nbErrors++;
3450 }
Daniel Veillard419a7682003-02-03 23:22:49 +00003451 } else if (IS_RELAXNG(node, "grammar")) {
3452 xmlRelaxNGGrammarPtr grammar, old;
3453 xmlRelaxNGGrammarPtr oldparent;
3454
Daniel Veillardc482e262003-02-26 14:48:48 +00003455#ifdef DEBUG_GRAMMAR
3456 xmlGenericError(xmlGenericErrorContext, "Found <grammar> pattern\n");
3457#endif
3458
Daniel Veillard419a7682003-02-03 23:22:49 +00003459 oldparent = ctxt->parentgrammar;
3460 old = ctxt->grammar;
3461 ctxt->parentgrammar = old;
3462 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3463 if (old != NULL) {
3464 ctxt->grammar = old;
3465 ctxt->parentgrammar = oldparent;
Daniel Veillardc482e262003-02-26 14:48:48 +00003466#if 0
Daniel Veillard419a7682003-02-03 23:22:49 +00003467 if (grammar != NULL) {
3468 grammar->next = old->next;
3469 old->next = grammar;
3470 }
Daniel Veillardc482e262003-02-26 14:48:48 +00003471#endif
Daniel Veillard419a7682003-02-03 23:22:49 +00003472 }
3473 if (grammar != NULL)
3474 def = grammar->start;
3475 else
3476 def = NULL;
3477 } else if (IS_RELAXNG(node, "parentRef")) {
3478 if (ctxt->parentgrammar == NULL) {
3479 if (ctxt->error != NULL)
3480 ctxt->error(ctxt->userData,
3481 "Use of parentRef without a parent grammar\n");
3482 ctxt->nbErrors++;
3483 return(NULL);
3484 }
3485 def = xmlRelaxNGNewDefine(ctxt, node);
3486 if (def == NULL)
3487 return(NULL);
3488 def->type = XML_RELAXNG_PARENTREF;
3489 def->name = xmlGetProp(node, BAD_CAST "name");
3490 if (def->name == NULL) {
3491 if (ctxt->error != NULL)
3492 ctxt->error(ctxt->userData,
3493 "parentRef has no name\n");
3494 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00003495 } else {
3496 xmlRelaxNGNormExtSpace(def->name);
3497 if (xmlValidateNCName(def->name, 0)) {
3498 if (ctxt->error != NULL)
3499 ctxt->error(ctxt->userData,
3500 "parentRef name '%s' is not an NCName\n",
3501 def->name);
3502 ctxt->nbErrors++;
3503 }
Daniel Veillard419a7682003-02-03 23:22:49 +00003504 }
3505 if (node->children != NULL) {
3506 if (ctxt->error != NULL)
3507 ctxt->error(ctxt->userData,
3508 "parentRef is not empty\n");
3509 ctxt->nbErrors++;
3510 }
3511 if (ctxt->parentgrammar->refs == NULL)
3512 ctxt->parentgrammar->refs = xmlHashCreate(10);
3513 if (ctxt->parentgrammar->refs == NULL) {
3514 if (ctxt->error != NULL)
3515 ctxt->error(ctxt->userData,
3516 "Could not create references hash\n");
3517 ctxt->nbErrors++;
3518 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00003519 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00003520 int tmp;
3521
3522 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
3523 if (tmp < 0) {
3524 xmlRelaxNGDefinePtr prev;
3525
3526 prev = (xmlRelaxNGDefinePtr)
3527 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
3528 if (prev == NULL) {
3529 if (ctxt->error != NULL)
3530 ctxt->error(ctxt->userData,
3531 "Internal error parentRef definitions '%s'\n",
3532 def->name);
3533 ctxt->nbErrors++;
3534 def = NULL;
3535 } else {
3536 def->nextHash = prev->nextHash;
3537 prev->nextHash = def;
3538 }
3539 }
3540 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003541 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00003542 if (node->children == NULL) {
3543 if (ctxt->error != NULL)
3544 ctxt->error(ctxt->userData,
3545 "Mixed is empty\n");
3546 ctxt->nbErrors++;
3547 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00003548 } else {
3549 def = xmlRelaxNGParseInterleave(ctxt, node);
3550 if (def != NULL) {
3551 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003552
3553 if ((def->content != NULL) && (def->content->next != NULL)) {
3554 tmp = xmlRelaxNGNewDefine(ctxt, node);
3555 if (tmp != NULL) {
3556 tmp->type = XML_RELAXNG_GROUP;
3557 tmp->content = def->content;
3558 def->content = tmp;
3559 }
3560 }
3561
Daniel Veillard416589a2003-02-17 17:25:42 +00003562 tmp = xmlRelaxNGNewDefine(ctxt, node);
3563 if (tmp == NULL)
3564 return(def);
3565 tmp->type = XML_RELAXNG_TEXT;
3566 tmp->next = def->content;
3567 def->content = tmp;
3568 }
3569 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003570 } else {
3571 if (ctxt->error != NULL)
3572 ctxt->error(ctxt->userData,
3573 "Unexpected node %s is not a pattern\n",
3574 node->name);
3575 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003576 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003577 }
3578 return(def);
3579}
3580
3581/**
3582 * xmlRelaxNGParseAttribute:
3583 * @ctxt: a Relax-NG parser context
3584 * @node: the element node
3585 *
3586 * parse the content of a RelaxNG attribute node.
3587 *
3588 * Returns the definition pointer or NULL in case of error.
3589 */
3590static xmlRelaxNGDefinePtr
3591xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00003592 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003593 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003594 int old_flags;
3595
3596 ret = xmlRelaxNGNewDefine(ctxt, node);
3597 if (ret == NULL)
3598 return(NULL);
3599 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003600 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003601 child = node->children;
3602 if (child == NULL) {
3603 if (ctxt->error != NULL)
3604 ctxt->error(ctxt->userData,
3605 "xmlRelaxNGParseattribute: attribute has no children\n");
3606 ctxt->nbErrors++;
3607 return(ret);
3608 }
3609 old_flags = ctxt->flags;
3610 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003611 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3612 if (cur != NULL)
3613 child = child->next;
3614
Daniel Veillardd2298792003-02-14 16:54:11 +00003615 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003616 cur = xmlRelaxNGParsePattern(ctxt, child);
3617 if (cur != NULL) {
3618 switch (cur->type) {
3619 case XML_RELAXNG_EMPTY:
3620 case XML_RELAXNG_NOT_ALLOWED:
3621 case XML_RELAXNG_TEXT:
3622 case XML_RELAXNG_ELEMENT:
3623 case XML_RELAXNG_DATATYPE:
3624 case XML_RELAXNG_VALUE:
3625 case XML_RELAXNG_LIST:
3626 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003627 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003628 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003629 case XML_RELAXNG_DEF:
3630 case XML_RELAXNG_ONEORMORE:
3631 case XML_RELAXNG_ZEROORMORE:
3632 case XML_RELAXNG_OPTIONAL:
3633 case XML_RELAXNG_CHOICE:
3634 case XML_RELAXNG_GROUP:
3635 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard77648bb2003-02-20 15:03:22 +00003636 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00003637 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003638 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003639 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003640 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003641 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003642 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00003643 if (ctxt->error != NULL)
3644 ctxt->error(ctxt->userData,
3645 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003646 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003647 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003648 case XML_RELAXNG_NOOP:
3649 TODO
3650 if (ctxt->error != NULL)
3651 ctxt->error(ctxt->userData,
3652 "Internal error, noop found\n");
3653 ctxt->nbErrors++;
3654 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003655 }
3656 }
3657 child = child->next;
3658 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003659 if (child != NULL) {
3660 if (ctxt->error != NULL)
3661 ctxt->error(ctxt->userData, "attribute has multiple children\n");
3662 ctxt->nbErrors++;
3663 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003664 ctxt->flags = old_flags;
3665 return(ret);
3666}
3667
3668/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003669 * xmlRelaxNGParseExceptNameClass:
3670 * @ctxt: a Relax-NG parser context
3671 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00003672 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003673 *
3674 * parse the content of a RelaxNG nameClass node.
3675 *
3676 * Returns the definition pointer or NULL in case of error.
3677 */
3678static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00003679xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
3680 xmlNodePtr node, int attr) {
3681 xmlRelaxNGDefinePtr ret, cur, last = NULL;
3682 xmlNodePtr child;
3683
Daniel Veillardd2298792003-02-14 16:54:11 +00003684 if (!IS_RELAXNG(node, "except")) {
3685 if (ctxt->error != NULL)
3686 ctxt->error(ctxt->userData,
3687 "Expecting an except node\n");
3688 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00003689 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00003690 }
3691 if (node->next != NULL) {
3692 if (ctxt->error != NULL)
3693 ctxt->error(ctxt->userData,
3694 "exceptNameClass allows only a single except node\n");
3695 ctxt->nbErrors++;
3696 }
Daniel Veillard144fae12003-02-03 13:17:57 +00003697 if (node->children == NULL) {
3698 if (ctxt->error != NULL)
3699 ctxt->error(ctxt->userData,
3700 "except has no content\n");
3701 ctxt->nbErrors++;
3702 return(NULL);
3703 }
3704
3705 ret = xmlRelaxNGNewDefine(ctxt, node);
3706 if (ret == NULL)
3707 return(NULL);
3708 ret->type = XML_RELAXNG_EXCEPT;
3709 child = node->children;
3710 while (child != NULL) {
3711 cur = xmlRelaxNGNewDefine(ctxt, child);
3712 if (cur == NULL)
3713 break;
3714 if (attr)
3715 cur->type = XML_RELAXNG_ATTRIBUTE;
3716 else
3717 cur->type = XML_RELAXNG_ELEMENT;
3718
Daniel Veillard419a7682003-02-03 23:22:49 +00003719 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00003720 if (last == NULL) {
3721 ret->content = cur;
3722 } else {
3723 last->next = cur;
3724 }
3725 last = cur;
3726 }
3727 child = child->next;
3728 }
3729
3730 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003731}
3732
3733/**
3734 * xmlRelaxNGParseNameClass:
3735 * @ctxt: a Relax-NG parser context
3736 * @node: the nameClass node
3737 * @def: the current definition
3738 *
3739 * parse the content of a RelaxNG nameClass node.
3740 *
3741 * Returns the definition pointer or NULL in case of error.
3742 */
3743static xmlRelaxNGDefinePtr
3744xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
3745 xmlRelaxNGDefinePtr def) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003746 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003747 xmlChar *val;
3748
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003749 ret = def;
3750 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
3751 (IS_RELAXNG(node, "nsName"))) {
3752 if ((def->type != XML_RELAXNG_ELEMENT) &&
3753 (def->type != XML_RELAXNG_ATTRIBUTE)) {
3754 ret = xmlRelaxNGNewDefine(ctxt, node);
3755 if (ret == NULL)
3756 return(NULL);
3757 ret->parent = def;
3758 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
3759 ret->type = XML_RELAXNG_ATTRIBUTE;
3760 else
3761 ret->type = XML_RELAXNG_ELEMENT;
3762 }
3763 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003764 if (IS_RELAXNG(node, "name")) {
3765 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003766 xmlRelaxNGNormExtSpace(val);
3767 if (xmlValidateNCName(val, 0)) {
3768 if (ctxt->error != NULL) {
3769 if (node->parent != NULL)
3770 ctxt->error(ctxt->userData,
3771 "Element %s name '%s' is not an NCName\n",
3772 node->parent->name, val);
3773 else
3774 ctxt->error(ctxt->userData,
3775 "name '%s' is not an NCName\n",
3776 val);
3777 }
3778 ctxt->nbErrors++;
3779 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003780 ret->name = val;
3781 val = xmlGetProp(node, BAD_CAST "ns");
3782 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003783 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3784 (val != NULL) &&
3785 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3786 ctxt->error(ctxt->userData,
3787 "Attribute with namespace '%s' is not allowed\n",
3788 val);
3789 ctxt->nbErrors++;
3790 }
3791 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3792 (val != NULL) &&
3793 (val[0] == 0) &&
3794 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3795 ctxt->error(ctxt->userData,
3796 "Attribute with QName 'xmlns' is not allowed\n",
3797 val);
3798 ctxt->nbErrors++;
3799 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003800 } else if (IS_RELAXNG(node, "anyName")) {
3801 ret->name = NULL;
3802 ret->ns = NULL;
3803 if (node->children != NULL) {
3804 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003805 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3806 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003807 }
3808 } else if (IS_RELAXNG(node, "nsName")) {
3809 ret->name = NULL;
3810 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3811 if (ret->ns == NULL) {
3812 if (ctxt->error != NULL)
3813 ctxt->error(ctxt->userData,
3814 "nsName has no ns attribute\n");
3815 ctxt->nbErrors++;
3816 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003817 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3818 (ret->ns != NULL) &&
3819 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3820 ctxt->error(ctxt->userData,
3821 "Attribute with namespace '%s' is not allowed\n",
3822 ret->ns);
3823 ctxt->nbErrors++;
3824 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003825 if (node->children != NULL) {
3826 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003827 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3828 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003829 }
3830 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003831 xmlNodePtr child;
3832 xmlRelaxNGDefinePtr last = NULL;
3833
Daniel Veillardd4310742003-02-18 21:12:46 +00003834 ret = xmlRelaxNGNewDefine(ctxt, node);
3835 if (ret == NULL)
3836 return(NULL);
3837 ret->parent = def;
3838 ret->type = XML_RELAXNG_CHOICE;
3839
Daniel Veillardd2298792003-02-14 16:54:11 +00003840 if (node->children == NULL) {
3841 if (ctxt->error != NULL)
3842 ctxt->error(ctxt->userData,
3843 "Element choice is empty\n");
3844 ctxt->nbErrors++;
3845 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003846
3847 child = node->children;
3848 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003849 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003850 if (tmp != NULL) {
3851 if (last == NULL) {
3852 last = ret->nameClass = tmp;
3853 } else {
3854 last->next = tmp;
3855 last = tmp;
3856 }
3857 }
3858 child = child->next;
3859 }
3860 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003861 } else {
3862 if (ctxt->error != NULL)
3863 ctxt->error(ctxt->userData,
3864 "expecting name, anyName, nsName or choice : got %s\n",
3865 node->name);
3866 ctxt->nbErrors++;
3867 return(NULL);
3868 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003869 if (ret != def) {
3870 if (def->nameClass == NULL) {
3871 def->nameClass = ret;
3872 } else {
3873 tmp = def->nameClass;
3874 while (tmp->next != NULL) {
3875 tmp = tmp->next;
3876 }
3877 tmp->next = ret;
3878 }
3879 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003880 return(ret);
3881}
3882
3883/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003884 * xmlRelaxNGParseElement:
3885 * @ctxt: a Relax-NG parser context
3886 * @node: the element node
3887 *
3888 * parse the content of a RelaxNG element node.
3889 *
3890 * Returns the definition pointer or NULL in case of error.
3891 */
3892static xmlRelaxNGDefinePtr
3893xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3894 xmlRelaxNGDefinePtr ret, cur, last;
3895 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003896 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003897
3898 ret = xmlRelaxNGNewDefine(ctxt, node);
3899 if (ret == NULL)
3900 return(NULL);
3901 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003902 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003903 child = node->children;
3904 if (child == NULL) {
3905 if (ctxt->error != NULL)
3906 ctxt->error(ctxt->userData,
3907 "xmlRelaxNGParseElement: element has no children\n");
3908 ctxt->nbErrors++;
3909 return(ret);
3910 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003911 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3912 if (cur != NULL)
3913 child = child->next;
3914
Daniel Veillard6eadf632003-01-23 18:29:16 +00003915 if (child == NULL) {
3916 if (ctxt->error != NULL)
3917 ctxt->error(ctxt->userData,
3918 "xmlRelaxNGParseElement: element has no content\n");
3919 ctxt->nbErrors++;
3920 return(ret);
3921 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003922 olddefine = ctxt->define;
3923 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003924 last = NULL;
3925 while (child != NULL) {
3926 cur = xmlRelaxNGParsePattern(ctxt, child);
3927 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003928 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003929 switch (cur->type) {
3930 case XML_RELAXNG_EMPTY:
3931 case XML_RELAXNG_NOT_ALLOWED:
3932 case XML_RELAXNG_TEXT:
3933 case XML_RELAXNG_ELEMENT:
3934 case XML_RELAXNG_DATATYPE:
3935 case XML_RELAXNG_VALUE:
3936 case XML_RELAXNG_LIST:
3937 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003938 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003939 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003940 case XML_RELAXNG_DEF:
3941 case XML_RELAXNG_ZEROORMORE:
3942 case XML_RELAXNG_ONEORMORE:
3943 case XML_RELAXNG_OPTIONAL:
3944 case XML_RELAXNG_CHOICE:
3945 case XML_RELAXNG_GROUP:
3946 case XML_RELAXNG_INTERLEAVE:
3947 if (last == NULL) {
3948 ret->content = last = cur;
3949 } else {
3950 if ((last->type == XML_RELAXNG_ELEMENT) &&
3951 (ret->content == last)) {
3952 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3953 if (ret->content != NULL) {
3954 ret->content->type = XML_RELAXNG_GROUP;
3955 ret->content->content = last;
3956 } else {
3957 ret->content = last;
3958 }
3959 }
3960 last->next = cur;
3961 last = cur;
3962 }
3963 break;
3964 case XML_RELAXNG_ATTRIBUTE:
3965 cur->next = ret->attrs;
3966 ret->attrs = cur;
3967 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003968 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003969 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003970 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003971 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003972 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003973 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003974 case XML_RELAXNG_NOOP:
3975 TODO
3976 if (ctxt->error != NULL)
3977 ctxt->error(ctxt->userData,
3978 "Internal error, noop found\n");
3979 ctxt->nbErrors++;
3980 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003981 }
3982 }
3983 child = child->next;
3984 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003985 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003986 return(ret);
3987}
3988
3989/**
3990 * xmlRelaxNGParsePatterns:
3991 * @ctxt: a Relax-NG parser context
3992 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003993 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003994 *
3995 * parse the content of a RelaxNG start node.
3996 *
3997 * Returns the definition pointer or NULL in case of error.
3998 */
3999static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00004000xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
4001 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004002 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004003
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004004 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004005 while (nodes != NULL) {
4006 if (IS_RELAXNG(nodes, "element")) {
4007 cur = xmlRelaxNGParseElement(ctxt, nodes);
4008 if (def == NULL) {
4009 def = last = cur;
4010 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00004011 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
4012 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004013 def = xmlRelaxNGNewDefine(ctxt, nodes);
4014 def->type = XML_RELAXNG_GROUP;
4015 def->content = last;
4016 }
4017 last->next = cur;
4018 last = cur;
4019 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004020 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004021 } else {
4022 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00004023 if (cur != NULL) {
4024 if (def == NULL) {
4025 def = last = cur;
4026 } else {
4027 last->next = cur;
4028 last = cur;
4029 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004030 }
4031 }
4032 nodes = nodes->next;
4033 }
4034 return(def);
4035}
4036
4037/**
4038 * xmlRelaxNGParseStart:
4039 * @ctxt: a Relax-NG parser context
4040 * @nodes: start children nodes
4041 *
4042 * parse the content of a RelaxNG start node.
4043 *
4044 * Returns 0 in case of success, -1 in case of error
4045 */
4046static int
4047xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
4048 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004049 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004050
Daniel Veillardd2298792003-02-14 16:54:11 +00004051 if (nodes == NULL) {
4052 if (ctxt->error != NULL)
4053 ctxt->error(ctxt->userData,
4054 "start has no children\n");
4055 ctxt->nbErrors++;
4056 return(-1);
4057 }
4058 if (IS_RELAXNG(nodes, "empty")) {
4059 def = xmlRelaxNGNewDefine(ctxt, nodes);
4060 if (def == NULL)
4061 return(-1);
4062 def->type = XML_RELAXNG_EMPTY;
4063 if (nodes->children != NULL) {
4064 if (ctxt->error != NULL)
4065 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004066 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004067 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004068 } else if (IS_RELAXNG(nodes, "notAllowed")) {
4069 def = xmlRelaxNGNewDefine(ctxt, nodes);
4070 if (def == NULL)
4071 return(-1);
4072 def->type = XML_RELAXNG_NOT_ALLOWED;
4073 if (nodes->children != NULL) {
4074 if (ctxt->error != NULL)
4075 ctxt->error(ctxt->userData,
4076 "element notAllowed is not empty\n");
4077 ctxt->nbErrors++;
4078 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004079 } else {
4080 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00004081 }
4082 if (ctxt->grammar->start != NULL) {
4083 last = ctxt->grammar->start;
4084 while (last->next != NULL)
4085 last = last->next;
4086 last->next = def;
4087 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004088 ctxt->grammar->start = def;
4089 }
4090 nodes = nodes->next;
4091 if (nodes != NULL) {
4092 if (ctxt->error != NULL)
4093 ctxt->error(ctxt->userData,
4094 "start more than one children\n");
4095 ctxt->nbErrors++;
4096 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004097 }
4098 return(ret);
4099}
4100
4101/**
4102 * xmlRelaxNGParseGrammarContent:
4103 * @ctxt: a Relax-NG parser context
4104 * @nodes: grammar children nodes
4105 *
4106 * parse the content of a RelaxNG grammar node.
4107 *
4108 * Returns 0 in case of success, -1 in case of error
4109 */
4110static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004111xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00004112{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004113 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004114
4115 if (nodes == NULL) {
4116 if (ctxt->error != NULL)
4117 ctxt->error(ctxt->userData,
4118 "grammar has no children\n");
4119 ctxt->nbErrors++;
4120 return(-1);
4121 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004122 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004123 if (IS_RELAXNG(nodes, "start")) {
4124 if (nodes->children == NULL) {
4125 if (ctxt->error != NULL)
4126 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00004127 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004128 ctxt->nbErrors++;
4129 } else {
4130 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
4131 if (tmp != 0)
4132 ret = -1;
4133 }
4134 } else if (IS_RELAXNG(nodes, "define")) {
4135 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
4136 if (tmp != 0)
4137 ret = -1;
4138 } else if (IS_RELAXNG(nodes, "include")) {
4139 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
4140 if (tmp != 0)
4141 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004142 } else {
4143 if (ctxt->error != NULL)
4144 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004145 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004146 ctxt->nbErrors++;
4147 ret = -1;
4148 }
4149 nodes = nodes->next;
4150 }
4151 return (ret);
4152}
4153
4154/**
4155 * xmlRelaxNGCheckReference:
4156 * @ref: the ref
4157 * @ctxt: a Relax-NG parser context
4158 * @name: the name associated to the defines
4159 *
4160 * Applies the 4.17. combine attribute rule for all the define
4161 * element of a given grammar using the same name.
4162 */
4163static void
4164xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
4165 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
4166 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004167 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004168
4169 grammar = ctxt->grammar;
4170 if (grammar == NULL) {
4171 if (ctxt->error != NULL)
4172 ctxt->error(ctxt->userData,
4173 "Internal error: no grammar in CheckReference %s\n",
4174 name);
4175 ctxt->nbErrors++;
4176 return;
4177 }
4178 if (ref->content != NULL) {
4179 if (ctxt->error != NULL)
4180 ctxt->error(ctxt->userData,
4181 "Internal error: reference has content in CheckReference %s\n",
4182 name);
4183 ctxt->nbErrors++;
4184 return;
4185 }
4186 if (grammar->defs != NULL) {
4187 def = xmlHashLookup(grammar->defs, name);
4188 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00004189 cur = ref;
4190 while (cur != NULL) {
4191 cur->content = def;
4192 cur = cur->nextHash;
4193 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004194 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00004195 if (ctxt->error != NULL)
4196 ctxt->error(ctxt->userData,
4197 "Reference %s has no matching definition\n",
4198 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004199 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004200 }
Daniel Veillardd4310742003-02-18 21:12:46 +00004201 } else {
4202 if (ctxt->error != NULL)
4203 ctxt->error(ctxt->userData,
4204 "Reference %s has no matching definition\n",
4205 name);
4206 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004207 }
4208 /*
4209 * TODO: make a closure and verify there is no loop !
4210 */
4211}
4212
4213/**
4214 * xmlRelaxNGCheckCombine:
4215 * @define: the define(s) list
4216 * @ctxt: a Relax-NG parser context
4217 * @name: the name associated to the defines
4218 *
4219 * Applies the 4.17. combine attribute rule for all the define
4220 * element of a given grammar using the same name.
4221 */
4222static void
4223xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
4224 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
4225 xmlChar *combine;
4226 int choiceOrInterleave = -1;
4227 int missing = 0;
4228 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
4229
4230 if (define->nextHash == NULL)
4231 return;
4232 cur = define;
4233 while (cur != NULL) {
4234 combine = xmlGetProp(cur->node, BAD_CAST "combine");
4235 if (combine != NULL) {
4236 if (xmlStrEqual(combine, BAD_CAST "choice")) {
4237 if (choiceOrInterleave == -1)
4238 choiceOrInterleave = 1;
4239 else if (choiceOrInterleave == 0) {
4240 if (ctxt->error != NULL)
4241 ctxt->error(ctxt->userData,
4242 "Defines for %s use both 'choice' and 'interleave'\n",
4243 name);
4244 ctxt->nbErrors++;
4245 }
Daniel Veillard154877e2003-01-30 12:17:05 +00004246 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004247 if (choiceOrInterleave == -1)
4248 choiceOrInterleave = 0;
4249 else if (choiceOrInterleave == 1) {
4250 if (ctxt->error != NULL)
4251 ctxt->error(ctxt->userData,
4252 "Defines for %s use both 'choice' and 'interleave'\n",
4253 name);
4254 ctxt->nbErrors++;
4255 }
4256 } else {
4257 if (ctxt->error != NULL)
4258 ctxt->error(ctxt->userData,
4259 "Defines for %s use unknown combine value '%s''\n",
4260 name, combine);
4261 ctxt->nbErrors++;
4262 }
4263 xmlFree(combine);
4264 } else {
4265 if (missing == 0)
4266 missing = 1;
4267 else {
4268 if (ctxt->error != NULL)
4269 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00004270 "Some defines for %s needs the combine attribute\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00004271 name);
4272 ctxt->nbErrors++;
4273 }
4274 }
4275
4276 cur = cur->nextHash;
4277 }
4278#ifdef DEBUG
4279 xmlGenericError(xmlGenericErrorContext,
4280 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
4281 name, choiceOrInterleave);
4282#endif
4283 if (choiceOrInterleave == -1)
4284 choiceOrInterleave = 0;
4285 cur = xmlRelaxNGNewDefine(ctxt, define->node);
4286 if (cur == NULL)
4287 return;
4288 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00004289 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00004290 else
4291 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004292 tmp = define;
4293 last = NULL;
4294 while (tmp != NULL) {
4295 if (tmp->content != NULL) {
4296 if (tmp->content->next != NULL) {
4297 /*
4298 * we need first to create a wrapper.
4299 */
4300 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
4301 if (tmp2 == NULL)
4302 break;
4303 tmp2->type = XML_RELAXNG_GROUP;
4304 tmp2->content = tmp->content;
4305 } else {
4306 tmp2 = tmp->content;
4307 }
4308 if (last == NULL) {
4309 cur->content = tmp2;
4310 } else {
4311 last->next = tmp2;
4312 }
4313 last = tmp2;
4314 tmp->content = NULL;
4315 }
4316 tmp = tmp->nextHash;
4317 }
4318 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00004319 if (choiceOrInterleave == 0) {
4320 if (ctxt->interleaves == NULL)
4321 ctxt->interleaves = xmlHashCreate(10);
4322 if (ctxt->interleaves == NULL) {
4323 if (ctxt->error != NULL)
4324 ctxt->error(ctxt->userData,
4325 "Failed to create interleaves hash table\n");
4326 ctxt->nbErrors++;
4327 } else {
4328 char tmpname[32];
4329
4330 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
4331 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
4332 if (ctxt->error != NULL)
4333 ctxt->error(ctxt->userData,
4334 "Failed to add %s to hash table\n", tmpname);
4335 ctxt->nbErrors++;
4336 }
4337 }
4338 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004339}
4340
4341/**
4342 * xmlRelaxNGCombineStart:
4343 * @ctxt: a Relax-NG parser context
4344 * @grammar: the grammar
4345 *
4346 * Applies the 4.17. combine rule for all the start
4347 * element of a given grammar.
4348 */
4349static void
4350xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
4351 xmlRelaxNGGrammarPtr grammar) {
4352 xmlRelaxNGDefinePtr starts;
4353 xmlChar *combine;
4354 int choiceOrInterleave = -1;
4355 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004356 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004357
Daniel Veillard2df2de22003-02-17 23:34:33 +00004358 starts = grammar->start;
4359 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00004360 return;
4361 cur = starts;
4362 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00004363 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
4364 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
4365 combine = NULL;
4366 if (ctxt->error != NULL)
4367 ctxt->error(ctxt->userData,
4368 "Internal error: start element not found\n");
4369 ctxt->nbErrors++;
4370 } else {
4371 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
4372 }
4373
Daniel Veillard6eadf632003-01-23 18:29:16 +00004374 if (combine != NULL) {
4375 if (xmlStrEqual(combine, BAD_CAST "choice")) {
4376 if (choiceOrInterleave == -1)
4377 choiceOrInterleave = 1;
4378 else if (choiceOrInterleave == 0) {
4379 if (ctxt->error != NULL)
4380 ctxt->error(ctxt->userData,
4381 "<start> use both 'choice' and 'interleave'\n");
4382 ctxt->nbErrors++;
4383 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00004384 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004385 if (choiceOrInterleave == -1)
4386 choiceOrInterleave = 0;
4387 else if (choiceOrInterleave == 1) {
4388 if (ctxt->error != NULL)
4389 ctxt->error(ctxt->userData,
4390 "<start> use both 'choice' and 'interleave'\n");
4391 ctxt->nbErrors++;
4392 }
4393 } else {
4394 if (ctxt->error != NULL)
4395 ctxt->error(ctxt->userData,
4396 "<start> uses unknown combine value '%s''\n", combine);
4397 ctxt->nbErrors++;
4398 }
4399 xmlFree(combine);
4400 } else {
4401 if (missing == 0)
4402 missing = 1;
4403 else {
4404 if (ctxt->error != NULL)
4405 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00004406 "Some <start> element miss the combine attribute\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +00004407 ctxt->nbErrors++;
4408 }
4409 }
4410
Daniel Veillard2df2de22003-02-17 23:34:33 +00004411 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004412 }
4413#ifdef DEBUG
4414 xmlGenericError(xmlGenericErrorContext,
4415 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
4416 choiceOrInterleave);
4417#endif
4418 if (choiceOrInterleave == -1)
4419 choiceOrInterleave = 0;
4420 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
4421 if (cur == NULL)
4422 return;
4423 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00004424 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004425 else
4426 cur->type = XML_RELAXNG_CHOICE;
4427 cur->content = grammar->start;
4428 grammar->start = cur;
4429 if (choiceOrInterleave == 0) {
4430 if (ctxt->interleaves == NULL)
4431 ctxt->interleaves = xmlHashCreate(10);
4432 if (ctxt->interleaves == NULL) {
4433 if (ctxt->error != NULL)
4434 ctxt->error(ctxt->userData,
4435 "Failed to create interleaves hash table\n");
4436 ctxt->nbErrors++;
4437 } else {
4438 char tmpname[32];
4439
4440 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
4441 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
4442 if (ctxt->error != NULL)
4443 ctxt->error(ctxt->userData,
4444 "Failed to add %s to hash table\n", tmpname);
4445 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004446 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004447 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004448 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004449}
4450
4451/**
Daniel Veillardd4310742003-02-18 21:12:46 +00004452 * xmlRelaxNGCheckCycles:
4453 * @ctxt: a Relax-NG parser context
4454 * @nodes: grammar children nodes
4455 * @depth: the counter
4456 *
4457 * Check for cycles.
4458 *
4459 * Returns 0 if check passed, and -1 in case of error
4460 */
4461static int
4462xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
4463 xmlRelaxNGDefinePtr cur, int depth) {
4464 int ret = 0;
4465
4466 while ((ret == 0) && (cur != NULL)) {
4467 if ((cur->type == XML_RELAXNG_REF) ||
4468 (cur->type == XML_RELAXNG_PARENTREF)) {
4469 if (cur->depth == -1) {
4470 cur->depth = depth;
4471 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
4472 cur->depth = -2;
4473 } else if (depth == cur->depth) {
4474 if (ctxt->error != NULL)
4475 ctxt->error(ctxt->userData,
4476 "Detected a cycle in %s references\n", cur->name);
4477 ctxt->nbErrors++;
4478 return(-1);
4479 }
4480 } else if (cur->type == XML_RELAXNG_ELEMENT) {
4481 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
4482 } else {
4483 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
4484 }
4485 cur = cur->next;
4486 }
4487 return(ret);
4488}
4489
4490/**
Daniel Veillard77648bb2003-02-20 15:03:22 +00004491 * xmlRelaxNGTryUnlink:
4492 * @ctxt: a Relax-NG parser context
4493 * @cur: the definition to unlink
4494 * @parent: the parent definition
4495 * @prev: the previous sibling definition
4496 *
4497 * Try to unlink a definition. If not possble make it a NOOP
4498 *
4499 * Returns the new prev definition
4500 */
4501static xmlRelaxNGDefinePtr
4502xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
4503 xmlRelaxNGDefinePtr cur,
4504 xmlRelaxNGDefinePtr parent,
4505 xmlRelaxNGDefinePtr prev) {
4506 if (prev != NULL) {
4507 prev->next = cur->next;
4508 } else {
4509 if (parent != NULL) {
4510 if (parent->content == cur)
4511 parent->content = cur->next;
4512 else if (parent->attrs == cur)
4513 parent->attrs = cur->next;
4514 else if (parent->nameClass == cur)
4515 parent->nameClass = cur->next;
4516 } else {
4517 cur->type = XML_RELAXNG_NOOP;
4518 prev = cur;
4519 }
4520 }
4521 return(prev);
4522}
4523
4524/**
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004525 * xmlRelaxNGSimplify:
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004526 * @ctxt: a Relax-NG parser context
4527 * @nodes: grammar children nodes
4528 *
4529 * Check for simplification of empty and notAllowed
4530 */
4531static void
4532xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
4533 xmlRelaxNGDefinePtr cur,
4534 xmlRelaxNGDefinePtr parent) {
4535 xmlRelaxNGDefinePtr prev = NULL;
4536
4537 while (cur != NULL) {
4538 if ((cur->type == XML_RELAXNG_REF) ||
4539 (cur->type == XML_RELAXNG_PARENTREF)) {
4540 if (cur->depth != -3) {
4541 cur->depth = -3;
4542 xmlRelaxNGSimplify(ctxt, cur->content, cur);
4543 }
4544 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004545 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004546 if ((parent != NULL) &&
4547 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4548 (parent->type == XML_RELAXNG_LIST) ||
4549 (parent->type == XML_RELAXNG_GROUP) ||
4550 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4551 (parent->type == XML_RELAXNG_ONEORMORE) ||
4552 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4553 parent->type = XML_RELAXNG_NOT_ALLOWED;
4554 break;
4555 }
4556 if ((parent != NULL) &&
4557 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004558 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004559 } else
4560 prev = cur;
4561 } else if (cur->type == XML_RELAXNG_EMPTY){
Daniel Veillard77648bb2003-02-20 15:03:22 +00004562 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004563 if ((parent != NULL) &&
4564 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4565 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4566 parent->type = XML_RELAXNG_EMPTY;
4567 break;
4568 }
4569 if ((parent != NULL) &&
4570 ((parent->type == XML_RELAXNG_GROUP) ||
4571 (parent->type == XML_RELAXNG_INTERLEAVE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004572 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004573 } else
4574 prev = cur;
4575 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004576 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004577 if (cur->content != NULL)
4578 xmlRelaxNGSimplify(ctxt, cur->content, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004579 if (cur->attrs != NULL)
4580 xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
4581 if (cur->nameClass != NULL)
4582 xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004583 /*
4584 * This may result in a simplification
4585 */
4586 if ((cur->type == XML_RELAXNG_GROUP) ||
4587 (cur->type == XML_RELAXNG_INTERLEAVE)) {
4588 if (cur->content == NULL)
4589 cur->type = XML_RELAXNG_EMPTY;
4590 else if (cur->content->next == NULL) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004591 if ((parent == NULL) && (prev == NULL)) {
4592 cur->type = XML_RELAXNG_NOOP;
4593 } else if (prev == NULL) {
4594 parent->content = cur->content;
4595 cur->content->next = cur->next;
4596 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004597 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004598 cur->content->next = cur->next;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004599 prev->next = cur->content;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004600 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004601 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004602 }
4603 }
4604 /*
4605 * the current node may have been transformed back
4606 */
4607 if ((cur->type == XML_RELAXNG_EXCEPT) &&
4608 (cur->content != NULL) &&
4609 (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004610 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004611 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
4612 if ((parent != NULL) &&
4613 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4614 (parent->type == XML_RELAXNG_LIST) ||
4615 (parent->type == XML_RELAXNG_GROUP) ||
4616 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4617 (parent->type == XML_RELAXNG_ONEORMORE) ||
4618 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4619 parent->type = XML_RELAXNG_NOT_ALLOWED;
4620 break;
4621 }
4622 if ((parent != NULL) &&
4623 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004624 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004625 } else
4626 prev = cur;
4627 } else if (cur->type == XML_RELAXNG_EMPTY){
4628 if ((parent != NULL) &&
4629 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4630 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4631 parent->type = XML_RELAXNG_EMPTY;
4632 break;
4633 }
4634 if ((parent != NULL) &&
4635 ((parent->type == XML_RELAXNG_GROUP) ||
4636 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4637 (parent->type == XML_RELAXNG_CHOICE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004638 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004639 } else
4640 prev = cur;
4641 } else {
4642 prev = cur;
4643 }
4644 }
4645 cur = cur->next;
4646 }
4647}
4648
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004649/**
4650 * xmlRelaxNGGroupContentType:
4651 * @ct1: the first content type
4652 * @ct2: the second content type
4653 *
4654 * Try to group 2 content types
4655 *
4656 * Returns the content type
4657 */
4658static xmlRelaxNGContentType
4659xmlRelaxNGGroupContentType(xmlRelaxNGContentType ct1,
4660 xmlRelaxNGContentType ct2) {
4661 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4662 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4663 return(XML_RELAXNG_CONTENT_ERROR);
4664 if (ct1 == XML_RELAXNG_CONTENT_EMPTY)
4665 return(ct2);
4666 if (ct2 == XML_RELAXNG_CONTENT_EMPTY)
4667 return(ct1);
4668 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) &&
4669 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4670 return(XML_RELAXNG_CONTENT_COMPLEX);
4671 return(XML_RELAXNG_CONTENT_ERROR);
4672}
4673
4674/**
4675 * xmlRelaxNGMaxContentType:
4676 * @ct1: the first content type
4677 * @ct2: the second content type
4678 *
4679 * Compute the max content-type
4680 *
4681 * Returns the content type
4682 */
4683static xmlRelaxNGContentType
4684xmlRelaxNGMaxContentType(xmlRelaxNGContentType ct1,
4685 xmlRelaxNGContentType ct2) {
4686 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4687 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4688 return(XML_RELAXNG_CONTENT_ERROR);
4689 if ((ct1 == XML_RELAXNG_CONTENT_SIMPLE) ||
4690 (ct2 == XML_RELAXNG_CONTENT_SIMPLE))
4691 return(XML_RELAXNG_CONTENT_SIMPLE);
4692 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) ||
4693 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4694 return(XML_RELAXNG_CONTENT_COMPLEX);
4695 return(XML_RELAXNG_CONTENT_EMPTY);
4696}
Daniel Veillard77648bb2003-02-20 15:03:22 +00004697
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004698/**
4699 * xmlRelaxNGCheckRules:
4700 * @ctxt: a Relax-NG parser context
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004701 * @cur: the current definition
Daniel Veillard77648bb2003-02-20 15:03:22 +00004702 * @flags: some accumulated flags
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004703 * @ptype: the parent type
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004704 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004705 * Check for rules in section 7.1 and 7.2
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004706 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004707 * Returns the content type of @cur
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004708 */
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004709static xmlRelaxNGContentType
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004710xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004711 xmlRelaxNGDefinePtr cur, int flags,
4712 xmlRelaxNGType ptype) {
4713 int nflags = flags;
4714 xmlRelaxNGContentType ret, tmp, val = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004715
Daniel Veillard77648bb2003-02-20 15:03:22 +00004716 while (cur != NULL) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004717 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004718 if ((cur->type == XML_RELAXNG_REF) ||
4719 (cur->type == XML_RELAXNG_PARENTREF)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004720 if (flags & XML_RELAXNG_IN_LIST) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004721 if (ctxt->error != NULL)
4722 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00004723 "Found forbidden pattern list//ref\n");
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004724 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004725 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00004726 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4727 if (ctxt->error != NULL)
4728 ctxt->error(ctxt->userData,
4729 "Found forbidden pattern data/except//ref\n");
4730 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004731 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00004732 if (cur->depth > -4) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004733 cur->depth = -4;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004734 ret = xmlRelaxNGCheckRules(ctxt, cur->content,
4735 flags, cur->type);
4736 cur->depth = ret - 15 ;
4737 } else if (cur->depth == -4) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004738 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004739 } else {
4740 ret = (xmlRelaxNGContentType) cur->depth + 15;
4741 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004742 } else if (cur->type == XML_RELAXNG_ELEMENT) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004743 /*
4744 * The 7.3 Attribute derivation rule for groups is plugged there
4745 */
4746 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004747 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4748 if (ctxt->error != NULL)
4749 ctxt->error(ctxt->userData,
4750 "Found forbidden pattern data/except//element(ref)\n");
4751 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004752 }
4753 if (flags & XML_RELAXNG_IN_LIST) {
4754 if (ctxt->error != NULL)
4755 ctxt->error(ctxt->userData,
4756 "Found forbidden pattern list//element(ref)\n");
4757 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004758 }
4759 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4760 if (ctxt->error != NULL)
4761 ctxt->error(ctxt->userData,
4762 "Found forbidden pattern attribute//element(ref)\n");
4763 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004764 }
Daniel Veillard463a5472003-02-27 21:30:32 +00004765 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4766 if (ctxt->error != NULL)
4767 ctxt->error(ctxt->userData,
4768 "Found forbidden pattern attribute//element(ref)\n");
4769 ctxt->nbErrors++;
4770 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00004771 /*
4772 * reset since in the simple form elements are only child
4773 * of grammar/define
4774 */
4775 nflags = 0;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004776 ret = xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags, cur->type);
4777 if (ret != XML_RELAXNG_CONTENT_EMPTY) {
4778 if (ctxt->error != NULL)
4779 ctxt->error(ctxt->userData,
4780 "Element %s attributes have a content type error\n",
4781 cur->name);
4782 ctxt->nbErrors++;
4783 }
4784 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4785 if (ret == XML_RELAXNG_CONTENT_ERROR) {
4786 if (ctxt->error != NULL)
4787 ctxt->error(ctxt->userData,
4788 "Element %s has a content type error\n",
4789 cur->name);
4790 ctxt->nbErrors++;
4791 } else {
4792 ret = XML_RELAXNG_CONTENT_COMPLEX;
4793 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00004794 } else if (cur->type == XML_RELAXNG_ATTRIBUTE) {
4795 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4796 if (ctxt->error != NULL)
4797 ctxt->error(ctxt->userData,
4798 "Found forbidden pattern attribute//attribute\n");
4799 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004800 }
4801 if (flags & XML_RELAXNG_IN_LIST) {
4802 if (ctxt->error != NULL)
4803 ctxt->error(ctxt->userData,
4804 "Found forbidden pattern list//attribute\n");
4805 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004806 }
4807 if (flags & XML_RELAXNG_IN_OOMGROUP) {
4808 if (ctxt->error != NULL)
4809 ctxt->error(ctxt->userData,
4810 "Found forbidden pattern oneOrMore//group//attribute\n");
4811 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004812 }
4813 if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) {
4814 if (ctxt->error != NULL)
4815 ctxt->error(ctxt->userData,
4816 "Found forbidden pattern oneOrMore//interleave//attribute\n");
4817 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004818 }
4819 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4820 if (ctxt->error != NULL)
4821 ctxt->error(ctxt->userData,
4822 "Found forbidden pattern data/except//attribute\n");
4823 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004824 }
4825 if (flags & XML_RELAXNG_IN_START) {
4826 if (ctxt->error != NULL)
4827 ctxt->error(ctxt->userData,
4828 "Found forbidden pattern start//attribute\n");
4829 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004830 }
4831 nflags = flags | XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004832 xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4833 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004834 } else if ((cur->type == XML_RELAXNG_ONEORMORE) ||
4835 (cur->type == XML_RELAXNG_ZEROORMORE)) {
4836 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4837 if (ctxt->error != NULL)
4838 ctxt->error(ctxt->userData,
4839 "Found forbidden pattern data/except//oneOrMore\n");
4840 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004841 }
4842 if (flags & XML_RELAXNG_IN_START) {
4843 if (ctxt->error != NULL)
4844 ctxt->error(ctxt->userData,
4845 "Found forbidden pattern start//oneOrMore\n");
4846 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004847 }
4848 nflags = flags | XML_RELAXNG_IN_ONEORMORE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004849 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4850 ret = xmlRelaxNGGroupContentType(ret, ret);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004851 } else if (cur->type == XML_RELAXNG_LIST) {
4852 if (flags & XML_RELAXNG_IN_LIST) {
4853 if (ctxt->error != NULL)
4854 ctxt->error(ctxt->userData,
4855 "Found forbidden pattern list//list\n");
4856 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004857 }
4858 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4859 if (ctxt->error != NULL)
4860 ctxt->error(ctxt->userData,
4861 "Found forbidden pattern data/except//list\n");
4862 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004863 }
4864 if (flags & XML_RELAXNG_IN_START) {
4865 if (ctxt->error != NULL)
4866 ctxt->error(ctxt->userData,
4867 "Found forbidden pattern start//list\n");
4868 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004869 }
4870 nflags = flags | XML_RELAXNG_IN_LIST;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004871 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004872 } else if (cur->type == XML_RELAXNG_GROUP) {
4873 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4874 if (ctxt->error != NULL)
4875 ctxt->error(ctxt->userData,
4876 "Found forbidden pattern data/except//group\n");
4877 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004878 }
4879 if (flags & XML_RELAXNG_IN_START) {
4880 if (ctxt->error != NULL)
4881 ctxt->error(ctxt->userData,
4882 "Found forbidden pattern start//group\n");
4883 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004884 }
4885 if (flags & XML_RELAXNG_IN_ONEORMORE)
4886 nflags = flags | XML_RELAXNG_IN_OOMGROUP;
4887 else
4888 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004889 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004890 /*
4891 * The 7.3 Attribute derivation rule for groups is plugged there
4892 */
4893 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004894 } else if (cur->type == XML_RELAXNG_INTERLEAVE) {
4895 if (flags & XML_RELAXNG_IN_LIST) {
4896 if (ctxt->error != NULL)
4897 ctxt->error(ctxt->userData,
4898 "Found forbidden pattern list//interleave\n");
4899 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004900 }
4901 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4902 if (ctxt->error != NULL)
4903 ctxt->error(ctxt->userData,
4904 "Found forbidden pattern data/except//interleave\n");
4905 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004906 }
4907 if (flags & XML_RELAXNG_IN_START) {
4908 if (ctxt->error != NULL)
4909 ctxt->error(ctxt->userData,
4910 "Found forbidden pattern start//interleave\n");
4911 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004912 }
4913 if (flags & XML_RELAXNG_IN_ONEORMORE)
4914 nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE;
4915 else
4916 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004917 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004918 } else if (cur->type == XML_RELAXNG_EXCEPT) {
4919 if ((cur->parent != NULL) &&
4920 (cur->parent->type == XML_RELAXNG_DATATYPE))
4921 nflags = flags | XML_RELAXNG_IN_DATAEXCEPT;
4922 else
4923 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004924 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004925 } else if (cur->type == XML_RELAXNG_DATATYPE) {
4926 if (flags & XML_RELAXNG_IN_START) {
4927 if (ctxt->error != NULL)
4928 ctxt->error(ctxt->userData,
4929 "Found forbidden pattern start//data\n");
4930 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004931 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004932 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4933 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004934 } else if (cur->type == XML_RELAXNG_VALUE) {
4935 if (flags & XML_RELAXNG_IN_START) {
4936 if (ctxt->error != NULL)
4937 ctxt->error(ctxt->userData,
4938 "Found forbidden pattern start//value\n");
4939 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004940 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004941 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4942 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004943 } else if (cur->type == XML_RELAXNG_TEXT) {
4944 if (flags & XML_RELAXNG_IN_LIST) {
4945 if (ctxt->error != NULL)
4946 ctxt->error(ctxt->userData,
4947 "Found forbidden pattern list//text\n");
4948 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004949 }
4950 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4951 if (ctxt->error != NULL)
4952 ctxt->error(ctxt->userData,
4953 "Found forbidden pattern data/except//text\n");
4954 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004955 }
4956 if (flags & XML_RELAXNG_IN_START) {
4957 if (ctxt->error != NULL)
4958 ctxt->error(ctxt->userData,
4959 "Found forbidden pattern start//text\n");
4960 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004961 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004962 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004963 } else if (cur->type == XML_RELAXNG_EMPTY) {
4964 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4965 if (ctxt->error != NULL)
4966 ctxt->error(ctxt->userData,
4967 "Found forbidden pattern data/except//empty\n");
4968 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004969 }
4970 if (flags & XML_RELAXNG_IN_START) {
4971 if (ctxt->error != NULL)
4972 ctxt->error(ctxt->userData,
4973 "Found forbidden pattern start//empty\n");
4974 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004975 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004976 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004977 } else {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004978 ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004979 }
4980 cur = cur->next;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004981 if (ptype == XML_RELAXNG_GROUP) {
4982 val = xmlRelaxNGGroupContentType(val, ret);
4983 } else if (ptype == XML_RELAXNG_INTERLEAVE) {
4984 tmp = xmlRelaxNGGroupContentType(val, ret);
4985 if (tmp != XML_RELAXNG_CONTENT_ERROR)
4986 tmp = xmlRelaxNGMaxContentType(val, ret);
4987 } else if (ptype == XML_RELAXNG_CHOICE) {
4988 val = xmlRelaxNGMaxContentType(val, ret);
4989 } else if (ptype == XML_RELAXNG_LIST) {
4990 val = XML_RELAXNG_CONTENT_SIMPLE;
4991 } else if (ptype == XML_RELAXNG_EXCEPT) {
4992 if (ret == XML_RELAXNG_CONTENT_ERROR)
4993 val = XML_RELAXNG_CONTENT_ERROR;
4994 else
4995 val = XML_RELAXNG_CONTENT_SIMPLE;
4996 } else {
4997 val = xmlRelaxNGGroupContentType(val, ret);
4998 }
4999
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005000 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00005001 return(val);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005002}
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005003
5004/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005005 * xmlRelaxNGParseGrammar:
5006 * @ctxt: a Relax-NG parser context
5007 * @nodes: grammar children nodes
5008 *
5009 * parse a Relax-NG <grammar> node
5010 *
5011 * Returns the internal xmlRelaxNGGrammarPtr built or
5012 * NULL in case of error
5013 */
5014static xmlRelaxNGGrammarPtr
5015xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
5016 xmlRelaxNGGrammarPtr ret, tmp, old;
5017
Daniel Veillardc482e262003-02-26 14:48:48 +00005018#ifdef DEBUG_GRAMMAR
5019 xmlGenericError(xmlGenericErrorContext, "Parsing a new grammar\n");
5020#endif
5021
Daniel Veillard6eadf632003-01-23 18:29:16 +00005022 ret = xmlRelaxNGNewGrammar(ctxt);
5023 if (ret == NULL)
5024 return(NULL);
5025
5026 /*
5027 * Link the new grammar in the tree
5028 */
5029 ret->parent = ctxt->grammar;
5030 if (ctxt->grammar != NULL) {
5031 tmp = ctxt->grammar->children;
5032 if (tmp == NULL) {
5033 ctxt->grammar->children = ret;
5034 } else {
5035 while (tmp->next != NULL)
5036 tmp = tmp->next;
5037 tmp->next = ret;
5038 }
5039 }
5040
5041 old = ctxt->grammar;
5042 ctxt->grammar = ret;
5043 xmlRelaxNGParseGrammarContent(ctxt, nodes);
5044 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00005045 if (ctxt->grammar == NULL) {
5046 if (ctxt->error != NULL)
5047 ctxt->error(ctxt->userData,
5048 "Failed to parse <grammar> content\n");
5049 ctxt->nbErrors++;
5050 } else if (ctxt->grammar->start == NULL) {
5051 if (ctxt->error != NULL)
5052 ctxt->error(ctxt->userData,
5053 "Element <grammar> has no <start>\n");
5054 ctxt->nbErrors++;
5055 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005056
5057 /*
5058 * Apply 4.17 mergingd rules to defines and starts
5059 */
5060 xmlRelaxNGCombineStart(ctxt, ret);
5061 if (ret->defs != NULL) {
5062 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
5063 ctxt);
5064 }
5065
5066 /*
5067 * link together defines and refs in this grammar
5068 */
5069 if (ret->refs != NULL) {
5070 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
5071 ctxt);
5072 }
5073 ctxt->grammar = old;
5074 return(ret);
5075}
5076
5077/**
5078 * xmlRelaxNGParseDocument:
5079 * @ctxt: a Relax-NG parser context
5080 * @node: the root node of the RelaxNG schema
5081 *
5082 * parse a Relax-NG definition resource and build an internal
5083 * xmlRelaxNG struture which can be used to validate instances.
5084 *
5085 * Returns the internal XML RelaxNG structure built or
5086 * NULL in case of error
5087 */
5088static xmlRelaxNGPtr
5089xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
5090 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005091 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00005092 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005093
5094 if ((ctxt == NULL) || (node == NULL))
5095 return (NULL);
5096
5097 schema = xmlRelaxNGNewRelaxNG(ctxt);
5098 if (schema == NULL)
5099 return(NULL);
5100
Daniel Veillard276be4a2003-01-24 01:03:34 +00005101 olddefine = ctxt->define;
5102 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005103 if (IS_RELAXNG(node, "grammar")) {
5104 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
5105 } else {
Daniel Veillardc482e262003-02-26 14:48:48 +00005106 xmlRelaxNGGrammarPtr tmp, ret;
5107
5108 schema->topgrammar = ret = xmlRelaxNGNewGrammar(ctxt);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005109 if (schema->topgrammar == NULL) {
5110 return(schema);
5111 }
Daniel Veillardc482e262003-02-26 14:48:48 +00005112 /*
5113 * Link the new grammar in the tree
5114 */
5115 ret->parent = ctxt->grammar;
5116 if (ctxt->grammar != NULL) {
5117 tmp = ctxt->grammar->children;
5118 if (tmp == NULL) {
5119 ctxt->grammar->children = ret;
5120 } else {
5121 while (tmp->next != NULL)
5122 tmp = tmp->next;
5123 tmp->next = ret;
5124 }
5125 }
Daniel Veillarde431a272003-01-29 23:02:33 +00005126 old = ctxt->grammar;
Daniel Veillardc482e262003-02-26 14:48:48 +00005127 ctxt->grammar = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005128 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00005129 if (old != NULL)
5130 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005131 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005132 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00005133 if (schema->topgrammar->start != NULL) {
5134 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
Daniel Veillard77648bb2003-02-20 15:03:22 +00005135 if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) {
5136 xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
5137 while ((schema->topgrammar->start != NULL) &&
5138 (schema->topgrammar->start->type == XML_RELAXNG_NOOP) &&
5139 (schema->topgrammar->start->next != NULL))
5140 schema->topgrammar->start = schema->topgrammar->start->content;
5141 xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00005142 XML_RELAXNG_IN_START, XML_RELAXNG_NOOP);
Daniel Veillard77648bb2003-02-20 15:03:22 +00005143 }
Daniel Veillardd4310742003-02-18 21:12:46 +00005144 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005145
5146#ifdef DEBUG
5147 if (schema == NULL)
5148 xmlGenericError(xmlGenericErrorContext,
5149 "xmlRelaxNGParseDocument() failed\n");
5150#endif
5151
5152 return (schema);
5153}
5154
5155/************************************************************************
5156 * *
5157 * Reading RelaxNGs *
5158 * *
5159 ************************************************************************/
5160
5161/**
5162 * xmlRelaxNGNewParserCtxt:
5163 * @URL: the location of the schema
5164 *
5165 * Create an XML RelaxNGs parse context for that file/resource expected
5166 * to contain an XML RelaxNGs file.
5167 *
5168 * Returns the parser context or NULL in case of error
5169 */
5170xmlRelaxNGParserCtxtPtr
5171xmlRelaxNGNewParserCtxt(const char *URL) {
5172 xmlRelaxNGParserCtxtPtr ret;
5173
5174 if (URL == NULL)
5175 return(NULL);
5176
5177 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
5178 if (ret == NULL) {
5179 xmlGenericError(xmlGenericErrorContext,
5180 "Failed to allocate new schama parser context for %s\n", URL);
5181 return (NULL);
5182 }
5183 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
5184 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005185 ret->error = xmlGenericError;
5186 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005187 return (ret);
5188}
5189
5190/**
5191 * xmlRelaxNGNewMemParserCtxt:
5192 * @buffer: a pointer to a char array containing the schemas
5193 * @size: the size of the array
5194 *
5195 * Create an XML RelaxNGs parse context for that memory buffer expected
5196 * to contain an XML RelaxNGs file.
5197 *
5198 * Returns the parser context or NULL in case of error
5199 */
5200xmlRelaxNGParserCtxtPtr
5201xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
5202 xmlRelaxNGParserCtxtPtr ret;
5203
5204 if ((buffer == NULL) || (size <= 0))
5205 return(NULL);
5206
5207 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
5208 if (ret == NULL) {
5209 xmlGenericError(xmlGenericErrorContext,
5210 "Failed to allocate new schama parser context\n");
5211 return (NULL);
5212 }
5213 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
5214 ret->buffer = buffer;
5215 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005216 ret->error = xmlGenericError;
5217 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005218 return (ret);
5219}
5220
5221/**
5222 * xmlRelaxNGFreeParserCtxt:
5223 * @ctxt: the schema parser context
5224 *
5225 * Free the resources associated to the schema parser context
5226 */
5227void
5228xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
5229 if (ctxt == NULL)
5230 return;
5231 if (ctxt->URL != NULL)
5232 xmlFree(ctxt->URL);
5233 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005234 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005235 if (ctxt->interleaves != NULL)
5236 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005237 if (ctxt->documents != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +00005238 xmlRelaxNGFreeDocumentList(ctxt->documents);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005239 if (ctxt->includes != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +00005240 xmlRelaxNGFreeIncludeList(ctxt->includes);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005241 if (ctxt->docTab != NULL)
5242 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005243 if (ctxt->incTab != NULL)
5244 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00005245 if (ctxt->defTab != NULL) {
5246 int i;
5247
5248 for (i = 0;i < ctxt->defNr;i++)
5249 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
5250 xmlFree(ctxt->defTab);
5251 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005252 xmlFree(ctxt);
5253}
5254
Daniel Veillard6eadf632003-01-23 18:29:16 +00005255/**
Daniel Veillardd2298792003-02-14 16:54:11 +00005256 * xmlRelaxNGNormExtSpace:
5257 * @value: a value
5258 *
5259 * Removes the leading and ending spaces of the value
5260 * The string is modified "in situ"
5261 */
5262static void
5263xmlRelaxNGNormExtSpace(xmlChar *value) {
5264 xmlChar *start = value;
5265 xmlChar *cur = value;
5266 if (value == NULL)
5267 return;
5268
5269 while (IS_BLANK(*cur)) cur++;
5270 if (cur == start) {
5271 do {
5272 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5273 if (*cur == 0)
5274 return;
5275 start = cur;
5276 while (IS_BLANK(*cur)) cur++;
5277 if (*cur == 0) {
5278 *start = 0;
5279 return;
5280 }
5281 } while (1);
5282 } else {
5283 do {
5284 while ((*cur != 0) && (!IS_BLANK(*cur)))
5285 *start++ = *cur++;
5286 if (*cur == 0) {
5287 *start = 0;
5288 return;
5289 }
5290 /* don't try to normalize the inner spaces */
5291 while (IS_BLANK(*cur)) cur++;
5292 *start++ = *cur++;
5293 if (*cur == 0) {
5294 *start = 0;
5295 return;
5296 }
5297 } while (1);
5298 }
5299}
5300
5301/**
5302 * xmlRelaxNGCheckAttributes:
5303 * @ctxt: a Relax-NG parser context
5304 * @node: a Relax-NG node
5305 *
5306 * Check all the attributes on the given node
5307 */
5308static void
5309xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
5310 xmlAttrPtr cur, next;
5311
5312 cur = node->properties;
5313 while (cur != NULL) {
5314 next = cur->next;
5315 if ((cur->ns == NULL) ||
5316 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
5317 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
5318 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
5319 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
5320 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
5321 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00005322 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00005323 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
5324 if (ctxt->error != NULL)
5325 ctxt->error(ctxt->userData,
5326 "Attribute %s is not allowed on %s\n",
5327 cur->name, node->name);
5328 ctxt->nbErrors++;
5329 }
5330 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
5331 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
5332 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
5333 if (ctxt->error != NULL)
5334 ctxt->error(ctxt->userData,
5335 "Attribute %s is not allowed on %s\n",
5336 cur->name, node->name);
5337 ctxt->nbErrors++;
5338 }
5339 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
5340 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
5341 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
5342 if (ctxt->error != NULL)
5343 ctxt->error(ctxt->userData,
5344 "Attribute %s is not allowed on %s\n",
5345 cur->name, node->name);
5346 ctxt->nbErrors++;
5347 }
5348 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
5349 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
5350 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
5351 if (ctxt->error != NULL)
5352 ctxt->error(ctxt->userData,
5353 "Attribute %s is not allowed on %s\n",
5354 cur->name, node->name);
5355 ctxt->nbErrors++;
5356 }
5357 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
5358 xmlChar *val;
5359 xmlURIPtr uri;
5360
5361 val = xmlNodeListGetString(node->doc, cur->children, 1);
5362 if (val != NULL) {
5363 if (val[0] != 0) {
5364 uri = xmlParseURI((const char *) val);
5365 if (uri == NULL) {
5366 if (ctxt->error != NULL)
5367 ctxt->error(ctxt->userData,
5368 "Attribute %s contains invalid URI %s\n",
5369 cur->name, val);
5370 ctxt->nbErrors++;
5371 } else {
5372 if (uri->scheme == NULL) {
5373 if (ctxt->error != NULL)
5374 ctxt->error(ctxt->userData,
5375 "Attribute %s URI %s is not absolute\n",
5376 cur->name, val);
5377 ctxt->nbErrors++;
5378 }
5379 if (uri->fragment != NULL) {
5380 if (ctxt->error != NULL)
5381 ctxt->error(ctxt->userData,
5382 "Attribute %s URI %s has a fragment ID\n",
5383 cur->name, val);
5384 ctxt->nbErrors++;
5385 }
5386 xmlFreeURI(uri);
5387 }
5388 }
5389 xmlFree(val);
5390 }
5391 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
5392 if (ctxt->error != NULL)
5393 ctxt->error(ctxt->userData,
5394 "Unknown attribute %s on %s\n",
5395 cur->name, node->name);
5396 ctxt->nbErrors++;
5397 }
5398 }
5399 cur = next;
5400 }
5401}
5402
5403/**
Daniel Veillardc5312d72003-02-21 17:14:10 +00005404 * xmlRelaxNGCleanupTree:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005405 * @ctxt: a Relax-NG parser context
Daniel Veillardc5312d72003-02-21 17:14:10 +00005406 * @root: an xmlNodePtr subtree
Daniel Veillard6eadf632003-01-23 18:29:16 +00005407 *
Daniel Veillardc5312d72003-02-21 17:14:10 +00005408 * Cleanup the subtree from unwanted nodes for parsing, resolve
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005409 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00005410 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005411static void
5412xmlRelaxNGCleanupTree(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr root) {
5413 xmlNodePtr cur, delete;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005414
Daniel Veillard6eadf632003-01-23 18:29:16 +00005415 delete = NULL;
5416 cur = root;
5417 while (cur != NULL) {
5418 if (delete != NULL) {
5419 xmlUnlinkNode(delete);
5420 xmlFreeNode(delete);
5421 delete = NULL;
5422 }
5423 if (cur->type == XML_ELEMENT_NODE) {
5424 /*
5425 * Simplification 4.1. Annotations
5426 */
5427 if ((cur->ns == NULL) ||
5428 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00005429 if ((cur->parent != NULL) &&
5430 (cur->parent->type == XML_ELEMENT_NODE) &&
5431 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
5432 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
5433 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
5434 if (ctxt->error != NULL)
5435 ctxt->error(ctxt->userData,
5436 "element %s doesn't allow foreign elements\n",
5437 cur->parent->name);
5438 ctxt->nbErrors++;
5439 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005440 delete = cur;
5441 goto skip_children;
5442 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00005443 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005444 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005445 xmlChar *href, *ns, *base, *URL;
5446 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00005447 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005448
5449 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00005450 if (ns == NULL) {
5451 tmp = cur->parent;
5452 while ((tmp != NULL) &&
5453 (tmp->type == XML_ELEMENT_NODE)) {
5454 ns = xmlGetProp(tmp, BAD_CAST "ns");
5455 if (ns != NULL)
5456 break;
5457 tmp = tmp->parent;
5458 }
5459 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005460 href = xmlGetProp(cur, BAD_CAST "href");
5461 if (href == NULL) {
5462 if (ctxt->error != NULL)
5463 ctxt->error(ctxt->userData,
5464 "xmlRelaxNGParse: externalRef has no href attribute\n");
5465 ctxt->nbErrors++;
5466 delete = cur;
5467 goto skip_children;
5468 }
5469 base = xmlNodeGetBase(cur->doc, cur);
5470 URL = xmlBuildURI(href, base);
5471 if (URL == NULL) {
5472 if (ctxt->error != NULL)
5473 ctxt->error(ctxt->userData,
5474 "Failed to compute URL for externalRef %s\n", href);
5475 ctxt->nbErrors++;
5476 if (href != NULL)
5477 xmlFree(href);
5478 if (base != NULL)
5479 xmlFree(base);
5480 delete = cur;
5481 goto skip_children;
5482 }
5483 if (href != NULL)
5484 xmlFree(href);
5485 if (base != NULL)
5486 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005487 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005488 if (docu == NULL) {
5489 if (ctxt->error != NULL)
5490 ctxt->error(ctxt->userData,
5491 "Failed to load externalRef %s\n", URL);
5492 ctxt->nbErrors++;
5493 xmlFree(URL);
5494 delete = cur;
5495 goto skip_children;
5496 }
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005497 if (ns != NULL)
5498 xmlFree(ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005499 xmlFree(URL);
5500 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005501 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005502 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005503 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00005504 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005505
5506 href = xmlGetProp(cur, BAD_CAST "href");
5507 if (href == NULL) {
5508 if (ctxt->error != NULL)
5509 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005510 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005511 ctxt->nbErrors++;
5512 delete = cur;
5513 goto skip_children;
5514 }
5515 base = xmlNodeGetBase(cur->doc, cur);
5516 URL = xmlBuildURI(href, base);
5517 if (URL == NULL) {
5518 if (ctxt->error != NULL)
5519 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005520 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005521 ctxt->nbErrors++;
5522 if (href != NULL)
5523 xmlFree(href);
5524 if (base != NULL)
5525 xmlFree(base);
5526 delete = cur;
5527 goto skip_children;
5528 }
5529 if (href != NULL)
5530 xmlFree(href);
5531 if (base != NULL)
5532 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00005533 ns = xmlGetProp(cur, BAD_CAST "ns");
5534 if (ns == NULL) {
5535 tmp = cur->parent;
5536 while ((tmp != NULL) &&
5537 (tmp->type == XML_ELEMENT_NODE)) {
5538 ns = xmlGetProp(tmp, BAD_CAST "ns");
5539 if (ns != NULL)
5540 break;
5541 tmp = tmp->parent;
5542 }
5543 }
5544 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
5545 if (ns != NULL)
5546 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005547 if (incl == NULL) {
5548 if (ctxt->error != NULL)
5549 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005550 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005551 ctxt->nbErrors++;
5552 xmlFree(URL);
5553 delete = cur;
5554 goto skip_children;
5555 }
5556 xmlFree(URL);
5557 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005558 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
5559 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00005560 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005561 xmlNodePtr text = NULL;
5562
5563 /*
5564 * Simplification 4.8. name attribute of element
5565 * and attribute elements
5566 */
5567 name = xmlGetProp(cur, BAD_CAST "name");
5568 if (name != NULL) {
5569 if (cur->children == NULL) {
5570 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
5571 name);
5572 } else {
5573 xmlNodePtr node;
5574 node = xmlNewNode(cur->ns, BAD_CAST "name");
5575 if (node != NULL) {
5576 xmlAddPrevSibling(cur->children, node);
5577 text = xmlNewText(name);
5578 xmlAddChild(node, text);
5579 text = node;
5580 }
5581 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005582 if (text == NULL) {
5583 if (ctxt->error != NULL)
5584 ctxt->error(ctxt->userData,
5585 "Failed to create a name %s element\n", name);
5586 ctxt->nbErrors++;
5587 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005588 xmlUnsetProp(cur, BAD_CAST "name");
5589 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00005590 ns = xmlGetProp(cur, BAD_CAST "ns");
5591 if (ns != NULL) {
5592 if (text != NULL) {
5593 xmlSetProp(text, BAD_CAST "ns", ns);
5594 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00005595 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005596 xmlFree(ns);
5597 } else if (xmlStrEqual(cur->name,
5598 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005599 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
5600 }
5601 }
5602 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
5603 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
5604 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
5605 /*
5606 * Simplification 4.8. name attribute of element
5607 * and attribute elements
5608 */
5609 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
5610 xmlNodePtr node;
5611 xmlChar *ns = NULL;
5612
5613 node = cur->parent;
5614 while ((node != NULL) &&
5615 (node->type == XML_ELEMENT_NODE)) {
5616 ns = xmlGetProp(node, BAD_CAST "ns");
5617 if (ns != NULL) {
5618 break;
5619 }
5620 node = node->parent;
5621 }
5622 if (ns == NULL) {
5623 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
5624 } else {
5625 xmlSetProp(cur, BAD_CAST "ns", ns);
5626 xmlFree(ns);
5627 }
5628 }
5629 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
5630 xmlChar *name, *local, *prefix;
5631
5632 /*
5633 * Simplification: 4.10. QNames
5634 */
5635 name = xmlNodeGetContent(cur);
5636 if (name != NULL) {
5637 local = xmlSplitQName2(name, &prefix);
5638 if (local != NULL) {
5639 xmlNsPtr ns;
5640
5641 ns = xmlSearchNs(cur->doc, cur, prefix);
5642 if (ns == NULL) {
5643 if (ctxt->error != NULL)
5644 ctxt->error(ctxt->userData,
5645 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
5646 ctxt->nbErrors++;
5647 } else {
5648 xmlSetProp(cur, BAD_CAST "ns", ns->href);
5649 xmlNodeSetContent(cur, local);
5650 }
5651 xmlFree(local);
5652 xmlFree(prefix);
5653 }
5654 xmlFree(name);
5655 }
5656 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005657 /*
5658 * 4.16
5659 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005660 if (xmlStrEqual(cur->name, BAD_CAST "nsName")) {
5661 if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5662 if (ctxt->error != NULL)
5663 ctxt->error(ctxt->userData,
5664 "Found nsName/except//nsName forbidden construct\n");
5665 ctxt->nbErrors++;
5666 }
5667 }
5668 } else if ((xmlStrEqual(cur->name, BAD_CAST "except")) &&
5669 (cur != root)) {
5670 int oldflags = ctxt->flags;
5671
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005672 /*
5673 * 4.16
5674 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005675 if ((cur->parent != NULL) &&
5676 (xmlStrEqual(cur->parent->name, BAD_CAST "anyName"))) {
5677 ctxt->flags |= XML_RELAXNG_IN_ANYEXCEPT;
5678 xmlRelaxNGCleanupTree(ctxt, cur);
5679 ctxt->flags = oldflags;
5680 goto skip_children;
5681 } else if ((cur->parent != NULL) &&
5682 (xmlStrEqual(cur->parent->name, BAD_CAST "nsName"))) {
5683 ctxt->flags |= XML_RELAXNG_IN_NSEXCEPT;
5684 xmlRelaxNGCleanupTree(ctxt, cur);
5685 ctxt->flags = oldflags;
5686 goto skip_children;
5687 }
5688 } else if (xmlStrEqual(cur->name, BAD_CAST "anyName")) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005689 /*
5690 * 4.16
5691 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005692 if (ctxt->flags & XML_RELAXNG_IN_ANYEXCEPT) {
5693 if (ctxt->error != NULL)
5694 ctxt->error(ctxt->userData,
5695 "Found anyName/except//anyName forbidden construct\n");
5696 ctxt->nbErrors++;
5697 } else if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5698 if (ctxt->error != NULL)
5699 ctxt->error(ctxt->userData,
5700 "Found nsName/except//anyName forbidden construct\n");
5701 ctxt->nbErrors++;
5702 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005703 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005704 /*
5705 * Thisd is not an else since "include" is transformed
5706 * into a div
5707 */
5708 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005709 xmlChar *ns;
5710 xmlNodePtr child, ins, tmp;
5711
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005712 /*
5713 * implements rule 4.11
5714 */
Daniel Veillard416589a2003-02-17 17:25:42 +00005715
5716 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005717
5718 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005719 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005720 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005721 if (ns != NULL) {
5722 if (!xmlHasProp(child, BAD_CAST "ns")) {
5723 xmlSetProp(child, BAD_CAST "ns", ns);
5724 }
5725 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005726 tmp = child->next;
5727 xmlUnlinkNode(child);
5728 ins = xmlAddNextSibling(ins, child);
5729 child = tmp;
5730 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005731 if (ns != NULL)
5732 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005733 delete = cur;
5734 goto skip_children;
5735 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005736 }
5737 }
5738 /*
5739 * Simplification 4.2 whitespaces
5740 */
5741 else if (cur->type == XML_TEXT_NODE) {
5742 if (IS_BLANK_NODE(cur)) {
5743 if (cur->parent->type == XML_ELEMENT_NODE) {
5744 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
5745 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
5746 delete = cur;
5747 } else {
5748 delete = cur;
5749 goto skip_children;
5750 }
5751 }
5752 } else if (cur->type != XML_CDATA_SECTION_NODE) {
5753 delete = cur;
5754 goto skip_children;
5755 }
5756
5757 /*
5758 * Skip to next node
5759 */
5760 if (cur->children != NULL) {
5761 if ((cur->children->type != XML_ENTITY_DECL) &&
5762 (cur->children->type != XML_ENTITY_REF_NODE) &&
5763 (cur->children->type != XML_ENTITY_NODE)) {
5764 cur = cur->children;
5765 continue;
5766 }
5767 }
5768skip_children:
5769 if (cur->next != NULL) {
5770 cur = cur->next;
5771 continue;
5772 }
5773
5774 do {
5775 cur = cur->parent;
5776 if (cur == NULL)
5777 break;
5778 if (cur == root) {
5779 cur = NULL;
5780 break;
5781 }
5782 if (cur->next != NULL) {
5783 cur = cur->next;
5784 break;
5785 }
5786 } while (cur != NULL);
5787 }
5788 if (delete != NULL) {
5789 xmlUnlinkNode(delete);
5790 xmlFreeNode(delete);
5791 delete = NULL;
5792 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00005793}
Daniel Veillard6eadf632003-01-23 18:29:16 +00005794
Daniel Veillardc5312d72003-02-21 17:14:10 +00005795/**
5796 * xmlRelaxNGCleanupDoc:
5797 * @ctxt: a Relax-NG parser context
5798 * @doc: an xmldocPtr document pointer
5799 *
5800 * Cleanup the document from unwanted nodes for parsing, resolve
5801 * Include and externalRef lookups.
5802 *
5803 * Returns the cleaned up document or NULL in case of error
5804 */
5805static xmlDocPtr
5806xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
5807 xmlNodePtr root;
5808
5809 /*
5810 * Extract the root
5811 */
5812 root = xmlDocGetRootElement(doc);
5813 if (root == NULL) {
5814 if (ctxt->error != NULL)
5815 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5816 ctxt->URL);
5817 ctxt->nbErrors++;
5818 return (NULL);
5819 }
5820 xmlRelaxNGCleanupTree(ctxt, root);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005821 return(doc);
5822}
5823
5824/**
5825 * xmlRelaxNGParse:
5826 * @ctxt: a Relax-NG parser context
5827 *
5828 * parse a schema definition resource and build an internal
5829 * XML Shema struture which can be used to validate instances.
5830 * *WARNING* this interface is highly subject to change
5831 *
5832 * Returns the internal XML RelaxNG structure built from the resource or
5833 * NULL in case of error
5834 */
5835xmlRelaxNGPtr
5836xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
5837{
5838 xmlRelaxNGPtr ret = NULL;
5839 xmlDocPtr doc;
5840 xmlNodePtr root;
5841
5842 xmlRelaxNGInitTypes();
5843
5844 if (ctxt == NULL)
5845 return (NULL);
5846
5847 /*
5848 * First step is to parse the input document into an DOM/Infoset
5849 */
5850 if (ctxt->URL != NULL) {
5851 doc = xmlParseFile((const char *) ctxt->URL);
5852 if (doc == NULL) {
5853 if (ctxt->error != NULL)
5854 ctxt->error(ctxt->userData,
5855 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
5856 ctxt->nbErrors++;
5857 return (NULL);
5858 }
5859 } else if (ctxt->buffer != NULL) {
5860 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
5861 if (doc == NULL) {
5862 if (ctxt->error != NULL)
5863 ctxt->error(ctxt->userData,
5864 "xmlRelaxNGParse: could not parse schemas\n");
5865 ctxt->nbErrors++;
5866 return (NULL);
5867 }
5868 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5869 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5870 } else {
5871 if (ctxt->error != NULL)
5872 ctxt->error(ctxt->userData,
5873 "xmlRelaxNGParse: nothing to parse\n");
5874 ctxt->nbErrors++;
5875 return (NULL);
5876 }
5877 ctxt->document = doc;
5878
5879 /*
5880 * Some preprocessing of the document content
5881 */
5882 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
5883 if (doc == NULL) {
5884 xmlFreeDoc(ctxt->document);
5885 ctxt->document = NULL;
5886 return(NULL);
5887 }
5888
Daniel Veillard6eadf632003-01-23 18:29:16 +00005889 /*
5890 * Then do the parsing for good
5891 */
5892 root = xmlDocGetRootElement(doc);
5893 if (root == NULL) {
5894 if (ctxt->error != NULL)
5895 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5896 ctxt->URL);
5897 ctxt->nbErrors++;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005898 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005899 return (NULL);
5900 }
5901 ret = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005902 if (ret == NULL) {
5903 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005904 return(NULL);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005905 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005906
5907 /*
5908 * Check the ref/defines links
5909 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005910 /*
5911 * try to preprocess interleaves
5912 */
5913 if (ctxt->interleaves != NULL) {
5914 xmlHashScan(ctxt->interleaves,
5915 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
5916 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005917
5918 /*
5919 * if there was a parsing error return NULL
5920 */
5921 if (ctxt->nbErrors > 0) {
5922 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005923 ctxt->document = NULL;
5924 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005925 return(NULL);
5926 }
5927
5928 /*
5929 * Transfer the pointer for cleanup at the schema level.
5930 */
5931 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005932 ctxt->document = NULL;
5933 ret->documents = ctxt->documents;
5934 ctxt->documents = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005935
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005936 ret->includes = ctxt->includes;
5937 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00005938 ret->defNr = ctxt->defNr;
5939 ret->defTab = ctxt->defTab;
5940 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005941
5942 return (ret);
5943}
5944
5945/**
5946 * xmlRelaxNGSetParserErrors:
5947 * @ctxt: a Relax-NG validation context
5948 * @err: the error callback
5949 * @warn: the warning callback
5950 * @ctx: contextual data for the callbacks
5951 *
5952 * Set the callback functions used to handle errors for a validation context
5953 */
5954void
5955xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
5956 xmlRelaxNGValidityErrorFunc err,
5957 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5958 if (ctxt == NULL)
5959 return;
5960 ctxt->error = err;
5961 ctxt->warning = warn;
5962 ctxt->userData = ctx;
5963}
5964/************************************************************************
5965 * *
5966 * Dump back a compiled form *
5967 * *
5968 ************************************************************************/
5969static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
5970
5971/**
5972 * xmlRelaxNGDumpDefines:
5973 * @output: the file output
5974 * @defines: a list of define structures
5975 *
5976 * Dump a RelaxNG structure back
5977 */
5978static void
5979xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
5980 while (defines != NULL) {
5981 xmlRelaxNGDumpDefine(output, defines);
5982 defines = defines->next;
5983 }
5984}
5985
5986/**
5987 * xmlRelaxNGDumpDefine:
5988 * @output: the file output
5989 * @define: a define structure
5990 *
5991 * Dump a RelaxNG structure back
5992 */
5993static void
5994xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
5995 if (define == NULL)
5996 return;
5997 switch(define->type) {
5998 case XML_RELAXNG_EMPTY:
5999 fprintf(output, "<empty/>\n");
6000 break;
6001 case XML_RELAXNG_NOT_ALLOWED:
6002 fprintf(output, "<notAllowed/>\n");
6003 break;
6004 case XML_RELAXNG_TEXT:
6005 fprintf(output, "<text/>\n");
6006 break;
6007 case XML_RELAXNG_ELEMENT:
6008 fprintf(output, "<element>\n");
6009 if (define->name != NULL) {
6010 fprintf(output, "<name");
6011 if (define->ns != NULL)
6012 fprintf(output, " ns=\"%s\"", define->ns);
6013 fprintf(output, ">%s</name>\n", define->name);
6014 }
6015 xmlRelaxNGDumpDefines(output, define->attrs);
6016 xmlRelaxNGDumpDefines(output, define->content);
6017 fprintf(output, "</element>\n");
6018 break;
6019 case XML_RELAXNG_LIST:
6020 fprintf(output, "<list>\n");
6021 xmlRelaxNGDumpDefines(output, define->content);
6022 fprintf(output, "</list>\n");
6023 break;
6024 case XML_RELAXNG_ONEORMORE:
6025 fprintf(output, "<oneOrMore>\n");
6026 xmlRelaxNGDumpDefines(output, define->content);
6027 fprintf(output, "</oneOrMore>\n");
6028 break;
6029 case XML_RELAXNG_ZEROORMORE:
6030 fprintf(output, "<zeroOrMore>\n");
6031 xmlRelaxNGDumpDefines(output, define->content);
6032 fprintf(output, "</zeroOrMore>\n");
6033 break;
6034 case XML_RELAXNG_CHOICE:
6035 fprintf(output, "<choice>\n");
6036 xmlRelaxNGDumpDefines(output, define->content);
6037 fprintf(output, "</choice>\n");
6038 break;
6039 case XML_RELAXNG_GROUP:
6040 fprintf(output, "<group>\n");
6041 xmlRelaxNGDumpDefines(output, define->content);
6042 fprintf(output, "</group>\n");
6043 break;
6044 case XML_RELAXNG_INTERLEAVE:
6045 fprintf(output, "<interleave>\n");
6046 xmlRelaxNGDumpDefines(output, define->content);
6047 fprintf(output, "</interleave>\n");
6048 break;
6049 case XML_RELAXNG_OPTIONAL:
6050 fprintf(output, "<optional>\n");
6051 xmlRelaxNGDumpDefines(output, define->content);
6052 fprintf(output, "</optional>\n");
6053 break;
6054 case XML_RELAXNG_ATTRIBUTE:
6055 fprintf(output, "<attribute>\n");
6056 xmlRelaxNGDumpDefines(output, define->content);
6057 fprintf(output, "</attribute>\n");
6058 break;
6059 case XML_RELAXNG_DEF:
6060 fprintf(output, "<define");
6061 if (define->name != NULL)
6062 fprintf(output, " name=\"%s\"", define->name);
6063 fprintf(output, ">\n");
6064 xmlRelaxNGDumpDefines(output, define->content);
6065 fprintf(output, "</define>\n");
6066 break;
6067 case XML_RELAXNG_REF:
6068 fprintf(output, "<ref");
6069 if (define->name != NULL)
6070 fprintf(output, " name=\"%s\"", define->name);
6071 fprintf(output, ">\n");
6072 xmlRelaxNGDumpDefines(output, define->content);
6073 fprintf(output, "</ref>\n");
6074 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00006075 case XML_RELAXNG_PARENTREF:
6076 fprintf(output, "<parentRef");
6077 if (define->name != NULL)
6078 fprintf(output, " name=\"%s\"", define->name);
6079 fprintf(output, ">\n");
6080 xmlRelaxNGDumpDefines(output, define->content);
6081 fprintf(output, "</parentRef>\n");
6082 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006083 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00006084 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00006085 xmlRelaxNGDumpDefines(output, define->content);
6086 fprintf(output, "</externalRef>\n");
6087 break;
6088 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006089 case XML_RELAXNG_VALUE:
6090 TODO
6091 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006092 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00006093 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00006094 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006095 TODO
6096 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00006097 case XML_RELAXNG_NOOP:
6098 xmlRelaxNGDumpDefines(output, define->content);
6099 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006100 }
6101}
6102
6103/**
6104 * xmlRelaxNGDumpGrammar:
6105 * @output: the file output
6106 * @grammar: a grammar structure
6107 * @top: is this a top grammar
6108 *
6109 * Dump a RelaxNG structure back
6110 */
6111static void
6112xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
6113{
6114 if (grammar == NULL)
6115 return;
6116
6117 fprintf(output, "<grammar");
6118 if (top)
6119 fprintf(output,
6120 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
6121 switch(grammar->combine) {
6122 case XML_RELAXNG_COMBINE_UNDEFINED:
6123 break;
6124 case XML_RELAXNG_COMBINE_CHOICE:
6125 fprintf(output, " combine=\"choice\"");
6126 break;
6127 case XML_RELAXNG_COMBINE_INTERLEAVE:
6128 fprintf(output, " combine=\"interleave\"");
6129 break;
6130 default:
6131 fprintf(output, " <!-- invalid combine value -->");
6132 }
6133 fprintf(output, ">\n");
6134 if (grammar->start == NULL) {
6135 fprintf(output, " <!-- grammar had no start -->");
6136 } else {
6137 fprintf(output, "<start>\n");
6138 xmlRelaxNGDumpDefine(output, grammar->start);
6139 fprintf(output, "</start>\n");
6140 }
6141 /* TODO ? Dump the defines ? */
6142 fprintf(output, "</grammar>\n");
6143}
6144
6145/**
6146 * xmlRelaxNGDump:
6147 * @output: the file output
6148 * @schema: a schema structure
6149 *
6150 * Dump a RelaxNG structure back
6151 */
6152void
6153xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
6154{
6155 if (schema == NULL) {
6156 fprintf(output, "RelaxNG empty or failed to compile\n");
6157 return;
6158 }
6159 fprintf(output, "RelaxNG: ");
6160 if (schema->doc == NULL) {
6161 fprintf(output, "no document\n");
6162 } else if (schema->doc->URL != NULL) {
6163 fprintf(output, "%s\n", schema->doc->URL);
6164 } else {
6165 fprintf(output, "\n");
6166 }
6167 if (schema->topgrammar == NULL) {
6168 fprintf(output, "RelaxNG has no top grammar\n");
6169 return;
6170 }
6171 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
6172}
6173
Daniel Veillardfebcca42003-02-16 15:44:18 +00006174/**
6175 * xmlRelaxNGDumpTree:
6176 * @output: the file output
6177 * @schema: a schema structure
6178 *
6179 * Dump the transformed RelaxNG tree.
6180 */
6181void
6182xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
6183{
6184 if (schema == NULL) {
6185 fprintf(output, "RelaxNG empty or failed to compile\n");
6186 return;
6187 }
6188 if (schema->doc == NULL) {
6189 fprintf(output, "no document\n");
6190 } else {
6191 xmlDocDump(output, schema->doc);
6192 }
6193}
6194
Daniel Veillard6eadf632003-01-23 18:29:16 +00006195/************************************************************************
6196 * *
6197 * Validation implementation *
6198 * *
6199 ************************************************************************/
6200static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
6201 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006202static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
6203 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006204
6205/**
6206 * xmlRelaxNGSkipIgnored:
6207 * @ctxt: a schema validation context
6208 * @node: the top node.
6209 *
6210 * Skip ignorable nodes in that context
6211 *
6212 * Returns the new sibling or NULL in case of error.
6213 */
6214static xmlNodePtr
6215xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
6216 xmlNodePtr node) {
6217 /*
6218 * TODO complete and handle entities
6219 */
6220 while ((node != NULL) &&
6221 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006222 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00006223 ((node->type == XML_TEXT_NODE) &&
6224 (IS_BLANK_NODE(node))))) {
6225 node = node->next;
6226 }
6227 return(node);
6228}
6229
6230/**
Daniel Veillardedc91922003-01-26 00:52:04 +00006231 * xmlRelaxNGNormalize:
6232 * @ctxt: a schema validation context
6233 * @str: the string to normalize
6234 *
6235 * Implements the normalizeWhiteSpace( s ) function from
6236 * section 6.2.9 of the spec
6237 *
6238 * Returns the new string or NULL in case of error.
6239 */
6240static xmlChar *
6241xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
6242 xmlChar *ret, *p;
6243 const xmlChar *tmp;
6244 int len;
6245
6246 if (str == NULL)
6247 return(NULL);
6248 tmp = str;
6249 while (*tmp != 0) tmp++;
6250 len = tmp - str;
6251
6252 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
6253 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00006254 if (ctxt != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006255 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardea3f3982003-01-26 19:45:18 +00006256 } else {
6257 xmlGenericError(xmlGenericErrorContext,
6258 "xmlRelaxNGNormalize: out of memory\n");
6259 }
Daniel Veillardedc91922003-01-26 00:52:04 +00006260 return(NULL);
6261 }
6262 p = ret;
6263 while (IS_BLANK(*str)) str++;
6264 while (*str != 0) {
6265 if (IS_BLANK(*str)) {
6266 while (IS_BLANK(*str)) str++;
6267 if (*str == 0)
6268 break;
6269 *p++ = ' ';
6270 } else
6271 *p++ = *str++;
6272 }
6273 *p = 0;
6274 return(ret);
6275}
6276
6277/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006278 * xmlRelaxNGValidateDatatype:
6279 * @ctxt: a Relax-NG validation context
6280 * @value: the string value
6281 * @type: the datatype definition
6282 *
6283 * Validate the given value against the dataype
6284 *
6285 * Returns 0 if the validation succeeded or an error code.
6286 */
6287static int
6288xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
6289 xmlRelaxNGDefinePtr define) {
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006290 int ret, tmp;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006291 xmlRelaxNGTypeLibraryPtr lib;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006292 void *result = NULL;
6293 xmlRelaxNGDefinePtr cur;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006294
6295 if ((define == NULL) || (define->data == NULL)) {
6296 return(-1);
6297 }
6298 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006299 if (lib->check != NULL) {
6300 if ((define->attrs != NULL) &&
6301 (define->attrs->type == XML_RELAXNG_PARAM)) {
6302 ret = lib->check(lib->data, define->name, value, &result);
6303 } else {
6304 ret = lib->check(lib->data, define->name, value, NULL);
6305 }
6306 } else
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006307 ret = -1;
6308 if (ret < 0) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006309 VALID_ERR2(XML_RELAXNG_ERR_TYPE, define->name);
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006310 if ((result != NULL) && (lib != NULL) && (lib->freef != NULL))
6311 lib->freef(lib->data, result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006312 return(-1);
6313 } else if (ret == 1) {
6314 ret = 0;
6315 } else {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006316 VALID_ERR3(XML_RELAXNG_ERR_TYPEVAL, define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006317 ret = -1;
6318 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006319 cur = define->attrs;
6320 while ((ret == 0) && (cur != NULL) && (cur->type == XML_RELAXNG_PARAM)) {
6321 if (lib->facet != NULL) {
6322 tmp = lib->facet(lib->data, define->name, cur->name,
6323 cur->value, value, result);
6324 if (tmp != 0)
6325 ret = -1;
6326 }
6327 cur = cur->next;
6328 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006329 if ((ret == 0) && (define->content != NULL)) {
6330 const xmlChar *oldvalue, *oldendvalue;
6331
6332 oldvalue = ctxt->state->value;
6333 oldendvalue = ctxt->state->endvalue;
6334 ctxt->state->value = (xmlChar *) value;
6335 ctxt->state->endvalue = NULL;
6336 ret = xmlRelaxNGValidateValue(ctxt, define->content);
6337 ctxt->state->value = (xmlChar *) oldvalue;
6338 ctxt->state->endvalue = (xmlChar *) oldendvalue;
6339 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006340 if ((result != NULL) && (lib != NULL) && (lib->freef != NULL))
6341 lib->freef(lib->data, result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006342 return(ret);
6343}
6344
6345/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006346 * xmlRelaxNGNextValue:
6347 * @ctxt: a Relax-NG validation context
6348 *
6349 * Skip to the next value when validating within a list
6350 *
6351 * Returns 0 if the operation succeeded or an error code.
6352 */
6353static int
6354xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
6355 xmlChar *cur;
6356
6357 cur = ctxt->state->value;
6358 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
6359 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006360 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006361 return(0);
6362 }
6363 while (*cur != 0) cur++;
6364 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
6365 if (cur == ctxt->state->endvalue)
6366 ctxt->state->value = NULL;
6367 else
6368 ctxt->state->value = cur;
6369 return(0);
6370}
6371
6372/**
6373 * xmlRelaxNGValidateValueList:
6374 * @ctxt: a Relax-NG validation context
6375 * @defines: the list of definitions to verify
6376 *
6377 * Validate the given set of definitions for the current value
6378 *
6379 * Returns 0 if the validation succeeded or an error code.
6380 */
6381static int
6382xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
6383 xmlRelaxNGDefinePtr defines) {
6384 int ret = 0;
6385
6386 while (defines != NULL) {
6387 ret = xmlRelaxNGValidateValue(ctxt, defines);
6388 if (ret != 0)
6389 break;
6390 defines = defines->next;
6391 }
6392 return(ret);
6393}
6394
6395/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006396 * xmlRelaxNGValidateValue:
6397 * @ctxt: a Relax-NG validation context
6398 * @define: the definition to verify
6399 *
6400 * Validate the given definition for the current value
6401 *
6402 * Returns 0 if the validation succeeded or an error code.
6403 */
6404static int
6405xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
6406 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00006407 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006408 xmlChar *value;
6409
6410 value = ctxt->state->value;
6411 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006412 case XML_RELAXNG_EMPTY: {
6413 if ((value != NULL) && (value[0] != 0)) {
6414 int idx = 0;
6415
6416 while (IS_BLANK(value[idx]))
6417 idx++;
6418 if (value[idx] != 0)
6419 ret = -1;
6420 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006421 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00006422 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006423 case XML_RELAXNG_TEXT:
6424 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00006425 case XML_RELAXNG_VALUE: {
6426 if (!xmlStrEqual(value, define->value)) {
6427 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00006428 xmlRelaxNGTypeLibraryPtr lib;
6429
6430 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
6431 if ((lib != NULL) && (lib->comp != NULL))
6432 ret = lib->comp(lib->data, define->name, value,
6433 define->value);
6434 else
6435 ret = -1;
6436 if (ret < 0) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006437 VALID_ERR2(XML_RELAXNG_ERR_TYPECMP, define->name);
Daniel Veillardea3f3982003-01-26 19:45:18 +00006438 return(-1);
6439 } else if (ret == 1) {
6440 ret = 0;
6441 } else {
6442 ret = -1;
6443 }
Daniel Veillardedc91922003-01-26 00:52:04 +00006444 } else {
6445 xmlChar *nval, *nvalue;
6446
6447 /*
6448 * TODO: trivial optimizations are possible by
6449 * computing at compile-time
6450 */
6451 nval = xmlRelaxNGNormalize(ctxt, define->value);
6452 nvalue = xmlRelaxNGNormalize(ctxt, value);
6453
Daniel Veillardea3f3982003-01-26 19:45:18 +00006454 if ((nval == NULL) || (nvalue == NULL) ||
6455 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00006456 ret = -1;
6457 if (nval != NULL)
6458 xmlFree(nval);
6459 if (nvalue != NULL)
6460 xmlFree(nvalue);
6461 }
6462 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006463 if (ret == 0)
6464 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00006465 break;
6466 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006467 case XML_RELAXNG_DATATYPE: {
6468 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
6469 if (ret == 0)
6470 xmlRelaxNGNextValue(ctxt);
6471
6472 break;
6473 }
6474 case XML_RELAXNG_CHOICE: {
6475 xmlRelaxNGDefinePtr list = define->content;
6476 xmlChar *oldvalue;
6477
6478 oldflags = ctxt->flags;
6479 ctxt->flags |= FLAGS_IGNORABLE;
6480
6481 oldvalue = ctxt->state->value;
6482 while (list != NULL) {
6483 ret = xmlRelaxNGValidateValue(ctxt, list);
6484 if (ret == 0) {
6485 break;
6486 }
6487 ctxt->state->value = oldvalue;
6488 list = list->next;
6489 }
6490 ctxt->flags = oldflags;
Daniel Veillard42f12e92003-03-07 18:32:59 +00006491 if (ret != 0) {
6492 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
6493 xmlRelaxNGDumpValidError(ctxt);
6494 } else {
6495 ctxt->errNr = 0;
6496 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006497 if (ret == 0)
6498 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006499 break;
6500 }
6501 case XML_RELAXNG_LIST: {
6502 xmlRelaxNGDefinePtr list = define->content;
6503 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00006504#ifdef DEBUG_LIST
6505 int nb_values = 0;
6506#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006507
6508 oldvalue = ctxt->state->value;
6509 oldend = ctxt->state->endvalue;
6510
6511 val = xmlStrdup(oldvalue);
6512 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006513 val = xmlStrdup(BAD_CAST "");
6514 }
6515 if (val == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006516 VALID_ERR(XML_RELAXNG_ERR_NOSTATE);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006517 return(-1);
6518 }
6519 cur = val;
6520 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006521 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006522 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006523 cur++;
6524#ifdef DEBUG_LIST
6525 nb_values++;
6526#endif
6527 while (IS_BLANK(*cur))
6528 *cur++ = 0;
6529 } else
6530 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006531 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006532#ifdef DEBUG_LIST
6533 xmlGenericError(xmlGenericErrorContext,
6534 "list value: '%s' found %d items\n", oldvalue, nb_values);
6535 nb_values = 0;
6536#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006537 ctxt->state->endvalue = cur;
6538 cur = val;
6539 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
6540
6541 ctxt->state->value = cur;
6542
6543 while (list != NULL) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006544 if (ctxt->state->value == ctxt->state->endvalue)
6545 ctxt->state->value = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006546 ret = xmlRelaxNGValidateValue(ctxt, list);
6547 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006548#ifdef DEBUG_LIST
6549 xmlGenericError(xmlGenericErrorContext,
6550 "Failed to validate value: '%s' with %d rule\n",
6551 ctxt->state->value, nb_values);
6552#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006553 break;
6554 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006555#ifdef DEBUG_LIST
6556 nb_values++;
6557#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006558 list = list->next;
6559 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006560
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006561 if ((ret == 0) && (ctxt->state->value != NULL) &&
6562 (ctxt->state->value != ctxt->state->endvalue)) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006563 VALID_ERR2(XML_RELAXNG_ERR_LISTEXTRA, ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006564 ret = -1;
6565 }
6566 xmlFree(val);
6567 ctxt->state->value = oldvalue;
6568 ctxt->state->endvalue = oldend;
6569 break;
6570 }
6571 case XML_RELAXNG_ONEORMORE:
6572 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6573 if (ret != 0) {
6574 break;
6575 }
6576 /* no break on purpose */
6577 case XML_RELAXNG_ZEROORMORE: {
6578 xmlChar *cur, *temp;
6579
6580 oldflags = ctxt->flags;
6581 ctxt->flags |= FLAGS_IGNORABLE;
6582 cur = ctxt->state->value;
6583 temp = NULL;
6584 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
6585 (temp != cur)) {
6586 temp = cur;
6587 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6588 if (ret != 0) {
6589 ctxt->state->value = temp;
6590 ret = 0;
6591 break;
6592 }
6593 cur = ctxt->state->value;
6594 }
6595 ctxt->flags = oldflags;
Daniel Veillard42f12e92003-03-07 18:32:59 +00006596 if (ret != 0) {
6597 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
6598 xmlRelaxNGDumpValidError(ctxt);
6599 } else {
6600 ctxt->errNr = 0;
6601 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006602 break;
6603 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006604 case XML_RELAXNG_EXCEPT: {
6605 xmlRelaxNGDefinePtr list;
6606
6607 list = define->content;
6608 while (list != NULL) {
6609 ret = xmlRelaxNGValidateValue(ctxt, list);
6610 if (ret == 0) {
6611 ret = -1;
6612 break;
6613 } else
6614 ret = 0;
6615 list = list->next;
6616 }
6617 break;
6618 }
Daniel Veillard463a5472003-02-27 21:30:32 +00006619 case XML_RELAXNG_DEF:
Daniel Veillardd4310742003-02-18 21:12:46 +00006620 case XML_RELAXNG_GROUP: {
6621 xmlRelaxNGDefinePtr list;
6622
6623 list = define->content;
6624 while (list != NULL) {
6625 ret = xmlRelaxNGValidateValue(ctxt, list);
6626 if (ret != 0) {
6627 ret = -1;
6628 break;
6629 } else
6630 ret = 0;
6631 list = list->next;
6632 }
6633 break;
6634 }
Daniel Veillard463a5472003-02-27 21:30:32 +00006635 case XML_RELAXNG_REF:
6636 case XML_RELAXNG_PARENTREF:
6637 ret = xmlRelaxNGValidateValue(ctxt, define->content);
6638 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006639 default:
6640 TODO
6641 ret = -1;
6642 }
6643 return(ret);
6644}
6645
6646/**
6647 * xmlRelaxNGValidateValueContent:
6648 * @ctxt: a Relax-NG validation context
6649 * @defines: the list of definitions to verify
6650 *
6651 * Validate the given definitions for the current value
6652 *
6653 * Returns 0 if the validation succeeded or an error code.
6654 */
6655static int
6656xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
6657 xmlRelaxNGDefinePtr defines) {
6658 int ret = 0;
6659
6660 while (defines != NULL) {
6661 ret = xmlRelaxNGValidateValue(ctxt, defines);
6662 if (ret != 0)
6663 break;
6664 defines = defines->next;
6665 }
6666 return(ret);
6667}
6668
6669/**
Daniel Veillard144fae12003-02-03 13:17:57 +00006670 * xmlRelaxNGAttributeMatch:
6671 * @ctxt: a Relax-NG validation context
6672 * @define: the definition to check
6673 * @prop: the attribute
6674 *
6675 * Check if the attribute matches the definition nameClass
6676 *
6677 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
6678 */
6679static int
6680xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
6681 xmlRelaxNGDefinePtr define,
6682 xmlAttrPtr prop) {
6683 int ret;
6684
6685 if (define->name != NULL) {
6686 if (!xmlStrEqual(define->name, prop->name))
6687 return(0);
6688 }
6689 if (define->ns != NULL) {
6690 if (define->ns[0] == 0) {
6691 if (prop->ns != NULL)
6692 return(0);
6693 } else {
6694 if ((prop->ns == NULL) ||
6695 (!xmlStrEqual(define->ns, prop->ns->href)))
6696 return(0);
6697 }
6698 }
6699 if (define->nameClass == NULL)
6700 return(1);
6701 define = define->nameClass;
6702 if (define->type == XML_RELAXNG_EXCEPT) {
6703 xmlRelaxNGDefinePtr list;
6704
6705 list = define->content;
6706 while (list != NULL) {
6707 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
6708 if (ret == 1)
6709 return(0);
6710 if (ret < 0)
6711 return(ret);
6712 list = list->next;
6713 }
6714 } else {
6715 TODO
6716 }
6717 return(1);
6718}
6719
6720/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006721 * xmlRelaxNGValidateAttribute:
6722 * @ctxt: a Relax-NG validation context
6723 * @define: the definition to verify
6724 *
6725 * Validate the given attribute definition for that node
6726 *
6727 * Returns 0 if the validation succeeded or an error code.
6728 */
6729static int
6730xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
6731 xmlRelaxNGDefinePtr define) {
6732 int ret = 0, i;
6733 xmlChar *value, *oldvalue;
6734 xmlAttrPtr prop = NULL, tmp;
6735
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006736 if (ctxt->state->nbAttrLeft <= 0)
6737 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006738 if (define->name != NULL) {
6739 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6740 tmp = ctxt->state->attrs[i];
6741 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
6742 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
6743 (tmp->ns == NULL)) ||
6744 ((tmp->ns != NULL) &&
6745 (xmlStrEqual(define->ns, tmp->ns->href)))) {
6746 prop = tmp;
6747 break;
6748 }
6749 }
6750 }
6751 if (prop != NULL) {
6752 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6753 oldvalue = ctxt->state->value;
6754 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00006755 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006756 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006757 if (ctxt->state->value != NULL)
6758 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006759 if (value != NULL)
6760 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006761 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006762 if (ret == 0) {
6763 /*
6764 * flag the attribute as processed
6765 */
6766 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006767 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006768 }
6769 } else {
6770 ret = -1;
6771 }
6772#ifdef DEBUG
6773 xmlGenericError(xmlGenericErrorContext,
6774 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
6775#endif
6776 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006777 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6778 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00006779 if ((tmp != NULL) &&
6780 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006781 prop = tmp;
6782 break;
6783 }
6784 }
6785 if (prop != NULL) {
6786 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6787 oldvalue = ctxt->state->value;
6788 ctxt->state->value = value;
6789 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006790 if (ctxt->state->value != NULL)
6791 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006792 if (value != NULL)
6793 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006794 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006795 if (ret == 0) {
6796 /*
6797 * flag the attribute as processed
6798 */
6799 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006800 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006801 }
6802 } else {
6803 ret = -1;
6804 }
6805#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00006806 if (define->ns != NULL) {
6807 xmlGenericError(xmlGenericErrorContext,
6808 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
6809 define->ns, ret);
6810 } else {
6811 xmlGenericError(xmlGenericErrorContext,
6812 "xmlRelaxNGValidateAttribute(anyName): %d\n",
6813 ret);
6814 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006815#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006816 }
6817
6818 return(ret);
6819}
6820
6821/**
6822 * xmlRelaxNGValidateAttributeList:
6823 * @ctxt: a Relax-NG validation context
6824 * @define: the list of definition to verify
6825 *
6826 * Validate the given node against the list of attribute definitions
6827 *
6828 * Returns 0 if the validation succeeded or an error code.
6829 */
6830static int
6831xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
6832 xmlRelaxNGDefinePtr defines) {
6833 int ret = 0;
6834 while (defines != NULL) {
6835 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
6836 ret = -1;
6837 defines = defines->next;
6838 }
6839 return(ret);
6840}
6841
6842/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006843 * xmlRelaxNGNodeMatchesList:
6844 * @node: the node
6845 * @list: a NULL terminated array of definitions
6846 *
6847 * Check if a node can be matched by one of the definitions
6848 *
6849 * Returns 1 if matches 0 otherwise
6850 */
6851static int
6852xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
6853 xmlRelaxNGDefinePtr cur;
6854 int i = 0;
6855
6856 if ((node == NULL) || (list == NULL))
6857 return(0);
6858
6859 cur = list[i++];
6860 while (cur != NULL) {
6861 if ((node->type == XML_ELEMENT_NODE) &&
6862 (cur->type == XML_RELAXNG_ELEMENT)) {
6863 if (cur->name == NULL) {
6864 if ((node->ns != NULL) &&
6865 (xmlStrEqual(node->ns->href, cur->ns)))
6866 return(1);
6867 } else if (xmlStrEqual(cur->name, node->name)) {
6868 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
6869 if (node->ns == NULL)
6870 return(1);
6871 } else {
6872 if ((node->ns != NULL) &&
6873 (xmlStrEqual(node->ns->href, cur->ns)))
6874 return(1);
6875 }
6876 }
6877 } else if ((node->type == XML_TEXT_NODE) &&
6878 (cur->type == XML_RELAXNG_TEXT)) {
6879 return(1);
6880 }
6881 cur = list[i++];
6882 }
6883 return(0);
6884}
6885
6886/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006887 * xmlRelaxNGValidateInterleave:
6888 * @ctxt: a Relax-NG validation context
6889 * @define: the definition to verify
6890 *
6891 * Validate an interleave definition for a node.
6892 *
6893 * Returns 0 if the validation succeeded or an error code.
6894 */
6895static int
6896xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
6897 xmlRelaxNGDefinePtr define) {
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006898 int ret = 0, i, nbgroups, left;
Daniel Veillard42f12e92003-03-07 18:32:59 +00006899 int errNr = ctxt->errNr;
6900
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006901 xmlRelaxNGPartitionPtr partitions;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006902 xmlRelaxNGInterleaveGroupPtr group = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006903 xmlNodePtr cur, start, last = NULL, lastchg = NULL, lastelem;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006904 xmlNodePtr *list = NULL, *lasts = NULL;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006905
6906 if (define->data != NULL) {
6907 partitions = (xmlRelaxNGPartitionPtr) define->data;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006908 nbgroups = partitions->nbgroups;
6909 left = nbgroups;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006910 } else {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006911 VALID_ERR(XML_RELAXNG_ERR_INTERNODATA);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006912 return(-1);
6913 }
6914
6915 /*
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006916 * Build arrays to store the first and last node of the chain
6917 * pertaining to each group
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006918 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006919 list = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
6920 if (list == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006921 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006922 return(-1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006923 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006924 memset(list, 0, nbgroups * sizeof(xmlNodePtr));
6925 lasts = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
6926 if (lasts == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006927 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006928 return(-1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006929 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006930 memset(lasts, 0, nbgroups * sizeof(xmlNodePtr));
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006931
6932 /*
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006933 * Walk the sequence of children finding the right group and
6934 * sorting them in sequences.
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006935 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006936 cur = ctxt->state->seq;
6937 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
6938 start = cur;
6939 while (cur != NULL) {
6940 ctxt->state->seq = cur;
6941 for (i = 0;i < nbgroups;i++) {
6942 group = partitions->groups[i];
6943 if (group == NULL)
6944 continue;
6945 if (xmlRelaxNGNodeMatchesList(cur, group->defs))
6946 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006947 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006948 /*
6949 * We break as soon as an element not matched is found
6950 */
6951 if (i >= nbgroups) {
6952 break;
6953 }
6954 if (lasts[i] != NULL) {
6955 lasts[i]->next = cur;
6956 lasts[i] = cur;
6957 } else {
6958 list[i] = cur;
6959 lasts[i] = cur;
6960 }
6961 if (cur->next != NULL)
6962 lastchg = cur->next;
6963 else
6964 lastchg = cur;
6965 cur = xmlRelaxNGSkipIgnored(ctxt, cur->next);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006966 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006967 if (ret != 0) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006968 VALID_ERR(XML_RELAXNG_ERR_INTERSEQ);
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006969 ret = -1;
6970 goto done;
6971 }
6972 lastelem = cur;
6973 for (i = 0;i < nbgroups;i++) {
6974 group = partitions->groups[i];
6975 if (lasts[i] != NULL) {
6976 last = lasts[i]->next;
6977 lasts[i]->next = NULL;
6978 }
6979 ctxt->state->seq = list[i];
6980 ret = xmlRelaxNGValidateDefinition(ctxt, group->rule);
6981 if (ret != 0)
6982 break;
6983 cur = ctxt->state->seq;
6984 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
6985 if (cur != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006986 VALID_ERR2(XML_RELAXNG_ERR_INTEREXTRA, cur->name);
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006987 ret = -1;
6988 goto done;
6989 }
6990 if (lasts[i] != NULL) {
6991 lasts[i]->next = last;
6992 }
6993 }
6994 ctxt->state->seq = lastelem;
6995 if (ret != 0) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006996 VALID_ERR(XML_RELAXNG_ERR_INTERSEQ);
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006997 ret = -1;
6998 goto done;
6999 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00007000
Daniel Veillardc64b8e92003-02-24 11:47:13 +00007001done:
7002 /*
7003 * builds the next links chain from the prev one
7004 */
7005 cur = lastchg;
7006 while (cur != NULL) {
7007 if ((cur == start) || (cur->prev == NULL))
7008 break;
7009 cur->prev->next = cur;
7010 cur = cur->prev;
7011 }
Daniel Veillard42f12e92003-03-07 18:32:59 +00007012 if (ret == 0) {
7013 ctxt->errNr = errNr;
7014 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00007015
7016 xmlFree(list);
7017 xmlFree(lasts);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00007018 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00007019}
7020
7021/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00007022 * xmlRelaxNGValidateElementContent:
7023 * @ctxt: a Relax-NG validation context
7024 * @define: the list of definition to verify
7025 *
7026 * Validate the given node content against the (list) of definitions
7027 *
7028 * Returns 0 if the validation succeeded or an error code.
7029 */
7030static int
7031xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
7032 xmlRelaxNGDefinePtr defines) {
7033 int ret = 0, res;
7034
7035 if (ctxt->state == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007036 VALID_ERR(XML_RELAXNG_ERR_NOSTATE);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007037 return(-1);
7038 }
7039 while (defines != NULL) {
7040 res = xmlRelaxNGValidateDefinition(ctxt, defines);
7041 if (res < 0)
7042 ret = -1;
7043 defines = defines->next;
7044 }
7045
7046 return(ret);
7047}
7048
7049/**
Daniel Veillard416589a2003-02-17 17:25:42 +00007050 * xmlRelaxNGElementMatch:
7051 * @ctxt: a Relax-NG validation context
7052 * @define: the definition to check
7053 * @elem: the element
7054 *
7055 * Check if the element matches the definition nameClass
7056 *
7057 * Returns 1 if the element matches, 0 if no, or -1 in case of error
7058 */
7059static int
7060xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
7061 xmlRelaxNGDefinePtr define,
7062 xmlNodePtr elem) {
7063 int ret, oldflags;
7064
7065 if (define->name != NULL) {
7066 if (!xmlStrEqual(elem->name, define->name)) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007067 VALID_ERR3(XML_RELAXNG_ERR_ELEMNAME, define->name, elem->name);
Daniel Veillard416589a2003-02-17 17:25:42 +00007068 return(0);
7069 }
7070 }
7071 if ((define->ns != NULL) && (define->ns[0] != 0)) {
7072 if (elem->ns == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007073 VALID_ERR2(XML_RELAXNG_ERR_ELEMNONS,
Daniel Veillard416589a2003-02-17 17:25:42 +00007074 elem->name);
7075 return(0);
7076 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007077 VALID_ERR3(XML_RELAXNG_ERR_ELEMWRONGNS,
Daniel Veillard416589a2003-02-17 17:25:42 +00007078 elem->name, define->ns);
7079 return(0);
7080 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00007081 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
7082 (define->name == NULL)) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007083 VALID_ERR2(XML_RELAXNG_ERR_ELEMEXTRANS,
7084 elem->name);
Daniel Veillard8fe98712003-02-19 00:19:14 +00007085 return(0);
7086 } else if ((elem->ns != NULL) && (define->name != NULL)) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007087 VALID_ERR2(XML_RELAXNG_ERR_ELEMEXTRANS,
Daniel Veillard8fe98712003-02-19 00:19:14 +00007088 define->name);
7089 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00007090 }
7091
7092 if (define->nameClass == NULL)
7093 return(1);
7094
7095 define = define->nameClass;
7096 if (define->type == XML_RELAXNG_EXCEPT) {
7097 xmlRelaxNGDefinePtr list;
7098 oldflags = ctxt->flags;
7099 ctxt->flags |= FLAGS_IGNORABLE;
7100
7101 list = define->content;
7102 while (list != NULL) {
7103 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
7104 if (ret == 1) {
7105 ctxt->flags = oldflags;
7106 return(0);
7107 }
7108 if (ret < 0) {
7109 ctxt->flags = oldflags;
7110 return(ret);
7111 }
7112 list = list->next;
7113 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007114 ret = 1;
7115 ctxt->flags = oldflags;
7116 } else if (define->type == XML_RELAXNG_CHOICE) {
7117 xmlRelaxNGDefinePtr list;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007118
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007119 oldflags = ctxt->flags;
7120 ctxt->flags |= FLAGS_IGNORABLE;
7121
7122 list = define->nameClass;
7123 while (list != NULL) {
7124 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
7125 if (ret == 1) {
7126 ctxt->flags = oldflags;
7127 return(1);
7128 }
7129 if (ret < 0) {
7130 ctxt->flags = oldflags;
7131 return(ret);
7132 }
7133 list = list->next;
7134 }
Daniel Veillard42f12e92003-03-07 18:32:59 +00007135 if (ret != 0) {
7136 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7137 xmlRelaxNGDumpValidError(ctxt);
7138 } else {
7139 ctxt->errNr = 0;
7140 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007141 ret = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00007142 ctxt->flags = oldflags;
7143 } else {
7144 TODO
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007145 ret = -1;
Daniel Veillard416589a2003-02-17 17:25:42 +00007146 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007147 return(ret);
Daniel Veillard416589a2003-02-17 17:25:42 +00007148}
7149
7150/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00007151 * xmlRelaxNGValidateDefinition:
7152 * @ctxt: a Relax-NG validation context
7153 * @define: the definition to verify
7154 *
7155 * Validate the current node against the definition
7156 *
7157 * Returns 0 if the validation succeeded or an error code.
7158 */
7159static int
7160xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
7161 xmlRelaxNGDefinePtr define) {
7162 xmlNodePtr node;
7163 int ret = 0, i, tmp, oldflags;
7164 xmlRelaxNGValidStatePtr oldstate, state;
7165
7166 if (define == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007167 VALID_ERR(XML_RELAXNG_ERR_NODEFINE);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007168 return(-1);
7169 }
7170 if (ctxt->state != NULL) {
7171 node = ctxt->state->seq;
7172 } else {
7173 node = NULL;
7174 }
Daniel Veillard231d7912003-02-09 14:22:17 +00007175#ifdef DEBUG
7176 for (i = 0;i < ctxt->depth;i++)
7177 xmlGenericError(xmlGenericErrorContext, " ");
7178 xmlGenericError(xmlGenericErrorContext,
7179 "Start validating %s ", xmlRelaxNGDefName(define));
7180 if (define->name != NULL)
7181 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
7182 if ((node != NULL) && (node->name != NULL))
7183 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
7184 else
7185 xmlGenericError(xmlGenericErrorContext, "\n");
7186#endif
7187 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007188 switch (define->type) {
7189 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00007190 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007191 if (node != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007192 VALID_ERR2(XML_RELAXNG_ERR_ELEMNOTEMPTY,
7193 ctxt->state->node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00007194 ret = -1;
7195 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007196 }
Daniel Veillard231d7912003-02-09 14:22:17 +00007197 ret = 0;
7198 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007199 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00007200 ret = -1;
7201 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007202 case XML_RELAXNG_TEXT:
Daniel Veillard6eadf632003-01-23 18:29:16 +00007203 while ((node != NULL) &&
7204 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007205 (node->type == XML_COMMENT_NODE) ||
7206 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00007207 (node->type == XML_CDATA_SECTION_NODE)))
7208 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00007209 ctxt->state->seq = node;
7210 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007211 case XML_RELAXNG_ELEMENT:
7212 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00007213 if (node == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007214 VALID_ERR2(XML_RELAXNG_ERR_NOELEM, define->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00007215 ret = -1;
7216 break;
7217 }
7218 if (node->type != XML_ELEMENT_NODE) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007219 VALID_ERR(XML_RELAXNG_ERR_NOTELEM);
Daniel Veillard231d7912003-02-09 14:22:17 +00007220 ret = -1;
7221 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007222 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00007223 /*
7224 * This node was already validated successfully against
7225 * this definition.
7226 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00007227 if (node->_private == define) {
7228 ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
Daniel Veillarde5b110b2003-02-04 14:43:39 +00007229 break;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00007230 }
Daniel Veillard416589a2003-02-17 17:25:42 +00007231
7232 ret = xmlRelaxNGElementMatch(ctxt, define, node);
7233 if (ret <= 0) {
7234 ret = -1;
7235 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007236 }
Daniel Veillard416589a2003-02-17 17:25:42 +00007237 ret = 0;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007238 if (ctxt->errNr != 0) {
7239 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7240 ctxt->errNr = 0;
7241 else {
7242 while ((ctxt->err != NULL) &&
7243 (ctxt->err->err == XML_RELAXNG_ERR_ELEMNAME) &&
7244 (xmlStrEqual(ctxt->err->arg2, node->name)))
7245 xmlRelaxNGValidErrorPop(ctxt);
7246 }
7247 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007248
7249 state = xmlRelaxNGNewValidState(ctxt, node);
7250 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00007251 ret = -1;
7252 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007253 }
7254
7255 oldstate = ctxt->state;
7256 ctxt->state = state;
7257 if (define->attrs != NULL) {
7258 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00007259 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00007260 ret = -1;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007261 VALID_ERR2(XML_RELAXNG_ERR_ATTRVALID, node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00007262 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007263 }
7264 if (define->content != NULL) {
7265 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00007266 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00007267 ret = -1;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007268 VALID_ERR2(XML_RELAXNG_ERR_CONTENTVALID, node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00007269 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007270 }
7271 state = ctxt->state;
7272 if (state->seq != NULL) {
7273 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
7274 if (state->seq != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007275 VALID_ERR3(XML_RELAXNG_ERR_EXTRACONTENT,
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007276 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007277 ret = -1;
7278 }
7279 }
7280 for (i = 0;i < state->nbAttrs;i++) {
7281 if (state->attrs[i] != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007282 VALID_ERR3(XML_RELAXNG_ERR_INVALIDATTR,
Daniel Veillard6eadf632003-01-23 18:29:16 +00007283 state->attrs[i]->name, node->name);
7284 ret = -1;
7285 }
7286 }
7287 ctxt->state = oldstate;
7288 xmlRelaxNGFreeValidState(state);
7289 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00007290 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
Daniel Veillard42f12e92003-03-07 18:32:59 +00007291 if (ret == 0) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00007292 node->_private = define;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007293 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007294
7295
7296#ifdef DEBUG
7297 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00007298 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00007299 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00007300 if (oldstate == NULL)
7301 xmlGenericError(xmlGenericErrorContext, ": no state\n");
7302 else if (oldstate->seq == NULL)
7303 xmlGenericError(xmlGenericErrorContext, ": done\n");
7304 else if (oldstate->seq->type == XML_ELEMENT_NODE)
7305 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
7306 oldstate->seq->name);
7307 else
7308 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
7309 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007310#endif
7311 break;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007312 case XML_RELAXNG_OPTIONAL: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00007313 oldflags = ctxt->flags;
7314 ctxt->flags |= FLAGS_IGNORABLE;
7315 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
7316 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7317 if (ret != 0) {
7318 xmlRelaxNGFreeValidState(ctxt->state);
7319 ctxt->state = oldstate;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007320 ctxt->flags = oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007321 ret = 0;
7322 break;
7323 }
7324 xmlRelaxNGFreeValidState(oldstate);
7325 ctxt->flags = oldflags;
7326 ret = 0;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007327 ctxt->errNr = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007328 break;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007329 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007330 case XML_RELAXNG_ONEORMORE:
7331 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7332 if (ret != 0) {
7333 break;
7334 }
7335 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00007336 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00007337 oldflags = ctxt->flags;
7338 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007339 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00007340 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
7341 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7342 if (ret != 0) {
7343 xmlRelaxNGFreeValidState(ctxt->state);
7344 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007345 break;
7346 }
7347 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007348 }
7349 if (ret == 0) {
7350 /*
7351 * There is no attribute left to be consumed,
7352 * we can check the closure by looking at ctxt->state->seq
7353 */
7354 xmlNodePtr cur, temp;
7355
Daniel Veillard276be4a2003-01-24 01:03:34 +00007356 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007357 temp = NULL;
7358 while ((cur != NULL) && (temp != cur)) {
7359 temp = cur;
7360 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
7361 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7362 if (ret != 0) {
7363 xmlRelaxNGFreeValidState(ctxt->state);
7364 ctxt->state = oldstate;
7365 break;
7366 }
7367 xmlRelaxNGFreeValidState(oldstate);
7368 cur = ctxt->state->seq;
7369 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007370 }
7371 ctxt->flags = oldflags;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007372 if (ret == 0) {
7373 ctxt->errNr = 0;
7374 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007375 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007376 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00007377 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007378 case XML_RELAXNG_CHOICE: {
7379 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardce14fa52003-02-19 17:32:48 +00007380 int success = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007381
7382 oldflags = ctxt->flags;
7383 ctxt->flags |= FLAGS_IGNORABLE;
7384
7385 while (list != NULL) {
7386 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
7387 ret = xmlRelaxNGValidateDefinition(ctxt, list);
7388 if (ret == 0) {
Daniel Veillardce14fa52003-02-19 17:32:48 +00007389 if (xmlRelaxNGEqualValidState(ctxt, ctxt->state, oldstate)){
7390 /*
7391 * if that pattern was nullable flag it but try
7392 * to make more progresses
7393 */
7394 success = 1;
7395 } else {
7396 xmlRelaxNGFreeValidState(oldstate);
7397 break;
7398 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007399 }
7400 xmlRelaxNGFreeValidState(ctxt->state);
7401 ctxt->state = oldstate;
7402 list = list->next;
7403 }
7404 ctxt->flags = oldflags;
Daniel Veillardce14fa52003-02-19 17:32:48 +00007405 if (success == 1)
7406 ret = 0;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007407 if (ret != 0) {
7408 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7409 xmlRelaxNGDumpValidError(ctxt);
7410 } else if ((ctxt->flags & FLAGS_IGNORABLE) == 0) {
7411 ctxt->errNr = 0;
7412 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007413 break;
7414 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00007415 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00007416 case XML_RELAXNG_GROUP: {
7417 xmlRelaxNGDefinePtr list = define->content;
7418
7419 while (list != NULL) {
7420 ret = xmlRelaxNGValidateDefinition(ctxt, list);
7421 if (ret != 0)
7422 break;
7423 list = list->next;
7424 }
7425 break;
7426 }
7427 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00007428 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007429 break;
7430 case XML_RELAXNG_ATTRIBUTE:
7431 ret = xmlRelaxNGValidateAttribute(ctxt, define);
7432 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00007433 case XML_RELAXNG_NOOP:
Daniel Veillard6eadf632003-01-23 18:29:16 +00007434 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00007435 case XML_RELAXNG_PARENTREF:
7436 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00007437 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7438 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007439 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00007440 xmlNodePtr child;
7441 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007442
Daniel Veillardd4310742003-02-18 21:12:46 +00007443 child = node;
7444 while (child != NULL) {
7445 if (child->type == XML_ELEMENT_NODE) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007446 VALID_ERR2(XML_RELAXNG_ERR_DATAELEM,
Daniel Veillardd4310742003-02-18 21:12:46 +00007447 node->parent->name);
7448 ret = -1;
7449 break;
7450 } else if ((child->type == XML_TEXT_NODE) ||
7451 (child->type == XML_CDATA_SECTION_NODE)) {
7452 content = xmlStrcat(content, child->content);
7453 }
7454 /* TODO: handle entities ... */
7455 child = child->next;
7456 }
7457 if (ret == -1) {
7458 if (content != NULL)
7459 xmlFree(content);
7460 break;
7461 }
7462 if (content == NULL) {
7463 content = xmlStrdup(BAD_CAST "");
7464 if (content == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007465 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardd4310742003-02-18 21:12:46 +00007466 ret = -1;
7467 break;
7468 }
7469 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007470 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
7471 if (ret == -1) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007472 VALID_ERR2(XML_RELAXNG_ERR_DATATYPE, define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007473 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007474 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007475 }
7476 if (content != NULL)
7477 xmlFree(content);
7478 break;
7479 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007480 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00007481 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007482 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00007483 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007484
Daniel Veillardd4310742003-02-18 21:12:46 +00007485 child = node;
7486 while (child != NULL) {
7487 if (child->type == XML_ELEMENT_NODE) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007488 VALID_ERR2(XML_RELAXNG_ERR_VALELEM,
Daniel Veillardd4310742003-02-18 21:12:46 +00007489 node->parent->name);
7490 ret = -1;
7491 break;
7492 } else if ((child->type == XML_TEXT_NODE) ||
7493 (child->type == XML_CDATA_SECTION_NODE)) {
7494 content = xmlStrcat(content, child->content);
7495 }
7496 /* TODO: handle entities ... */
7497 child = child->next;
7498 }
7499 if (ret == -1) {
7500 if (content != NULL)
7501 xmlFree(content);
7502 break;
7503 }
7504 if (content == NULL) {
7505 content = xmlStrdup(BAD_CAST "");
7506 if (content == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007507 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardd4310742003-02-18 21:12:46 +00007508 ret = -1;
7509 break;
7510 }
7511 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007512 oldvalue = ctxt->state->value;
7513 ctxt->state->value = content;
7514 ret = xmlRelaxNGValidateValue(ctxt, define);
7515 ctxt->state->value = oldvalue;
7516 if (ret == -1) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007517 VALID_ERR2(XML_RELAXNG_ERR_VALUE, define->name);
Daniel Veillardea3f3982003-01-26 19:45:18 +00007518 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007519 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007520 }
7521 if (content != NULL)
7522 xmlFree(content);
7523 break;
7524 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007525 case XML_RELAXNG_LIST: {
7526 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00007527 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007528 xmlChar *oldvalue, *oldendvalue;
7529 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007530
Daniel Veillardd4310742003-02-18 21:12:46 +00007531 /*
7532 * Make sure it's only text nodes
7533 */
7534
7535 content = NULL;
7536 child = node;
7537 while (child != NULL) {
7538 if (child->type == XML_ELEMENT_NODE) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007539 VALID_ERR2(XML_RELAXNG_ERR_LISTELEM,
Daniel Veillardd4310742003-02-18 21:12:46 +00007540 node->parent->name);
7541 ret = -1;
7542 break;
7543 } else if ((child->type == XML_TEXT_NODE) ||
7544 (child->type == XML_CDATA_SECTION_NODE)) {
7545 content = xmlStrcat(content, child->content);
7546 }
7547 /* TODO: handle entities ... */
7548 child = child->next;
7549 }
7550 if (ret == -1) {
7551 if (content != NULL)
7552 xmlFree(content);
7553 break;
7554 }
7555 if (content == NULL) {
7556 content = xmlStrdup(BAD_CAST "");
7557 if (content == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007558 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardd4310742003-02-18 21:12:46 +00007559 ret = -1;
7560 break;
7561 }
7562 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007563 len = xmlStrlen(content);
7564 oldvalue = ctxt->state->value;
7565 oldendvalue = ctxt->state->endvalue;
7566 ctxt->state->value = content;
7567 ctxt->state->endvalue = content + len;
7568 ret = xmlRelaxNGValidateValue(ctxt, define);
7569 ctxt->state->value = oldvalue;
7570 ctxt->state->endvalue = oldendvalue;
7571 if (ret == -1) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007572 VALID_ERR(XML_RELAXNG_ERR_LIST);
Daniel Veillardd4310742003-02-18 21:12:46 +00007573 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007574 ctxt->state->seq = node->next;
7575 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007576 if (content != NULL)
7577 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007578 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007579 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007580 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00007581 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00007582 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007583 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00007584 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007585 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007586 }
Daniel Veillard231d7912003-02-09 14:22:17 +00007587 ctxt->depth--;
7588#ifdef DEBUG
7589 for (i = 0;i < ctxt->depth;i++)
7590 xmlGenericError(xmlGenericErrorContext, " ");
7591 xmlGenericError(xmlGenericErrorContext,
7592 "Validating %s ", xmlRelaxNGDefName(define));
7593 if (define->name != NULL)
7594 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
7595 if (ret == 0)
7596 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
7597 else
7598 xmlGenericError(xmlGenericErrorContext, "failed\n");
7599#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00007600 return(ret);
7601}
7602
7603/**
7604 * xmlRelaxNGValidateDocument:
7605 * @ctxt: a Relax-NG validation context
7606 * @doc: the document
7607 *
7608 * Validate the given document
7609 *
7610 * Returns 0 if the validation succeeded or an error code.
7611 */
7612static int
7613xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7614 int ret;
7615 xmlRelaxNGPtr schema;
7616 xmlRelaxNGGrammarPtr grammar;
7617 xmlRelaxNGValidStatePtr state;
7618
7619 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
7620 return(-1);
7621
7622 schema = ctxt->schema;
7623 grammar = schema->topgrammar;
7624 if (grammar == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007625 VALID_ERR(XML_RELAXNG_ERR_NOGRAMMAR);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007626 return(-1);
7627 }
7628 state = xmlRelaxNGNewValidState(ctxt, NULL);
7629 ctxt->state = state;
7630 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
7631 state = ctxt->state;
7632 if ((state != NULL) && (state->seq != NULL)) {
7633 xmlNodePtr node;
7634
7635 node = state->seq;
7636 node = xmlRelaxNGSkipIgnored(ctxt, node);
7637 if (node != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007638 VALID_ERR(XML_RELAXNG_ERR_EXTRADATA);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007639 ret = -1;
7640 }
7641 }
7642 xmlRelaxNGFreeValidState(state);
7643
7644 return(ret);
7645}
7646
7647/************************************************************************
7648 * *
7649 * Validation interfaces *
7650 * *
7651 ************************************************************************/
7652/**
7653 * xmlRelaxNGNewValidCtxt:
7654 * @schema: a precompiled XML RelaxNGs
7655 *
7656 * Create an XML RelaxNGs validation context based on the given schema
7657 *
7658 * Returns the validation context or NULL in case of error
7659 */
7660xmlRelaxNGValidCtxtPtr
7661xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
7662 xmlRelaxNGValidCtxtPtr ret;
7663
7664 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
7665 if (ret == NULL) {
7666 xmlGenericError(xmlGenericErrorContext,
7667 "Failed to allocate new schama validation context\n");
7668 return (NULL);
7669 }
7670 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
7671 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00007672 ret->error = xmlGenericError;
7673 ret->userData = xmlGenericErrorContext;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007674 ret->errNr = 0;
7675 ret->errMax = 0;
7676 ret->err = NULL;
7677 ret->errTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007678 return (ret);
7679}
7680
7681/**
7682 * xmlRelaxNGFreeValidCtxt:
7683 * @ctxt: the schema validation context
7684 *
7685 * Free the resources associated to the schema validation context
7686 */
7687void
7688xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
7689 if (ctxt == NULL)
7690 return;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007691 if (ctxt->errTab != NULL)
7692 xmlFree(ctxt->errTab);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007693 xmlFree(ctxt);
7694}
7695
7696/**
7697 * xmlRelaxNGSetValidErrors:
7698 * @ctxt: a Relax-NG validation context
7699 * @err: the error function
7700 * @warn: the warning function
7701 * @ctx: the functions context
7702 *
7703 * Set the error and warning callback informations
7704 */
7705void
7706xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
7707 xmlRelaxNGValidityErrorFunc err,
7708 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
7709 if (ctxt == NULL)
7710 return;
7711 ctxt->error = err;
7712 ctxt->warning = warn;
7713 ctxt->userData = ctx;
7714}
7715
7716/**
7717 * xmlRelaxNGValidateDoc:
7718 * @ctxt: a Relax-NG validation context
7719 * @doc: a parsed document tree
7720 *
7721 * Validate a document tree in memory.
7722 *
7723 * Returns 0 if the document is valid, a positive error code
7724 * number otherwise and -1 in case of internal or API error.
7725 */
7726int
7727xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7728 int ret;
7729
7730 if ((ctxt == NULL) || (doc == NULL))
7731 return(-1);
7732
7733 ctxt->doc = doc;
7734
7735 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00007736 /*
7737 * TODO: build error codes
7738 */
7739 if (ret == -1)
7740 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007741 return(ret);
7742}
7743
7744#endif /* LIBXML_SCHEMAS_ENABLED */
7745