blob: f8e0b9f923ba0e75c12d8a5ac5e0c22f2ab77fc9 [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 Veillard6eadf632003-01-23 18:29:16 +000056
57#define UNBOUNDED (1 << 30)
58#define TODO \
59 xmlGenericError(xmlGenericErrorContext, \
60 "Unimplemented block at %s:%d\n", \
61 __FILE__, __LINE__);
62
63typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
64typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
65
66typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
67typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
68
Daniel Veillardd41f4f42003-01-29 21:07:52 +000069typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
70typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
71
Daniel Veillarda9d912d2003-02-01 17:43:10 +000072typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
73typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
74
Daniel Veillard6eadf632003-01-23 18:29:16 +000075typedef enum {
76 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
77 XML_RELAXNG_COMBINE_CHOICE, /* choice */
78 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
79} xmlRelaxNGCombine;
80
Daniel Veillard4c5cf702003-02-21 15:40:34 +000081typedef enum {
82 XML_RELAXNG_CONTENT_ERROR = -1,
83 XML_RELAXNG_CONTENT_EMPTY = 0,
84 XML_RELAXNG_CONTENT_SIMPLE,
85 XML_RELAXNG_CONTENT_COMPLEX
86} xmlRelaxNGContentType;
87
Daniel Veillard6eadf632003-01-23 18:29:16 +000088typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
89typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
90
91struct _xmlRelaxNGGrammar {
92 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
93 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
94 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
95 xmlRelaxNGDefinePtr start; /* <start> content */
96 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000097 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000098 xmlHashTablePtr defs; /* define* */
99 xmlHashTablePtr refs; /* references */
100};
101
102
Daniel Veillard6eadf632003-01-23 18:29:16 +0000103typedef enum {
Daniel Veillard77648bb2003-02-20 15:03:22 +0000104 XML_RELAXNG_NOOP = -1, /* a no operation from simplification */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000105 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
106 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +0000107 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000108 XML_RELAXNG_TEXT, /* textual content */
109 XML_RELAXNG_ELEMENT, /* an element */
110 XML_RELAXNG_DATATYPE, /* extenal data type definition */
Daniel Veillard8fe98712003-02-19 00:19:14 +0000111 XML_RELAXNG_PARAM, /* extenal data type parameter */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000112 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
113 XML_RELAXNG_LIST, /* a list of patterns */
114 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
115 XML_RELAXNG_DEF, /* a definition */
116 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000117 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000118 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000119 XML_RELAXNG_OPTIONAL, /* optional patterns */
120 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
121 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
122 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
123 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000124 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
125 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000126} xmlRelaxNGType;
127
128struct _xmlRelaxNGDefine {
129 xmlRelaxNGType type; /* the type of definition */
130 xmlNodePtr node; /* the node in the source */
131 xmlChar *name; /* the element local name if present */
132 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000133 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000134 void *data; /* data lib or specific pointer */
Daniel Veillardd4310742003-02-18 21:12:46 +0000135 int depth; /* used for the cycle detection */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000136 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000137 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000138 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
139 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000140 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000141 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
142};
143
144/**
145 * _xmlRelaxNG:
146 *
147 * A RelaxNGs definition
148 */
149struct _xmlRelaxNG {
150 xmlRelaxNGGrammarPtr topgrammar;
151 xmlDocPtr doc;
152
153 xmlHashTablePtr defs; /* define */
154 xmlHashTablePtr refs; /* references */
Daniel Veillardc482e262003-02-26 14:48:48 +0000155 xmlRelaxNGDocumentPtr documents; /* all the documents loaded */
156 xmlRelaxNGIncludePtr includes; /* all the includes loaded */
Daniel Veillard419a7682003-02-03 23:22:49 +0000157 int defNr; /* number of defines used */
158 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000159 void *_private; /* unused by the library for users or bindings */
160};
161
Daniel Veillard77648bb2003-02-20 15:03:22 +0000162#define XML_RELAXNG_IN_ATTRIBUTE (1 << 0)
163#define XML_RELAXNG_IN_ONEORMORE (1 << 1)
164#define XML_RELAXNG_IN_LIST (1 << 2)
165#define XML_RELAXNG_IN_DATAEXCEPT (1 << 3)
166#define XML_RELAXNG_IN_START (1 << 4)
167#define XML_RELAXNG_IN_OOMGROUP (1 << 5)
168#define XML_RELAXNG_IN_OOMINTERLEAVE (1 << 6)
169#define XML_RELAXNG_IN_EXTERNALREF (1 << 7)
Daniel Veillardc5312d72003-02-21 17:14:10 +0000170#define XML_RELAXNG_IN_ANYEXCEPT (1 << 8)
171#define XML_RELAXNG_IN_NSEXCEPT (1 << 9)
Daniel Veillard6eadf632003-01-23 18:29:16 +0000172
173struct _xmlRelaxNGParserCtxt {
174 void *userData; /* user specific data block */
175 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
176 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
Daniel Veillard42f12e92003-03-07 18:32:59 +0000177 xmlRelaxNGValidErr err;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000178
179 xmlRelaxNGPtr schema; /* The schema in use */
180 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000181 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000182 int flags; /* parser flags */
183 int nbErrors; /* number of errors at parse time */
184 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000185 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000186 xmlRelaxNGDefinePtr def; /* the current define */
187
188 int nbInterleaves;
189 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000190
Daniel Veillardc482e262003-02-26 14:48:48 +0000191 xmlRelaxNGDocumentPtr documents; /* all the documents loaded */
192 xmlRelaxNGIncludePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000193 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000194 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000195
Daniel Veillard419a7682003-02-03 23:22:49 +0000196 int defNr; /* number of defines used */
197 int defMax; /* number of defines aloocated */
198 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
199
Daniel Veillard6eadf632003-01-23 18:29:16 +0000200 const char *buffer;
201 int size;
202
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000203 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000204 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000205 int docNr; /* Depth of the parsing stack */
206 int docMax; /* Max depth of the parsing stack */
207 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000208
209 /* the include stack */
Daniel Veillardd4310742003-02-18 21:12:46 +0000210 xmlRelaxNGIncludePtr inc; /* Current parsed include */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000211 int incNr; /* Depth of the include parsing stack */
212 int incMax; /* Max depth of the parsing stack */
213 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000214};
215
216#define FLAGS_IGNORABLE 1
217#define FLAGS_NEGATIVE 2
218
219/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000220 * xmlRelaxNGInterleaveGroup:
221 *
222 * A RelaxNGs partition set associated to lists of definitions
223 */
224typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
225typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
226struct _xmlRelaxNGInterleaveGroup {
227 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
228 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000229 xmlRelaxNGDefinePtr *attrs; /* the array of attributes definitions */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000230};
231
232/**
233 * xmlRelaxNGPartitions:
234 *
235 * A RelaxNGs partition associated to an interleave group
236 */
237typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
238typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
239struct _xmlRelaxNGPartition {
240 int nbgroups; /* number of groups in the partitions */
241 xmlRelaxNGInterleaveGroupPtr *groups;
242};
243
244/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000245 * xmlRelaxNGValidState:
246 *
247 * A RelaxNGs validation state
248 */
249#define MAX_ATTR 20
250typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
251typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
252struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000253 xmlNodePtr node; /* the current node */
254 xmlNodePtr seq; /* the sequence of children left to validate */
255 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000256 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000257 xmlChar *value; /* the value when operating on string */
258 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000259 xmlAttrPtr attrs[1]; /* the array of attributes */
260};
261
262/**
Daniel Veillard42f12e92003-03-07 18:32:59 +0000263 * xmlRelaxNGValidError:
264 *
265 * A RelaxNGs validation error
266 */
267typedef struct _xmlRelaxNGValidError xmlRelaxNGValidError;
268typedef xmlRelaxNGValidError *xmlRelaxNGValidErrorPtr;
269struct _xmlRelaxNGValidError {
270 xmlRelaxNGValidErr err; /* the error number */
271 xmlNodePtr node; /* the current node */
272 xmlNodePtr seq; /* the current child */
273 const xmlChar * arg1; /* first arg */
274 const xmlChar * arg2; /* second arg */
275};
276
277/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000278 * xmlRelaxNGValidCtxt:
279 *
280 * A RelaxNGs validation context
281 */
282
283struct _xmlRelaxNGValidCtxt {
284 void *userData; /* user specific data block */
285 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
286 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
287
288 xmlRelaxNGPtr schema; /* The schema in use */
289 xmlDocPtr doc; /* the document being validated */
290 xmlRelaxNGValidStatePtr state; /* the current validation state */
291 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000292 int depth; /* validation depth */
Daniel Veillard42f12e92003-03-07 18:32:59 +0000293
294 /*
295 * Errors accumulated in branches may have to be stacked to be
296 * provided back when it's sure they affect validation.
297 */
298 xmlRelaxNGValidErrorPtr err; /* Last error */
299 int errNr; /* Depth of the error stack */
300 int errMax; /* Max depth of the error stack */
301 xmlRelaxNGValidErrorPtr errTab; /* stack of errors */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000302};
303
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000304/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000305 * xmlRelaxNGInclude:
306 *
307 * Structure associated to a RelaxNGs document element
308 */
309struct _xmlRelaxNGInclude {
Daniel Veillardc482e262003-02-26 14:48:48 +0000310 xmlRelaxNGIncludePtr next; /* keep a chain of includes */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000311 xmlChar *href; /* the normalized href value */
312 xmlDocPtr doc; /* the associated XML document */
313 xmlRelaxNGDefinePtr content;/* the definitions */
314 xmlRelaxNGPtr schema; /* the schema */
315};
316
317/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000318 * xmlRelaxNGDocument:
319 *
320 * Structure associated to a RelaxNGs document element
321 */
322struct _xmlRelaxNGDocument {
Daniel Veillardc482e262003-02-26 14:48:48 +0000323 xmlRelaxNGDocumentPtr next; /* keep a chain of documents */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000324 xmlChar *href; /* the normalized href value */
325 xmlDocPtr doc; /* the associated XML document */
326 xmlRelaxNGDefinePtr content;/* the definitions */
327 xmlRelaxNGPtr schema; /* the schema */
328};
329
Daniel Veillard3ebc7d42003-02-24 17:17:58 +0000330
Daniel Veillard6eadf632003-01-23 18:29:16 +0000331/************************************************************************
332 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000333 * Preliminary type checking interfaces *
334 * *
335 ************************************************************************/
336/**
337 * xmlRelaxNGTypeHave:
338 * @data: data needed for the library
339 * @type: the type name
340 * @value: the value to check
341 *
342 * Function provided by a type library to check if a type is exported
343 *
344 * Returns 1 if yes, 0 if no and -1 in case of error.
345 */
346typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
347
348/**
349 * xmlRelaxNGTypeCheck:
350 * @data: data needed for the library
351 * @type: the type name
352 * @value: the value to check
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000353 * @result: place to store the result if needed
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000354 *
355 * Function provided by a type library to check if a value match a type
356 *
357 * Returns 1 if yes, 0 if no and -1 in case of error.
358 */
359typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000360 const xmlChar *value, void **result);
361
362/**
363 * xmlRelaxNGFacetCheck:
364 * @data: data needed for the library
365 * @type: the type name
366 * @facet: the facet name
367 * @val: the facet value
368 * @strval: the string value
369 * @value: the value to check
370 *
371 * Function provided by a type library to check a value facet
372 *
373 * Returns 1 if yes, 0 if no and -1 in case of error.
374 */
375typedef int (*xmlRelaxNGFacetCheck) (void *data, const xmlChar *type,
376 const xmlChar *facet, const xmlChar *val,
377 const xmlChar *strval, void *value);
378
379/**
380 * xmlRelaxNGTypeFree:
381 * @data: data needed for the library
382 * @result: the value to free
383 *
384 * Function provided by a type library to free a returned result
385 */
386typedef void (*xmlRelaxNGTypeFree) (void *data, void *result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000387
388/**
389 * xmlRelaxNGTypeCompare:
390 * @data: data needed for the library
391 * @type: the type name
392 * @value1: the first value
393 * @value2: the second value
394 *
395 * Function provided by a type library to compare two values accordingly
396 * to a type.
397 *
398 * Returns 1 if yes, 0 if no and -1 in case of error.
399 */
400typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
401 const xmlChar *value1,
402 const xmlChar *value2);
403typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
404typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
405struct _xmlRelaxNGTypeLibrary {
406 const xmlChar *namespace; /* the datatypeLibrary value */
407 void *data; /* data needed for the library */
408 xmlRelaxNGTypeHave have; /* the export function */
409 xmlRelaxNGTypeCheck check; /* the checking function */
410 xmlRelaxNGTypeCompare comp; /* the compare function */
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000411 xmlRelaxNGFacetCheck facet; /* the facet check function */
412 xmlRelaxNGTypeFree freef; /* the freeing function */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000413};
414
415/************************************************************************
416 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000417 * Allocation functions *
418 * *
419 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000420static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
421static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000422static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillardc482e262003-02-26 14:48:48 +0000423static void xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000424
425/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000426 * xmlRelaxNGFreeDocument:
427 * @docu: a document structure
428 *
429 * Deallocate a RelaxNG document structure.
430 */
431static void
432xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
433{
434 if (docu == NULL)
435 return;
436
437 if (docu->href != NULL)
438 xmlFree(docu->href);
439 if (docu->doc != NULL)
440 xmlFreeDoc(docu->doc);
441 if (docu->schema != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000442 xmlRelaxNGFreeInnerSchema(docu->schema);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000443 xmlFree(docu);
444}
445
446/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000447 * xmlRelaxNGFreeDocumentList:
448 * @docu: a list of document structure
449 *
450 * Deallocate a RelaxNG document structures.
451 */
452static void
453xmlRelaxNGFreeDocumentList(xmlRelaxNGDocumentPtr docu)
454{
455 xmlRelaxNGDocumentPtr next;
456 while (docu != NULL) {
457 next = docu->next;
458 xmlRelaxNGFreeDocument(docu);
459 docu = next;
460 }
461}
462
463/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000464 * xmlRelaxNGFreeInclude:
465 * @incl: a include structure
466 *
467 * Deallocate a RelaxNG include structure.
468 */
469static void
470xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
471{
472 if (incl == NULL)
473 return;
474
475 if (incl->href != NULL)
476 xmlFree(incl->href);
477 if (incl->doc != NULL)
478 xmlFreeDoc(incl->doc);
479 if (incl->schema != NULL)
480 xmlRelaxNGFree(incl->schema);
481 xmlFree(incl);
482}
483
484/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000485 * xmlRelaxNGFreeIncludeList:
486 * @incl: a include structure list
487 *
488 * Deallocate a RelaxNG include structure.
489 */
490static void
491xmlRelaxNGFreeIncludeList(xmlRelaxNGIncludePtr incl)
492{
493 xmlRelaxNGIncludePtr next;
494 while (incl != NULL) {
495 next = incl->next;
496 xmlRelaxNGFreeInclude(incl);
497 incl = next;
498 }
499}
500
501/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000502 * xmlRelaxNGNewRelaxNG:
503 * @ctxt: a Relax-NG validation context (optional)
504 *
505 * Allocate a new RelaxNG structure.
506 *
507 * Returns the newly allocated structure or NULL in case or error
508 */
509static xmlRelaxNGPtr
510xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
511{
512 xmlRelaxNGPtr ret;
513
514 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
515 if (ret == NULL) {
516 if ((ctxt != NULL) && (ctxt->error != NULL))
517 ctxt->error(ctxt->userData, "Out of memory\n");
518 ctxt->nbErrors++;
519 return (NULL);
520 }
521 memset(ret, 0, sizeof(xmlRelaxNG));
522
523 return (ret);
524}
525
526/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000527 * xmlRelaxNGFreeInnerSchema:
528 * @schema: a schema structure
529 *
530 * Deallocate a RelaxNG schema structure.
531 */
532static void
533xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema)
534{
535 if (schema == NULL)
536 return;
537
538 if (schema->doc != NULL)
539 xmlFreeDoc(schema->doc);
540 if (schema->defTab != NULL) {
541 int i;
542
543 for (i = 0;i < schema->defNr;i++)
544 xmlRelaxNGFreeDefine(schema->defTab[i]);
545 xmlFree(schema->defTab);
546 }
547
548 xmlFree(schema);
549}
550
551/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000552 * xmlRelaxNGFree:
553 * @schema: a schema structure
554 *
555 * Deallocate a RelaxNG structure.
556 */
557void
558xmlRelaxNGFree(xmlRelaxNGPtr schema)
559{
560 if (schema == NULL)
561 return;
562
Daniel Veillard6eadf632003-01-23 18:29:16 +0000563 if (schema->topgrammar != NULL)
564 xmlRelaxNGFreeGrammar(schema->topgrammar);
565 if (schema->doc != NULL)
566 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000567 if (schema->documents != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000568 xmlRelaxNGFreeDocumentList(schema->documents);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000569 if (schema->includes != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000570 xmlRelaxNGFreeIncludeList(schema->includes);
Daniel Veillard419a7682003-02-03 23:22:49 +0000571 if (schema->defTab != NULL) {
572 int i;
573
574 for (i = 0;i < schema->defNr;i++)
575 xmlRelaxNGFreeDefine(schema->defTab[i]);
576 xmlFree(schema->defTab);
577 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000578
579 xmlFree(schema);
580}
581
582/**
583 * xmlRelaxNGNewGrammar:
584 * @ctxt: a Relax-NG validation context (optional)
585 *
586 * Allocate a new RelaxNG grammar.
587 *
588 * Returns the newly allocated structure or NULL in case or error
589 */
590static xmlRelaxNGGrammarPtr
591xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
592{
593 xmlRelaxNGGrammarPtr ret;
594
595 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
596 if (ret == NULL) {
597 if ((ctxt != NULL) && (ctxt->error != NULL))
598 ctxt->error(ctxt->userData, "Out of memory\n");
599 ctxt->nbErrors++;
600 return (NULL);
601 }
602 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
603
604 return (ret);
605}
606
607/**
608 * xmlRelaxNGFreeGrammar:
609 * @grammar: a grammar structure
610 *
611 * Deallocate a RelaxNG grammar structure.
612 */
613static void
614xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
615{
616 if (grammar == NULL)
617 return;
618
Daniel Veillardc482e262003-02-26 14:48:48 +0000619 if (grammar->children != NULL) {
620 xmlRelaxNGFreeGrammar(grammar->children);
621 }
Daniel Veillard419a7682003-02-03 23:22:49 +0000622 if (grammar->next != NULL) {
623 xmlRelaxNGFreeGrammar(grammar->next);
624 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000625 if (grammar->refs != NULL) {
626 xmlHashFree(grammar->refs, NULL);
627 }
628 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000629 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000630 }
631
632 xmlFree(grammar);
633}
634
635/**
636 * xmlRelaxNGNewDefine:
637 * @ctxt: a Relax-NG validation context
638 * @node: the node in the input document.
639 *
640 * Allocate a new RelaxNG define.
641 *
642 * Returns the newly allocated structure or NULL in case or error
643 */
644static xmlRelaxNGDefinePtr
645xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
646{
647 xmlRelaxNGDefinePtr ret;
648
Daniel Veillard419a7682003-02-03 23:22:49 +0000649 if (ctxt->defMax == 0) {
650 ctxt->defMax = 16;
651 ctxt->defNr = 0;
652 ctxt->defTab = (xmlRelaxNGDefinePtr *)
653 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
654 if (ctxt->defTab == NULL) {
655 if ((ctxt != NULL) && (ctxt->error != NULL))
656 ctxt->error(ctxt->userData, "Out of memory\n");
657 ctxt->nbErrors++;
658 return (NULL);
659 }
660 } else if (ctxt->defMax <= ctxt->defNr) {
661 xmlRelaxNGDefinePtr *tmp;
662 ctxt->defMax *= 2;
663 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
664 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
665 if (tmp == NULL) {
666 if ((ctxt != NULL) && (ctxt->error != NULL))
667 ctxt->error(ctxt->userData, "Out of memory\n");
668 ctxt->nbErrors++;
669 return (NULL);
670 }
671 ctxt->defTab = tmp;
672 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000673 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
674 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000675 if ((ctxt != NULL) && (ctxt->error != NULL))
676 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000677 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000678 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000679 }
680 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000681 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000682 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000683 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000684 return (ret);
685}
686
687/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000688 * xmlRelaxNGFreePartition:
689 * @partitions: a partition set structure
690 *
691 * Deallocate RelaxNG partition set structures.
692 */
693static void
694xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
695 xmlRelaxNGInterleaveGroupPtr group;
696 int j;
697
698 if (partitions != NULL) {
699 if (partitions->groups != NULL) {
700 for (j = 0;j < partitions->nbgroups;j++) {
701 group = partitions->groups[j];
702 if (group != NULL) {
703 if (group->defs != NULL)
704 xmlFree(group->defs);
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000705 if (group->attrs != NULL)
706 xmlFree(group->attrs);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000707 xmlFree(group);
708 }
709 }
710 xmlFree(partitions->groups);
711 }
712 xmlFree(partitions);
713 }
714}
715/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000716 * xmlRelaxNGFreeDefine:
717 * @define: a define structure
718 *
719 * Deallocate a RelaxNG define structure.
720 */
721static void
722xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
723{
724 if (define == NULL)
725 return;
726
Daniel Veillard419a7682003-02-03 23:22:49 +0000727 if ((define->data != NULL) &&
728 (define->type == XML_RELAXNG_INTERLEAVE))
729 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000730 if (define->name != NULL)
731 xmlFree(define->name);
732 if (define->ns != NULL)
733 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000734 if (define->value != NULL)
735 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000736 xmlFree(define);
737}
738
739/**
740 * xmlRelaxNGNewValidState:
741 * @ctxt: a Relax-NG validation context
742 * @node: the current node or NULL for the document
743 *
744 * Allocate a new RelaxNG validation state
745 *
746 * Returns the newly allocated structure or NULL in case or error
747 */
748static xmlRelaxNGValidStatePtr
749xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
750{
751 xmlRelaxNGValidStatePtr ret;
752 xmlAttrPtr attr;
753 xmlAttrPtr attrs[MAX_ATTR];
754 int nbAttrs = 0;
755 xmlNodePtr root = NULL;
756
757 if (node == NULL) {
758 root = xmlDocGetRootElement(ctxt->doc);
759 if (root == NULL)
760 return(NULL);
761 } else {
762 attr = node->properties;
763 while (attr != NULL) {
764 if (nbAttrs < MAX_ATTR)
765 attrs[nbAttrs++] = attr;
766 else
767 nbAttrs++;
768 attr = attr->next;
769 }
770 }
771
772 if (nbAttrs < MAX_ATTR)
773 attrs[nbAttrs] = NULL;
774 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
775 nbAttrs * sizeof(xmlAttrPtr));
776 if (ret == NULL) {
777 if ((ctxt != NULL) && (ctxt->error != NULL))
778 ctxt->error(ctxt->userData, "Out of memory\n");
779 return (NULL);
780 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000781 ret->value = NULL;
782 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000783 if (node == NULL) {
784 ret->node = (xmlNodePtr) ctxt->doc;
785 ret->seq = root;
786 ret->nbAttrs = 0;
787 } else {
788 ret->node = node;
789 ret->seq = node->children;
790 ret->nbAttrs = nbAttrs;
791 if (nbAttrs > 0) {
792 if (nbAttrs < MAX_ATTR) {
793 memcpy(&(ret->attrs[0]), attrs,
794 sizeof(xmlAttrPtr) * (nbAttrs + 1));
795 } else {
796 attr = node->properties;
797 nbAttrs = 0;
798 while (attr != NULL) {
799 ret->attrs[nbAttrs++] = attr;
800 attr = attr->next;
801 }
802 ret->attrs[nbAttrs] = NULL;
803 }
804 }
805 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000806 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000807 return (ret);
808}
809
810/**
811 * xmlRelaxNGCopyValidState:
812 * @ctxt: a Relax-NG validation context
813 * @state: a validation state
814 *
815 * Copy the validation state
816 *
817 * Returns the newly allocated structure or NULL in case or error
818 */
819static xmlRelaxNGValidStatePtr
820xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
821 xmlRelaxNGValidStatePtr state)
822{
823 xmlRelaxNGValidStatePtr ret;
824 unsigned int size;
825
826 if (state == NULL)
827 return(NULL);
828
829 size = sizeof(xmlRelaxNGValidState) +
830 state->nbAttrs * sizeof(xmlAttrPtr);
831 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
832 if (ret == NULL) {
833 if ((ctxt != NULL) && (ctxt->error != NULL))
834 ctxt->error(ctxt->userData, "Out of memory\n");
835 return (NULL);
836 }
837 memcpy(ret, state, size);
838 return(ret);
839}
840
841/**
Daniel Veillardce14fa52003-02-19 17:32:48 +0000842 * xmlRelaxNGEqualValidState:
843 * @ctxt: a Relax-NG validation context
844 * @state1: a validation state
845 * @state2: a validation state
846 *
847 * Compare the validation states for equality
848 *
849 * Returns 1 if equald, 0 otherwise
850 */
851static int
852xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
853 xmlRelaxNGValidStatePtr state1,
854 xmlRelaxNGValidStatePtr state2)
855{
856 int i;
857
858 if ((state1 == NULL) || (state2 == NULL))
859 return(0);
860 if (state1 == state2)
861 return(1);
862 if (state1->node != state2->node)
863 return(0);
864 if (state1->seq != state2->seq)
865 return(0);
866 if (state1->nbAttrLeft != state2->nbAttrLeft)
867 return(0);
868 if (state1->nbAttrs != state2->nbAttrs)
869 return(0);
870 if (state1->endvalue != state2->endvalue)
871 return(0);
872 if ((state1->value != state2->value) &&
873 (!xmlStrEqual(state1->value, state2->value)))
874 return(0);
875 for (i = 0;i < state1->nbAttrs;i++) {
876 if (state1->attrs[i] != state2->attrs[i])
877 return(0);
878 }
879 return(1);
880}
881
882/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000883 * xmlRelaxNGFreeValidState:
884 * @state: a validation state structure
885 *
886 * Deallocate a RelaxNG validation state structure.
887 */
888static void
889xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
890{
891 if (state == NULL)
892 return;
893
894 xmlFree(state);
895}
896
897/************************************************************************
898 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000899 * Document functions *
900 * *
901 ************************************************************************/
902static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
903 xmlDocPtr doc);
904
905/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000906 * xmlRelaxNGIncludePush:
907 * @ctxt: the parser context
908 * @value: the element doc
909 *
910 * Pushes a new include on top of the include stack
911 *
912 * Returns 0 in case of error, the index in the stack otherwise
913 */
914static int
915xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
916 xmlRelaxNGIncludePtr value)
917{
918 if (ctxt->incTab == NULL) {
919 ctxt->incMax = 4;
920 ctxt->incNr = 0;
921 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
922 ctxt->incMax * sizeof(ctxt->incTab[0]));
923 if (ctxt->incTab == NULL) {
924 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
925 return (0);
926 }
927 }
928 if (ctxt->incNr >= ctxt->incMax) {
929 ctxt->incMax *= 2;
930 ctxt->incTab =
931 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
932 ctxt->incMax *
933 sizeof(ctxt->incTab[0]));
934 if (ctxt->incTab == NULL) {
935 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
936 return (0);
937 }
938 }
939 ctxt->incTab[ctxt->incNr] = value;
940 ctxt->inc = value;
941 return (ctxt->incNr++);
942}
943
944/**
945 * xmlRelaxNGIncludePop:
946 * @ctxt: the parser context
947 *
948 * Pops the top include from the include stack
949 *
950 * Returns the include just removed
951 */
952static xmlRelaxNGIncludePtr
953xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
954{
955 xmlRelaxNGIncludePtr ret;
956
957 if (ctxt->incNr <= 0)
958 return (0);
959 ctxt->incNr--;
960 if (ctxt->incNr > 0)
961 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
962 else
963 ctxt->inc = NULL;
964 ret = ctxt->incTab[ctxt->incNr];
965 ctxt->incTab[ctxt->incNr] = 0;
966 return (ret);
967}
968
969/**
970 * xmlRelaxNGLoadInclude:
971 * @ctxt: the parser context
972 * @URL: the normalized URL
973 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +0000974 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000975 *
976 * First lookup if the document is already loaded into the parser context,
977 * check against recursion. If not found the resource is loaded and
978 * the content is preprocessed before being returned back to the caller.
979 *
980 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
981 */
982static xmlRelaxNGIncludePtr
983xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +0000984 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000985 xmlRelaxNGIncludePtr ret = NULL;
986 xmlDocPtr doc;
987 int i;
988 xmlNodePtr root, tmp, tmp2, cur;
989
990 /*
991 * check against recursion in the stack
992 */
993 for (i = 0;i < ctxt->incNr;i++) {
994 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
995 if (ctxt->error != NULL)
996 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +0000997 "Detected an Include recursion for %s\n",
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000998 URL);
999 ctxt->nbErrors++;
1000 return(NULL);
1001 }
1002 }
1003
1004 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001005 * load the document
1006 */
1007 doc = xmlParseFile((const char *) URL);
1008 if (doc == NULL) {
1009 if (ctxt->error != NULL)
1010 ctxt->error(ctxt->userData,
1011 "xmlRelaxNG: could not load %s\n", URL);
1012 ctxt->nbErrors++;
1013 return (NULL);
1014 }
1015
1016 /*
1017 * Allocate the document structures and register it first.
1018 */
1019 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
1020 if (ret == NULL) {
1021 if (ctxt->error != NULL)
1022 ctxt->error(ctxt->userData,
1023 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1024 ctxt->nbErrors++;
1025 xmlFreeDoc(doc);
1026 return (NULL);
1027 }
1028 memset(ret, 0, sizeof(xmlRelaxNGInclude));
1029 ret->doc = doc;
1030 ret->href = xmlStrdup(URL);
Daniel Veillardc482e262003-02-26 14:48:48 +00001031 ret->next = ctxt->includes;
1032 ctxt->includes = ret;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001033
1034 /*
Daniel Veillard416589a2003-02-17 17:25:42 +00001035 * transmit the ns if needed
1036 */
1037 if (ns != NULL) {
1038 root = xmlDocGetRootElement(doc);
1039 if (root != NULL) {
1040 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1041 xmlSetProp(root, BAD_CAST"ns", ns);
1042 }
1043 }
1044 }
1045
1046 /*
Daniel Veillardc482e262003-02-26 14:48:48 +00001047 * push it on the stack
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001048 */
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001049 xmlRelaxNGIncludePush(ctxt, ret);
1050
1051 /*
1052 * Some preprocessing of the document content, this include recursing
1053 * in the include stack.
1054 */
1055 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1056 if (doc == NULL) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001057 ctxt->inc = NULL;
1058 return(NULL);
1059 }
1060
1061 /*
1062 * Pop up the include from the stack
1063 */
1064 xmlRelaxNGIncludePop(ctxt);
1065
1066 /*
1067 * Check that the top element is a grammar
1068 */
1069 root = xmlDocGetRootElement(doc);
1070 if (root == NULL) {
1071 if (ctxt->error != NULL)
1072 ctxt->error(ctxt->userData,
1073 "xmlRelaxNG: included document is empty %s\n", URL);
1074 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001075 return (NULL);
1076 }
1077 if (!IS_RELAXNG(root, "grammar")) {
1078 if (ctxt->error != NULL)
1079 ctxt->error(ctxt->userData,
1080 "xmlRelaxNG: included document %s root is not a grammar\n",
1081 URL);
1082 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001083 return (NULL);
1084 }
1085
1086 /*
1087 * Elimination of redefined rules in the include.
1088 */
1089 cur = node->children;
1090 while (cur != NULL) {
1091 if (IS_RELAXNG(cur, "start")) {
1092 int found = 0;
1093
1094 tmp = root->children;
1095 while (tmp != NULL) {
1096 tmp2 = tmp->next;
1097 if (IS_RELAXNG(tmp, "start")) {
1098 found = 1;
1099 xmlUnlinkNode(tmp);
1100 xmlFreeNode(tmp);
1101 }
1102 tmp = tmp2;
1103 }
1104 if (!found) {
1105 if (ctxt->error != NULL)
1106 ctxt->error(ctxt->userData,
1107 "xmlRelaxNG: include %s has a start but not the included grammar\n",
1108 URL);
1109 ctxt->nbErrors++;
1110 }
1111 } else if (IS_RELAXNG(cur, "define")) {
1112 xmlChar *name, *name2;
1113
1114 name = xmlGetProp(cur, BAD_CAST "name");
1115 if (name == NULL) {
1116 if (ctxt->error != NULL)
1117 ctxt->error(ctxt->userData,
1118 "xmlRelaxNG: include %s has define without name\n",
1119 URL);
1120 ctxt->nbErrors++;
1121 } else {
1122 int found = 0;
1123
Daniel Veillardd2298792003-02-14 16:54:11 +00001124 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001125 tmp = root->children;
1126 while (tmp != NULL) {
1127 tmp2 = tmp->next;
1128 if (IS_RELAXNG(tmp, "define")) {
1129 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +00001130 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001131 if (name2 != NULL) {
1132 if (xmlStrEqual(name, name2)) {
1133 found = 1;
1134 xmlUnlinkNode(tmp);
1135 xmlFreeNode(tmp);
1136 }
1137 xmlFree(name2);
1138 }
1139 }
1140 tmp = tmp2;
1141 }
1142 if (!found) {
1143 if (ctxt->error != NULL)
1144 ctxt->error(ctxt->userData,
1145 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1146 URL, name);
1147 ctxt->nbErrors++;
1148 }
1149 xmlFree(name);
1150 }
1151 }
1152 cur = cur->next;
1153 }
1154
1155
1156 return(ret);
1157}
1158
1159/**
Daniel Veillard42f12e92003-03-07 18:32:59 +00001160 * xmlRelaxNGValidErrorPush:
1161 * @ctxt: the validation context
1162 * @err: the error code
1163 * @arg1: the first string argument
1164 * @arg2: the second string argument
1165 *
1166 * Pushes a new error on top of the error stack
1167 *
1168 * Returns 0 in case of error, the index in the stack otherwise
1169 */
1170static int
1171xmlRelaxNGValidErrorPush(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err,
1172 const xmlChar *arg1, const xmlChar *arg2)
1173{
1174 xmlRelaxNGValidErrorPtr cur;
1175 if (ctxt->errTab == NULL) {
1176 ctxt->errMax = 8;
1177 ctxt->errNr = 0;
1178 ctxt->errTab = (xmlRelaxNGValidErrorPtr) xmlMalloc(
1179 ctxt->errMax * sizeof(xmlRelaxNGValidError));
1180 if (ctxt->errTab == NULL) {
1181 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1182 return (0);
1183 }
1184 }
1185 if (ctxt->errNr >= ctxt->errMax) {
1186 ctxt->errMax *= 2;
1187 ctxt->errTab =
1188 (xmlRelaxNGValidErrorPtr) xmlRealloc(ctxt->errTab,
1189 ctxt->errMax * sizeof(xmlRelaxNGValidError));
1190 if (ctxt->errTab == NULL) {
1191 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1192 return (0);
1193 }
1194 }
1195 cur = &ctxt->errTab[ctxt->errNr];
1196 cur->err = err;
1197 cur->arg1 = arg1;
1198 cur->arg2 = arg2;
1199 if (ctxt->state != NULL) {
1200 cur->node = ctxt->state->node;
1201 cur->seq = ctxt->state->seq;
1202 } else {
1203 cur->node = NULL;
1204 cur->seq = NULL;
1205 }
1206 ctxt->err = cur;
1207 return (ctxt->errNr++);
1208}
1209
1210/**
1211 * xmlRelaxNGValidErrorPop:
1212 * @ctxt: the validation context
1213 *
1214 * Pops the top error from the error stack
1215 *
1216 * Returns the error just removed
1217 */
1218static xmlRelaxNGValidErrorPtr
1219xmlRelaxNGValidErrorPop(xmlRelaxNGValidCtxtPtr ctxt)
1220{
1221 xmlRelaxNGValidErrorPtr ret;
1222
1223 if (ctxt->errNr <= 0)
1224 return (NULL);
1225 ctxt->errNr--;
1226 if (ctxt->errNr > 0)
1227 ctxt->err = &ctxt->errTab[ctxt->errNr - 1];
1228 else
1229 ctxt->err = NULL;
1230 ret = &ctxt->errTab[ctxt->errNr];
1231 return (ret);
1232}
1233
1234
1235/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001236 * xmlRelaxNGDocumentPush:
1237 * @ctxt: the parser context
1238 * @value: the element doc
1239 *
1240 * Pushes a new doc on top of the doc stack
1241 *
1242 * Returns 0 in case of error, the index in the stack otherwise
1243 */
1244static int
1245xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1246 xmlRelaxNGDocumentPtr value)
1247{
1248 if (ctxt->docTab == NULL) {
1249 ctxt->docMax = 4;
1250 ctxt->docNr = 0;
1251 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1252 ctxt->docMax * sizeof(ctxt->docTab[0]));
1253 if (ctxt->docTab == NULL) {
1254 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1255 return (0);
1256 }
1257 }
1258 if (ctxt->docNr >= ctxt->docMax) {
1259 ctxt->docMax *= 2;
1260 ctxt->docTab =
1261 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1262 ctxt->docMax *
1263 sizeof(ctxt->docTab[0]));
1264 if (ctxt->docTab == NULL) {
1265 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1266 return (0);
1267 }
1268 }
1269 ctxt->docTab[ctxt->docNr] = value;
1270 ctxt->doc = value;
1271 return (ctxt->docNr++);
1272}
1273
1274/**
1275 * xmlRelaxNGDocumentPop:
1276 * @ctxt: the parser context
1277 *
1278 * Pops the top doc from the doc stack
1279 *
1280 * Returns the doc just removed
1281 */
1282static xmlRelaxNGDocumentPtr
1283xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1284{
1285 xmlRelaxNGDocumentPtr ret;
1286
1287 if (ctxt->docNr <= 0)
1288 return (0);
1289 ctxt->docNr--;
1290 if (ctxt->docNr > 0)
1291 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1292 else
1293 ctxt->doc = NULL;
1294 ret = ctxt->docTab[ctxt->docNr];
1295 ctxt->docTab[ctxt->docNr] = 0;
1296 return (ret);
1297}
1298
1299/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001300 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001301 * @ctxt: the parser context
1302 * @URL: the normalized URL
1303 * @ns: the inherited ns if any
1304 *
1305 * First lookup if the document is already loaded into the parser context,
1306 * check against recursion. If not found the resource is loaded and
1307 * the content is preprocessed before being returned back to the caller.
1308 *
1309 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1310 */
1311static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001312xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001313 const xmlChar *ns) {
1314 xmlRelaxNGDocumentPtr ret = NULL;
1315 xmlDocPtr doc;
1316 xmlNodePtr root;
1317 int i;
1318
1319 /*
1320 * check against recursion in the stack
1321 */
1322 for (i = 0;i < ctxt->docNr;i++) {
1323 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1324 if (ctxt->error != NULL)
1325 ctxt->error(ctxt->userData,
1326 "Detected an externalRef recursion for %s\n",
1327 URL);
1328 ctxt->nbErrors++;
1329 return(NULL);
1330 }
1331 }
1332
1333 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001334 * load the document
1335 */
1336 doc = xmlParseFile((const char *) URL);
1337 if (doc == NULL) {
1338 if (ctxt->error != NULL)
1339 ctxt->error(ctxt->userData,
1340 "xmlRelaxNG: could not load %s\n", URL);
1341 ctxt->nbErrors++;
1342 return (NULL);
1343 }
1344
1345 /*
1346 * Allocate the document structures and register it first.
1347 */
1348 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1349 if (ret == NULL) {
1350 if (ctxt->error != NULL)
1351 ctxt->error(ctxt->userData,
1352 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1353 ctxt->nbErrors++;
1354 xmlFreeDoc(doc);
1355 return (NULL);
1356 }
1357 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1358 ret->doc = doc;
1359 ret->href = xmlStrdup(URL);
Daniel Veillardc482e262003-02-26 14:48:48 +00001360 ret->next = ctxt->documents;
1361 ctxt->documents = ret;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001362
1363 /*
1364 * transmit the ns if needed
1365 */
1366 if (ns != NULL) {
1367 root = xmlDocGetRootElement(doc);
1368 if (root != NULL) {
1369 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1370 xmlSetProp(root, BAD_CAST"ns", ns);
1371 }
1372 }
1373 }
1374
1375 /*
1376 * push it on the stack and register it in the hash table
1377 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001378 xmlRelaxNGDocumentPush(ctxt, ret);
1379
1380 /*
1381 * Some preprocessing of the document content
1382 */
1383 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1384 if (doc == NULL) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001385 ctxt->doc = NULL;
1386 return(NULL);
1387 }
1388
1389 xmlRelaxNGDocumentPop(ctxt);
1390
1391 return(ret);
1392}
1393
1394/************************************************************************
1395 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001396 * Error functions *
1397 * *
1398 ************************************************************************/
1399
Daniel Veillard42f12e92003-03-07 18:32:59 +00001400#define VALID_ERR(a) xmlRelaxNGAddValidError(ctxt, a, NULL, NULL);
1401#define VALID_ERR2(a, b) xmlRelaxNGAddValidError(ctxt, a, b, NULL);
1402#define VALID_ERR3(a, b, c) xmlRelaxNGAddValidError(ctxt, a, b, c);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001403
Daniel Veillardd2298792003-02-14 16:54:11 +00001404#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001405static const char *
1406xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1407 if (def == NULL)
1408 return("none");
1409 switch(def->type) {
1410 case XML_RELAXNG_EMPTY: return("empty");
1411 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1412 case XML_RELAXNG_EXCEPT: return("except");
1413 case XML_RELAXNG_TEXT: return("text");
1414 case XML_RELAXNG_ELEMENT: return("element");
1415 case XML_RELAXNG_DATATYPE: return("datatype");
1416 case XML_RELAXNG_VALUE: return("value");
1417 case XML_RELAXNG_LIST: return("list");
1418 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1419 case XML_RELAXNG_DEF: return("def");
1420 case XML_RELAXNG_REF: return("ref");
1421 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1422 case XML_RELAXNG_PARENTREF: return("parentRef");
1423 case XML_RELAXNG_OPTIONAL: return("optional");
1424 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1425 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1426 case XML_RELAXNG_CHOICE: return("choice");
1427 case XML_RELAXNG_GROUP: return("group");
1428 case XML_RELAXNG_INTERLEAVE: return("interleave");
1429 case XML_RELAXNG_START: return("start");
1430 }
1431 return("unknown");
1432}
Daniel Veillardd2298792003-02-14 16:54:11 +00001433#endif
1434
Daniel Veillard6eadf632003-01-23 18:29:16 +00001435/**
Daniel Veillard42f12e92003-03-07 18:32:59 +00001436 * xmlRelaxNGGetErrorString:
1437 * @err: the error code
1438 * @arg1: the first string argument
1439 * @arg2: the second string argument
Daniel Veillard6eadf632003-01-23 18:29:16 +00001440 *
Daniel Veillard42f12e92003-03-07 18:32:59 +00001441 * computes a formatted error string for the given error code and args
1442 *
1443 * Returns the error string, it must be deallocated by the caller
1444 */
1445static xmlChar *
1446xmlRelaxNGGetErrorString(xmlRelaxNGValidErr err, const xmlChar *arg1,
1447 const xmlChar *arg2) {
1448 char msg[1000];
1449
1450 if (arg1 == NULL)
1451 arg1 = BAD_CAST "";
1452 if (arg2 == NULL)
1453 arg2 = BAD_CAST "";
1454
1455 msg[0] = 0;
1456 switch (err) {
1457 case XML_RELAXNG_OK:
1458 return(NULL);
1459 case XML_RELAXNG_ERR_MEMORY:
1460 return(xmlCharStrdup("out of memory"));
1461 case XML_RELAXNG_ERR_TYPE:
1462 snprintf(msg, 1000, "failed to validate type %s", arg1);
1463 break;
1464 case XML_RELAXNG_ERR_TYPEVAL:
1465 snprintf(msg, 1000, "Type %s doesn't allow value %s", arg1, arg2);
1466 break;
1467 case XML_RELAXNG_ERR_TYPECMP:
1468 snprintf(msg, 1000, "failed to compare type %s", arg1);
1469 break;
1470 case XML_RELAXNG_ERR_NOSTATE:
1471 return(xmlCharStrdup("Internal error: no state"));
1472 case XML_RELAXNG_ERR_NODEFINE:
1473 return(xmlCharStrdup("Internal error: no define"));
1474 case XML_RELAXNG_ERR_LISTEXTRA:
1475 snprintf(msg, 1000, "Extra data in list: %s", arg1);
1476 break;
1477 case XML_RELAXNG_ERR_INTERNODATA:
1478 return(xmlCharStrdup("Internal: interleave block has no data"));
1479 case XML_RELAXNG_ERR_INTERSEQ:
1480 return(xmlCharStrdup("Invalid sequence in interleave"));
1481 case XML_RELAXNG_ERR_INTEREXTRA:
1482 snprintf(msg, 1000, "Extra element %s in interleave", arg1);
1483 break;
1484 case XML_RELAXNG_ERR_ELEMNAME:
1485 snprintf(msg, 1000, "Expecting element %s, got %s", arg1, arg2);
1486 break;
1487 case XML_RELAXNG_ERR_ELEMNONS:
1488 snprintf(msg, 1000, "Expecting a namespace for element %s", arg1);
1489 break;
1490 case XML_RELAXNG_ERR_ELEMWRONGNS:
1491 snprintf(msg, 1000, "Element %s has wrong namespace: expecting %s",
1492 arg1, arg2);
1493 break;
1494 case XML_RELAXNG_ERR_ELEMEXTRANS:
1495 snprintf(msg, 1000, "Expecting no namespace for element %s", arg1);
1496 break;
1497 case XML_RELAXNG_ERR_ELEMNOTEMPTY:
1498 snprintf(msg, 1000, "Expecting element %s to be empty", arg1);
1499 break;
1500 case XML_RELAXNG_ERR_NOELEM:
1501 snprintf(msg, 1000, "Expecting an element %s, got nothing", arg1);
1502 break;
1503 case XML_RELAXNG_ERR_NOTELEM:
1504 return(xmlCharStrdup("Expecting an element got text"));
1505 case XML_RELAXNG_ERR_ATTRVALID:
1506 snprintf(msg, 1000, "Element %s failed to validate attributes",
1507 arg1);
1508 break;
1509 case XML_RELAXNG_ERR_CONTENTVALID:
1510 snprintf(msg, 1000, "Element %s failed to validate content",
1511 arg1);
1512 break;
1513 case XML_RELAXNG_ERR_EXTRACONTENT:
1514 snprintf(msg, 1000, "Element %s has extra content: %s",
1515 arg1, arg2);
1516 break;
1517 case XML_RELAXNG_ERR_INVALIDATTR:
1518 snprintf(msg, 1000, "Invalid attribute %s for element %s",
1519 arg1, arg2);
1520 break;
1521 case XML_RELAXNG_ERR_DATAELEM:
1522 snprintf(msg, 1000, "Datatype element %s has child elements",
1523 arg1);
1524 break;
1525 case XML_RELAXNG_ERR_VALELEM:
1526 snprintf(msg, 1000, "Value element %s has child elements",
1527 arg1);
1528 break;
1529 case XML_RELAXNG_ERR_LISTELEM:
1530 snprintf(msg, 1000, "List element %s has child elements",
1531 arg1);
1532 break;
1533 case XML_RELAXNG_ERR_DATATYPE:
1534 snprintf(msg, 1000, "Error validating datatype %s",
1535 arg1);
1536 break;
1537 case XML_RELAXNG_ERR_VALUE:
1538 snprintf(msg, 1000, "Error validating value %s",
1539 arg1);
1540 break;
1541 case XML_RELAXNG_ERR_LIST:
1542 return(xmlCharStrdup("Error validating list"));
1543 case XML_RELAXNG_ERR_NOGRAMMAR:
1544 return(xmlCharStrdup("No top grammar defined"));
1545 case XML_RELAXNG_ERR_EXTRADATA:
1546 return(xmlCharStrdup("Extra data in the document"));
1547 }
1548 if (msg[0] == 0) {
1549 snprintf(msg, 1000, "Unknown error code %d", err);
1550 }
1551 msg[1000] = 0;
1552 return(xmlStrdup((xmlChar *) msg));
1553}
1554
1555/**
1556 * xmlRelaxNGValidErrorContext:
1557 * @ctxt: the validation context
1558 * @node: the node
1559 * @child: the node child generating the problem.
1560 *
1561 * Dump informations about the kocation of the error in the instance
Daniel Veillard6eadf632003-01-23 18:29:16 +00001562 */
1563static void
Daniel Veillard42f12e92003-03-07 18:32:59 +00001564xmlRelaxNGValidErrorContext(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node,
1565 xmlNodePtr child)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001566{
1567 int line = 0;
1568 const xmlChar *file = NULL;
1569 const xmlChar *name = NULL;
1570 const char *type = "error";
1571
1572 if ((ctxt == NULL) || (ctxt->error == NULL))
1573 return;
1574
1575 if (child != NULL)
1576 node = child;
1577
1578 if (node != NULL) {
1579 if ((node->type == XML_DOCUMENT_NODE) ||
1580 (node->type == XML_HTML_DOCUMENT_NODE)) {
1581 xmlDocPtr doc = (xmlDocPtr) node;
1582
1583 file = doc->URL;
1584 } else {
1585 /*
1586 * Try to find contextual informations to report
1587 */
1588 if (node->type == XML_ELEMENT_NODE) {
1589 line = (int) node->content;
1590 } else if ((node->prev != NULL) &&
1591 (node->prev->type == XML_ELEMENT_NODE)) {
1592 line = (int) node->prev->content;
1593 } else if ((node->parent != NULL) &&
1594 (node->parent->type == XML_ELEMENT_NODE)) {
1595 line = (int) node->parent->content;
1596 }
1597 if ((node->doc != NULL) && (node->doc->URL != NULL))
1598 file = node->doc->URL;
1599 if (node->name != NULL)
1600 name = node->name;
1601 }
1602 }
1603
Daniel Veillard42f12e92003-03-07 18:32:59 +00001604 type = "RNG validity error";
Daniel Veillard6eadf632003-01-23 18:29:16 +00001605
1606 if ((file != NULL) && (line != 0) && (name != NULL))
1607 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1608 type, file, line, name);
1609 else if ((file != NULL) && (name != NULL))
1610 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1611 type, file, name);
1612 else if ((file != NULL) && (line != 0))
1613 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1614 else if (file != NULL)
1615 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1616 else if (name != NULL)
1617 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1618 else
1619 ctxt->error(ctxt->userData, "%s\n", type);
1620}
Daniel Veillard42f12e92003-03-07 18:32:59 +00001621
1622/**
1623 * xmlRelaxNGShowValidError:
1624 * @ctxt: the validation context
1625 * @err: the error number
1626 * @node: the node
1627 * @child: the node child generating the problem.
1628 * @arg1: the first argument
1629 * @arg2: the second argument
1630 *
1631 * Show a validation error.
1632 */
1633static void
1634xmlRelaxNGShowValidError(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err,
1635 xmlNodePtr node, xmlNodePtr child,
1636 const xmlChar *arg1, const xmlChar *arg2)
1637{
1638 xmlChar *msg;
1639
1640 if (ctxt->error == NULL)
1641 return;
1642
1643 msg = xmlRelaxNGGetErrorString(err, arg1, arg2);
1644 if (msg == NULL)
1645 return;
1646
1647 xmlRelaxNGValidErrorContext(ctxt, node, child);
1648 ctxt->error(ctxt->userData, "%s\n", msg);
1649 xmlFree(msg);
1650}
1651
1652/**
1653 * xmlRelaxNGDumpValidError:
1654 * @ctxt: the validation context
1655 *
1656 * Show all validation error over a given index.
1657 */
1658static void
1659xmlRelaxNGDumpValidError(xmlRelaxNGValidCtxtPtr ctxt) {
1660 int i;
1661 xmlRelaxNGValidErrorPtr err;
1662
1663 for (i = 0;i < ctxt->errNr;i++) {
1664 err = &ctxt->errTab[i];
1665 xmlRelaxNGShowValidError(ctxt, err->err, err->node, err->seq,
1666 err->arg1, err->arg2);
1667 }
1668 ctxt->errNr = 0;
1669}
1670/**
1671 * xmlRelaxNGAddValidError:
1672 * @ctxt: the validation context
1673 * @err: the error number
1674 * @arg1: the first argument
1675 * @arg2: the second argument
1676 *
1677 * Register a validation error, either generating it if it's sure
1678 * or stacking it for later handling if unsure.
1679 */
1680static void
1681xmlRelaxNGAddValidError(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err,
1682 const xmlChar *arg1, const xmlChar *arg2)
1683{
1684 if ((ctxt == NULL) || (ctxt->error == NULL))
1685 return;
1686
1687 /*
1688 * generate the error directly
1689 */
1690 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) {
1691 xmlNodePtr node, seq;
1692 /*
1693 * Flush first any stacked error which might be the
1694 * real cause of the problem.
1695 */
1696 if (ctxt->errNr != 0)
1697 xmlRelaxNGDumpValidError(ctxt);
1698 if (ctxt->state != NULL) {
1699 node = ctxt->state->node;
1700 seq = ctxt->state->seq;
1701 } else {
1702 node = seq = NULL;
1703 }
1704 xmlRelaxNGShowValidError(ctxt, err, node, seq, arg1, arg2);
1705 }
1706 /*
1707 * Stack the error for later processing if needed
1708 */
1709 else {
1710 xmlRelaxNGValidErrorPush(ctxt, err, arg1, arg2);
1711 }
1712}
1713
Daniel Veillard6eadf632003-01-23 18:29:16 +00001714
1715/************************************************************************
1716 * *
1717 * Type library hooks *
1718 * *
1719 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001720static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1721 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001722
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001723/**
1724 * xmlRelaxNGSchemaTypeHave:
1725 * @data: data needed for the library
1726 * @type: the type name
1727 *
1728 * Check if the given type is provided by
1729 * the W3C XMLSchema Datatype library.
1730 *
1731 * Returns 1 if yes, 0 if no and -1 in case of error.
1732 */
1733static int
1734xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001735 const xmlChar *type) {
1736 xmlSchemaTypePtr typ;
1737
1738 if (type == NULL)
1739 return(-1);
1740 typ = xmlSchemaGetPredefinedType(type,
1741 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1742 if (typ == NULL)
1743 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001744 return(1);
1745}
1746
1747/**
1748 * xmlRelaxNGSchemaTypeCheck:
1749 * @data: data needed for the library
1750 * @type: the type name
1751 * @value: the value to check
1752 *
1753 * Check if the given type and value are validated by
1754 * the W3C XMLSchema Datatype library.
1755 *
1756 * Returns 1 if yes, 0 if no and -1 in case of error.
1757 */
1758static int
1759xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001760 const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001761 const xmlChar *value,
1762 void **result) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001763 xmlSchemaTypePtr typ;
1764 int ret;
1765
1766 /*
1767 * TODO: the type should be cached ab provided back, interface subject
1768 * to changes.
1769 * TODO: handle facets, may require an additional interface and keep
1770 * the value returned from the validation.
1771 */
1772 if ((type == NULL) || (value == NULL))
1773 return(-1);
1774 typ = xmlSchemaGetPredefinedType(type,
1775 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1776 if (typ == NULL)
1777 return(-1);
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001778 ret = xmlSchemaValidatePredefinedType(typ, value,
1779 (xmlSchemaValPtr *) result);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001780 if (ret == 0)
1781 return(1);
1782 if (ret > 0)
1783 return(0);
1784 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001785}
1786
1787/**
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001788 * xmlRelaxNGSchemaFacetCheck:
1789 * @data: data needed for the library
1790 * @type: the type name
1791 * @facet: the facet name
1792 * @val: the facet value
1793 * @strval: the string value
1794 * @value: the value to check
1795 *
1796 * Function provided by a type library to check a value facet
1797 *
1798 * Returns 1 if yes, 0 if no and -1 in case of error.
1799 */
1800static int
Daniel Veillard42f12e92003-03-07 18:32:59 +00001801xmlRelaxNGSchemaFacetCheck (void *data ATTRIBUTE_UNUSED, const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001802 const xmlChar *facetname, const xmlChar *val,
1803 const xmlChar *strval, void *value) {
1804 xmlSchemaFacetPtr facet;
1805 xmlSchemaTypePtr typ;
1806 int ret;
1807
1808 if ((type == NULL) || (strval == NULL))
1809 return(-1);
1810 typ = xmlSchemaGetPredefinedType(type,
1811 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1812 if (typ == NULL)
1813 return(-1);
1814
1815 facet = xmlSchemaNewFacet();
1816 if (facet == NULL)
1817 return(-1);
1818
1819 if (xmlStrEqual(facetname, BAD_CAST "minInclusive")) {
1820 facet->type = XML_SCHEMA_FACET_MININCLUSIVE;
1821 } else if (xmlStrEqual(facetname, BAD_CAST "minExclusive")) {
1822 facet->type = XML_SCHEMA_FACET_MINEXCLUSIVE;
1823 } else if (xmlStrEqual(facetname, BAD_CAST "maxInclusive")) {
1824 facet->type = XML_SCHEMA_FACET_MAXINCLUSIVE;
1825 } else if (xmlStrEqual(facetname, BAD_CAST "maxExclusive")) {
1826 facet->type = XML_SCHEMA_FACET_MAXEXCLUSIVE;
1827 } else if (xmlStrEqual(facetname, BAD_CAST "totalDigits")) {
1828 facet->type = XML_SCHEMA_FACET_TOTALDIGITS;
1829 } else if (xmlStrEqual(facetname, BAD_CAST "fractionDigits")) {
1830 facet->type = XML_SCHEMA_FACET_FRACTIONDIGITS;
1831 } else if (xmlStrEqual(facetname, BAD_CAST "pattern")) {
1832 facet->type = XML_SCHEMA_FACET_PATTERN;
1833 } else if (xmlStrEqual(facetname, BAD_CAST "enumeration")) {
1834 facet->type = XML_SCHEMA_FACET_ENUMERATION;
1835 } else if (xmlStrEqual(facetname, BAD_CAST "whiteSpace")) {
1836 facet->type = XML_SCHEMA_FACET_WHITESPACE;
1837 } else if (xmlStrEqual(facetname, BAD_CAST "length")) {
1838 facet->type = XML_SCHEMA_FACET_LENGTH;
1839 } else if (xmlStrEqual(facetname, BAD_CAST "maxLength")) {
1840 facet->type = XML_SCHEMA_FACET_MAXLENGTH;
1841 } else if (xmlStrEqual(facetname, BAD_CAST "minLength")) {
1842 facet->type = XML_SCHEMA_FACET_MINLENGTH;
1843 } else {
1844 xmlSchemaFreeFacet(facet);
1845 return(-1);
1846 }
1847 facet->value = xmlStrdup(val);
1848 ret = xmlSchemaCheckFacet(facet, typ, NULL, type);
1849 if (ret != 0) {
1850 xmlSchemaFreeFacet(facet);
1851 return(-1);
1852 }
1853 ret = xmlSchemaValidateFacet(typ, facet, strval, value);
1854 xmlSchemaFreeFacet(facet);
1855 if (ret != 0)
1856 return(-1);
1857 return(0);
1858}
1859
1860/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001861 * xmlRelaxNGSchemaTypeCompare:
1862 * @data: data needed for the library
1863 * @type: the type name
1864 * @value1: the first value
1865 * @value2: the second value
1866 *
1867 * Compare two values accordingly a type from the W3C XMLSchema
1868 * Datatype library.
1869 *
1870 * Returns 1 if yes, 0 if no and -1 in case of error.
1871 */
1872static int
1873xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1874 const xmlChar *type ATTRIBUTE_UNUSED,
1875 const xmlChar *value1 ATTRIBUTE_UNUSED,
1876 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1877 TODO
1878 return(1);
1879}
1880
1881/**
1882 * xmlRelaxNGDefaultTypeHave:
1883 * @data: data needed for the library
1884 * @type: the type name
1885 *
1886 * Check if the given type is provided by
1887 * the default datatype library.
1888 *
1889 * Returns 1 if yes, 0 if no and -1 in case of error.
1890 */
1891static int
1892xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1893 if (type == NULL)
1894 return(-1);
1895 if (xmlStrEqual(type, BAD_CAST "string"))
1896 return(1);
1897 if (xmlStrEqual(type, BAD_CAST "token"))
1898 return(1);
1899 return(0);
1900}
1901
1902/**
1903 * xmlRelaxNGDefaultTypeCheck:
1904 * @data: data needed for the library
1905 * @type: the type name
1906 * @value: the value to check
1907 *
1908 * Check if the given type and value are validated by
1909 * the default datatype library.
1910 *
1911 * Returns 1 if yes, 0 if no and -1 in case of error.
1912 */
1913static int
1914xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1915 const xmlChar *type ATTRIBUTE_UNUSED,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001916 const xmlChar *value ATTRIBUTE_UNUSED,
1917 void **result ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001918 if (value == NULL)
1919 return(-1);
1920 if (xmlStrEqual(type, BAD_CAST "string"))
1921 return(1);
1922 if (xmlStrEqual(type, BAD_CAST "token")) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001923 return(1);
1924 }
1925
1926 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001927}
1928
1929/**
1930 * xmlRelaxNGDefaultTypeCompare:
1931 * @data: data needed for the library
1932 * @type: the type name
1933 * @value1: the first value
1934 * @value2: the second value
1935 *
1936 * Compare two values accordingly a type from the default
1937 * datatype library.
1938 *
1939 * Returns 1 if yes, 0 if no and -1 in case of error.
1940 */
1941static int
1942xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1943 const xmlChar *type ATTRIBUTE_UNUSED,
1944 const xmlChar *value1 ATTRIBUTE_UNUSED,
1945 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001946 int ret = -1;
1947
1948 if (xmlStrEqual(type, BAD_CAST "string")) {
1949 ret = xmlStrEqual(value1, value2);
1950 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1951 if (!xmlStrEqual(value1, value2)) {
1952 xmlChar *nval, *nvalue;
1953
1954 /*
1955 * TODO: trivial optimizations are possible by
1956 * computing at compile-time
1957 */
1958 nval = xmlRelaxNGNormalize(NULL, value1);
1959 nvalue = xmlRelaxNGNormalize(NULL, value2);
1960
Daniel Veillardd4310742003-02-18 21:12:46 +00001961 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00001962 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00001963 else if (xmlStrEqual(nval, nvalue))
1964 ret = 1;
1965 else
1966 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001967 if (nval != NULL)
1968 xmlFree(nval);
1969 if (nvalue != NULL)
1970 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00001971 } else
1972 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001973 }
1974 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001975}
1976
1977static int xmlRelaxNGTypeInitialized = 0;
1978static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1979
1980/**
1981 * xmlRelaxNGFreeTypeLibrary:
1982 * @lib: the type library structure
1983 * @namespace: the URI bound to the library
1984 *
1985 * Free the structure associated to the type library
1986 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001987static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001988xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1989 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1990 if (lib == NULL)
1991 return;
1992 if (lib->namespace != NULL)
1993 xmlFree((xmlChar *)lib->namespace);
1994 xmlFree(lib);
1995}
1996
1997/**
1998 * xmlRelaxNGRegisterTypeLibrary:
1999 * @namespace: the URI bound to the library
2000 * @data: data associated to the library
2001 * @have: the provide function
2002 * @check: the checking function
2003 * @comp: the comparison function
2004 *
2005 * Register a new type library
2006 *
2007 * Returns 0 in case of success and -1 in case of error.
2008 */
2009static int
2010xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
2011 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002012 xmlRelaxNGTypeCompare comp, xmlRelaxNGFacetCheck facet,
2013 xmlRelaxNGTypeFree freef) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002014 xmlRelaxNGTypeLibraryPtr lib;
2015 int ret;
2016
2017 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
2018 (check == NULL) || (comp == NULL))
2019 return(-1);
2020 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
2021 xmlGenericError(xmlGenericErrorContext,
2022 "Relax-NG types library '%s' already registered\n",
2023 namespace);
2024 return(-1);
2025 }
2026 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
2027 if (lib == NULL) {
2028 xmlGenericError(xmlGenericErrorContext,
2029 "Relax-NG types library '%s' malloc() failed\n",
2030 namespace);
2031 return (-1);
2032 }
2033 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
2034 lib->namespace = xmlStrdup(namespace);
2035 lib->data = data;
2036 lib->have = have;
2037 lib->comp = comp;
2038 lib->check = check;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002039 lib->facet = facet;
2040 lib->freef = freef;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002041 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
2042 if (ret < 0) {
2043 xmlGenericError(xmlGenericErrorContext,
2044 "Relax-NG types library failed to register '%s'\n",
2045 namespace);
2046 xmlRelaxNGFreeTypeLibrary(lib, namespace);
2047 return(-1);
2048 }
2049 return(0);
2050}
2051
2052/**
2053 * xmlRelaxNGInitTypes:
2054 *
2055 * Initilize the default type libraries.
2056 *
2057 * Returns 0 in case of success and -1 in case of error.
2058 */
2059static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00002060xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002061 if (xmlRelaxNGTypeInitialized != 0)
2062 return(0);
2063 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
2064 if (xmlRelaxNGRegisteredTypes == NULL) {
2065 xmlGenericError(xmlGenericErrorContext,
2066 "Failed to allocate sh table for Relax-NG types\n");
2067 return(-1);
2068 }
2069 xmlRelaxNGRegisterTypeLibrary(
2070 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
2071 NULL,
2072 xmlRelaxNGSchemaTypeHave,
2073 xmlRelaxNGSchemaTypeCheck,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002074 xmlRelaxNGSchemaTypeCompare,
2075 xmlRelaxNGSchemaFacetCheck,
2076 (xmlRelaxNGTypeFree) xmlSchemaFreeValue);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002077 xmlRelaxNGRegisterTypeLibrary(
2078 xmlRelaxNGNs,
2079 NULL,
2080 xmlRelaxNGDefaultTypeHave,
2081 xmlRelaxNGDefaultTypeCheck,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002082 xmlRelaxNGDefaultTypeCompare,
2083 NULL,
2084 NULL);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002085 xmlRelaxNGTypeInitialized = 1;
2086 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002087}
2088
2089/**
2090 * xmlRelaxNGCleanupTypes:
2091 *
2092 * Cleanup the default Schemas type library associated to RelaxNG
2093 */
2094void
2095xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002096 if (xmlRelaxNGTypeInitialized == 0)
2097 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002098 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002099 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
2100 xmlRelaxNGFreeTypeLibrary);
2101 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002102}
2103
2104/************************************************************************
2105 * *
2106 * Parsing functions *
2107 * *
2108 ************************************************************************/
2109
2110static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
2111 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
2112static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
2113 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
2114static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00002115 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002116static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
2117 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002118static xmlRelaxNGPtr xmlRelaxNGParseDocument(
2119 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002120static int xmlRelaxNGParseGrammarContent(
2121 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002122static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
2123 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2124 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00002125static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
2126 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard17bed982003-02-24 20:11:43 +00002127static int xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
2128 xmlRelaxNGDefinePtr define, xmlNodePtr elem);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002129
2130
2131#define IS_BLANK_NODE(n) \
2132 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
2133
2134/**
2135 * xmlRelaxNGIsBlank:
2136 * @str: a string
2137 *
2138 * Check if a string is ignorable c.f. 4.2. Whitespace
2139 *
2140 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
2141 */
2142static int
2143xmlRelaxNGIsBlank(xmlChar *str) {
2144 if (str == NULL)
2145 return(1);
2146 while (*str != 0) {
2147 if (!(IS_BLANK(*str))) return(0);
2148 str++;
2149 }
2150 return(1);
2151}
2152
Daniel Veillard6eadf632003-01-23 18:29:16 +00002153/**
2154 * xmlRelaxNGGetDataTypeLibrary:
2155 * @ctxt: a Relax-NG parser context
2156 * @node: the current data or value element
2157 *
2158 * Applies algorithm from 4.3. datatypeLibrary attribute
2159 *
2160 * Returns the datatypeLibary value or NULL if not found
2161 */
2162static xmlChar *
2163xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2164 xmlNodePtr node) {
2165 xmlChar *ret, *escape;
2166
Daniel Veillard6eadf632003-01-23 18:29:16 +00002167 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
2168 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
2169 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002170 if (ret[0] == 0) {
2171 xmlFree(ret);
2172 return(NULL);
2173 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002174 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00002175 if (escape == NULL) {
2176 return(ret);
2177 }
2178 xmlFree(ret);
2179 return(escape);
2180 }
2181 }
2182 node = node->parent;
2183 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00002184 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
2185 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002186 if (ret[0] == 0) {
2187 xmlFree(ret);
2188 return(NULL);
2189 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00002190 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
2191 if (escape == NULL) {
2192 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002193 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00002194 xmlFree(ret);
2195 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002196 }
2197 node = node->parent;
2198 }
2199 return(NULL);
2200}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002201
2202/**
Daniel Veillardedc91922003-01-26 00:52:04 +00002203 * xmlRelaxNGParseValue:
2204 * @ctxt: a Relax-NG parser context
2205 * @node: the data node.
2206 *
2207 * parse the content of a RelaxNG value node.
2208 *
2209 * Returns the definition pointer or NULL in case of error
2210 */
2211static xmlRelaxNGDefinePtr
2212xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2213 xmlRelaxNGDefinePtr def = NULL;
2214 xmlRelaxNGTypeLibraryPtr lib;
2215 xmlChar *type;
2216 xmlChar *library;
2217 int tmp;
2218
2219 def = xmlRelaxNGNewDefine(ctxt, node);
2220 if (def == NULL)
2221 return(NULL);
2222 def->type = XML_RELAXNG_VALUE;
2223
2224 type = xmlGetProp(node, BAD_CAST "type");
2225 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002226 xmlRelaxNGNormExtSpace(type);
2227 if (xmlValidateNCName(type, 0)) {
2228 if (ctxt->error != NULL)
2229 ctxt->error(ctxt->userData,
2230 "value type '%s' is not an NCName\n",
2231 type);
2232 ctxt->nbErrors++;
2233 }
Daniel Veillardedc91922003-01-26 00:52:04 +00002234 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
2235 if (library == NULL)
2236 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
2237
2238 def->name = type;
2239 def->ns = library;
2240
2241 lib = (xmlRelaxNGTypeLibraryPtr)
2242 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
2243 if (lib == NULL) {
2244 if (ctxt->error != NULL)
2245 ctxt->error(ctxt->userData,
2246 "Use of unregistered type library '%s'\n",
2247 library);
2248 ctxt->nbErrors++;
2249 def->data = NULL;
2250 } else {
2251 def->data = lib;
2252 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002253 if (ctxt->error != NULL)
2254 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00002255 "Internal error with type library '%s': no 'have'\n",
2256 library);
2257 ctxt->nbErrors++;
2258 } else {
2259 tmp = lib->have(lib->data, def->name);
2260 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002261 if (ctxt->error != NULL)
2262 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00002263 "Error type '%s' is not exported by type library '%s'\n",
2264 def->name, library);
2265 ctxt->nbErrors++;
2266 }
2267 }
2268 }
2269 }
2270 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00002271 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00002272 } else if ((node->children->type != XML_TEXT_NODE) ||
2273 (node->children->next != NULL)) {
2274 if (ctxt->error != NULL)
2275 ctxt->error(ctxt->userData,
2276 "Expecting a single text value for <value>content\n");
2277 ctxt->nbErrors++;
2278 } else {
2279 def->value = xmlNodeGetContent(node);
2280 if (def->value == NULL) {
2281 if (ctxt->error != NULL)
2282 ctxt->error(ctxt->userData,
2283 "Element <value> has no content\n");
2284 ctxt->nbErrors++;
2285 }
2286 }
2287 /* TODO check ahead of time that the value is okay per the type */
2288 return(def);
2289}
2290
2291/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002292 * xmlRelaxNGParseData:
2293 * @ctxt: a Relax-NG parser context
2294 * @node: the data node.
2295 *
2296 * parse the content of a RelaxNG data node.
2297 *
2298 * Returns the definition pointer or NULL in case of error
2299 */
2300static xmlRelaxNGDefinePtr
2301xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002302 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002303 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002304 xmlRelaxNGTypeLibraryPtr lib;
2305 xmlChar *type;
2306 xmlChar *library;
2307 xmlNodePtr content;
2308 int tmp;
2309
2310 type = xmlGetProp(node, BAD_CAST "type");
2311 if (type == NULL) {
2312 if (ctxt->error != NULL)
2313 ctxt->error(ctxt->userData,
2314 "data has no type\n");
2315 ctxt->nbErrors++;
2316 return(NULL);
2317 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002318 xmlRelaxNGNormExtSpace(type);
2319 if (xmlValidateNCName(type, 0)) {
2320 if (ctxt->error != NULL)
2321 ctxt->error(ctxt->userData,
2322 "data type '%s' is not an NCName\n",
2323 type);
2324 ctxt->nbErrors++;
2325 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002326 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
2327 if (library == NULL)
2328 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
2329
2330 def = xmlRelaxNGNewDefine(ctxt, node);
2331 if (def == NULL) {
2332 xmlFree(type);
2333 return(NULL);
2334 }
2335 def->type = XML_RELAXNG_DATATYPE;
2336 def->name = type;
2337 def->ns = library;
2338
2339 lib = (xmlRelaxNGTypeLibraryPtr)
2340 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
2341 if (lib == NULL) {
2342 if (ctxt->error != NULL)
2343 ctxt->error(ctxt->userData,
2344 "Use of unregistered type library '%s'\n",
2345 library);
2346 ctxt->nbErrors++;
2347 def->data = NULL;
2348 } else {
2349 def->data = lib;
2350 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002351 if (ctxt->error != NULL)
2352 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002353 "Internal error with type library '%s': no 'have'\n",
2354 library);
2355 ctxt->nbErrors++;
2356 } else {
2357 tmp = lib->have(lib->data, def->name);
2358 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002359 if (ctxt->error != NULL)
2360 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002361 "Error type '%s' is not exported by type library '%s'\n",
2362 def->name, library);
2363 ctxt->nbErrors++;
2364 }
2365 }
2366 }
2367 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00002368
2369 /*
2370 * Handle optional params
2371 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002372 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002373 if (!xmlStrEqual(content->name, BAD_CAST "param"))
2374 break;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002375 if (xmlStrEqual(library,
2376 BAD_CAST"http://relaxng.org/ns/structure/1.0")) {
2377 if (ctxt->error != NULL)
2378 ctxt->error(ctxt->userData,
2379 "Type library '%s' does not allow type parameters\n",
2380 library);
2381 ctxt->nbErrors++;
2382 content = content->next;
2383 while ((content != NULL) &&
2384 (xmlStrEqual(content->name, BAD_CAST "param")))
2385 content = content->next;
2386 } else {
2387 param = xmlRelaxNGNewDefine(ctxt, node);
2388 if (param != NULL) {
2389 param->type = XML_RELAXNG_PARAM;
2390 param->name = xmlGetProp(content, BAD_CAST "name");
2391 if (param->name == NULL) {
2392 if (ctxt->error != NULL)
2393 ctxt->error(ctxt->userData,
2394 "param has no name\n");
2395 ctxt->nbErrors++;
2396 }
2397 param->value = xmlNodeGetContent(content);
2398 if (lastparam == NULL) {
2399 def->attrs = lastparam = param;
2400 } else {
2401 lastparam->next = param;
2402 lastparam = param;
2403 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002404 if (lib != NULL) {
2405 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00002406 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002407 content = content->next;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002408 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002409 }
Daniel Veillard416589a2003-02-17 17:25:42 +00002410 /*
2411 * Handle optional except
2412 */
2413 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
2414 xmlNodePtr child;
2415 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
2416
2417 except = xmlRelaxNGNewDefine(ctxt, node);
2418 if (except == NULL) {
2419 return(def);
2420 }
2421 except->type = XML_RELAXNG_EXCEPT;
2422 child = content->children;
2423 if (last == NULL) {
2424 def->content = except;
2425 } else {
2426 last->next = except;
2427 }
2428 if (child == NULL) {
2429 if (ctxt->error != NULL)
2430 ctxt->error(ctxt->userData,
2431 "except has no content\n");
2432 ctxt->nbErrors++;
2433 }
2434 while (child != NULL) {
2435 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
2436 if (tmp2 != NULL) {
2437 if (last2 == NULL) {
2438 except->content = last2 = tmp2;
2439 } else {
2440 last2->next = tmp2;
2441 last2 = tmp2;
2442 }
2443 }
2444 child = child->next;
2445 }
2446 content = content->next;
2447 }
2448 /*
2449 * Check there is no unhandled data
2450 */
2451 if (content != NULL) {
2452 if (ctxt->error != NULL)
2453 ctxt->error(ctxt->userData,
2454 "Element data has unexpected content %s\n", content->name);
2455 ctxt->nbErrors++;
2456 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002457
2458 return(def);
2459}
2460
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002461static const xmlChar *invalidName = BAD_CAST "\1";
2462
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002463/**
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002464 * xmlRelaxNGCompareNameClasses:
2465 * @defs1: the first element/attribute defs
2466 * @defs2: the second element/attribute defs
2467 * @name: the restriction on the name
2468 * @ns: the restriction on the namespace
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002469 *
2470 * Compare the 2 lists of element definitions. The comparison is
2471 * that if both lists do not accept the same QNames, it returns 1
2472 * If the 2 lists can accept the same QName the comparison returns 0
2473 *
2474 * Returns 1 disttinct, 0 if equal
2475 */
2476static int
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002477xmlRelaxNGCompareNameClasses(xmlRelaxNGDefinePtr def1,
2478 xmlRelaxNGDefinePtr def2) {
2479 int ret = 1;
2480 xmlNode node;
2481 xmlNs ns;
2482 xmlRelaxNGValidCtxt ctxt;
2483 ctxt.flags = FLAGS_IGNORABLE;
2484
Daniel Veillard42f12e92003-03-07 18:32:59 +00002485 memset(&ctxt, 0, sizeof(xmlRelaxNGValidCtxt));
2486
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002487 if ((def1->type == XML_RELAXNG_ELEMENT) ||
2488 (def1->type == XML_RELAXNG_ATTRIBUTE)) {
2489 if (def2->type == XML_RELAXNG_TEXT)
2490 return(1);
2491 if (def1->name != NULL) {
2492 node.name = def1->name;
2493 } else {
2494 node.name = invalidName;
2495 }
2496 node.ns = &ns;
2497 if (def1->ns != NULL) {
2498 if (def1->ns[0] == 0) {
2499 node.ns = NULL;
2500 } else {
2501 ns.href = def1->ns;
2502 }
2503 } else {
2504 ns.href = invalidName;
2505 }
2506 if (xmlRelaxNGElementMatch(&ctxt, def2, &node)) {
2507 if (def1->nameClass != NULL) {
2508 ret = xmlRelaxNGCompareNameClasses(def1->nameClass, def2);
2509 } else {
2510 ret = 0;
2511 }
2512 } else {
2513 ret = 1;
2514 }
2515 } else if (def1->type == XML_RELAXNG_TEXT) {
2516 if (def2->type == XML_RELAXNG_TEXT)
2517 return(0);
2518 return(1);
2519 } else if (def1->type == XML_RELAXNG_EXCEPT) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002520 TODO
2521 ret = 0;
2522 } else {
2523 TODO
2524 ret = 0;
2525 }
2526 if (ret == 0)
2527 return(ret);
2528 if ((def2->type == XML_RELAXNG_ELEMENT) ||
2529 (def2->type == XML_RELAXNG_ATTRIBUTE)) {
2530 if (def2->name != NULL) {
2531 node.name = def2->name;
2532 } else {
2533 node.name = invalidName;
2534 }
2535 node.ns = &ns;
2536 if (def2->ns != NULL) {
2537 if (def2->ns[0] == 0) {
2538 node.ns = NULL;
2539 } else {
2540 ns.href = def2->ns;
2541 }
2542 } else {
2543 ns.href = invalidName;
2544 }
2545 if (xmlRelaxNGElementMatch(&ctxt, def1, &node)) {
2546 if (def2->nameClass != NULL) {
2547 ret = xmlRelaxNGCompareNameClasses(def2->nameClass, def1);
2548 } else {
2549 ret = 0;
2550 }
2551 } else {
2552 ret = 1;
2553 }
2554 } else {
2555 TODO
2556 ret = 0;
2557 }
2558
2559 return(ret);
2560}
2561
2562/**
2563 * xmlRelaxNGCompareElemDefLists:
2564 * @ctxt: a Relax-NG parser context
2565 * @defs1: the first list of element/attribute defs
2566 * @defs2: the second list of element/attribute defs
2567 *
2568 * Compare the 2 lists of element or attribute definitions. The comparison
2569 * is that if both lists do not accept the same QNames, it returns 1
2570 * If the 2 lists can accept the same QName the comparison returns 0
2571 *
2572 * Returns 1 disttinct, 0 if equal
2573 */
2574static int
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002575xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2576 xmlRelaxNGDefinePtr *def1,
2577 xmlRelaxNGDefinePtr *def2) {
2578 xmlRelaxNGDefinePtr *basedef2 = def2;
2579
Daniel Veillard154877e2003-01-30 12:17:05 +00002580 if ((def1 == NULL) || (def2 == NULL))
2581 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002582 if ((*def1 == NULL) || (*def2 == NULL))
2583 return(1);
2584 while (*def1 != NULL) {
2585 while ((*def2) != NULL) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002586 if (xmlRelaxNGCompareNameClasses(*def1, *def2) == 0)
2587 return(0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002588 def2++;
2589 }
2590 def2 = basedef2;
2591 def1++;
2592 }
2593 return(1);
2594}
2595
2596/**
2597 * xmlRelaxNGGetElements:
2598 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002599 * @def: the definition definition
2600 * @eora: gather elements (0) or attributes (1)
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002601 *
2602 * Compute the list of top elements a definition can generate
2603 *
2604 * Returns a list of elements or NULL if none was found.
2605 */
2606static xmlRelaxNGDefinePtr *
2607xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002608 xmlRelaxNGDefinePtr def,
2609 int eora) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002610 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2611 int len = 0;
2612 int max = 0;
2613
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002614 /*
2615 * Don't run that check in case of error. Infinite recursion
2616 * becomes possible.
2617 */
2618 if (ctxt->nbErrors != 0)
2619 return(NULL);
2620
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002621 parent = NULL;
2622 cur = def;
2623 while (cur != NULL) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002624 if (((eora == 0) && ((cur->type == XML_RELAXNG_ELEMENT) ||
2625 (cur->type == XML_RELAXNG_TEXT))) ||
2626 ((eora == 1) && (cur->type == XML_RELAXNG_ATTRIBUTE))) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002627 if (ret == NULL) {
2628 max = 10;
2629 ret = (xmlRelaxNGDefinePtr *)
2630 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2631 if (ret == NULL) {
2632 if (ctxt->error != NULL)
2633 ctxt->error(ctxt->userData,
2634 "Out of memory in element search\n");
2635 ctxt->nbErrors++;
2636 return(NULL);
2637 }
2638 } else if (max <= len) {
2639 max *= 2;
2640 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2641 if (ret == NULL) {
2642 if (ctxt->error != NULL)
2643 ctxt->error(ctxt->userData,
2644 "Out of memory in element search\n");
2645 ctxt->nbErrors++;
2646 return(NULL);
2647 }
2648 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002649 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002650 ret[len] = NULL;
2651 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2652 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2653 (cur->type == XML_RELAXNG_GROUP) ||
2654 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002655 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2656 (cur->type == XML_RELAXNG_OPTIONAL) ||
2657 (cur->type == XML_RELAXNG_REF) ||
2658 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002659 /*
2660 * Don't go within elements or attributes or string values.
2661 * Just gather the element top list
2662 */
2663 if (cur->content != NULL) {
2664 parent = cur;
2665 cur = cur->content;
2666 tmp = cur;
2667 while (tmp != NULL) {
2668 tmp->parent = parent;
2669 tmp = tmp->next;
2670 }
2671 continue;
2672 }
2673 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002674 if (cur == def)
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002675 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002676 if (cur->next != NULL) {
2677 cur = cur->next;
2678 continue;
2679 }
2680 do {
2681 cur = cur->parent;
2682 if (cur == NULL) break;
2683 if (cur == def) return(ret);
2684 if (cur->next != NULL) {
2685 cur = cur->next;
2686 break;
2687 }
2688 } while (cur != NULL);
2689 }
2690 return(ret);
2691}
2692
2693/**
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002694 * xmlRelaxNGCheckGroupAttrs:
2695 * @ctxt: a Relax-NG parser context
2696 * @def: the group definition
2697 *
2698 * Detects violations of rule 7.3
2699 */
2700static void
2701xmlRelaxNGCheckGroupAttrs(xmlRelaxNGParserCtxtPtr ctxt,
2702 xmlRelaxNGDefinePtr def) {
2703 xmlRelaxNGDefinePtr **list;
2704 xmlRelaxNGDefinePtr cur;
2705 int nbchild = 0, i, j, ret;
2706
2707 if ((def == NULL) ||
2708 ((def->type != XML_RELAXNG_GROUP) &&
2709 (def->type != XML_RELAXNG_ELEMENT)))
2710 return;
2711
2712 /*
2713 * Don't run that check in case of error. Infinite recursion
2714 * becomes possible.
2715 */
2716 if (ctxt->nbErrors != 0)
2717 return;
2718
2719 cur = def->attrs;
2720 while (cur != NULL) {
2721 nbchild++;
2722 cur = cur->next;
2723 }
2724 cur = def->content;
2725 while (cur != NULL) {
2726 nbchild++;
2727 cur = cur->next;
2728 }
2729
2730 list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
2731 sizeof(xmlRelaxNGDefinePtr *));
2732 if (list == NULL) {
2733 if (ctxt->error != NULL)
2734 ctxt->error(ctxt->userData,
2735 "Out of memory in group computation\n");
2736 ctxt->nbErrors++;
2737 return;
2738 }
2739 i = 0;
2740 cur = def->attrs;
2741 while (cur != NULL) {
2742 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2743 i++;
2744 cur = cur->next;
2745 }
2746 cur = def->content;
2747 while (cur != NULL) {
2748 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2749 i++;
2750 cur = cur->next;
2751 }
2752
2753 for (i = 0;i < nbchild;i++) {
2754 if (list[i] == NULL)
2755 continue;
2756 for (j = 0;j < i;j++) {
2757 if (list[j] == NULL)
2758 continue;
2759 ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
2760 if (ret == 0) {
2761 if (ctxt->error != NULL)
2762 ctxt->error(ctxt->userData,
2763 "Attributes conflicts in group\n");
2764 ctxt->nbErrors++;
2765 }
2766 }
2767 }
2768 for (i = 0;i < nbchild;i++) {
2769 if (list[i] != NULL)
2770 xmlFree(list[i]);
2771 }
2772 xmlFree(list);
2773}
2774
2775/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002776 * xmlRelaxNGComputeInterleaves:
2777 * @def: the interleave definition
2778 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002779 * @name: the definition name
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002780 *
2781 * A lot of work for preprocessing interleave definitions
2782 * is potentially needed to get a decent execution speed at runtime
2783 * - trying to get a total order on the element nodes generated
2784 * by the interleaves, order the list of interleave definitions
2785 * following that order.
2786 * - if <text/> is used to handle mixed content, it is better to
2787 * flag this in the define and simplify the runtime checking
2788 * algorithm
2789 */
2790static void
2791xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2792 xmlRelaxNGParserCtxtPtr ctxt,
2793 xmlChar *name ATTRIBUTE_UNUSED) {
2794 xmlRelaxNGDefinePtr cur;
2795
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002796 xmlRelaxNGPartitionPtr partitions = NULL;
2797 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2798 xmlRelaxNGInterleaveGroupPtr group;
2799 int i,j,ret;
2800 int nbgroups = 0;
2801 int nbchild = 0;
2802
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002803 /*
2804 * Don't run that check in case of error. Infinite recursion
2805 * becomes possible.
2806 */
2807 if (ctxt->nbErrors != 0)
2808 return;
2809
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002810#ifdef DEBUG_INTERLEAVE
2811 xmlGenericError(xmlGenericErrorContext,
2812 "xmlRelaxNGComputeInterleaves(%s)\n",
2813 name);
2814#endif
2815 cur = def->content;
2816 while (cur != NULL) {
2817 nbchild++;
2818 cur = cur->next;
2819 }
2820
2821#ifdef DEBUG_INTERLEAVE
2822 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2823#endif
2824 groups = (xmlRelaxNGInterleaveGroupPtr *)
2825 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2826 if (groups == NULL)
2827 goto error;
2828 cur = def->content;
2829 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002830 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2831 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2832 if (groups[nbgroups] == NULL)
2833 goto error;
2834 groups[nbgroups]->rule = cur;
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002835 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur, 0);
2836 groups[nbgroups]->attrs = xmlRelaxNGGetElements(ctxt, cur, 1);
Daniel Veillard154877e2003-01-30 12:17:05 +00002837 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002838 cur = cur->next;
2839 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002840#ifdef DEBUG_INTERLEAVE
2841 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2842#endif
2843
2844 /*
2845 * Let's check that all rules makes a partitions according to 7.4
2846 */
2847 partitions = (xmlRelaxNGPartitionPtr)
2848 xmlMalloc(sizeof(xmlRelaxNGPartition));
2849 if (partitions == NULL)
2850 goto error;
2851 partitions->nbgroups = nbgroups;
2852 for (i = 0;i < nbgroups;i++) {
2853 group = groups[i];
2854 for (j = i+1;j < nbgroups;j++) {
2855 if (groups[j] == NULL)
2856 continue;
2857 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2858 groups[j]->defs);
2859 if (ret == 0) {
2860 if (ctxt->error != NULL)
2861 ctxt->error(ctxt->userData,
2862 "Element or text conflicts in interleave\n");
2863 ctxt->nbErrors++;
2864 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002865 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->attrs,
2866 groups[j]->attrs);
2867 if (ret == 0) {
2868 if (ctxt->error != NULL)
2869 ctxt->error(ctxt->userData,
2870 "Attributes conflicts in interleave\n");
2871 ctxt->nbErrors++;
2872 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002873 }
2874 }
2875 partitions->groups = groups;
2876
2877 /*
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002878 * and save the partition list back in the def
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002879 */
2880 def->data = partitions;
2881 return;
2882
2883error:
2884 if (ctxt->error != NULL)
2885 ctxt->error(ctxt->userData,
2886 "Out of memory in interleave computation\n");
2887 ctxt->nbErrors++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002888 if (groups != NULL) {
2889 for (i = 0;i < nbgroups;i++)
2890 if (groups[i] != NULL) {
2891 if (groups[i]->defs != NULL)
2892 xmlFree(groups[i]->defs);
2893 xmlFree(groups[i]);
2894 }
2895 xmlFree(groups);
2896 }
2897 xmlRelaxNGFreePartition(partitions);
2898}
2899
2900/**
2901 * xmlRelaxNGParseInterleave:
2902 * @ctxt: a Relax-NG parser context
2903 * @node: the data node.
2904 *
2905 * parse the content of a RelaxNG interleave node.
2906 *
2907 * Returns the definition pointer or NULL in case of error
2908 */
2909static xmlRelaxNGDefinePtr
2910xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2911 xmlRelaxNGDefinePtr def = NULL;
2912 xmlRelaxNGDefinePtr last = NULL, cur;
2913 xmlNodePtr child;
2914
2915 def = xmlRelaxNGNewDefine(ctxt, node);
2916 if (def == NULL) {
2917 return(NULL);
2918 }
2919 def->type = XML_RELAXNG_INTERLEAVE;
2920
2921 if (ctxt->interleaves == NULL)
2922 ctxt->interleaves = xmlHashCreate(10);
2923 if (ctxt->interleaves == NULL) {
2924 if (ctxt->error != NULL)
2925 ctxt->error(ctxt->userData,
2926 "Failed to create interleaves hash table\n");
2927 ctxt->nbErrors++;
2928 } else {
2929 char name[32];
2930
2931 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2932 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2933 if (ctxt->error != NULL)
2934 ctxt->error(ctxt->userData,
2935 "Failed to add %s to hash table\n", name);
2936 ctxt->nbErrors++;
2937 }
2938 }
2939 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002940 if (child == NULL) {
2941 if (ctxt->error != NULL)
2942 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2943 ctxt->nbErrors++;
2944 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002945 while (child != NULL) {
2946 if (IS_RELAXNG(child, "element")) {
2947 cur = xmlRelaxNGParseElement(ctxt, child);
2948 } else {
2949 cur = xmlRelaxNGParsePattern(ctxt, child);
2950 }
2951 if (cur != NULL) {
2952 cur->parent = def;
2953 if (last == NULL) {
2954 def->content = last = cur;
2955 } else {
2956 last->next = cur;
2957 last = cur;
2958 }
2959 }
2960 child = child->next;
2961 }
2962
2963 return(def);
2964}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002965
2966/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002967 * xmlRelaxNGParseInclude:
2968 * @ctxt: a Relax-NG parser context
2969 * @node: the include node
2970 *
2971 * Integrate the content of an include node in the current grammar
2972 *
2973 * Returns 0 in case of success or -1 in case of error
2974 */
2975static int
2976xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2977 xmlRelaxNGIncludePtr incl;
2978 xmlNodePtr root;
2979 int ret = 0, tmp;
2980
2981 incl = node->_private;
2982 if (incl == NULL) {
2983 if (ctxt->error != NULL)
2984 ctxt->error(ctxt->userData,
2985 "Include node has no data\n");
2986 ctxt->nbErrors++;
2987 return(-1);
2988 }
2989 root = xmlDocGetRootElement(incl->doc);
2990 if (root == NULL) {
2991 if (ctxt->error != NULL)
2992 ctxt->error(ctxt->userData,
2993 "Include document is empty\n");
2994 ctxt->nbErrors++;
2995 return(-1);
2996 }
2997 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2998 if (ctxt->error != NULL)
2999 ctxt->error(ctxt->userData,
3000 "Include document root is not a grammar\n");
3001 ctxt->nbErrors++;
3002 return(-1);
3003 }
3004
3005 /*
3006 * Merge the definition from both the include and the internal list
3007 */
3008 if (root->children != NULL) {
3009 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
3010 if (tmp != 0)
3011 ret = -1;
3012 }
3013 if (node->children != NULL) {
3014 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
3015 if (tmp != 0)
3016 ret = -1;
3017 }
3018 return(ret);
3019}
3020
3021/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00003022 * xmlRelaxNGParseDefine:
3023 * @ctxt: a Relax-NG parser context
3024 * @node: the define node
3025 *
3026 * parse the content of a RelaxNG define element node.
3027 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003028 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00003029 */
3030static int
3031xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3032 xmlChar *name;
3033 int ret = 0, tmp;
3034 xmlRelaxNGDefinePtr def;
3035 const xmlChar *olddefine;
3036
3037 name = xmlGetProp(node, BAD_CAST "name");
3038 if (name == NULL) {
3039 if (ctxt->error != NULL)
3040 ctxt->error(ctxt->userData,
3041 "define has no name\n");
3042 ctxt->nbErrors++;
3043 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003044 xmlRelaxNGNormExtSpace(name);
3045 if (xmlValidateNCName(name, 0)) {
3046 if (ctxt->error != NULL)
3047 ctxt->error(ctxt->userData,
3048 "define name '%s' is not an NCName\n",
3049 name);
3050 ctxt->nbErrors++;
3051 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003052 def = xmlRelaxNGNewDefine(ctxt, node);
3053 if (def == NULL) {
3054 xmlFree(name);
3055 return(-1);
3056 }
3057 def->type = XML_RELAXNG_DEF;
3058 def->name = name;
3059 if (node->children == NULL) {
3060 if (ctxt->error != NULL)
3061 ctxt->error(ctxt->userData,
3062 "define has no children\n");
3063 ctxt->nbErrors++;
3064 } else {
3065 olddefine = ctxt->define;
3066 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00003067 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00003068 ctxt->define = olddefine;
3069 }
3070 if (ctxt->grammar->defs == NULL)
3071 ctxt->grammar->defs = xmlHashCreate(10);
3072 if (ctxt->grammar->defs == NULL) {
3073 if (ctxt->error != NULL)
3074 ctxt->error(ctxt->userData,
3075 "Could not create definition hash\n");
3076 ctxt->nbErrors++;
3077 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003078 } else {
3079 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
3080 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00003081 xmlRelaxNGDefinePtr prev;
3082
3083 prev = xmlHashLookup(ctxt->grammar->defs, name);
3084 if (prev == NULL) {
3085 if (ctxt->error != NULL)
3086 ctxt->error(ctxt->userData,
3087 "Internal error on define aggregation of %s\n",
3088 name);
3089 ctxt->nbErrors++;
3090 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00003091 } else {
3092 while (prev->nextHash != NULL)
3093 prev = prev->nextHash;
3094 prev->nextHash = def;
3095 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003096 }
3097 }
3098 }
3099 return(ret);
3100}
3101
3102/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00003103 * xmlRelaxNGProcessExternalRef:
3104 * @ctxt: the parser context
3105 * @node: the externlRef node
3106 *
3107 * Process and compile an externlRef node
3108 *
3109 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
3110 */
3111static xmlRelaxNGDefinePtr
3112xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3113 xmlRelaxNGDocumentPtr docu;
3114 xmlNodePtr root, tmp;
3115 xmlChar *ns;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003116 int newNs = 0, oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003117 xmlRelaxNGDefinePtr def;
3118
3119 docu = node->_private;
3120 if (docu != NULL) {
3121 def = xmlRelaxNGNewDefine(ctxt, node);
3122 if (def == NULL)
3123 return(NULL);
3124 def->type = XML_RELAXNG_EXTERNALREF;
3125
3126 if (docu->content == NULL) {
3127 /*
3128 * Then do the parsing for good
3129 */
3130 root = xmlDocGetRootElement(docu->doc);
3131 if (root == NULL) {
3132 if (ctxt->error != NULL)
3133 ctxt->error(ctxt->userData,
3134 "xmlRelaxNGParse: %s is empty\n",
3135 ctxt->URL);
3136 ctxt->nbErrors++;
3137 return (NULL);
3138 }
3139 /*
3140 * ns transmission rules
3141 */
3142 ns = xmlGetProp(root, BAD_CAST "ns");
3143 if (ns == NULL) {
3144 tmp = node;
3145 while ((tmp != NULL) &&
3146 (tmp->type == XML_ELEMENT_NODE)) {
3147 ns = xmlGetProp(tmp, BAD_CAST "ns");
3148 if (ns != NULL) {
3149 break;
3150 }
3151 tmp = tmp->parent;
3152 }
3153 if (ns != NULL) {
3154 xmlSetProp(root, BAD_CAST "ns", ns);
3155 newNs = 1;
3156 xmlFree(ns);
3157 }
3158 } else {
3159 xmlFree(ns);
3160 }
3161
3162 /*
3163 * Parsing to get a precompiled schemas.
3164 */
Daniel Veillard77648bb2003-02-20 15:03:22 +00003165 oldflags = ctxt->flags;
3166 ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003167 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard77648bb2003-02-20 15:03:22 +00003168 ctxt->flags = oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003169 if ((docu->schema != NULL) &&
3170 (docu->schema->topgrammar != NULL)) {
3171 docu->content = docu->schema->topgrammar->start;
3172 }
3173
3174 /*
3175 * the externalRef may be reused in a different ns context
3176 */
3177 if (newNs == 1) {
3178 xmlUnsetProp(root, BAD_CAST "ns");
3179 }
3180 }
3181 def->content = docu->content;
3182 } else {
3183 def = NULL;
3184 }
3185 return(def);
3186}
3187
3188/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003189 * xmlRelaxNGParsePattern:
3190 * @ctxt: a Relax-NG parser context
3191 * @node: the pattern node.
3192 *
3193 * parse the content of a RelaxNG pattern node.
3194 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00003195 * Returns the definition pointer or NULL in case of error or if no
3196 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003197 */
3198static xmlRelaxNGDefinePtr
3199xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3200 xmlRelaxNGDefinePtr def = NULL;
3201
Daniel Veillardd2298792003-02-14 16:54:11 +00003202 if (node == NULL) {
3203 return(NULL);
3204 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003205 if (IS_RELAXNG(node, "element")) {
3206 def = xmlRelaxNGParseElement(ctxt, node);
3207 } else if (IS_RELAXNG(node, "attribute")) {
3208 def = xmlRelaxNGParseAttribute(ctxt, node);
3209 } else if (IS_RELAXNG(node, "empty")) {
3210 def = xmlRelaxNGNewDefine(ctxt, node);
3211 if (def == NULL)
3212 return(NULL);
3213 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00003214 if (node->children != NULL) {
3215 if (ctxt->error != NULL)
3216 ctxt->error(ctxt->userData, "empty: had a child node\n");
3217 ctxt->nbErrors++;
3218 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003219 } else if (IS_RELAXNG(node, "text")) {
3220 def = xmlRelaxNGNewDefine(ctxt, node);
3221 if (def == NULL)
3222 return(NULL);
3223 def->type = XML_RELAXNG_TEXT;
3224 if (node->children != NULL) {
3225 if (ctxt->error != NULL)
3226 ctxt->error(ctxt->userData, "text: had a child node\n");
3227 ctxt->nbErrors++;
3228 }
3229 } else if (IS_RELAXNG(node, "zeroOrMore")) {
3230 def = xmlRelaxNGNewDefine(ctxt, node);
3231 if (def == NULL)
3232 return(NULL);
3233 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00003234 if (node->children == NULL) {
3235 if (ctxt->error != NULL)
3236 ctxt->error(ctxt->userData,
3237 "Element %s is empty\n", node->name);
3238 ctxt->nbErrors++;
3239 } else {
3240 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
3241 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003242 } else if (IS_RELAXNG(node, "oneOrMore")) {
3243 def = xmlRelaxNGNewDefine(ctxt, node);
3244 if (def == NULL)
3245 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003246 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00003247 if (node->children == NULL) {
3248 if (ctxt->error != NULL)
3249 ctxt->error(ctxt->userData,
3250 "Element %s is empty\n", node->name);
3251 ctxt->nbErrors++;
3252 } else {
3253 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
3254 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003255 } else if (IS_RELAXNG(node, "optional")) {
3256 def = xmlRelaxNGNewDefine(ctxt, node);
3257 if (def == NULL)
3258 return(NULL);
3259 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00003260 if (node->children == NULL) {
3261 if (ctxt->error != NULL)
3262 ctxt->error(ctxt->userData,
3263 "Element %s is empty\n", node->name);
3264 ctxt->nbErrors++;
3265 } else {
3266 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
3267 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003268 } else if (IS_RELAXNG(node, "choice")) {
3269 def = xmlRelaxNGNewDefine(ctxt, node);
3270 if (def == NULL)
3271 return(NULL);
3272 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00003273 if (node->children == NULL) {
3274 if (ctxt->error != NULL)
3275 ctxt->error(ctxt->userData,
3276 "Element %s is empty\n", node->name);
3277 ctxt->nbErrors++;
3278 } else {
3279 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3280 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003281 } else if (IS_RELAXNG(node, "group")) {
3282 def = xmlRelaxNGNewDefine(ctxt, node);
3283 if (def == NULL)
3284 return(NULL);
3285 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00003286 if (node->children == NULL) {
3287 if (ctxt->error != NULL)
3288 ctxt->error(ctxt->userData,
3289 "Element %s is empty\n", node->name);
3290 ctxt->nbErrors++;
3291 } else {
3292 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3293 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003294 } else if (IS_RELAXNG(node, "ref")) {
3295 def = xmlRelaxNGNewDefine(ctxt, node);
3296 if (def == NULL)
3297 return(NULL);
3298 def->type = XML_RELAXNG_REF;
3299 def->name = xmlGetProp(node, BAD_CAST "name");
3300 if (def->name == NULL) {
3301 if (ctxt->error != NULL)
3302 ctxt->error(ctxt->userData,
3303 "ref has no name\n");
3304 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003305 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003306 xmlRelaxNGNormExtSpace(def->name);
3307 if (xmlValidateNCName(def->name, 0)) {
3308 if (ctxt->error != NULL)
3309 ctxt->error(ctxt->userData,
3310 "ref name '%s' is not an NCName\n",
3311 def->name);
3312 ctxt->nbErrors++;
3313 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003314 }
3315 if (node->children != NULL) {
3316 if (ctxt->error != NULL)
3317 ctxt->error(ctxt->userData,
3318 "ref is not empty\n");
3319 ctxt->nbErrors++;
3320 }
3321 if (ctxt->grammar->refs == NULL)
3322 ctxt->grammar->refs = xmlHashCreate(10);
3323 if (ctxt->grammar->refs == NULL) {
3324 if (ctxt->error != NULL)
3325 ctxt->error(ctxt->userData,
3326 "Could not create references hash\n");
3327 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003328 def = NULL;
3329 } else {
3330 int tmp;
3331
3332 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
3333 if (tmp < 0) {
3334 xmlRelaxNGDefinePtr prev;
3335
3336 prev = (xmlRelaxNGDefinePtr)
3337 xmlHashLookup(ctxt->grammar->refs, def->name);
3338 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00003339 if (def->name != NULL) {
3340 if (ctxt->error != NULL)
3341 ctxt->error(ctxt->userData,
3342 "Error refs definitions '%s'\n",
3343 def->name);
3344 } else {
3345 if (ctxt->error != NULL)
3346 ctxt->error(ctxt->userData,
3347 "Error refs definitions\n");
3348 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003349 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003350 def = NULL;
3351 } else {
3352 def->nextHash = prev->nextHash;
3353 prev->nextHash = def;
3354 }
3355 }
3356 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00003357 } else if (IS_RELAXNG(node, "data")) {
3358 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardedc91922003-01-26 00:52:04 +00003359 } else if (IS_RELAXNG(node, "value")) {
3360 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003361 } else if (IS_RELAXNG(node, "list")) {
3362 def = xmlRelaxNGNewDefine(ctxt, node);
3363 if (def == NULL)
3364 return(NULL);
3365 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00003366 if (node->children == NULL) {
3367 if (ctxt->error != NULL)
3368 ctxt->error(ctxt->userData,
3369 "Element %s is empty\n", node->name);
3370 ctxt->nbErrors++;
3371 } else {
3372 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3373 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003374 } else if (IS_RELAXNG(node, "interleave")) {
3375 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003376 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00003377 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003378 } else if (IS_RELAXNG(node, "notAllowed")) {
3379 def = xmlRelaxNGNewDefine(ctxt, node);
3380 if (def == NULL)
3381 return(NULL);
3382 def->type = XML_RELAXNG_NOT_ALLOWED;
3383 if (node->children != NULL) {
3384 if (ctxt->error != NULL)
3385 ctxt->error(ctxt->userData,
3386 "xmlRelaxNGParse: notAllowed element is not empty\n");
3387 ctxt->nbErrors++;
3388 }
Daniel Veillard419a7682003-02-03 23:22:49 +00003389 } else if (IS_RELAXNG(node, "grammar")) {
3390 xmlRelaxNGGrammarPtr grammar, old;
3391 xmlRelaxNGGrammarPtr oldparent;
3392
Daniel Veillardc482e262003-02-26 14:48:48 +00003393#ifdef DEBUG_GRAMMAR
3394 xmlGenericError(xmlGenericErrorContext, "Found <grammar> pattern\n");
3395#endif
3396
Daniel Veillard419a7682003-02-03 23:22:49 +00003397 oldparent = ctxt->parentgrammar;
3398 old = ctxt->grammar;
3399 ctxt->parentgrammar = old;
3400 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3401 if (old != NULL) {
3402 ctxt->grammar = old;
3403 ctxt->parentgrammar = oldparent;
Daniel Veillardc482e262003-02-26 14:48:48 +00003404#if 0
Daniel Veillard419a7682003-02-03 23:22:49 +00003405 if (grammar != NULL) {
3406 grammar->next = old->next;
3407 old->next = grammar;
3408 }
Daniel Veillardc482e262003-02-26 14:48:48 +00003409#endif
Daniel Veillard419a7682003-02-03 23:22:49 +00003410 }
3411 if (grammar != NULL)
3412 def = grammar->start;
3413 else
3414 def = NULL;
3415 } else if (IS_RELAXNG(node, "parentRef")) {
3416 if (ctxt->parentgrammar == NULL) {
3417 if (ctxt->error != NULL)
3418 ctxt->error(ctxt->userData,
3419 "Use of parentRef without a parent grammar\n");
3420 ctxt->nbErrors++;
3421 return(NULL);
3422 }
3423 def = xmlRelaxNGNewDefine(ctxt, node);
3424 if (def == NULL)
3425 return(NULL);
3426 def->type = XML_RELAXNG_PARENTREF;
3427 def->name = xmlGetProp(node, BAD_CAST "name");
3428 if (def->name == NULL) {
3429 if (ctxt->error != NULL)
3430 ctxt->error(ctxt->userData,
3431 "parentRef has no name\n");
3432 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00003433 } else {
3434 xmlRelaxNGNormExtSpace(def->name);
3435 if (xmlValidateNCName(def->name, 0)) {
3436 if (ctxt->error != NULL)
3437 ctxt->error(ctxt->userData,
3438 "parentRef name '%s' is not an NCName\n",
3439 def->name);
3440 ctxt->nbErrors++;
3441 }
Daniel Veillard419a7682003-02-03 23:22:49 +00003442 }
3443 if (node->children != NULL) {
3444 if (ctxt->error != NULL)
3445 ctxt->error(ctxt->userData,
3446 "parentRef is not empty\n");
3447 ctxt->nbErrors++;
3448 }
3449 if (ctxt->parentgrammar->refs == NULL)
3450 ctxt->parentgrammar->refs = xmlHashCreate(10);
3451 if (ctxt->parentgrammar->refs == NULL) {
3452 if (ctxt->error != NULL)
3453 ctxt->error(ctxt->userData,
3454 "Could not create references hash\n");
3455 ctxt->nbErrors++;
3456 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00003457 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00003458 int tmp;
3459
3460 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
3461 if (tmp < 0) {
3462 xmlRelaxNGDefinePtr prev;
3463
3464 prev = (xmlRelaxNGDefinePtr)
3465 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
3466 if (prev == NULL) {
3467 if (ctxt->error != NULL)
3468 ctxt->error(ctxt->userData,
3469 "Internal error parentRef definitions '%s'\n",
3470 def->name);
3471 ctxt->nbErrors++;
3472 def = NULL;
3473 } else {
3474 def->nextHash = prev->nextHash;
3475 prev->nextHash = def;
3476 }
3477 }
3478 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003479 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00003480 if (node->children == NULL) {
3481 if (ctxt->error != NULL)
3482 ctxt->error(ctxt->userData,
3483 "Mixed is empty\n");
3484 ctxt->nbErrors++;
3485 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00003486 } else {
3487 def = xmlRelaxNGParseInterleave(ctxt, node);
3488 if (def != NULL) {
3489 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003490
3491 if ((def->content != NULL) && (def->content->next != NULL)) {
3492 tmp = xmlRelaxNGNewDefine(ctxt, node);
3493 if (tmp != NULL) {
3494 tmp->type = XML_RELAXNG_GROUP;
3495 tmp->content = def->content;
3496 def->content = tmp;
3497 }
3498 }
3499
Daniel Veillard416589a2003-02-17 17:25:42 +00003500 tmp = xmlRelaxNGNewDefine(ctxt, node);
3501 if (tmp == NULL)
3502 return(def);
3503 tmp->type = XML_RELAXNG_TEXT;
3504 tmp->next = def->content;
3505 def->content = tmp;
3506 }
3507 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003508 } else {
3509 if (ctxt->error != NULL)
3510 ctxt->error(ctxt->userData,
3511 "Unexpected node %s is not a pattern\n",
3512 node->name);
3513 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003514 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003515 }
3516 return(def);
3517}
3518
3519/**
3520 * xmlRelaxNGParseAttribute:
3521 * @ctxt: a Relax-NG parser context
3522 * @node: the element node
3523 *
3524 * parse the content of a RelaxNG attribute node.
3525 *
3526 * Returns the definition pointer or NULL in case of error.
3527 */
3528static xmlRelaxNGDefinePtr
3529xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00003530 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003531 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003532 int old_flags;
3533
3534 ret = xmlRelaxNGNewDefine(ctxt, node);
3535 if (ret == NULL)
3536 return(NULL);
3537 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003538 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003539 child = node->children;
3540 if (child == NULL) {
3541 if (ctxt->error != NULL)
3542 ctxt->error(ctxt->userData,
3543 "xmlRelaxNGParseattribute: attribute has no children\n");
3544 ctxt->nbErrors++;
3545 return(ret);
3546 }
3547 old_flags = ctxt->flags;
3548 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003549 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3550 if (cur != NULL)
3551 child = child->next;
3552
Daniel Veillardd2298792003-02-14 16:54:11 +00003553 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003554 cur = xmlRelaxNGParsePattern(ctxt, child);
3555 if (cur != NULL) {
3556 switch (cur->type) {
3557 case XML_RELAXNG_EMPTY:
3558 case XML_RELAXNG_NOT_ALLOWED:
3559 case XML_RELAXNG_TEXT:
3560 case XML_RELAXNG_ELEMENT:
3561 case XML_RELAXNG_DATATYPE:
3562 case XML_RELAXNG_VALUE:
3563 case XML_RELAXNG_LIST:
3564 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003565 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003566 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003567 case XML_RELAXNG_DEF:
3568 case XML_RELAXNG_ONEORMORE:
3569 case XML_RELAXNG_ZEROORMORE:
3570 case XML_RELAXNG_OPTIONAL:
3571 case XML_RELAXNG_CHOICE:
3572 case XML_RELAXNG_GROUP:
3573 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard77648bb2003-02-20 15:03:22 +00003574 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00003575 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003576 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003577 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003578 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003579 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003580 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00003581 if (ctxt->error != NULL)
3582 ctxt->error(ctxt->userData,
3583 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003584 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003585 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003586 case XML_RELAXNG_NOOP:
3587 TODO
3588 if (ctxt->error != NULL)
3589 ctxt->error(ctxt->userData,
3590 "Internal error, noop found\n");
3591 ctxt->nbErrors++;
3592 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003593 }
3594 }
3595 child = child->next;
3596 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003597 if (child != NULL) {
3598 if (ctxt->error != NULL)
3599 ctxt->error(ctxt->userData, "attribute has multiple children\n");
3600 ctxt->nbErrors++;
3601 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003602 ctxt->flags = old_flags;
3603 return(ret);
3604}
3605
3606/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003607 * xmlRelaxNGParseExceptNameClass:
3608 * @ctxt: a Relax-NG parser context
3609 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00003610 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003611 *
3612 * parse the content of a RelaxNG nameClass node.
3613 *
3614 * Returns the definition pointer or NULL in case of error.
3615 */
3616static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00003617xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
3618 xmlNodePtr node, int attr) {
3619 xmlRelaxNGDefinePtr ret, cur, last = NULL;
3620 xmlNodePtr child;
3621
Daniel Veillardd2298792003-02-14 16:54:11 +00003622 if (!IS_RELAXNG(node, "except")) {
3623 if (ctxt->error != NULL)
3624 ctxt->error(ctxt->userData,
3625 "Expecting an except node\n");
3626 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00003627 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00003628 }
3629 if (node->next != NULL) {
3630 if (ctxt->error != NULL)
3631 ctxt->error(ctxt->userData,
3632 "exceptNameClass allows only a single except node\n");
3633 ctxt->nbErrors++;
3634 }
Daniel Veillard144fae12003-02-03 13:17:57 +00003635 if (node->children == NULL) {
3636 if (ctxt->error != NULL)
3637 ctxt->error(ctxt->userData,
3638 "except has no content\n");
3639 ctxt->nbErrors++;
3640 return(NULL);
3641 }
3642
3643 ret = xmlRelaxNGNewDefine(ctxt, node);
3644 if (ret == NULL)
3645 return(NULL);
3646 ret->type = XML_RELAXNG_EXCEPT;
3647 child = node->children;
3648 while (child != NULL) {
3649 cur = xmlRelaxNGNewDefine(ctxt, child);
3650 if (cur == NULL)
3651 break;
3652 if (attr)
3653 cur->type = XML_RELAXNG_ATTRIBUTE;
3654 else
3655 cur->type = XML_RELAXNG_ELEMENT;
3656
Daniel Veillard419a7682003-02-03 23:22:49 +00003657 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00003658 if (last == NULL) {
3659 ret->content = cur;
3660 } else {
3661 last->next = cur;
3662 }
3663 last = cur;
3664 }
3665 child = child->next;
3666 }
3667
3668 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003669}
3670
3671/**
3672 * xmlRelaxNGParseNameClass:
3673 * @ctxt: a Relax-NG parser context
3674 * @node: the nameClass node
3675 * @def: the current definition
3676 *
3677 * parse the content of a RelaxNG nameClass node.
3678 *
3679 * Returns the definition pointer or NULL in case of error.
3680 */
3681static xmlRelaxNGDefinePtr
3682xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
3683 xmlRelaxNGDefinePtr def) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003684 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003685 xmlChar *val;
3686
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003687 ret = def;
3688 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
3689 (IS_RELAXNG(node, "nsName"))) {
3690 if ((def->type != XML_RELAXNG_ELEMENT) &&
3691 (def->type != XML_RELAXNG_ATTRIBUTE)) {
3692 ret = xmlRelaxNGNewDefine(ctxt, node);
3693 if (ret == NULL)
3694 return(NULL);
3695 ret->parent = def;
3696 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
3697 ret->type = XML_RELAXNG_ATTRIBUTE;
3698 else
3699 ret->type = XML_RELAXNG_ELEMENT;
3700 }
3701 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003702 if (IS_RELAXNG(node, "name")) {
3703 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003704 xmlRelaxNGNormExtSpace(val);
3705 if (xmlValidateNCName(val, 0)) {
3706 if (ctxt->error != NULL) {
3707 if (node->parent != NULL)
3708 ctxt->error(ctxt->userData,
3709 "Element %s name '%s' is not an NCName\n",
3710 node->parent->name, val);
3711 else
3712 ctxt->error(ctxt->userData,
3713 "name '%s' is not an NCName\n",
3714 val);
3715 }
3716 ctxt->nbErrors++;
3717 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003718 ret->name = val;
3719 val = xmlGetProp(node, BAD_CAST "ns");
3720 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003721 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3722 (val != NULL) &&
3723 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3724 ctxt->error(ctxt->userData,
3725 "Attribute with namespace '%s' is not allowed\n",
3726 val);
3727 ctxt->nbErrors++;
3728 }
3729 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3730 (val != NULL) &&
3731 (val[0] == 0) &&
3732 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3733 ctxt->error(ctxt->userData,
3734 "Attribute with QName 'xmlns' is not allowed\n",
3735 val);
3736 ctxt->nbErrors++;
3737 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003738 } else if (IS_RELAXNG(node, "anyName")) {
3739 ret->name = NULL;
3740 ret->ns = NULL;
3741 if (node->children != NULL) {
3742 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003743 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3744 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003745 }
3746 } else if (IS_RELAXNG(node, "nsName")) {
3747 ret->name = NULL;
3748 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3749 if (ret->ns == NULL) {
3750 if (ctxt->error != NULL)
3751 ctxt->error(ctxt->userData,
3752 "nsName has no ns attribute\n");
3753 ctxt->nbErrors++;
3754 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003755 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3756 (ret->ns != NULL) &&
3757 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3758 ctxt->error(ctxt->userData,
3759 "Attribute with namespace '%s' is not allowed\n",
3760 ret->ns);
3761 ctxt->nbErrors++;
3762 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003763 if (node->children != NULL) {
3764 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003765 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3766 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003767 }
3768 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003769 xmlNodePtr child;
3770 xmlRelaxNGDefinePtr last = NULL;
3771
Daniel Veillardd4310742003-02-18 21:12:46 +00003772 ret = xmlRelaxNGNewDefine(ctxt, node);
3773 if (ret == NULL)
3774 return(NULL);
3775 ret->parent = def;
3776 ret->type = XML_RELAXNG_CHOICE;
3777
Daniel Veillardd2298792003-02-14 16:54:11 +00003778 if (node->children == NULL) {
3779 if (ctxt->error != NULL)
3780 ctxt->error(ctxt->userData,
3781 "Element choice is empty\n");
3782 ctxt->nbErrors++;
3783 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003784
3785 child = node->children;
3786 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003787 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003788 if (tmp != NULL) {
3789 if (last == NULL) {
3790 last = ret->nameClass = tmp;
3791 } else {
3792 last->next = tmp;
3793 last = tmp;
3794 }
3795 }
3796 child = child->next;
3797 }
3798 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003799 } else {
3800 if (ctxt->error != NULL)
3801 ctxt->error(ctxt->userData,
3802 "expecting name, anyName, nsName or choice : got %s\n",
3803 node->name);
3804 ctxt->nbErrors++;
3805 return(NULL);
3806 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003807 if (ret != def) {
3808 if (def->nameClass == NULL) {
3809 def->nameClass = ret;
3810 } else {
3811 tmp = def->nameClass;
3812 while (tmp->next != NULL) {
3813 tmp = tmp->next;
3814 }
3815 tmp->next = ret;
3816 }
3817 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003818 return(ret);
3819}
3820
3821/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003822 * xmlRelaxNGParseElement:
3823 * @ctxt: a Relax-NG parser context
3824 * @node: the element node
3825 *
3826 * parse the content of a RelaxNG element node.
3827 *
3828 * Returns the definition pointer or NULL in case of error.
3829 */
3830static xmlRelaxNGDefinePtr
3831xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3832 xmlRelaxNGDefinePtr ret, cur, last;
3833 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003834 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003835
3836 ret = xmlRelaxNGNewDefine(ctxt, node);
3837 if (ret == NULL)
3838 return(NULL);
3839 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003840 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003841 child = node->children;
3842 if (child == NULL) {
3843 if (ctxt->error != NULL)
3844 ctxt->error(ctxt->userData,
3845 "xmlRelaxNGParseElement: element has no children\n");
3846 ctxt->nbErrors++;
3847 return(ret);
3848 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003849 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3850 if (cur != NULL)
3851 child = child->next;
3852
Daniel Veillard6eadf632003-01-23 18:29:16 +00003853 if (child == NULL) {
3854 if (ctxt->error != NULL)
3855 ctxt->error(ctxt->userData,
3856 "xmlRelaxNGParseElement: element has no content\n");
3857 ctxt->nbErrors++;
3858 return(ret);
3859 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003860 olddefine = ctxt->define;
3861 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003862 last = NULL;
3863 while (child != NULL) {
3864 cur = xmlRelaxNGParsePattern(ctxt, child);
3865 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003866 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003867 switch (cur->type) {
3868 case XML_RELAXNG_EMPTY:
3869 case XML_RELAXNG_NOT_ALLOWED:
3870 case XML_RELAXNG_TEXT:
3871 case XML_RELAXNG_ELEMENT:
3872 case XML_RELAXNG_DATATYPE:
3873 case XML_RELAXNG_VALUE:
3874 case XML_RELAXNG_LIST:
3875 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003876 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003877 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003878 case XML_RELAXNG_DEF:
3879 case XML_RELAXNG_ZEROORMORE:
3880 case XML_RELAXNG_ONEORMORE:
3881 case XML_RELAXNG_OPTIONAL:
3882 case XML_RELAXNG_CHOICE:
3883 case XML_RELAXNG_GROUP:
3884 case XML_RELAXNG_INTERLEAVE:
3885 if (last == NULL) {
3886 ret->content = last = cur;
3887 } else {
3888 if ((last->type == XML_RELAXNG_ELEMENT) &&
3889 (ret->content == last)) {
3890 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3891 if (ret->content != NULL) {
3892 ret->content->type = XML_RELAXNG_GROUP;
3893 ret->content->content = last;
3894 } else {
3895 ret->content = last;
3896 }
3897 }
3898 last->next = cur;
3899 last = cur;
3900 }
3901 break;
3902 case XML_RELAXNG_ATTRIBUTE:
3903 cur->next = ret->attrs;
3904 ret->attrs = cur;
3905 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003906 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003907 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003908 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003909 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003910 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003911 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003912 case XML_RELAXNG_NOOP:
3913 TODO
3914 if (ctxt->error != NULL)
3915 ctxt->error(ctxt->userData,
3916 "Internal error, noop found\n");
3917 ctxt->nbErrors++;
3918 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003919 }
3920 }
3921 child = child->next;
3922 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003923 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003924 return(ret);
3925}
3926
3927/**
3928 * xmlRelaxNGParsePatterns:
3929 * @ctxt: a Relax-NG parser context
3930 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003931 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003932 *
3933 * parse the content of a RelaxNG start node.
3934 *
3935 * Returns the definition pointer or NULL in case of error.
3936 */
3937static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003938xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3939 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003940 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003941
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003942 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003943 while (nodes != NULL) {
3944 if (IS_RELAXNG(nodes, "element")) {
3945 cur = xmlRelaxNGParseElement(ctxt, nodes);
3946 if (def == NULL) {
3947 def = last = cur;
3948 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003949 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3950 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003951 def = xmlRelaxNGNewDefine(ctxt, nodes);
3952 def->type = XML_RELAXNG_GROUP;
3953 def->content = last;
3954 }
3955 last->next = cur;
3956 last = cur;
3957 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003958 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003959 } else {
3960 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003961 if (cur != NULL) {
3962 if (def == NULL) {
3963 def = last = cur;
3964 } else {
3965 last->next = cur;
3966 last = cur;
3967 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003968 }
3969 }
3970 nodes = nodes->next;
3971 }
3972 return(def);
3973}
3974
3975/**
3976 * xmlRelaxNGParseStart:
3977 * @ctxt: a Relax-NG parser context
3978 * @nodes: start children nodes
3979 *
3980 * parse the content of a RelaxNG start node.
3981 *
3982 * Returns 0 in case of success, -1 in case of error
3983 */
3984static int
3985xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3986 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003987 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003988
Daniel Veillardd2298792003-02-14 16:54:11 +00003989 if (nodes == NULL) {
3990 if (ctxt->error != NULL)
3991 ctxt->error(ctxt->userData,
3992 "start has no children\n");
3993 ctxt->nbErrors++;
3994 return(-1);
3995 }
3996 if (IS_RELAXNG(nodes, "empty")) {
3997 def = xmlRelaxNGNewDefine(ctxt, nodes);
3998 if (def == NULL)
3999 return(-1);
4000 def->type = XML_RELAXNG_EMPTY;
4001 if (nodes->children != NULL) {
4002 if (ctxt->error != NULL)
4003 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004004 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004005 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004006 } else if (IS_RELAXNG(nodes, "notAllowed")) {
4007 def = xmlRelaxNGNewDefine(ctxt, nodes);
4008 if (def == NULL)
4009 return(-1);
4010 def->type = XML_RELAXNG_NOT_ALLOWED;
4011 if (nodes->children != NULL) {
4012 if (ctxt->error != NULL)
4013 ctxt->error(ctxt->userData,
4014 "element notAllowed is not empty\n");
4015 ctxt->nbErrors++;
4016 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004017 } else {
4018 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00004019 }
4020 if (ctxt->grammar->start != NULL) {
4021 last = ctxt->grammar->start;
4022 while (last->next != NULL)
4023 last = last->next;
4024 last->next = def;
4025 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004026 ctxt->grammar->start = def;
4027 }
4028 nodes = nodes->next;
4029 if (nodes != NULL) {
4030 if (ctxt->error != NULL)
4031 ctxt->error(ctxt->userData,
4032 "start more than one children\n");
4033 ctxt->nbErrors++;
4034 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004035 }
4036 return(ret);
4037}
4038
4039/**
4040 * xmlRelaxNGParseGrammarContent:
4041 * @ctxt: a Relax-NG parser context
4042 * @nodes: grammar children nodes
4043 *
4044 * parse the content of a RelaxNG grammar node.
4045 *
4046 * Returns 0 in case of success, -1 in case of error
4047 */
4048static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004049xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00004050{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004051 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004052
4053 if (nodes == NULL) {
4054 if (ctxt->error != NULL)
4055 ctxt->error(ctxt->userData,
4056 "grammar has no children\n");
4057 ctxt->nbErrors++;
4058 return(-1);
4059 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004060 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004061 if (IS_RELAXNG(nodes, "start")) {
4062 if (nodes->children == NULL) {
4063 if (ctxt->error != NULL)
4064 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00004065 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004066 ctxt->nbErrors++;
4067 } else {
4068 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
4069 if (tmp != 0)
4070 ret = -1;
4071 }
4072 } else if (IS_RELAXNG(nodes, "define")) {
4073 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
4074 if (tmp != 0)
4075 ret = -1;
4076 } else if (IS_RELAXNG(nodes, "include")) {
4077 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
4078 if (tmp != 0)
4079 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004080 } else {
4081 if (ctxt->error != NULL)
4082 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004083 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004084 ctxt->nbErrors++;
4085 ret = -1;
4086 }
4087 nodes = nodes->next;
4088 }
4089 return (ret);
4090}
4091
4092/**
4093 * xmlRelaxNGCheckReference:
4094 * @ref: the ref
4095 * @ctxt: a Relax-NG parser context
4096 * @name: the name associated to the defines
4097 *
4098 * Applies the 4.17. combine attribute rule for all the define
4099 * element of a given grammar using the same name.
4100 */
4101static void
4102xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
4103 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
4104 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004105 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004106
4107 grammar = ctxt->grammar;
4108 if (grammar == NULL) {
4109 if (ctxt->error != NULL)
4110 ctxt->error(ctxt->userData,
4111 "Internal error: no grammar in CheckReference %s\n",
4112 name);
4113 ctxt->nbErrors++;
4114 return;
4115 }
4116 if (ref->content != NULL) {
4117 if (ctxt->error != NULL)
4118 ctxt->error(ctxt->userData,
4119 "Internal error: reference has content in CheckReference %s\n",
4120 name);
4121 ctxt->nbErrors++;
4122 return;
4123 }
4124 if (grammar->defs != NULL) {
4125 def = xmlHashLookup(grammar->defs, name);
4126 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00004127 cur = ref;
4128 while (cur != NULL) {
4129 cur->content = def;
4130 cur = cur->nextHash;
4131 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004132 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00004133 if (ctxt->error != NULL)
4134 ctxt->error(ctxt->userData,
4135 "Reference %s has no matching definition\n",
4136 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004137 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004138 }
Daniel Veillardd4310742003-02-18 21:12:46 +00004139 } else {
4140 if (ctxt->error != NULL)
4141 ctxt->error(ctxt->userData,
4142 "Reference %s has no matching definition\n",
4143 name);
4144 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004145 }
4146 /*
4147 * TODO: make a closure and verify there is no loop !
4148 */
4149}
4150
4151/**
4152 * xmlRelaxNGCheckCombine:
4153 * @define: the define(s) list
4154 * @ctxt: a Relax-NG parser context
4155 * @name: the name associated to the defines
4156 *
4157 * Applies the 4.17. combine attribute rule for all the define
4158 * element of a given grammar using the same name.
4159 */
4160static void
4161xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
4162 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
4163 xmlChar *combine;
4164 int choiceOrInterleave = -1;
4165 int missing = 0;
4166 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
4167
4168 if (define->nextHash == NULL)
4169 return;
4170 cur = define;
4171 while (cur != NULL) {
4172 combine = xmlGetProp(cur->node, BAD_CAST "combine");
4173 if (combine != NULL) {
4174 if (xmlStrEqual(combine, BAD_CAST "choice")) {
4175 if (choiceOrInterleave == -1)
4176 choiceOrInterleave = 1;
4177 else if (choiceOrInterleave == 0) {
4178 if (ctxt->error != NULL)
4179 ctxt->error(ctxt->userData,
4180 "Defines for %s use both 'choice' and 'interleave'\n",
4181 name);
4182 ctxt->nbErrors++;
4183 }
Daniel Veillard154877e2003-01-30 12:17:05 +00004184 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004185 if (choiceOrInterleave == -1)
4186 choiceOrInterleave = 0;
4187 else if (choiceOrInterleave == 1) {
4188 if (ctxt->error != NULL)
4189 ctxt->error(ctxt->userData,
4190 "Defines for %s use both 'choice' and 'interleave'\n",
4191 name);
4192 ctxt->nbErrors++;
4193 }
4194 } else {
4195 if (ctxt->error != NULL)
4196 ctxt->error(ctxt->userData,
4197 "Defines for %s use unknown combine value '%s''\n",
4198 name, combine);
4199 ctxt->nbErrors++;
4200 }
4201 xmlFree(combine);
4202 } else {
4203 if (missing == 0)
4204 missing = 1;
4205 else {
4206 if (ctxt->error != NULL)
4207 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00004208 "Some defines for %s needs the combine attribute\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00004209 name);
4210 ctxt->nbErrors++;
4211 }
4212 }
4213
4214 cur = cur->nextHash;
4215 }
4216#ifdef DEBUG
4217 xmlGenericError(xmlGenericErrorContext,
4218 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
4219 name, choiceOrInterleave);
4220#endif
4221 if (choiceOrInterleave == -1)
4222 choiceOrInterleave = 0;
4223 cur = xmlRelaxNGNewDefine(ctxt, define->node);
4224 if (cur == NULL)
4225 return;
4226 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00004227 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00004228 else
4229 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004230 tmp = define;
4231 last = NULL;
4232 while (tmp != NULL) {
4233 if (tmp->content != NULL) {
4234 if (tmp->content->next != NULL) {
4235 /*
4236 * we need first to create a wrapper.
4237 */
4238 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
4239 if (tmp2 == NULL)
4240 break;
4241 tmp2->type = XML_RELAXNG_GROUP;
4242 tmp2->content = tmp->content;
4243 } else {
4244 tmp2 = tmp->content;
4245 }
4246 if (last == NULL) {
4247 cur->content = tmp2;
4248 } else {
4249 last->next = tmp2;
4250 }
4251 last = tmp2;
4252 tmp->content = NULL;
4253 }
4254 tmp = tmp->nextHash;
4255 }
4256 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00004257 if (choiceOrInterleave == 0) {
4258 if (ctxt->interleaves == NULL)
4259 ctxt->interleaves = xmlHashCreate(10);
4260 if (ctxt->interleaves == NULL) {
4261 if (ctxt->error != NULL)
4262 ctxt->error(ctxt->userData,
4263 "Failed to create interleaves hash table\n");
4264 ctxt->nbErrors++;
4265 } else {
4266 char tmpname[32];
4267
4268 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
4269 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
4270 if (ctxt->error != NULL)
4271 ctxt->error(ctxt->userData,
4272 "Failed to add %s to hash table\n", tmpname);
4273 ctxt->nbErrors++;
4274 }
4275 }
4276 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004277}
4278
4279/**
4280 * xmlRelaxNGCombineStart:
4281 * @ctxt: a Relax-NG parser context
4282 * @grammar: the grammar
4283 *
4284 * Applies the 4.17. combine rule for all the start
4285 * element of a given grammar.
4286 */
4287static void
4288xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
4289 xmlRelaxNGGrammarPtr grammar) {
4290 xmlRelaxNGDefinePtr starts;
4291 xmlChar *combine;
4292 int choiceOrInterleave = -1;
4293 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004294 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004295
Daniel Veillard2df2de22003-02-17 23:34:33 +00004296 starts = grammar->start;
4297 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00004298 return;
4299 cur = starts;
4300 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00004301 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
4302 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
4303 combine = NULL;
4304 if (ctxt->error != NULL)
4305 ctxt->error(ctxt->userData,
4306 "Internal error: start element not found\n");
4307 ctxt->nbErrors++;
4308 } else {
4309 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
4310 }
4311
Daniel Veillard6eadf632003-01-23 18:29:16 +00004312 if (combine != NULL) {
4313 if (xmlStrEqual(combine, BAD_CAST "choice")) {
4314 if (choiceOrInterleave == -1)
4315 choiceOrInterleave = 1;
4316 else if (choiceOrInterleave == 0) {
4317 if (ctxt->error != NULL)
4318 ctxt->error(ctxt->userData,
4319 "<start> use both 'choice' and 'interleave'\n");
4320 ctxt->nbErrors++;
4321 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00004322 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004323 if (choiceOrInterleave == -1)
4324 choiceOrInterleave = 0;
4325 else if (choiceOrInterleave == 1) {
4326 if (ctxt->error != NULL)
4327 ctxt->error(ctxt->userData,
4328 "<start> use both 'choice' and 'interleave'\n");
4329 ctxt->nbErrors++;
4330 }
4331 } else {
4332 if (ctxt->error != NULL)
4333 ctxt->error(ctxt->userData,
4334 "<start> uses unknown combine value '%s''\n", combine);
4335 ctxt->nbErrors++;
4336 }
4337 xmlFree(combine);
4338 } else {
4339 if (missing == 0)
4340 missing = 1;
4341 else {
4342 if (ctxt->error != NULL)
4343 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00004344 "Some <start> element miss the combine attribute\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +00004345 ctxt->nbErrors++;
4346 }
4347 }
4348
Daniel Veillard2df2de22003-02-17 23:34:33 +00004349 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004350 }
4351#ifdef DEBUG
4352 xmlGenericError(xmlGenericErrorContext,
4353 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
4354 choiceOrInterleave);
4355#endif
4356 if (choiceOrInterleave == -1)
4357 choiceOrInterleave = 0;
4358 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
4359 if (cur == NULL)
4360 return;
4361 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00004362 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004363 else
4364 cur->type = XML_RELAXNG_CHOICE;
4365 cur->content = grammar->start;
4366 grammar->start = cur;
4367 if (choiceOrInterleave == 0) {
4368 if (ctxt->interleaves == NULL)
4369 ctxt->interleaves = xmlHashCreate(10);
4370 if (ctxt->interleaves == NULL) {
4371 if (ctxt->error != NULL)
4372 ctxt->error(ctxt->userData,
4373 "Failed to create interleaves hash table\n");
4374 ctxt->nbErrors++;
4375 } else {
4376 char tmpname[32];
4377
4378 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
4379 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
4380 if (ctxt->error != NULL)
4381 ctxt->error(ctxt->userData,
4382 "Failed to add %s to hash table\n", tmpname);
4383 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004384 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004385 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004386 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004387}
4388
4389/**
Daniel Veillardd4310742003-02-18 21:12:46 +00004390 * xmlRelaxNGCheckCycles:
4391 * @ctxt: a Relax-NG parser context
4392 * @nodes: grammar children nodes
4393 * @depth: the counter
4394 *
4395 * Check for cycles.
4396 *
4397 * Returns 0 if check passed, and -1 in case of error
4398 */
4399static int
4400xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
4401 xmlRelaxNGDefinePtr cur, int depth) {
4402 int ret = 0;
4403
4404 while ((ret == 0) && (cur != NULL)) {
4405 if ((cur->type == XML_RELAXNG_REF) ||
4406 (cur->type == XML_RELAXNG_PARENTREF)) {
4407 if (cur->depth == -1) {
4408 cur->depth = depth;
4409 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
4410 cur->depth = -2;
4411 } else if (depth == cur->depth) {
4412 if (ctxt->error != NULL)
4413 ctxt->error(ctxt->userData,
4414 "Detected a cycle in %s references\n", cur->name);
4415 ctxt->nbErrors++;
4416 return(-1);
4417 }
4418 } else if (cur->type == XML_RELAXNG_ELEMENT) {
4419 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
4420 } else {
4421 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
4422 }
4423 cur = cur->next;
4424 }
4425 return(ret);
4426}
4427
4428/**
Daniel Veillard77648bb2003-02-20 15:03:22 +00004429 * xmlRelaxNGTryUnlink:
4430 * @ctxt: a Relax-NG parser context
4431 * @cur: the definition to unlink
4432 * @parent: the parent definition
4433 * @prev: the previous sibling definition
4434 *
4435 * Try to unlink a definition. If not possble make it a NOOP
4436 *
4437 * Returns the new prev definition
4438 */
4439static xmlRelaxNGDefinePtr
4440xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
4441 xmlRelaxNGDefinePtr cur,
4442 xmlRelaxNGDefinePtr parent,
4443 xmlRelaxNGDefinePtr prev) {
4444 if (prev != NULL) {
4445 prev->next = cur->next;
4446 } else {
4447 if (parent != NULL) {
4448 if (parent->content == cur)
4449 parent->content = cur->next;
4450 else if (parent->attrs == cur)
4451 parent->attrs = cur->next;
4452 else if (parent->nameClass == cur)
4453 parent->nameClass = cur->next;
4454 } else {
4455 cur->type = XML_RELAXNG_NOOP;
4456 prev = cur;
4457 }
4458 }
4459 return(prev);
4460}
4461
4462/**
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004463 * xmlRelaxNGSimplify:
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004464 * @ctxt: a Relax-NG parser context
4465 * @nodes: grammar children nodes
4466 *
4467 * Check for simplification of empty and notAllowed
4468 */
4469static void
4470xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
4471 xmlRelaxNGDefinePtr cur,
4472 xmlRelaxNGDefinePtr parent) {
4473 xmlRelaxNGDefinePtr prev = NULL;
4474
4475 while (cur != NULL) {
4476 if ((cur->type == XML_RELAXNG_REF) ||
4477 (cur->type == XML_RELAXNG_PARENTREF)) {
4478 if (cur->depth != -3) {
4479 cur->depth = -3;
4480 xmlRelaxNGSimplify(ctxt, cur->content, cur);
4481 }
4482 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004483 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004484 if ((parent != NULL) &&
4485 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4486 (parent->type == XML_RELAXNG_LIST) ||
4487 (parent->type == XML_RELAXNG_GROUP) ||
4488 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4489 (parent->type == XML_RELAXNG_ONEORMORE) ||
4490 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4491 parent->type = XML_RELAXNG_NOT_ALLOWED;
4492 break;
4493 }
4494 if ((parent != NULL) &&
4495 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004496 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004497 } else
4498 prev = cur;
4499 } else if (cur->type == XML_RELAXNG_EMPTY){
Daniel Veillard77648bb2003-02-20 15:03:22 +00004500 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004501 if ((parent != NULL) &&
4502 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4503 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4504 parent->type = XML_RELAXNG_EMPTY;
4505 break;
4506 }
4507 if ((parent != NULL) &&
4508 ((parent->type == XML_RELAXNG_GROUP) ||
4509 (parent->type == XML_RELAXNG_INTERLEAVE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004510 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004511 } else
4512 prev = cur;
4513 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004514 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004515 if (cur->content != NULL)
4516 xmlRelaxNGSimplify(ctxt, cur->content, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004517 if (cur->attrs != NULL)
4518 xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
4519 if (cur->nameClass != NULL)
4520 xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004521 /*
4522 * This may result in a simplification
4523 */
4524 if ((cur->type == XML_RELAXNG_GROUP) ||
4525 (cur->type == XML_RELAXNG_INTERLEAVE)) {
4526 if (cur->content == NULL)
4527 cur->type = XML_RELAXNG_EMPTY;
4528 else if (cur->content->next == NULL) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004529 if ((parent == NULL) && (prev == NULL)) {
4530 cur->type = XML_RELAXNG_NOOP;
4531 } else if (prev == NULL) {
4532 parent->content = cur->content;
4533 cur->content->next = cur->next;
4534 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004535 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004536 cur->content->next = cur->next;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004537 prev->next = cur->content;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004538 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004539 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004540 }
4541 }
4542 /*
4543 * the current node may have been transformed back
4544 */
4545 if ((cur->type == XML_RELAXNG_EXCEPT) &&
4546 (cur->content != NULL) &&
4547 (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004548 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004549 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
4550 if ((parent != NULL) &&
4551 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4552 (parent->type == XML_RELAXNG_LIST) ||
4553 (parent->type == XML_RELAXNG_GROUP) ||
4554 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4555 (parent->type == XML_RELAXNG_ONEORMORE) ||
4556 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4557 parent->type = XML_RELAXNG_NOT_ALLOWED;
4558 break;
4559 }
4560 if ((parent != NULL) &&
4561 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004562 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004563 } else
4564 prev = cur;
4565 } else if (cur->type == XML_RELAXNG_EMPTY){
4566 if ((parent != NULL) &&
4567 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4568 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4569 parent->type = XML_RELAXNG_EMPTY;
4570 break;
4571 }
4572 if ((parent != NULL) &&
4573 ((parent->type == XML_RELAXNG_GROUP) ||
4574 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4575 (parent->type == XML_RELAXNG_CHOICE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004576 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004577 } else
4578 prev = cur;
4579 } else {
4580 prev = cur;
4581 }
4582 }
4583 cur = cur->next;
4584 }
4585}
4586
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004587/**
4588 * xmlRelaxNGGroupContentType:
4589 * @ct1: the first content type
4590 * @ct2: the second content type
4591 *
4592 * Try to group 2 content types
4593 *
4594 * Returns the content type
4595 */
4596static xmlRelaxNGContentType
4597xmlRelaxNGGroupContentType(xmlRelaxNGContentType ct1,
4598 xmlRelaxNGContentType ct2) {
4599 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4600 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4601 return(XML_RELAXNG_CONTENT_ERROR);
4602 if (ct1 == XML_RELAXNG_CONTENT_EMPTY)
4603 return(ct2);
4604 if (ct2 == XML_RELAXNG_CONTENT_EMPTY)
4605 return(ct1);
4606 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) &&
4607 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4608 return(XML_RELAXNG_CONTENT_COMPLEX);
4609 return(XML_RELAXNG_CONTENT_ERROR);
4610}
4611
4612/**
4613 * xmlRelaxNGMaxContentType:
4614 * @ct1: the first content type
4615 * @ct2: the second content type
4616 *
4617 * Compute the max content-type
4618 *
4619 * Returns the content type
4620 */
4621static xmlRelaxNGContentType
4622xmlRelaxNGMaxContentType(xmlRelaxNGContentType ct1,
4623 xmlRelaxNGContentType ct2) {
4624 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4625 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4626 return(XML_RELAXNG_CONTENT_ERROR);
4627 if ((ct1 == XML_RELAXNG_CONTENT_SIMPLE) ||
4628 (ct2 == XML_RELAXNG_CONTENT_SIMPLE))
4629 return(XML_RELAXNG_CONTENT_SIMPLE);
4630 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) ||
4631 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4632 return(XML_RELAXNG_CONTENT_COMPLEX);
4633 return(XML_RELAXNG_CONTENT_EMPTY);
4634}
Daniel Veillard77648bb2003-02-20 15:03:22 +00004635
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004636/**
4637 * xmlRelaxNGCheckRules:
4638 * @ctxt: a Relax-NG parser context
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004639 * @cur: the current definition
Daniel Veillard77648bb2003-02-20 15:03:22 +00004640 * @flags: some accumulated flags
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004641 * @ptype: the parent type
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004642 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004643 * Check for rules in section 7.1 and 7.2
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004644 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004645 * Returns the content type of @cur
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004646 */
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004647static xmlRelaxNGContentType
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004648xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004649 xmlRelaxNGDefinePtr cur, int flags,
4650 xmlRelaxNGType ptype) {
4651 int nflags = flags;
4652 xmlRelaxNGContentType ret, tmp, val = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004653
Daniel Veillard77648bb2003-02-20 15:03:22 +00004654 while (cur != NULL) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004655 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004656 if ((cur->type == XML_RELAXNG_REF) ||
4657 (cur->type == XML_RELAXNG_PARENTREF)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004658 if (flags & XML_RELAXNG_IN_LIST) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004659 if (ctxt->error != NULL)
4660 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00004661 "Found forbidden pattern list//ref\n");
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004662 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004663 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00004664 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4665 if (ctxt->error != NULL)
4666 ctxt->error(ctxt->userData,
4667 "Found forbidden pattern data/except//ref\n");
4668 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004669 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00004670 if (cur->depth > -4) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004671 cur->depth = -4;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004672 ret = xmlRelaxNGCheckRules(ctxt, cur->content,
4673 flags, cur->type);
4674 cur->depth = ret - 15 ;
4675 } else if (cur->depth == -4) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004676 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004677 } else {
4678 ret = (xmlRelaxNGContentType) cur->depth + 15;
4679 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004680 } else if (cur->type == XML_RELAXNG_ELEMENT) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004681 /*
4682 * The 7.3 Attribute derivation rule for groups is plugged there
4683 */
4684 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004685 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4686 if (ctxt->error != NULL)
4687 ctxt->error(ctxt->userData,
4688 "Found forbidden pattern data/except//element(ref)\n");
4689 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004690 }
4691 if (flags & XML_RELAXNG_IN_LIST) {
4692 if (ctxt->error != NULL)
4693 ctxt->error(ctxt->userData,
4694 "Found forbidden pattern list//element(ref)\n");
4695 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004696 }
4697 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4698 if (ctxt->error != NULL)
4699 ctxt->error(ctxt->userData,
4700 "Found forbidden pattern attribute//element(ref)\n");
4701 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004702 }
Daniel Veillard463a5472003-02-27 21:30:32 +00004703 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4704 if (ctxt->error != NULL)
4705 ctxt->error(ctxt->userData,
4706 "Found forbidden pattern attribute//element(ref)\n");
4707 ctxt->nbErrors++;
4708 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00004709 /*
4710 * reset since in the simple form elements are only child
4711 * of grammar/define
4712 */
4713 nflags = 0;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004714 ret = xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags, cur->type);
4715 if (ret != XML_RELAXNG_CONTENT_EMPTY) {
4716 if (ctxt->error != NULL)
4717 ctxt->error(ctxt->userData,
4718 "Element %s attributes have a content type error\n",
4719 cur->name);
4720 ctxt->nbErrors++;
4721 }
4722 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4723 if (ret == XML_RELAXNG_CONTENT_ERROR) {
4724 if (ctxt->error != NULL)
4725 ctxt->error(ctxt->userData,
4726 "Element %s has a content type error\n",
4727 cur->name);
4728 ctxt->nbErrors++;
4729 } else {
4730 ret = XML_RELAXNG_CONTENT_COMPLEX;
4731 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00004732 } else if (cur->type == XML_RELAXNG_ATTRIBUTE) {
4733 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4734 if (ctxt->error != NULL)
4735 ctxt->error(ctxt->userData,
4736 "Found forbidden pattern attribute//attribute\n");
4737 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004738 }
4739 if (flags & XML_RELAXNG_IN_LIST) {
4740 if (ctxt->error != NULL)
4741 ctxt->error(ctxt->userData,
4742 "Found forbidden pattern list//attribute\n");
4743 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004744 }
4745 if (flags & XML_RELAXNG_IN_OOMGROUP) {
4746 if (ctxt->error != NULL)
4747 ctxt->error(ctxt->userData,
4748 "Found forbidden pattern oneOrMore//group//attribute\n");
4749 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004750 }
4751 if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) {
4752 if (ctxt->error != NULL)
4753 ctxt->error(ctxt->userData,
4754 "Found forbidden pattern oneOrMore//interleave//attribute\n");
4755 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004756 }
4757 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4758 if (ctxt->error != NULL)
4759 ctxt->error(ctxt->userData,
4760 "Found forbidden pattern data/except//attribute\n");
4761 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004762 }
4763 if (flags & XML_RELAXNG_IN_START) {
4764 if (ctxt->error != NULL)
4765 ctxt->error(ctxt->userData,
4766 "Found forbidden pattern start//attribute\n");
4767 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004768 }
4769 nflags = flags | XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004770 xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4771 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004772 } else if ((cur->type == XML_RELAXNG_ONEORMORE) ||
4773 (cur->type == XML_RELAXNG_ZEROORMORE)) {
4774 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4775 if (ctxt->error != NULL)
4776 ctxt->error(ctxt->userData,
4777 "Found forbidden pattern data/except//oneOrMore\n");
4778 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004779 }
4780 if (flags & XML_RELAXNG_IN_START) {
4781 if (ctxt->error != NULL)
4782 ctxt->error(ctxt->userData,
4783 "Found forbidden pattern start//oneOrMore\n");
4784 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004785 }
4786 nflags = flags | XML_RELAXNG_IN_ONEORMORE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004787 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4788 ret = xmlRelaxNGGroupContentType(ret, ret);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004789 } else if (cur->type == XML_RELAXNG_LIST) {
4790 if (flags & XML_RELAXNG_IN_LIST) {
4791 if (ctxt->error != NULL)
4792 ctxt->error(ctxt->userData,
4793 "Found forbidden pattern list//list\n");
4794 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004795 }
4796 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4797 if (ctxt->error != NULL)
4798 ctxt->error(ctxt->userData,
4799 "Found forbidden pattern data/except//list\n");
4800 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004801 }
4802 if (flags & XML_RELAXNG_IN_START) {
4803 if (ctxt->error != NULL)
4804 ctxt->error(ctxt->userData,
4805 "Found forbidden pattern start//list\n");
4806 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004807 }
4808 nflags = flags | XML_RELAXNG_IN_LIST;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004809 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004810 } else if (cur->type == XML_RELAXNG_GROUP) {
4811 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4812 if (ctxt->error != NULL)
4813 ctxt->error(ctxt->userData,
4814 "Found forbidden pattern data/except//group\n");
4815 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004816 }
4817 if (flags & XML_RELAXNG_IN_START) {
4818 if (ctxt->error != NULL)
4819 ctxt->error(ctxt->userData,
4820 "Found forbidden pattern start//group\n");
4821 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004822 }
4823 if (flags & XML_RELAXNG_IN_ONEORMORE)
4824 nflags = flags | XML_RELAXNG_IN_OOMGROUP;
4825 else
4826 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004827 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004828 /*
4829 * The 7.3 Attribute derivation rule for groups is plugged there
4830 */
4831 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004832 } else if (cur->type == XML_RELAXNG_INTERLEAVE) {
4833 if (flags & XML_RELAXNG_IN_LIST) {
4834 if (ctxt->error != NULL)
4835 ctxt->error(ctxt->userData,
4836 "Found forbidden pattern list//interleave\n");
4837 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004838 }
4839 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4840 if (ctxt->error != NULL)
4841 ctxt->error(ctxt->userData,
4842 "Found forbidden pattern data/except//interleave\n");
4843 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004844 }
4845 if (flags & XML_RELAXNG_IN_START) {
4846 if (ctxt->error != NULL)
4847 ctxt->error(ctxt->userData,
4848 "Found forbidden pattern start//interleave\n");
4849 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004850 }
4851 if (flags & XML_RELAXNG_IN_ONEORMORE)
4852 nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE;
4853 else
4854 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004855 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004856 } else if (cur->type == XML_RELAXNG_EXCEPT) {
4857 if ((cur->parent != NULL) &&
4858 (cur->parent->type == XML_RELAXNG_DATATYPE))
4859 nflags = flags | XML_RELAXNG_IN_DATAEXCEPT;
4860 else
4861 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004862 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004863 } else if (cur->type == XML_RELAXNG_DATATYPE) {
4864 if (flags & XML_RELAXNG_IN_START) {
4865 if (ctxt->error != NULL)
4866 ctxt->error(ctxt->userData,
4867 "Found forbidden pattern start//data\n");
4868 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004869 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004870 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4871 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004872 } else if (cur->type == XML_RELAXNG_VALUE) {
4873 if (flags & XML_RELAXNG_IN_START) {
4874 if (ctxt->error != NULL)
4875 ctxt->error(ctxt->userData,
4876 "Found forbidden pattern start//value\n");
4877 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004878 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004879 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4880 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004881 } else if (cur->type == XML_RELAXNG_TEXT) {
4882 if (flags & XML_RELAXNG_IN_LIST) {
4883 if (ctxt->error != NULL)
4884 ctxt->error(ctxt->userData,
4885 "Found forbidden pattern list//text\n");
4886 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004887 }
4888 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4889 if (ctxt->error != NULL)
4890 ctxt->error(ctxt->userData,
4891 "Found forbidden pattern data/except//text\n");
4892 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004893 }
4894 if (flags & XML_RELAXNG_IN_START) {
4895 if (ctxt->error != NULL)
4896 ctxt->error(ctxt->userData,
4897 "Found forbidden pattern start//text\n");
4898 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004899 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004900 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004901 } else if (cur->type == XML_RELAXNG_EMPTY) {
4902 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4903 if (ctxt->error != NULL)
4904 ctxt->error(ctxt->userData,
4905 "Found forbidden pattern data/except//empty\n");
4906 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004907 }
4908 if (flags & XML_RELAXNG_IN_START) {
4909 if (ctxt->error != NULL)
4910 ctxt->error(ctxt->userData,
4911 "Found forbidden pattern start//empty\n");
4912 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004913 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004914 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004915 } else {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004916 ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004917 }
4918 cur = cur->next;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004919 if (ptype == XML_RELAXNG_GROUP) {
4920 val = xmlRelaxNGGroupContentType(val, ret);
4921 } else if (ptype == XML_RELAXNG_INTERLEAVE) {
4922 tmp = xmlRelaxNGGroupContentType(val, ret);
4923 if (tmp != XML_RELAXNG_CONTENT_ERROR)
4924 tmp = xmlRelaxNGMaxContentType(val, ret);
4925 } else if (ptype == XML_RELAXNG_CHOICE) {
4926 val = xmlRelaxNGMaxContentType(val, ret);
4927 } else if (ptype == XML_RELAXNG_LIST) {
4928 val = XML_RELAXNG_CONTENT_SIMPLE;
4929 } else if (ptype == XML_RELAXNG_EXCEPT) {
4930 if (ret == XML_RELAXNG_CONTENT_ERROR)
4931 val = XML_RELAXNG_CONTENT_ERROR;
4932 else
4933 val = XML_RELAXNG_CONTENT_SIMPLE;
4934 } else {
4935 val = xmlRelaxNGGroupContentType(val, ret);
4936 }
4937
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004938 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004939 return(val);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004940}
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004941
4942/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004943 * xmlRelaxNGParseGrammar:
4944 * @ctxt: a Relax-NG parser context
4945 * @nodes: grammar children nodes
4946 *
4947 * parse a Relax-NG <grammar> node
4948 *
4949 * Returns the internal xmlRelaxNGGrammarPtr built or
4950 * NULL in case of error
4951 */
4952static xmlRelaxNGGrammarPtr
4953xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
4954 xmlRelaxNGGrammarPtr ret, tmp, old;
4955
Daniel Veillardc482e262003-02-26 14:48:48 +00004956#ifdef DEBUG_GRAMMAR
4957 xmlGenericError(xmlGenericErrorContext, "Parsing a new grammar\n");
4958#endif
4959
Daniel Veillard6eadf632003-01-23 18:29:16 +00004960 ret = xmlRelaxNGNewGrammar(ctxt);
4961 if (ret == NULL)
4962 return(NULL);
4963
4964 /*
4965 * Link the new grammar in the tree
4966 */
4967 ret->parent = ctxt->grammar;
4968 if (ctxt->grammar != NULL) {
4969 tmp = ctxt->grammar->children;
4970 if (tmp == NULL) {
4971 ctxt->grammar->children = ret;
4972 } else {
4973 while (tmp->next != NULL)
4974 tmp = tmp->next;
4975 tmp->next = ret;
4976 }
4977 }
4978
4979 old = ctxt->grammar;
4980 ctxt->grammar = ret;
4981 xmlRelaxNGParseGrammarContent(ctxt, nodes);
4982 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004983 if (ctxt->grammar == NULL) {
4984 if (ctxt->error != NULL)
4985 ctxt->error(ctxt->userData,
4986 "Failed to parse <grammar> content\n");
4987 ctxt->nbErrors++;
4988 } else if (ctxt->grammar->start == NULL) {
4989 if (ctxt->error != NULL)
4990 ctxt->error(ctxt->userData,
4991 "Element <grammar> has no <start>\n");
4992 ctxt->nbErrors++;
4993 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004994
4995 /*
4996 * Apply 4.17 mergingd rules to defines and starts
4997 */
4998 xmlRelaxNGCombineStart(ctxt, ret);
4999 if (ret->defs != NULL) {
5000 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
5001 ctxt);
5002 }
5003
5004 /*
5005 * link together defines and refs in this grammar
5006 */
5007 if (ret->refs != NULL) {
5008 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
5009 ctxt);
5010 }
5011 ctxt->grammar = old;
5012 return(ret);
5013}
5014
5015/**
5016 * xmlRelaxNGParseDocument:
5017 * @ctxt: a Relax-NG parser context
5018 * @node: the root node of the RelaxNG schema
5019 *
5020 * parse a Relax-NG definition resource and build an internal
5021 * xmlRelaxNG struture which can be used to validate instances.
5022 *
5023 * Returns the internal XML RelaxNG structure built or
5024 * NULL in case of error
5025 */
5026static xmlRelaxNGPtr
5027xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
5028 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005029 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00005030 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005031
5032 if ((ctxt == NULL) || (node == NULL))
5033 return (NULL);
5034
5035 schema = xmlRelaxNGNewRelaxNG(ctxt);
5036 if (schema == NULL)
5037 return(NULL);
5038
Daniel Veillard276be4a2003-01-24 01:03:34 +00005039 olddefine = ctxt->define;
5040 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005041 if (IS_RELAXNG(node, "grammar")) {
5042 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
5043 } else {
Daniel Veillardc482e262003-02-26 14:48:48 +00005044 xmlRelaxNGGrammarPtr tmp, ret;
5045
5046 schema->topgrammar = ret = xmlRelaxNGNewGrammar(ctxt);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005047 if (schema->topgrammar == NULL) {
5048 return(schema);
5049 }
Daniel Veillardc482e262003-02-26 14:48:48 +00005050 /*
5051 * Link the new grammar in the tree
5052 */
5053 ret->parent = ctxt->grammar;
5054 if (ctxt->grammar != NULL) {
5055 tmp = ctxt->grammar->children;
5056 if (tmp == NULL) {
5057 ctxt->grammar->children = ret;
5058 } else {
5059 while (tmp->next != NULL)
5060 tmp = tmp->next;
5061 tmp->next = ret;
5062 }
5063 }
Daniel Veillarde431a272003-01-29 23:02:33 +00005064 old = ctxt->grammar;
Daniel Veillardc482e262003-02-26 14:48:48 +00005065 ctxt->grammar = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005066 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00005067 if (old != NULL)
5068 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005069 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005070 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00005071 if (schema->topgrammar->start != NULL) {
5072 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
Daniel Veillard77648bb2003-02-20 15:03:22 +00005073 if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) {
5074 xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
5075 while ((schema->topgrammar->start != NULL) &&
5076 (schema->topgrammar->start->type == XML_RELAXNG_NOOP) &&
5077 (schema->topgrammar->start->next != NULL))
5078 schema->topgrammar->start = schema->topgrammar->start->content;
5079 xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00005080 XML_RELAXNG_IN_START, XML_RELAXNG_NOOP);
Daniel Veillard77648bb2003-02-20 15:03:22 +00005081 }
Daniel Veillardd4310742003-02-18 21:12:46 +00005082 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005083
5084#ifdef DEBUG
5085 if (schema == NULL)
5086 xmlGenericError(xmlGenericErrorContext,
5087 "xmlRelaxNGParseDocument() failed\n");
5088#endif
5089
5090 return (schema);
5091}
5092
5093/************************************************************************
5094 * *
5095 * Reading RelaxNGs *
5096 * *
5097 ************************************************************************/
5098
5099/**
5100 * xmlRelaxNGNewParserCtxt:
5101 * @URL: the location of the schema
5102 *
5103 * Create an XML RelaxNGs parse context for that file/resource expected
5104 * to contain an XML RelaxNGs file.
5105 *
5106 * Returns the parser context or NULL in case of error
5107 */
5108xmlRelaxNGParserCtxtPtr
5109xmlRelaxNGNewParserCtxt(const char *URL) {
5110 xmlRelaxNGParserCtxtPtr ret;
5111
5112 if (URL == NULL)
5113 return(NULL);
5114
5115 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
5116 if (ret == NULL) {
5117 xmlGenericError(xmlGenericErrorContext,
5118 "Failed to allocate new schama parser context for %s\n", URL);
5119 return (NULL);
5120 }
5121 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
5122 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005123 ret->error = xmlGenericError;
5124 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005125 return (ret);
5126}
5127
5128/**
5129 * xmlRelaxNGNewMemParserCtxt:
5130 * @buffer: a pointer to a char array containing the schemas
5131 * @size: the size of the array
5132 *
5133 * Create an XML RelaxNGs parse context for that memory buffer expected
5134 * to contain an XML RelaxNGs file.
5135 *
5136 * Returns the parser context or NULL in case of error
5137 */
5138xmlRelaxNGParserCtxtPtr
5139xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
5140 xmlRelaxNGParserCtxtPtr ret;
5141
5142 if ((buffer == NULL) || (size <= 0))
5143 return(NULL);
5144
5145 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
5146 if (ret == NULL) {
5147 xmlGenericError(xmlGenericErrorContext,
5148 "Failed to allocate new schama parser context\n");
5149 return (NULL);
5150 }
5151 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
5152 ret->buffer = buffer;
5153 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005154 ret->error = xmlGenericError;
5155 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005156 return (ret);
5157}
5158
5159/**
5160 * xmlRelaxNGFreeParserCtxt:
5161 * @ctxt: the schema parser context
5162 *
5163 * Free the resources associated to the schema parser context
5164 */
5165void
5166xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
5167 if (ctxt == NULL)
5168 return;
5169 if (ctxt->URL != NULL)
5170 xmlFree(ctxt->URL);
5171 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005172 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005173 if (ctxt->interleaves != NULL)
5174 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005175 if (ctxt->documents != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +00005176 xmlRelaxNGFreeDocumentList(ctxt->documents);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005177 if (ctxt->includes != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +00005178 xmlRelaxNGFreeIncludeList(ctxt->includes);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005179 if (ctxt->docTab != NULL)
5180 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005181 if (ctxt->incTab != NULL)
5182 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00005183 if (ctxt->defTab != NULL) {
5184 int i;
5185
5186 for (i = 0;i < ctxt->defNr;i++)
5187 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
5188 xmlFree(ctxt->defTab);
5189 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005190 xmlFree(ctxt);
5191}
5192
Daniel Veillard6eadf632003-01-23 18:29:16 +00005193/**
Daniel Veillardd2298792003-02-14 16:54:11 +00005194 * xmlRelaxNGNormExtSpace:
5195 * @value: a value
5196 *
5197 * Removes the leading and ending spaces of the value
5198 * The string is modified "in situ"
5199 */
5200static void
5201xmlRelaxNGNormExtSpace(xmlChar *value) {
5202 xmlChar *start = value;
5203 xmlChar *cur = value;
5204 if (value == NULL)
5205 return;
5206
5207 while (IS_BLANK(*cur)) cur++;
5208 if (cur == start) {
5209 do {
5210 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5211 if (*cur == 0)
5212 return;
5213 start = cur;
5214 while (IS_BLANK(*cur)) cur++;
5215 if (*cur == 0) {
5216 *start = 0;
5217 return;
5218 }
5219 } while (1);
5220 } else {
5221 do {
5222 while ((*cur != 0) && (!IS_BLANK(*cur)))
5223 *start++ = *cur++;
5224 if (*cur == 0) {
5225 *start = 0;
5226 return;
5227 }
5228 /* don't try to normalize the inner spaces */
5229 while (IS_BLANK(*cur)) cur++;
5230 *start++ = *cur++;
5231 if (*cur == 0) {
5232 *start = 0;
5233 return;
5234 }
5235 } while (1);
5236 }
5237}
5238
5239/**
5240 * xmlRelaxNGCheckAttributes:
5241 * @ctxt: a Relax-NG parser context
5242 * @node: a Relax-NG node
5243 *
5244 * Check all the attributes on the given node
5245 */
5246static void
5247xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
5248 xmlAttrPtr cur, next;
5249
5250 cur = node->properties;
5251 while (cur != NULL) {
5252 next = cur->next;
5253 if ((cur->ns == NULL) ||
5254 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
5255 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
5256 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
5257 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
5258 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
5259 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00005260 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00005261 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
5262 if (ctxt->error != NULL)
5263 ctxt->error(ctxt->userData,
5264 "Attribute %s is not allowed on %s\n",
5265 cur->name, node->name);
5266 ctxt->nbErrors++;
5267 }
5268 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
5269 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
5270 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
5271 if (ctxt->error != NULL)
5272 ctxt->error(ctxt->userData,
5273 "Attribute %s is not allowed on %s\n",
5274 cur->name, node->name);
5275 ctxt->nbErrors++;
5276 }
5277 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
5278 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
5279 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
5280 if (ctxt->error != NULL)
5281 ctxt->error(ctxt->userData,
5282 "Attribute %s is not allowed on %s\n",
5283 cur->name, node->name);
5284 ctxt->nbErrors++;
5285 }
5286 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
5287 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
5288 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
5289 if (ctxt->error != NULL)
5290 ctxt->error(ctxt->userData,
5291 "Attribute %s is not allowed on %s\n",
5292 cur->name, node->name);
5293 ctxt->nbErrors++;
5294 }
5295 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
5296 xmlChar *val;
5297 xmlURIPtr uri;
5298
5299 val = xmlNodeListGetString(node->doc, cur->children, 1);
5300 if (val != NULL) {
5301 if (val[0] != 0) {
5302 uri = xmlParseURI((const char *) val);
5303 if (uri == NULL) {
5304 if (ctxt->error != NULL)
5305 ctxt->error(ctxt->userData,
5306 "Attribute %s contains invalid URI %s\n",
5307 cur->name, val);
5308 ctxt->nbErrors++;
5309 } else {
5310 if (uri->scheme == NULL) {
5311 if (ctxt->error != NULL)
5312 ctxt->error(ctxt->userData,
5313 "Attribute %s URI %s is not absolute\n",
5314 cur->name, val);
5315 ctxt->nbErrors++;
5316 }
5317 if (uri->fragment != NULL) {
5318 if (ctxt->error != NULL)
5319 ctxt->error(ctxt->userData,
5320 "Attribute %s URI %s has a fragment ID\n",
5321 cur->name, val);
5322 ctxt->nbErrors++;
5323 }
5324 xmlFreeURI(uri);
5325 }
5326 }
5327 xmlFree(val);
5328 }
5329 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
5330 if (ctxt->error != NULL)
5331 ctxt->error(ctxt->userData,
5332 "Unknown attribute %s on %s\n",
5333 cur->name, node->name);
5334 ctxt->nbErrors++;
5335 }
5336 }
5337 cur = next;
5338 }
5339}
5340
5341/**
Daniel Veillardc5312d72003-02-21 17:14:10 +00005342 * xmlRelaxNGCleanupTree:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005343 * @ctxt: a Relax-NG parser context
Daniel Veillardc5312d72003-02-21 17:14:10 +00005344 * @root: an xmlNodePtr subtree
Daniel Veillard6eadf632003-01-23 18:29:16 +00005345 *
Daniel Veillardc5312d72003-02-21 17:14:10 +00005346 * Cleanup the subtree from unwanted nodes for parsing, resolve
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005347 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00005348 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005349static void
5350xmlRelaxNGCleanupTree(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr root) {
5351 xmlNodePtr cur, delete;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005352
Daniel Veillard6eadf632003-01-23 18:29:16 +00005353 delete = NULL;
5354 cur = root;
5355 while (cur != NULL) {
5356 if (delete != NULL) {
5357 xmlUnlinkNode(delete);
5358 xmlFreeNode(delete);
5359 delete = NULL;
5360 }
5361 if (cur->type == XML_ELEMENT_NODE) {
5362 /*
5363 * Simplification 4.1. Annotations
5364 */
5365 if ((cur->ns == NULL) ||
5366 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00005367 if ((cur->parent != NULL) &&
5368 (cur->parent->type == XML_ELEMENT_NODE) &&
5369 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
5370 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
5371 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
5372 if (ctxt->error != NULL)
5373 ctxt->error(ctxt->userData,
5374 "element %s doesn't allow foreign elements\n",
5375 cur->parent->name);
5376 ctxt->nbErrors++;
5377 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005378 delete = cur;
5379 goto skip_children;
5380 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00005381 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005382 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005383 xmlChar *href, *ns, *base, *URL;
5384 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00005385 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005386
5387 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00005388 if (ns == NULL) {
5389 tmp = cur->parent;
5390 while ((tmp != NULL) &&
5391 (tmp->type == XML_ELEMENT_NODE)) {
5392 ns = xmlGetProp(tmp, BAD_CAST "ns");
5393 if (ns != NULL)
5394 break;
5395 tmp = tmp->parent;
5396 }
5397 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005398 href = xmlGetProp(cur, BAD_CAST "href");
5399 if (href == NULL) {
5400 if (ctxt->error != NULL)
5401 ctxt->error(ctxt->userData,
5402 "xmlRelaxNGParse: externalRef has no href attribute\n");
5403 ctxt->nbErrors++;
5404 delete = cur;
5405 goto skip_children;
5406 }
5407 base = xmlNodeGetBase(cur->doc, cur);
5408 URL = xmlBuildURI(href, base);
5409 if (URL == NULL) {
5410 if (ctxt->error != NULL)
5411 ctxt->error(ctxt->userData,
5412 "Failed to compute URL for externalRef %s\n", href);
5413 ctxt->nbErrors++;
5414 if (href != NULL)
5415 xmlFree(href);
5416 if (base != NULL)
5417 xmlFree(base);
5418 delete = cur;
5419 goto skip_children;
5420 }
5421 if (href != NULL)
5422 xmlFree(href);
5423 if (base != NULL)
5424 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005425 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005426 if (docu == NULL) {
5427 if (ctxt->error != NULL)
5428 ctxt->error(ctxt->userData,
5429 "Failed to load externalRef %s\n", URL);
5430 ctxt->nbErrors++;
5431 xmlFree(URL);
5432 delete = cur;
5433 goto skip_children;
5434 }
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005435 if (ns != NULL)
5436 xmlFree(ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005437 xmlFree(URL);
5438 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005439 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005440 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005441 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00005442 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005443
5444 href = xmlGetProp(cur, BAD_CAST "href");
5445 if (href == NULL) {
5446 if (ctxt->error != NULL)
5447 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005448 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005449 ctxt->nbErrors++;
5450 delete = cur;
5451 goto skip_children;
5452 }
5453 base = xmlNodeGetBase(cur->doc, cur);
5454 URL = xmlBuildURI(href, base);
5455 if (URL == NULL) {
5456 if (ctxt->error != NULL)
5457 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005458 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005459 ctxt->nbErrors++;
5460 if (href != NULL)
5461 xmlFree(href);
5462 if (base != NULL)
5463 xmlFree(base);
5464 delete = cur;
5465 goto skip_children;
5466 }
5467 if (href != NULL)
5468 xmlFree(href);
5469 if (base != NULL)
5470 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00005471 ns = xmlGetProp(cur, BAD_CAST "ns");
5472 if (ns == NULL) {
5473 tmp = cur->parent;
5474 while ((tmp != NULL) &&
5475 (tmp->type == XML_ELEMENT_NODE)) {
5476 ns = xmlGetProp(tmp, BAD_CAST "ns");
5477 if (ns != NULL)
5478 break;
5479 tmp = tmp->parent;
5480 }
5481 }
5482 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
5483 if (ns != NULL)
5484 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005485 if (incl == NULL) {
5486 if (ctxt->error != NULL)
5487 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005488 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005489 ctxt->nbErrors++;
5490 xmlFree(URL);
5491 delete = cur;
5492 goto skip_children;
5493 }
5494 xmlFree(URL);
5495 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005496 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
5497 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00005498 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005499 xmlNodePtr text = NULL;
5500
5501 /*
5502 * Simplification 4.8. name attribute of element
5503 * and attribute elements
5504 */
5505 name = xmlGetProp(cur, BAD_CAST "name");
5506 if (name != NULL) {
5507 if (cur->children == NULL) {
5508 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
5509 name);
5510 } else {
5511 xmlNodePtr node;
5512 node = xmlNewNode(cur->ns, BAD_CAST "name");
5513 if (node != NULL) {
5514 xmlAddPrevSibling(cur->children, node);
5515 text = xmlNewText(name);
5516 xmlAddChild(node, text);
5517 text = node;
5518 }
5519 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005520 if (text == NULL) {
5521 if (ctxt->error != NULL)
5522 ctxt->error(ctxt->userData,
5523 "Failed to create a name %s element\n", name);
5524 ctxt->nbErrors++;
5525 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005526 xmlUnsetProp(cur, BAD_CAST "name");
5527 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00005528 ns = xmlGetProp(cur, BAD_CAST "ns");
5529 if (ns != NULL) {
5530 if (text != NULL) {
5531 xmlSetProp(text, BAD_CAST "ns", ns);
5532 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00005533 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005534 xmlFree(ns);
5535 } else if (xmlStrEqual(cur->name,
5536 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005537 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
5538 }
5539 }
5540 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
5541 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
5542 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
5543 /*
5544 * Simplification 4.8. name attribute of element
5545 * and attribute elements
5546 */
5547 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
5548 xmlNodePtr node;
5549 xmlChar *ns = NULL;
5550
5551 node = cur->parent;
5552 while ((node != NULL) &&
5553 (node->type == XML_ELEMENT_NODE)) {
5554 ns = xmlGetProp(node, BAD_CAST "ns");
5555 if (ns != NULL) {
5556 break;
5557 }
5558 node = node->parent;
5559 }
5560 if (ns == NULL) {
5561 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
5562 } else {
5563 xmlSetProp(cur, BAD_CAST "ns", ns);
5564 xmlFree(ns);
5565 }
5566 }
5567 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
5568 xmlChar *name, *local, *prefix;
5569
5570 /*
5571 * Simplification: 4.10. QNames
5572 */
5573 name = xmlNodeGetContent(cur);
5574 if (name != NULL) {
5575 local = xmlSplitQName2(name, &prefix);
5576 if (local != NULL) {
5577 xmlNsPtr ns;
5578
5579 ns = xmlSearchNs(cur->doc, cur, prefix);
5580 if (ns == NULL) {
5581 if (ctxt->error != NULL)
5582 ctxt->error(ctxt->userData,
5583 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
5584 ctxt->nbErrors++;
5585 } else {
5586 xmlSetProp(cur, BAD_CAST "ns", ns->href);
5587 xmlNodeSetContent(cur, local);
5588 }
5589 xmlFree(local);
5590 xmlFree(prefix);
5591 }
5592 xmlFree(name);
5593 }
5594 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005595 /*
5596 * 4.16
5597 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005598 if (xmlStrEqual(cur->name, BAD_CAST "nsName")) {
5599 if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5600 if (ctxt->error != NULL)
5601 ctxt->error(ctxt->userData,
5602 "Found nsName/except//nsName forbidden construct\n");
5603 ctxt->nbErrors++;
5604 }
5605 }
5606 } else if ((xmlStrEqual(cur->name, BAD_CAST "except")) &&
5607 (cur != root)) {
5608 int oldflags = ctxt->flags;
5609
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005610 /*
5611 * 4.16
5612 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005613 if ((cur->parent != NULL) &&
5614 (xmlStrEqual(cur->parent->name, BAD_CAST "anyName"))) {
5615 ctxt->flags |= XML_RELAXNG_IN_ANYEXCEPT;
5616 xmlRelaxNGCleanupTree(ctxt, cur);
5617 ctxt->flags = oldflags;
5618 goto skip_children;
5619 } else if ((cur->parent != NULL) &&
5620 (xmlStrEqual(cur->parent->name, BAD_CAST "nsName"))) {
5621 ctxt->flags |= XML_RELAXNG_IN_NSEXCEPT;
5622 xmlRelaxNGCleanupTree(ctxt, cur);
5623 ctxt->flags = oldflags;
5624 goto skip_children;
5625 }
5626 } else if (xmlStrEqual(cur->name, BAD_CAST "anyName")) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005627 /*
5628 * 4.16
5629 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005630 if (ctxt->flags & XML_RELAXNG_IN_ANYEXCEPT) {
5631 if (ctxt->error != NULL)
5632 ctxt->error(ctxt->userData,
5633 "Found anyName/except//anyName forbidden construct\n");
5634 ctxt->nbErrors++;
5635 } else if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5636 if (ctxt->error != NULL)
5637 ctxt->error(ctxt->userData,
5638 "Found nsName/except//anyName forbidden construct\n");
5639 ctxt->nbErrors++;
5640 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005641 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005642 /*
5643 * Thisd is not an else since "include" is transformed
5644 * into a div
5645 */
5646 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005647 xmlChar *ns;
5648 xmlNodePtr child, ins, tmp;
5649
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005650 /*
5651 * implements rule 4.11
5652 */
Daniel Veillard416589a2003-02-17 17:25:42 +00005653
5654 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005655
5656 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005657 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005658 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005659 if (ns != NULL) {
5660 if (!xmlHasProp(child, BAD_CAST "ns")) {
5661 xmlSetProp(child, BAD_CAST "ns", ns);
5662 }
5663 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005664 tmp = child->next;
5665 xmlUnlinkNode(child);
5666 ins = xmlAddNextSibling(ins, child);
5667 child = tmp;
5668 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005669 if (ns != NULL)
5670 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005671 delete = cur;
5672 goto skip_children;
5673 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005674 }
5675 }
5676 /*
5677 * Simplification 4.2 whitespaces
5678 */
5679 else if (cur->type == XML_TEXT_NODE) {
5680 if (IS_BLANK_NODE(cur)) {
5681 if (cur->parent->type == XML_ELEMENT_NODE) {
5682 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
5683 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
5684 delete = cur;
5685 } else {
5686 delete = cur;
5687 goto skip_children;
5688 }
5689 }
5690 } else if (cur->type != XML_CDATA_SECTION_NODE) {
5691 delete = cur;
5692 goto skip_children;
5693 }
5694
5695 /*
5696 * Skip to next node
5697 */
5698 if (cur->children != NULL) {
5699 if ((cur->children->type != XML_ENTITY_DECL) &&
5700 (cur->children->type != XML_ENTITY_REF_NODE) &&
5701 (cur->children->type != XML_ENTITY_NODE)) {
5702 cur = cur->children;
5703 continue;
5704 }
5705 }
5706skip_children:
5707 if (cur->next != NULL) {
5708 cur = cur->next;
5709 continue;
5710 }
5711
5712 do {
5713 cur = cur->parent;
5714 if (cur == NULL)
5715 break;
5716 if (cur == root) {
5717 cur = NULL;
5718 break;
5719 }
5720 if (cur->next != NULL) {
5721 cur = cur->next;
5722 break;
5723 }
5724 } while (cur != NULL);
5725 }
5726 if (delete != NULL) {
5727 xmlUnlinkNode(delete);
5728 xmlFreeNode(delete);
5729 delete = NULL;
5730 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00005731}
Daniel Veillard6eadf632003-01-23 18:29:16 +00005732
Daniel Veillardc5312d72003-02-21 17:14:10 +00005733/**
5734 * xmlRelaxNGCleanupDoc:
5735 * @ctxt: a Relax-NG parser context
5736 * @doc: an xmldocPtr document pointer
5737 *
5738 * Cleanup the document from unwanted nodes for parsing, resolve
5739 * Include and externalRef lookups.
5740 *
5741 * Returns the cleaned up document or NULL in case of error
5742 */
5743static xmlDocPtr
5744xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
5745 xmlNodePtr root;
5746
5747 /*
5748 * Extract the root
5749 */
5750 root = xmlDocGetRootElement(doc);
5751 if (root == NULL) {
5752 if (ctxt->error != NULL)
5753 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5754 ctxt->URL);
5755 ctxt->nbErrors++;
5756 return (NULL);
5757 }
5758 xmlRelaxNGCleanupTree(ctxt, root);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005759 return(doc);
5760}
5761
5762/**
5763 * xmlRelaxNGParse:
5764 * @ctxt: a Relax-NG parser context
5765 *
5766 * parse a schema definition resource and build an internal
5767 * XML Shema struture which can be used to validate instances.
5768 * *WARNING* this interface is highly subject to change
5769 *
5770 * Returns the internal XML RelaxNG structure built from the resource or
5771 * NULL in case of error
5772 */
5773xmlRelaxNGPtr
5774xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
5775{
5776 xmlRelaxNGPtr ret = NULL;
5777 xmlDocPtr doc;
5778 xmlNodePtr root;
5779
5780 xmlRelaxNGInitTypes();
5781
5782 if (ctxt == NULL)
5783 return (NULL);
5784
5785 /*
5786 * First step is to parse the input document into an DOM/Infoset
5787 */
5788 if (ctxt->URL != NULL) {
5789 doc = xmlParseFile((const char *) ctxt->URL);
5790 if (doc == NULL) {
5791 if (ctxt->error != NULL)
5792 ctxt->error(ctxt->userData,
5793 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
5794 ctxt->nbErrors++;
5795 return (NULL);
5796 }
5797 } else if (ctxt->buffer != NULL) {
5798 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
5799 if (doc == NULL) {
5800 if (ctxt->error != NULL)
5801 ctxt->error(ctxt->userData,
5802 "xmlRelaxNGParse: could not parse schemas\n");
5803 ctxt->nbErrors++;
5804 return (NULL);
5805 }
5806 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5807 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5808 } else {
5809 if (ctxt->error != NULL)
5810 ctxt->error(ctxt->userData,
5811 "xmlRelaxNGParse: nothing to parse\n");
5812 ctxt->nbErrors++;
5813 return (NULL);
5814 }
5815 ctxt->document = doc;
5816
5817 /*
5818 * Some preprocessing of the document content
5819 */
5820 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
5821 if (doc == NULL) {
5822 xmlFreeDoc(ctxt->document);
5823 ctxt->document = NULL;
5824 return(NULL);
5825 }
5826
Daniel Veillard6eadf632003-01-23 18:29:16 +00005827 /*
5828 * Then do the parsing for good
5829 */
5830 root = xmlDocGetRootElement(doc);
5831 if (root == NULL) {
5832 if (ctxt->error != NULL)
5833 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5834 ctxt->URL);
5835 ctxt->nbErrors++;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005836 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005837 return (NULL);
5838 }
5839 ret = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005840 if (ret == NULL) {
5841 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005842 return(NULL);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005843 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005844
5845 /*
5846 * Check the ref/defines links
5847 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005848 /*
5849 * try to preprocess interleaves
5850 */
5851 if (ctxt->interleaves != NULL) {
5852 xmlHashScan(ctxt->interleaves,
5853 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
5854 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005855
5856 /*
5857 * if there was a parsing error return NULL
5858 */
5859 if (ctxt->nbErrors > 0) {
5860 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005861 ctxt->document = NULL;
5862 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005863 return(NULL);
5864 }
5865
5866 /*
5867 * Transfer the pointer for cleanup at the schema level.
5868 */
5869 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005870 ctxt->document = NULL;
5871 ret->documents = ctxt->documents;
5872 ctxt->documents = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005873
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005874 ret->includes = ctxt->includes;
5875 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00005876 ret->defNr = ctxt->defNr;
5877 ret->defTab = ctxt->defTab;
5878 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005879
5880 return (ret);
5881}
5882
5883/**
5884 * xmlRelaxNGSetParserErrors:
5885 * @ctxt: a Relax-NG validation context
5886 * @err: the error callback
5887 * @warn: the warning callback
5888 * @ctx: contextual data for the callbacks
5889 *
5890 * Set the callback functions used to handle errors for a validation context
5891 */
5892void
5893xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
5894 xmlRelaxNGValidityErrorFunc err,
5895 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5896 if (ctxt == NULL)
5897 return;
5898 ctxt->error = err;
5899 ctxt->warning = warn;
5900 ctxt->userData = ctx;
5901}
5902/************************************************************************
5903 * *
5904 * Dump back a compiled form *
5905 * *
5906 ************************************************************************/
5907static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
5908
5909/**
5910 * xmlRelaxNGDumpDefines:
5911 * @output: the file output
5912 * @defines: a list of define structures
5913 *
5914 * Dump a RelaxNG structure back
5915 */
5916static void
5917xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
5918 while (defines != NULL) {
5919 xmlRelaxNGDumpDefine(output, defines);
5920 defines = defines->next;
5921 }
5922}
5923
5924/**
5925 * xmlRelaxNGDumpDefine:
5926 * @output: the file output
5927 * @define: a define structure
5928 *
5929 * Dump a RelaxNG structure back
5930 */
5931static void
5932xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
5933 if (define == NULL)
5934 return;
5935 switch(define->type) {
5936 case XML_RELAXNG_EMPTY:
5937 fprintf(output, "<empty/>\n");
5938 break;
5939 case XML_RELAXNG_NOT_ALLOWED:
5940 fprintf(output, "<notAllowed/>\n");
5941 break;
5942 case XML_RELAXNG_TEXT:
5943 fprintf(output, "<text/>\n");
5944 break;
5945 case XML_RELAXNG_ELEMENT:
5946 fprintf(output, "<element>\n");
5947 if (define->name != NULL) {
5948 fprintf(output, "<name");
5949 if (define->ns != NULL)
5950 fprintf(output, " ns=\"%s\"", define->ns);
5951 fprintf(output, ">%s</name>\n", define->name);
5952 }
5953 xmlRelaxNGDumpDefines(output, define->attrs);
5954 xmlRelaxNGDumpDefines(output, define->content);
5955 fprintf(output, "</element>\n");
5956 break;
5957 case XML_RELAXNG_LIST:
5958 fprintf(output, "<list>\n");
5959 xmlRelaxNGDumpDefines(output, define->content);
5960 fprintf(output, "</list>\n");
5961 break;
5962 case XML_RELAXNG_ONEORMORE:
5963 fprintf(output, "<oneOrMore>\n");
5964 xmlRelaxNGDumpDefines(output, define->content);
5965 fprintf(output, "</oneOrMore>\n");
5966 break;
5967 case XML_RELAXNG_ZEROORMORE:
5968 fprintf(output, "<zeroOrMore>\n");
5969 xmlRelaxNGDumpDefines(output, define->content);
5970 fprintf(output, "</zeroOrMore>\n");
5971 break;
5972 case XML_RELAXNG_CHOICE:
5973 fprintf(output, "<choice>\n");
5974 xmlRelaxNGDumpDefines(output, define->content);
5975 fprintf(output, "</choice>\n");
5976 break;
5977 case XML_RELAXNG_GROUP:
5978 fprintf(output, "<group>\n");
5979 xmlRelaxNGDumpDefines(output, define->content);
5980 fprintf(output, "</group>\n");
5981 break;
5982 case XML_RELAXNG_INTERLEAVE:
5983 fprintf(output, "<interleave>\n");
5984 xmlRelaxNGDumpDefines(output, define->content);
5985 fprintf(output, "</interleave>\n");
5986 break;
5987 case XML_RELAXNG_OPTIONAL:
5988 fprintf(output, "<optional>\n");
5989 xmlRelaxNGDumpDefines(output, define->content);
5990 fprintf(output, "</optional>\n");
5991 break;
5992 case XML_RELAXNG_ATTRIBUTE:
5993 fprintf(output, "<attribute>\n");
5994 xmlRelaxNGDumpDefines(output, define->content);
5995 fprintf(output, "</attribute>\n");
5996 break;
5997 case XML_RELAXNG_DEF:
5998 fprintf(output, "<define");
5999 if (define->name != NULL)
6000 fprintf(output, " name=\"%s\"", define->name);
6001 fprintf(output, ">\n");
6002 xmlRelaxNGDumpDefines(output, define->content);
6003 fprintf(output, "</define>\n");
6004 break;
6005 case XML_RELAXNG_REF:
6006 fprintf(output, "<ref");
6007 if (define->name != NULL)
6008 fprintf(output, " name=\"%s\"", define->name);
6009 fprintf(output, ">\n");
6010 xmlRelaxNGDumpDefines(output, define->content);
6011 fprintf(output, "</ref>\n");
6012 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00006013 case XML_RELAXNG_PARENTREF:
6014 fprintf(output, "<parentRef");
6015 if (define->name != NULL)
6016 fprintf(output, " name=\"%s\"", define->name);
6017 fprintf(output, ">\n");
6018 xmlRelaxNGDumpDefines(output, define->content);
6019 fprintf(output, "</parentRef>\n");
6020 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006021 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00006022 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00006023 xmlRelaxNGDumpDefines(output, define->content);
6024 fprintf(output, "</externalRef>\n");
6025 break;
6026 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006027 case XML_RELAXNG_VALUE:
6028 TODO
6029 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006030 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00006031 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00006032 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006033 TODO
6034 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00006035 case XML_RELAXNG_NOOP:
6036 xmlRelaxNGDumpDefines(output, define->content);
6037 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006038 }
6039}
6040
6041/**
6042 * xmlRelaxNGDumpGrammar:
6043 * @output: the file output
6044 * @grammar: a grammar structure
6045 * @top: is this a top grammar
6046 *
6047 * Dump a RelaxNG structure back
6048 */
6049static void
6050xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
6051{
6052 if (grammar == NULL)
6053 return;
6054
6055 fprintf(output, "<grammar");
6056 if (top)
6057 fprintf(output,
6058 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
6059 switch(grammar->combine) {
6060 case XML_RELAXNG_COMBINE_UNDEFINED:
6061 break;
6062 case XML_RELAXNG_COMBINE_CHOICE:
6063 fprintf(output, " combine=\"choice\"");
6064 break;
6065 case XML_RELAXNG_COMBINE_INTERLEAVE:
6066 fprintf(output, " combine=\"interleave\"");
6067 break;
6068 default:
6069 fprintf(output, " <!-- invalid combine value -->");
6070 }
6071 fprintf(output, ">\n");
6072 if (grammar->start == NULL) {
6073 fprintf(output, " <!-- grammar had no start -->");
6074 } else {
6075 fprintf(output, "<start>\n");
6076 xmlRelaxNGDumpDefine(output, grammar->start);
6077 fprintf(output, "</start>\n");
6078 }
6079 /* TODO ? Dump the defines ? */
6080 fprintf(output, "</grammar>\n");
6081}
6082
6083/**
6084 * xmlRelaxNGDump:
6085 * @output: the file output
6086 * @schema: a schema structure
6087 *
6088 * Dump a RelaxNG structure back
6089 */
6090void
6091xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
6092{
6093 if (schema == NULL) {
6094 fprintf(output, "RelaxNG empty or failed to compile\n");
6095 return;
6096 }
6097 fprintf(output, "RelaxNG: ");
6098 if (schema->doc == NULL) {
6099 fprintf(output, "no document\n");
6100 } else if (schema->doc->URL != NULL) {
6101 fprintf(output, "%s\n", schema->doc->URL);
6102 } else {
6103 fprintf(output, "\n");
6104 }
6105 if (schema->topgrammar == NULL) {
6106 fprintf(output, "RelaxNG has no top grammar\n");
6107 return;
6108 }
6109 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
6110}
6111
Daniel Veillardfebcca42003-02-16 15:44:18 +00006112/**
6113 * xmlRelaxNGDumpTree:
6114 * @output: the file output
6115 * @schema: a schema structure
6116 *
6117 * Dump the transformed RelaxNG tree.
6118 */
6119void
6120xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
6121{
6122 if (schema == NULL) {
6123 fprintf(output, "RelaxNG empty or failed to compile\n");
6124 return;
6125 }
6126 if (schema->doc == NULL) {
6127 fprintf(output, "no document\n");
6128 } else {
6129 xmlDocDump(output, schema->doc);
6130 }
6131}
6132
Daniel Veillard6eadf632003-01-23 18:29:16 +00006133/************************************************************************
6134 * *
6135 * Validation implementation *
6136 * *
6137 ************************************************************************/
6138static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
6139 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006140static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
6141 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006142
6143/**
6144 * xmlRelaxNGSkipIgnored:
6145 * @ctxt: a schema validation context
6146 * @node: the top node.
6147 *
6148 * Skip ignorable nodes in that context
6149 *
6150 * Returns the new sibling or NULL in case of error.
6151 */
6152static xmlNodePtr
6153xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
6154 xmlNodePtr node) {
6155 /*
6156 * TODO complete and handle entities
6157 */
6158 while ((node != NULL) &&
6159 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006160 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00006161 ((node->type == XML_TEXT_NODE) &&
6162 (IS_BLANK_NODE(node))))) {
6163 node = node->next;
6164 }
6165 return(node);
6166}
6167
6168/**
Daniel Veillardedc91922003-01-26 00:52:04 +00006169 * xmlRelaxNGNormalize:
6170 * @ctxt: a schema validation context
6171 * @str: the string to normalize
6172 *
6173 * Implements the normalizeWhiteSpace( s ) function from
6174 * section 6.2.9 of the spec
6175 *
6176 * Returns the new string or NULL in case of error.
6177 */
6178static xmlChar *
6179xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
6180 xmlChar *ret, *p;
6181 const xmlChar *tmp;
6182 int len;
6183
6184 if (str == NULL)
6185 return(NULL);
6186 tmp = str;
6187 while (*tmp != 0) tmp++;
6188 len = tmp - str;
6189
6190 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
6191 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00006192 if (ctxt != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006193 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardea3f3982003-01-26 19:45:18 +00006194 } else {
6195 xmlGenericError(xmlGenericErrorContext,
6196 "xmlRelaxNGNormalize: out of memory\n");
6197 }
Daniel Veillardedc91922003-01-26 00:52:04 +00006198 return(NULL);
6199 }
6200 p = ret;
6201 while (IS_BLANK(*str)) str++;
6202 while (*str != 0) {
6203 if (IS_BLANK(*str)) {
6204 while (IS_BLANK(*str)) str++;
6205 if (*str == 0)
6206 break;
6207 *p++ = ' ';
6208 } else
6209 *p++ = *str++;
6210 }
6211 *p = 0;
6212 return(ret);
6213}
6214
6215/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006216 * xmlRelaxNGValidateDatatype:
6217 * @ctxt: a Relax-NG validation context
6218 * @value: the string value
6219 * @type: the datatype definition
6220 *
6221 * Validate the given value against the dataype
6222 *
6223 * Returns 0 if the validation succeeded or an error code.
6224 */
6225static int
6226xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
6227 xmlRelaxNGDefinePtr define) {
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006228 int ret, tmp;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006229 xmlRelaxNGTypeLibraryPtr lib;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006230 void *result = NULL;
6231 xmlRelaxNGDefinePtr cur;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006232
6233 if ((define == NULL) || (define->data == NULL)) {
6234 return(-1);
6235 }
6236 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006237 if (lib->check != NULL) {
6238 if ((define->attrs != NULL) &&
6239 (define->attrs->type == XML_RELAXNG_PARAM)) {
6240 ret = lib->check(lib->data, define->name, value, &result);
6241 } else {
6242 ret = lib->check(lib->data, define->name, value, NULL);
6243 }
6244 } else
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006245 ret = -1;
6246 if (ret < 0) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006247 VALID_ERR2(XML_RELAXNG_ERR_TYPE, define->name);
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006248 if ((result != NULL) && (lib != NULL) && (lib->freef != NULL))
6249 lib->freef(lib->data, result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006250 return(-1);
6251 } else if (ret == 1) {
6252 ret = 0;
6253 } else {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006254 VALID_ERR3(XML_RELAXNG_ERR_TYPEVAL, define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006255 ret = -1;
6256 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006257 cur = define->attrs;
6258 while ((ret == 0) && (cur != NULL) && (cur->type == XML_RELAXNG_PARAM)) {
6259 if (lib->facet != NULL) {
6260 tmp = lib->facet(lib->data, define->name, cur->name,
6261 cur->value, value, result);
6262 if (tmp != 0)
6263 ret = -1;
6264 }
6265 cur = cur->next;
6266 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006267 if ((ret == 0) && (define->content != NULL)) {
6268 const xmlChar *oldvalue, *oldendvalue;
6269
6270 oldvalue = ctxt->state->value;
6271 oldendvalue = ctxt->state->endvalue;
6272 ctxt->state->value = (xmlChar *) value;
6273 ctxt->state->endvalue = NULL;
6274 ret = xmlRelaxNGValidateValue(ctxt, define->content);
6275 ctxt->state->value = (xmlChar *) oldvalue;
6276 ctxt->state->endvalue = (xmlChar *) oldendvalue;
6277 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006278 if ((result != NULL) && (lib != NULL) && (lib->freef != NULL))
6279 lib->freef(lib->data, result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006280 return(ret);
6281}
6282
6283/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006284 * xmlRelaxNGNextValue:
6285 * @ctxt: a Relax-NG validation context
6286 *
6287 * Skip to the next value when validating within a list
6288 *
6289 * Returns 0 if the operation succeeded or an error code.
6290 */
6291static int
6292xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
6293 xmlChar *cur;
6294
6295 cur = ctxt->state->value;
6296 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
6297 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006298 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006299 return(0);
6300 }
6301 while (*cur != 0) cur++;
6302 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
6303 if (cur == ctxt->state->endvalue)
6304 ctxt->state->value = NULL;
6305 else
6306 ctxt->state->value = cur;
6307 return(0);
6308}
6309
6310/**
6311 * xmlRelaxNGValidateValueList:
6312 * @ctxt: a Relax-NG validation context
6313 * @defines: the list of definitions to verify
6314 *
6315 * Validate the given set of definitions for the current value
6316 *
6317 * Returns 0 if the validation succeeded or an error code.
6318 */
6319static int
6320xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
6321 xmlRelaxNGDefinePtr defines) {
6322 int ret = 0;
6323
6324 while (defines != NULL) {
6325 ret = xmlRelaxNGValidateValue(ctxt, defines);
6326 if (ret != 0)
6327 break;
6328 defines = defines->next;
6329 }
6330 return(ret);
6331}
6332
6333/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006334 * xmlRelaxNGValidateValue:
6335 * @ctxt: a Relax-NG validation context
6336 * @define: the definition to verify
6337 *
6338 * Validate the given definition for the current value
6339 *
6340 * Returns 0 if the validation succeeded or an error code.
6341 */
6342static int
6343xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
6344 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00006345 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006346 xmlChar *value;
6347
6348 value = ctxt->state->value;
6349 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006350 case XML_RELAXNG_EMPTY: {
6351 if ((value != NULL) && (value[0] != 0)) {
6352 int idx = 0;
6353
6354 while (IS_BLANK(value[idx]))
6355 idx++;
6356 if (value[idx] != 0)
6357 ret = -1;
6358 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006359 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00006360 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006361 case XML_RELAXNG_TEXT:
6362 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00006363 case XML_RELAXNG_VALUE: {
6364 if (!xmlStrEqual(value, define->value)) {
6365 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00006366 xmlRelaxNGTypeLibraryPtr lib;
6367
6368 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
6369 if ((lib != NULL) && (lib->comp != NULL))
6370 ret = lib->comp(lib->data, define->name, value,
6371 define->value);
6372 else
6373 ret = -1;
6374 if (ret < 0) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006375 VALID_ERR2(XML_RELAXNG_ERR_TYPECMP, define->name);
Daniel Veillardea3f3982003-01-26 19:45:18 +00006376 return(-1);
6377 } else if (ret == 1) {
6378 ret = 0;
6379 } else {
6380 ret = -1;
6381 }
Daniel Veillardedc91922003-01-26 00:52:04 +00006382 } else {
6383 xmlChar *nval, *nvalue;
6384
6385 /*
6386 * TODO: trivial optimizations are possible by
6387 * computing at compile-time
6388 */
6389 nval = xmlRelaxNGNormalize(ctxt, define->value);
6390 nvalue = xmlRelaxNGNormalize(ctxt, value);
6391
Daniel Veillardea3f3982003-01-26 19:45:18 +00006392 if ((nval == NULL) || (nvalue == NULL) ||
6393 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00006394 ret = -1;
6395 if (nval != NULL)
6396 xmlFree(nval);
6397 if (nvalue != NULL)
6398 xmlFree(nvalue);
6399 }
6400 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006401 if (ret == 0)
6402 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00006403 break;
6404 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006405 case XML_RELAXNG_DATATYPE: {
6406 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
6407 if (ret == 0)
6408 xmlRelaxNGNextValue(ctxt);
6409
6410 break;
6411 }
6412 case XML_RELAXNG_CHOICE: {
6413 xmlRelaxNGDefinePtr list = define->content;
6414 xmlChar *oldvalue;
6415
6416 oldflags = ctxt->flags;
6417 ctxt->flags |= FLAGS_IGNORABLE;
6418
6419 oldvalue = ctxt->state->value;
6420 while (list != NULL) {
6421 ret = xmlRelaxNGValidateValue(ctxt, list);
6422 if (ret == 0) {
6423 break;
6424 }
6425 ctxt->state->value = oldvalue;
6426 list = list->next;
6427 }
6428 ctxt->flags = oldflags;
Daniel Veillard42f12e92003-03-07 18:32:59 +00006429 if (ret != 0) {
6430 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
6431 xmlRelaxNGDumpValidError(ctxt);
6432 } else {
6433 ctxt->errNr = 0;
6434 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006435 if (ret == 0)
6436 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006437 break;
6438 }
6439 case XML_RELAXNG_LIST: {
6440 xmlRelaxNGDefinePtr list = define->content;
6441 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00006442#ifdef DEBUG_LIST
6443 int nb_values = 0;
6444#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006445
6446 oldvalue = ctxt->state->value;
6447 oldend = ctxt->state->endvalue;
6448
6449 val = xmlStrdup(oldvalue);
6450 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006451 val = xmlStrdup(BAD_CAST "");
6452 }
6453 if (val == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006454 VALID_ERR(XML_RELAXNG_ERR_NOSTATE);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006455 return(-1);
6456 }
6457 cur = val;
6458 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006459 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006460 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006461 cur++;
6462#ifdef DEBUG_LIST
6463 nb_values++;
6464#endif
6465 while (IS_BLANK(*cur))
6466 *cur++ = 0;
6467 } else
6468 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006469 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006470#ifdef DEBUG_LIST
6471 xmlGenericError(xmlGenericErrorContext,
6472 "list value: '%s' found %d items\n", oldvalue, nb_values);
6473 nb_values = 0;
6474#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006475 ctxt->state->endvalue = cur;
6476 cur = val;
6477 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
6478
6479 ctxt->state->value = cur;
6480
6481 while (list != NULL) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006482 if (ctxt->state->value == ctxt->state->endvalue)
6483 ctxt->state->value = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006484 ret = xmlRelaxNGValidateValue(ctxt, list);
6485 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006486#ifdef DEBUG_LIST
6487 xmlGenericError(xmlGenericErrorContext,
6488 "Failed to validate value: '%s' with %d rule\n",
6489 ctxt->state->value, nb_values);
6490#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006491 break;
6492 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006493#ifdef DEBUG_LIST
6494 nb_values++;
6495#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006496 list = list->next;
6497 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006498
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006499 if ((ret == 0) && (ctxt->state->value != NULL) &&
6500 (ctxt->state->value != ctxt->state->endvalue)) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006501 VALID_ERR2(XML_RELAXNG_ERR_LISTEXTRA, ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006502 ret = -1;
6503 }
6504 xmlFree(val);
6505 ctxt->state->value = oldvalue;
6506 ctxt->state->endvalue = oldend;
6507 break;
6508 }
6509 case XML_RELAXNG_ONEORMORE:
6510 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6511 if (ret != 0) {
6512 break;
6513 }
6514 /* no break on purpose */
6515 case XML_RELAXNG_ZEROORMORE: {
6516 xmlChar *cur, *temp;
6517
6518 oldflags = ctxt->flags;
6519 ctxt->flags |= FLAGS_IGNORABLE;
6520 cur = ctxt->state->value;
6521 temp = NULL;
6522 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
6523 (temp != cur)) {
6524 temp = cur;
6525 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6526 if (ret != 0) {
6527 ctxt->state->value = temp;
6528 ret = 0;
6529 break;
6530 }
6531 cur = ctxt->state->value;
6532 }
6533 ctxt->flags = oldflags;
Daniel Veillard42f12e92003-03-07 18:32:59 +00006534 if (ret != 0) {
6535 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
6536 xmlRelaxNGDumpValidError(ctxt);
6537 } else {
6538 ctxt->errNr = 0;
6539 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006540 break;
6541 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006542 case XML_RELAXNG_EXCEPT: {
6543 xmlRelaxNGDefinePtr list;
6544
6545 list = define->content;
6546 while (list != NULL) {
6547 ret = xmlRelaxNGValidateValue(ctxt, list);
6548 if (ret == 0) {
6549 ret = -1;
6550 break;
6551 } else
6552 ret = 0;
6553 list = list->next;
6554 }
6555 break;
6556 }
Daniel Veillard463a5472003-02-27 21:30:32 +00006557 case XML_RELAXNG_DEF:
Daniel Veillardd4310742003-02-18 21:12:46 +00006558 case XML_RELAXNG_GROUP: {
6559 xmlRelaxNGDefinePtr list;
6560
6561 list = define->content;
6562 while (list != NULL) {
6563 ret = xmlRelaxNGValidateValue(ctxt, list);
6564 if (ret != 0) {
6565 ret = -1;
6566 break;
6567 } else
6568 ret = 0;
6569 list = list->next;
6570 }
6571 break;
6572 }
Daniel Veillard463a5472003-02-27 21:30:32 +00006573 case XML_RELAXNG_REF:
6574 case XML_RELAXNG_PARENTREF:
6575 ret = xmlRelaxNGValidateValue(ctxt, define->content);
6576 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006577 default:
6578 TODO
6579 ret = -1;
6580 }
6581 return(ret);
6582}
6583
6584/**
6585 * xmlRelaxNGValidateValueContent:
6586 * @ctxt: a Relax-NG validation context
6587 * @defines: the list of definitions to verify
6588 *
6589 * Validate the given definitions for the current value
6590 *
6591 * Returns 0 if the validation succeeded or an error code.
6592 */
6593static int
6594xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
6595 xmlRelaxNGDefinePtr defines) {
6596 int ret = 0;
6597
6598 while (defines != NULL) {
6599 ret = xmlRelaxNGValidateValue(ctxt, defines);
6600 if (ret != 0)
6601 break;
6602 defines = defines->next;
6603 }
6604 return(ret);
6605}
6606
6607/**
Daniel Veillard144fae12003-02-03 13:17:57 +00006608 * xmlRelaxNGAttributeMatch:
6609 * @ctxt: a Relax-NG validation context
6610 * @define: the definition to check
6611 * @prop: the attribute
6612 *
6613 * Check if the attribute matches the definition nameClass
6614 *
6615 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
6616 */
6617static int
6618xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
6619 xmlRelaxNGDefinePtr define,
6620 xmlAttrPtr prop) {
6621 int ret;
6622
6623 if (define->name != NULL) {
6624 if (!xmlStrEqual(define->name, prop->name))
6625 return(0);
6626 }
6627 if (define->ns != NULL) {
6628 if (define->ns[0] == 0) {
6629 if (prop->ns != NULL)
6630 return(0);
6631 } else {
6632 if ((prop->ns == NULL) ||
6633 (!xmlStrEqual(define->ns, prop->ns->href)))
6634 return(0);
6635 }
6636 }
6637 if (define->nameClass == NULL)
6638 return(1);
6639 define = define->nameClass;
6640 if (define->type == XML_RELAXNG_EXCEPT) {
6641 xmlRelaxNGDefinePtr list;
6642
6643 list = define->content;
6644 while (list != NULL) {
6645 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
6646 if (ret == 1)
6647 return(0);
6648 if (ret < 0)
6649 return(ret);
6650 list = list->next;
6651 }
6652 } else {
6653 TODO
6654 }
6655 return(1);
6656}
6657
6658/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006659 * xmlRelaxNGValidateAttribute:
6660 * @ctxt: a Relax-NG validation context
6661 * @define: the definition to verify
6662 *
6663 * Validate the given attribute definition for that node
6664 *
6665 * Returns 0 if the validation succeeded or an error code.
6666 */
6667static int
6668xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
6669 xmlRelaxNGDefinePtr define) {
6670 int ret = 0, i;
6671 xmlChar *value, *oldvalue;
6672 xmlAttrPtr prop = NULL, tmp;
6673
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006674 if (ctxt->state->nbAttrLeft <= 0)
6675 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006676 if (define->name != NULL) {
6677 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6678 tmp = ctxt->state->attrs[i];
6679 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
6680 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
6681 (tmp->ns == NULL)) ||
6682 ((tmp->ns != NULL) &&
6683 (xmlStrEqual(define->ns, tmp->ns->href)))) {
6684 prop = tmp;
6685 break;
6686 }
6687 }
6688 }
6689 if (prop != NULL) {
6690 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6691 oldvalue = ctxt->state->value;
6692 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00006693 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006694 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006695 if (ctxt->state->value != NULL)
6696 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006697 if (value != NULL)
6698 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006699 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006700 if (ret == 0) {
6701 /*
6702 * flag the attribute as processed
6703 */
6704 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006705 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006706 }
6707 } else {
6708 ret = -1;
6709 }
6710#ifdef DEBUG
6711 xmlGenericError(xmlGenericErrorContext,
6712 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
6713#endif
6714 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006715 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6716 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00006717 if ((tmp != NULL) &&
6718 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006719 prop = tmp;
6720 break;
6721 }
6722 }
6723 if (prop != NULL) {
6724 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6725 oldvalue = ctxt->state->value;
6726 ctxt->state->value = value;
6727 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006728 if (ctxt->state->value != NULL)
6729 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006730 if (value != NULL)
6731 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006732 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006733 if (ret == 0) {
6734 /*
6735 * flag the attribute as processed
6736 */
6737 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006738 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006739 }
6740 } else {
6741 ret = -1;
6742 }
6743#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00006744 if (define->ns != NULL) {
6745 xmlGenericError(xmlGenericErrorContext,
6746 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
6747 define->ns, ret);
6748 } else {
6749 xmlGenericError(xmlGenericErrorContext,
6750 "xmlRelaxNGValidateAttribute(anyName): %d\n",
6751 ret);
6752 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006753#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006754 }
6755
6756 return(ret);
6757}
6758
6759/**
6760 * xmlRelaxNGValidateAttributeList:
6761 * @ctxt: a Relax-NG validation context
6762 * @define: the list of definition to verify
6763 *
6764 * Validate the given node against the list of attribute definitions
6765 *
6766 * Returns 0 if the validation succeeded or an error code.
6767 */
6768static int
6769xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
6770 xmlRelaxNGDefinePtr defines) {
6771 int ret = 0;
6772 while (defines != NULL) {
6773 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
6774 ret = -1;
6775 defines = defines->next;
6776 }
6777 return(ret);
6778}
6779
6780/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006781 * xmlRelaxNGNodeMatchesList:
6782 * @node: the node
6783 * @list: a NULL terminated array of definitions
6784 *
6785 * Check if a node can be matched by one of the definitions
6786 *
6787 * Returns 1 if matches 0 otherwise
6788 */
6789static int
6790xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
6791 xmlRelaxNGDefinePtr cur;
6792 int i = 0;
6793
6794 if ((node == NULL) || (list == NULL))
6795 return(0);
6796
6797 cur = list[i++];
6798 while (cur != NULL) {
6799 if ((node->type == XML_ELEMENT_NODE) &&
6800 (cur->type == XML_RELAXNG_ELEMENT)) {
6801 if (cur->name == NULL) {
6802 if ((node->ns != NULL) &&
6803 (xmlStrEqual(node->ns->href, cur->ns)))
6804 return(1);
6805 } else if (xmlStrEqual(cur->name, node->name)) {
6806 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
6807 if (node->ns == NULL)
6808 return(1);
6809 } else {
6810 if ((node->ns != NULL) &&
6811 (xmlStrEqual(node->ns->href, cur->ns)))
6812 return(1);
6813 }
6814 }
6815 } else if ((node->type == XML_TEXT_NODE) &&
6816 (cur->type == XML_RELAXNG_TEXT)) {
6817 return(1);
6818 }
6819 cur = list[i++];
6820 }
6821 return(0);
6822}
6823
6824/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006825 * xmlRelaxNGValidateInterleave:
6826 * @ctxt: a Relax-NG validation context
6827 * @define: the definition to verify
6828 *
6829 * Validate an interleave definition for a node.
6830 *
6831 * Returns 0 if the validation succeeded or an error code.
6832 */
6833static int
6834xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
6835 xmlRelaxNGDefinePtr define) {
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006836 int ret = 0, i, nbgroups, left;
Daniel Veillard42f12e92003-03-07 18:32:59 +00006837 int errNr = ctxt->errNr;
6838
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006839 xmlRelaxNGPartitionPtr partitions;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006840 xmlRelaxNGInterleaveGroupPtr group = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006841 xmlNodePtr cur, start, last = NULL, lastchg = NULL, lastelem;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006842 xmlNodePtr *list = NULL, *lasts = NULL;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006843
6844 if (define->data != NULL) {
6845 partitions = (xmlRelaxNGPartitionPtr) define->data;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006846 nbgroups = partitions->nbgroups;
6847 left = nbgroups;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006848 } else {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006849 VALID_ERR(XML_RELAXNG_ERR_INTERNODATA);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006850 return(-1);
6851 }
6852
6853 /*
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006854 * Build arrays to store the first and last node of the chain
6855 * pertaining to each group
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006856 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006857 list = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
6858 if (list == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006859 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006860 return(-1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006861 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006862 memset(list, 0, nbgroups * sizeof(xmlNodePtr));
6863 lasts = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
6864 if (lasts == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006865 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006866 return(-1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006867 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006868 memset(lasts, 0, nbgroups * sizeof(xmlNodePtr));
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006869
6870 /*
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006871 * Walk the sequence of children finding the right group and
6872 * sorting them in sequences.
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006873 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006874 cur = ctxt->state->seq;
6875 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
6876 start = cur;
6877 while (cur != NULL) {
6878 ctxt->state->seq = cur;
6879 for (i = 0;i < nbgroups;i++) {
6880 group = partitions->groups[i];
6881 if (group == NULL)
6882 continue;
6883 if (xmlRelaxNGNodeMatchesList(cur, group->defs))
6884 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006885 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006886 /*
6887 * We break as soon as an element not matched is found
6888 */
6889 if (i >= nbgroups) {
6890 break;
6891 }
6892 if (lasts[i] != NULL) {
6893 lasts[i]->next = cur;
6894 lasts[i] = cur;
6895 } else {
6896 list[i] = cur;
6897 lasts[i] = cur;
6898 }
6899 if (cur->next != NULL)
6900 lastchg = cur->next;
6901 else
6902 lastchg = cur;
6903 cur = xmlRelaxNGSkipIgnored(ctxt, cur->next);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006904 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006905 if (ret != 0) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006906 VALID_ERR(XML_RELAXNG_ERR_INTERSEQ);
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006907 ret = -1;
6908 goto done;
6909 }
6910 lastelem = cur;
6911 for (i = 0;i < nbgroups;i++) {
6912 group = partitions->groups[i];
6913 if (lasts[i] != NULL) {
6914 last = lasts[i]->next;
6915 lasts[i]->next = NULL;
6916 }
6917 ctxt->state->seq = list[i];
6918 ret = xmlRelaxNGValidateDefinition(ctxt, group->rule);
6919 if (ret != 0)
6920 break;
6921 cur = ctxt->state->seq;
6922 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
6923 if (cur != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006924 VALID_ERR2(XML_RELAXNG_ERR_INTEREXTRA, cur->name);
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006925 ret = -1;
6926 goto done;
6927 }
6928 if (lasts[i] != NULL) {
6929 lasts[i]->next = last;
6930 }
6931 }
6932 ctxt->state->seq = lastelem;
6933 if (ret != 0) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006934 VALID_ERR(XML_RELAXNG_ERR_INTERSEQ);
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006935 ret = -1;
6936 goto done;
6937 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006938
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006939done:
6940 /*
6941 * builds the next links chain from the prev one
6942 */
6943 cur = lastchg;
6944 while (cur != NULL) {
6945 if ((cur == start) || (cur->prev == NULL))
6946 break;
6947 cur->prev->next = cur;
6948 cur = cur->prev;
6949 }
Daniel Veillard42f12e92003-03-07 18:32:59 +00006950 if (ret == 0) {
6951 ctxt->errNr = errNr;
6952 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006953
6954 xmlFree(list);
6955 xmlFree(lasts);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006956 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006957}
6958
6959/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006960 * xmlRelaxNGValidateElementContent:
6961 * @ctxt: a Relax-NG validation context
6962 * @define: the list of definition to verify
6963 *
6964 * Validate the given node content against the (list) of definitions
6965 *
6966 * Returns 0 if the validation succeeded or an error code.
6967 */
6968static int
6969xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
6970 xmlRelaxNGDefinePtr defines) {
6971 int ret = 0, res;
6972
6973 if (ctxt->state == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006974 VALID_ERR(XML_RELAXNG_ERR_NOSTATE);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006975 return(-1);
6976 }
6977 while (defines != NULL) {
6978 res = xmlRelaxNGValidateDefinition(ctxt, defines);
6979 if (res < 0)
6980 ret = -1;
6981 defines = defines->next;
6982 }
6983
6984 return(ret);
6985}
6986
6987/**
Daniel Veillard416589a2003-02-17 17:25:42 +00006988 * xmlRelaxNGElementMatch:
6989 * @ctxt: a Relax-NG validation context
6990 * @define: the definition to check
6991 * @elem: the element
6992 *
6993 * Check if the element matches the definition nameClass
6994 *
6995 * Returns 1 if the element matches, 0 if no, or -1 in case of error
6996 */
6997static int
6998xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
6999 xmlRelaxNGDefinePtr define,
7000 xmlNodePtr elem) {
7001 int ret, oldflags;
7002
7003 if (define->name != NULL) {
7004 if (!xmlStrEqual(elem->name, define->name)) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007005 VALID_ERR3(XML_RELAXNG_ERR_ELEMNAME, define->name, elem->name);
Daniel Veillard416589a2003-02-17 17:25:42 +00007006 return(0);
7007 }
7008 }
7009 if ((define->ns != NULL) && (define->ns[0] != 0)) {
7010 if (elem->ns == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007011 VALID_ERR2(XML_RELAXNG_ERR_ELEMNONS,
Daniel Veillard416589a2003-02-17 17:25:42 +00007012 elem->name);
7013 return(0);
7014 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007015 VALID_ERR3(XML_RELAXNG_ERR_ELEMWRONGNS,
Daniel Veillard416589a2003-02-17 17:25:42 +00007016 elem->name, define->ns);
7017 return(0);
7018 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00007019 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
7020 (define->name == NULL)) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007021 VALID_ERR2(XML_RELAXNG_ERR_ELEMEXTRANS,
7022 elem->name);
Daniel Veillard8fe98712003-02-19 00:19:14 +00007023 return(0);
7024 } else if ((elem->ns != NULL) && (define->name != NULL)) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007025 VALID_ERR2(XML_RELAXNG_ERR_ELEMEXTRANS,
Daniel Veillard8fe98712003-02-19 00:19:14 +00007026 define->name);
7027 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00007028 }
7029
7030 if (define->nameClass == NULL)
7031 return(1);
7032
7033 define = define->nameClass;
7034 if (define->type == XML_RELAXNG_EXCEPT) {
7035 xmlRelaxNGDefinePtr list;
7036 oldflags = ctxt->flags;
7037 ctxt->flags |= FLAGS_IGNORABLE;
7038
7039 list = define->content;
7040 while (list != NULL) {
7041 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
7042 if (ret == 1) {
7043 ctxt->flags = oldflags;
7044 return(0);
7045 }
7046 if (ret < 0) {
7047 ctxt->flags = oldflags;
7048 return(ret);
7049 }
7050 list = list->next;
7051 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007052 ret = 1;
7053 ctxt->flags = oldflags;
7054 } else if (define->type == XML_RELAXNG_CHOICE) {
7055 xmlRelaxNGDefinePtr list;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007056
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007057 oldflags = ctxt->flags;
7058 ctxt->flags |= FLAGS_IGNORABLE;
7059
7060 list = define->nameClass;
7061 while (list != NULL) {
7062 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
7063 if (ret == 1) {
7064 ctxt->flags = oldflags;
7065 return(1);
7066 }
7067 if (ret < 0) {
7068 ctxt->flags = oldflags;
7069 return(ret);
7070 }
7071 list = list->next;
7072 }
Daniel Veillard42f12e92003-03-07 18:32:59 +00007073 if (ret != 0) {
7074 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7075 xmlRelaxNGDumpValidError(ctxt);
7076 } else {
7077 ctxt->errNr = 0;
7078 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007079 ret = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00007080 ctxt->flags = oldflags;
7081 } else {
7082 TODO
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007083 ret = -1;
Daniel Veillard416589a2003-02-17 17:25:42 +00007084 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007085 return(ret);
Daniel Veillard416589a2003-02-17 17:25:42 +00007086}
7087
7088/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00007089 * xmlRelaxNGValidateDefinition:
7090 * @ctxt: a Relax-NG validation context
7091 * @define: the definition to verify
7092 *
7093 * Validate the current node against the definition
7094 *
7095 * Returns 0 if the validation succeeded or an error code.
7096 */
7097static int
7098xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
7099 xmlRelaxNGDefinePtr define) {
7100 xmlNodePtr node;
7101 int ret = 0, i, tmp, oldflags;
7102 xmlRelaxNGValidStatePtr oldstate, state;
7103
7104 if (define == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007105 VALID_ERR(XML_RELAXNG_ERR_NODEFINE);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007106 return(-1);
7107 }
7108 if (ctxt->state != NULL) {
7109 node = ctxt->state->seq;
7110 } else {
7111 node = NULL;
7112 }
Daniel Veillard231d7912003-02-09 14:22:17 +00007113#ifdef DEBUG
7114 for (i = 0;i < ctxt->depth;i++)
7115 xmlGenericError(xmlGenericErrorContext, " ");
7116 xmlGenericError(xmlGenericErrorContext,
7117 "Start validating %s ", xmlRelaxNGDefName(define));
7118 if (define->name != NULL)
7119 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
7120 if ((node != NULL) && (node->name != NULL))
7121 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
7122 else
7123 xmlGenericError(xmlGenericErrorContext, "\n");
7124#endif
7125 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007126 switch (define->type) {
7127 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00007128 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007129 if (node != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007130 VALID_ERR2(XML_RELAXNG_ERR_ELEMNOTEMPTY,
7131 ctxt->state->node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00007132 ret = -1;
7133 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007134 }
Daniel Veillard231d7912003-02-09 14:22:17 +00007135 ret = 0;
7136 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007137 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00007138 ret = -1;
7139 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007140 case XML_RELAXNG_TEXT:
Daniel Veillard6eadf632003-01-23 18:29:16 +00007141 while ((node != NULL) &&
7142 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007143 (node->type == XML_COMMENT_NODE) ||
7144 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00007145 (node->type == XML_CDATA_SECTION_NODE)))
7146 node = node->next;
Daniel Veillard276be4a2003-01-24 01:03:34 +00007147 ctxt->state->seq = node;
7148 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007149 case XML_RELAXNG_ELEMENT:
7150 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00007151 if (node == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007152 VALID_ERR2(XML_RELAXNG_ERR_NOELEM, define->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00007153 ret = -1;
7154 break;
7155 }
7156 if (node->type != XML_ELEMENT_NODE) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007157 VALID_ERR(XML_RELAXNG_ERR_NOTELEM);
Daniel Veillard231d7912003-02-09 14:22:17 +00007158 ret = -1;
7159 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007160 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00007161 /*
7162 * This node was already validated successfully against
7163 * this definition.
7164 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00007165 if (node->_private == define) {
7166 ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
Daniel Veillarde5b110b2003-02-04 14:43:39 +00007167 break;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00007168 }
Daniel Veillard416589a2003-02-17 17:25:42 +00007169
7170 ret = xmlRelaxNGElementMatch(ctxt, define, node);
7171 if (ret <= 0) {
7172 ret = -1;
7173 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007174 }
Daniel Veillard416589a2003-02-17 17:25:42 +00007175 ret = 0;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007176 if (ctxt->errNr != 0) {
7177 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7178 ctxt->errNr = 0;
7179 else {
7180 while ((ctxt->err != NULL) &&
7181 (ctxt->err->err == XML_RELAXNG_ERR_ELEMNAME) &&
7182 (xmlStrEqual(ctxt->err->arg2, node->name)))
7183 xmlRelaxNGValidErrorPop(ctxt);
7184 }
7185 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007186
7187 state = xmlRelaxNGNewValidState(ctxt, node);
7188 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00007189 ret = -1;
7190 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007191 }
7192
7193 oldstate = ctxt->state;
7194 ctxt->state = state;
7195 if (define->attrs != NULL) {
7196 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00007197 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00007198 ret = -1;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007199 VALID_ERR2(XML_RELAXNG_ERR_ATTRVALID, node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00007200 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007201 }
7202 if (define->content != NULL) {
7203 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00007204 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00007205 ret = -1;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007206 VALID_ERR2(XML_RELAXNG_ERR_CONTENTVALID, node->name);
Daniel Veillard231d7912003-02-09 14:22:17 +00007207 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007208 }
7209 state = ctxt->state;
7210 if (state->seq != NULL) {
7211 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
7212 if (state->seq != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007213 VALID_ERR3(XML_RELAXNG_ERR_EXTRACONTENT,
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007214 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007215 ret = -1;
7216 }
7217 }
7218 for (i = 0;i < state->nbAttrs;i++) {
7219 if (state->attrs[i] != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007220 VALID_ERR3(XML_RELAXNG_ERR_INVALIDATTR,
Daniel Veillard6eadf632003-01-23 18:29:16 +00007221 state->attrs[i]->name, node->name);
7222 ret = -1;
7223 }
7224 }
7225 ctxt->state = oldstate;
7226 xmlRelaxNGFreeValidState(state);
7227 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00007228 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
Daniel Veillard42f12e92003-03-07 18:32:59 +00007229 if (ret == 0) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00007230 node->_private = define;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007231 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007232
7233
7234#ifdef DEBUG
7235 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00007236 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00007237 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00007238 if (oldstate == NULL)
7239 xmlGenericError(xmlGenericErrorContext, ": no state\n");
7240 else if (oldstate->seq == NULL)
7241 xmlGenericError(xmlGenericErrorContext, ": done\n");
7242 else if (oldstate->seq->type == XML_ELEMENT_NODE)
7243 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
7244 oldstate->seq->name);
7245 else
7246 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
7247 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007248#endif
7249 break;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007250 case XML_RELAXNG_OPTIONAL: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00007251 oldflags = ctxt->flags;
7252 ctxt->flags |= FLAGS_IGNORABLE;
7253 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
7254 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7255 if (ret != 0) {
7256 xmlRelaxNGFreeValidState(ctxt->state);
7257 ctxt->state = oldstate;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007258 ctxt->flags = oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007259 ret = 0;
7260 break;
7261 }
7262 xmlRelaxNGFreeValidState(oldstate);
7263 ctxt->flags = oldflags;
7264 ret = 0;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007265 ctxt->errNr = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007266 break;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007267 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007268 case XML_RELAXNG_ONEORMORE:
7269 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7270 if (ret != 0) {
7271 break;
7272 }
7273 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00007274 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00007275 oldflags = ctxt->flags;
7276 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007277 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00007278 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
7279 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7280 if (ret != 0) {
7281 xmlRelaxNGFreeValidState(ctxt->state);
7282 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007283 break;
7284 }
7285 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007286 }
7287 if (ret == 0) {
7288 /*
7289 * There is no attribute left to be consumed,
7290 * we can check the closure by looking at ctxt->state->seq
7291 */
7292 xmlNodePtr cur, temp;
7293
Daniel Veillard276be4a2003-01-24 01:03:34 +00007294 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007295 temp = NULL;
7296 while ((cur != NULL) && (temp != cur)) {
7297 temp = cur;
7298 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
7299 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7300 if (ret != 0) {
7301 xmlRelaxNGFreeValidState(ctxt->state);
7302 ctxt->state = oldstate;
7303 break;
7304 }
7305 xmlRelaxNGFreeValidState(oldstate);
7306 cur = ctxt->state->seq;
7307 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007308 }
7309 ctxt->flags = oldflags;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007310 if (ret == 0) {
7311 ctxt->errNr = 0;
7312 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007313 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007314 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00007315 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007316 case XML_RELAXNG_CHOICE: {
7317 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardce14fa52003-02-19 17:32:48 +00007318 int success = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007319
7320 oldflags = ctxt->flags;
7321 ctxt->flags |= FLAGS_IGNORABLE;
7322
7323 while (list != NULL) {
7324 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
7325 ret = xmlRelaxNGValidateDefinition(ctxt, list);
7326 if (ret == 0) {
Daniel Veillardce14fa52003-02-19 17:32:48 +00007327 if (xmlRelaxNGEqualValidState(ctxt, ctxt->state, oldstate)){
7328 /*
7329 * if that pattern was nullable flag it but try
7330 * to make more progresses
7331 */
7332 success = 1;
7333 } else {
7334 xmlRelaxNGFreeValidState(oldstate);
7335 break;
7336 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007337 }
7338 xmlRelaxNGFreeValidState(ctxt->state);
7339 ctxt->state = oldstate;
7340 list = list->next;
7341 }
7342 ctxt->flags = oldflags;
Daniel Veillardce14fa52003-02-19 17:32:48 +00007343 if (success == 1)
7344 ret = 0;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007345 if (ret != 0) {
7346 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7347 xmlRelaxNGDumpValidError(ctxt);
7348 } else if ((ctxt->flags & FLAGS_IGNORABLE) == 0) {
7349 ctxt->errNr = 0;
7350 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007351 break;
7352 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00007353 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00007354 case XML_RELAXNG_GROUP: {
7355 xmlRelaxNGDefinePtr list = define->content;
7356
7357 while (list != NULL) {
7358 ret = xmlRelaxNGValidateDefinition(ctxt, list);
7359 if (ret != 0)
7360 break;
7361 list = list->next;
7362 }
7363 break;
7364 }
7365 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00007366 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007367 break;
7368 case XML_RELAXNG_ATTRIBUTE:
7369 ret = xmlRelaxNGValidateAttribute(ctxt, define);
7370 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00007371 case XML_RELAXNG_NOOP:
Daniel Veillard6eadf632003-01-23 18:29:16 +00007372 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00007373 case XML_RELAXNG_PARENTREF:
7374 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00007375 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7376 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007377 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00007378 xmlNodePtr child;
7379 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007380
Daniel Veillardd4310742003-02-18 21:12:46 +00007381 child = node;
7382 while (child != NULL) {
7383 if (child->type == XML_ELEMENT_NODE) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007384 VALID_ERR2(XML_RELAXNG_ERR_DATAELEM,
Daniel Veillardd4310742003-02-18 21:12:46 +00007385 node->parent->name);
7386 ret = -1;
7387 break;
7388 } else if ((child->type == XML_TEXT_NODE) ||
7389 (child->type == XML_CDATA_SECTION_NODE)) {
7390 content = xmlStrcat(content, child->content);
7391 }
7392 /* TODO: handle entities ... */
7393 child = child->next;
7394 }
7395 if (ret == -1) {
7396 if (content != NULL)
7397 xmlFree(content);
7398 break;
7399 }
7400 if (content == NULL) {
7401 content = xmlStrdup(BAD_CAST "");
7402 if (content == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007403 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardd4310742003-02-18 21:12:46 +00007404 ret = -1;
7405 break;
7406 }
7407 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007408 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
7409 if (ret == -1) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007410 VALID_ERR2(XML_RELAXNG_ERR_DATATYPE, define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007411 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007412 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007413 }
7414 if (content != NULL)
7415 xmlFree(content);
7416 break;
7417 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007418 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00007419 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007420 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00007421 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007422
Daniel Veillardd4310742003-02-18 21:12:46 +00007423 child = node;
7424 while (child != NULL) {
7425 if (child->type == XML_ELEMENT_NODE) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007426 VALID_ERR2(XML_RELAXNG_ERR_VALELEM,
Daniel Veillardd4310742003-02-18 21:12:46 +00007427 node->parent->name);
7428 ret = -1;
7429 break;
7430 } else if ((child->type == XML_TEXT_NODE) ||
7431 (child->type == XML_CDATA_SECTION_NODE)) {
7432 content = xmlStrcat(content, child->content);
7433 }
7434 /* TODO: handle entities ... */
7435 child = child->next;
7436 }
7437 if (ret == -1) {
7438 if (content != NULL)
7439 xmlFree(content);
7440 break;
7441 }
7442 if (content == NULL) {
7443 content = xmlStrdup(BAD_CAST "");
7444 if (content == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007445 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardd4310742003-02-18 21:12:46 +00007446 ret = -1;
7447 break;
7448 }
7449 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007450 oldvalue = ctxt->state->value;
7451 ctxt->state->value = content;
7452 ret = xmlRelaxNGValidateValue(ctxt, define);
7453 ctxt->state->value = oldvalue;
7454 if (ret == -1) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007455 VALID_ERR2(XML_RELAXNG_ERR_VALUE, define->name);
Daniel Veillardea3f3982003-01-26 19:45:18 +00007456 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007457 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007458 }
7459 if (content != NULL)
7460 xmlFree(content);
7461 break;
7462 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007463 case XML_RELAXNG_LIST: {
7464 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00007465 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007466 xmlChar *oldvalue, *oldendvalue;
7467 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007468
Daniel Veillardd4310742003-02-18 21:12:46 +00007469 /*
7470 * Make sure it's only text nodes
7471 */
7472
7473 content = NULL;
7474 child = node;
7475 while (child != NULL) {
7476 if (child->type == XML_ELEMENT_NODE) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007477 VALID_ERR2(XML_RELAXNG_ERR_LISTELEM,
Daniel Veillardd4310742003-02-18 21:12:46 +00007478 node->parent->name);
7479 ret = -1;
7480 break;
7481 } else if ((child->type == XML_TEXT_NODE) ||
7482 (child->type == XML_CDATA_SECTION_NODE)) {
7483 content = xmlStrcat(content, child->content);
7484 }
7485 /* TODO: handle entities ... */
7486 child = child->next;
7487 }
7488 if (ret == -1) {
7489 if (content != NULL)
7490 xmlFree(content);
7491 break;
7492 }
7493 if (content == NULL) {
7494 content = xmlStrdup(BAD_CAST "");
7495 if (content == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007496 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardd4310742003-02-18 21:12:46 +00007497 ret = -1;
7498 break;
7499 }
7500 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007501 len = xmlStrlen(content);
7502 oldvalue = ctxt->state->value;
7503 oldendvalue = ctxt->state->endvalue;
7504 ctxt->state->value = content;
7505 ctxt->state->endvalue = content + len;
7506 ret = xmlRelaxNGValidateValue(ctxt, define);
7507 ctxt->state->value = oldvalue;
7508 ctxt->state->endvalue = oldendvalue;
7509 if (ret == -1) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007510 VALID_ERR(XML_RELAXNG_ERR_LIST);
Daniel Veillardd4310742003-02-18 21:12:46 +00007511 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007512 ctxt->state->seq = node->next;
7513 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007514 if (content != NULL)
7515 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007516 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007517 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007518 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00007519 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00007520 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007521 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00007522 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007523 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007524 }
Daniel Veillard231d7912003-02-09 14:22:17 +00007525 ctxt->depth--;
7526#ifdef DEBUG
7527 for (i = 0;i < ctxt->depth;i++)
7528 xmlGenericError(xmlGenericErrorContext, " ");
7529 xmlGenericError(xmlGenericErrorContext,
7530 "Validating %s ", xmlRelaxNGDefName(define));
7531 if (define->name != NULL)
7532 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
7533 if (ret == 0)
7534 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
7535 else
7536 xmlGenericError(xmlGenericErrorContext, "failed\n");
7537#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00007538 return(ret);
7539}
7540
7541/**
7542 * xmlRelaxNGValidateDocument:
7543 * @ctxt: a Relax-NG validation context
7544 * @doc: the document
7545 *
7546 * Validate the given document
7547 *
7548 * Returns 0 if the validation succeeded or an error code.
7549 */
7550static int
7551xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7552 int ret;
7553 xmlRelaxNGPtr schema;
7554 xmlRelaxNGGrammarPtr grammar;
7555 xmlRelaxNGValidStatePtr state;
7556
7557 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
7558 return(-1);
7559
7560 schema = ctxt->schema;
7561 grammar = schema->topgrammar;
7562 if (grammar == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007563 VALID_ERR(XML_RELAXNG_ERR_NOGRAMMAR);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007564 return(-1);
7565 }
7566 state = xmlRelaxNGNewValidState(ctxt, NULL);
7567 ctxt->state = state;
7568 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
7569 state = ctxt->state;
7570 if ((state != NULL) && (state->seq != NULL)) {
7571 xmlNodePtr node;
7572
7573 node = state->seq;
7574 node = xmlRelaxNGSkipIgnored(ctxt, node);
7575 if (node != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007576 VALID_ERR(XML_RELAXNG_ERR_EXTRADATA);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007577 ret = -1;
7578 }
7579 }
7580 xmlRelaxNGFreeValidState(state);
7581
7582 return(ret);
7583}
7584
7585/************************************************************************
7586 * *
7587 * Validation interfaces *
7588 * *
7589 ************************************************************************/
7590/**
7591 * xmlRelaxNGNewValidCtxt:
7592 * @schema: a precompiled XML RelaxNGs
7593 *
7594 * Create an XML RelaxNGs validation context based on the given schema
7595 *
7596 * Returns the validation context or NULL in case of error
7597 */
7598xmlRelaxNGValidCtxtPtr
7599xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
7600 xmlRelaxNGValidCtxtPtr ret;
7601
7602 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
7603 if (ret == NULL) {
7604 xmlGenericError(xmlGenericErrorContext,
7605 "Failed to allocate new schama validation context\n");
7606 return (NULL);
7607 }
7608 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
7609 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00007610 ret->error = xmlGenericError;
7611 ret->userData = xmlGenericErrorContext;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007612 ret->errNr = 0;
7613 ret->errMax = 0;
7614 ret->err = NULL;
7615 ret->errTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007616 return (ret);
7617}
7618
7619/**
7620 * xmlRelaxNGFreeValidCtxt:
7621 * @ctxt: the schema validation context
7622 *
7623 * Free the resources associated to the schema validation context
7624 */
7625void
7626xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
7627 if (ctxt == NULL)
7628 return;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007629 if (ctxt->errTab != NULL)
7630 xmlFree(ctxt->errTab);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007631 xmlFree(ctxt);
7632}
7633
7634/**
7635 * xmlRelaxNGSetValidErrors:
7636 * @ctxt: a Relax-NG validation context
7637 * @err: the error function
7638 * @warn: the warning function
7639 * @ctx: the functions context
7640 *
7641 * Set the error and warning callback informations
7642 */
7643void
7644xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
7645 xmlRelaxNGValidityErrorFunc err,
7646 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
7647 if (ctxt == NULL)
7648 return;
7649 ctxt->error = err;
7650 ctxt->warning = warn;
7651 ctxt->userData = ctx;
7652}
7653
7654/**
7655 * xmlRelaxNGValidateDoc:
7656 * @ctxt: a Relax-NG validation context
7657 * @doc: a parsed document tree
7658 *
7659 * Validate a document tree in memory.
7660 *
7661 * Returns 0 if the document is valid, a positive error code
7662 * number otherwise and -1 in case of internal or API error.
7663 */
7664int
7665xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7666 int ret;
7667
7668 if ((ctxt == NULL) || (doc == NULL))
7669 return(-1);
7670
7671 ctxt->doc = doc;
7672
7673 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00007674 /*
7675 * TODO: build error codes
7676 */
7677 if (ret == -1)
7678 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007679 return(ret);
7680}
7681
7682#endif /* LIBXML_SCHEMAS_ENABLED */
7683