blob: 3f3cd1645c2257de44a99bcd68f8ab1bb1eb8b0e [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 Veillardfd573f12003-03-16 17:52:32 +000015 * - report better mem allocations at runtime and abort immediately.
Daniel Veillardd41f4f42003-01-29 21:07:52 +000016 */
17
Daniel Veillard6eadf632003-01-23 18:29:16 +000018#define IN_LIBXML
19#include "libxml.h"
20
21#ifdef LIBXML_SCHEMAS_ENABLED
22
23#include <string.h>
24#include <stdio.h>
25#include <libxml/xmlmemory.h>
26#include <libxml/parser.h>
27#include <libxml/parserInternals.h>
28#include <libxml/hash.h>
29#include <libxml/uri.h>
30
31#include <libxml/relaxng.h>
32
33#include <libxml/xmlschemastypes.h>
34#include <libxml/xmlautomata.h>
35#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000036#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000037
38/*
39 * The Relax-NG namespace
40 */
41static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
42 "http://relaxng.org/ns/structure/1.0";
43
44#define IS_RELAXNG(node, type) \
45 ((node != NULL) && (node->ns != NULL) && \
46 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
47 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
48
49
Daniel Veillardfd573f12003-03-16 17:52:32 +000050/* #define DEBUG 1 */
Daniel Veillardc482e262003-02-26 14:48:48 +000051/* #define DEBUG_GRAMMAR 1 */
Daniel Veillard71531f32003-02-05 13:19:53 +000052/* #define DEBUG_CONTENT 1 */
53/* #define DEBUG_TYPE 1 */
54/* #define DEBUG_VALID 1 */
Daniel Veillarde5b110b2003-02-04 14:43:39 +000055/* #define DEBUG_INTERLEAVE 1 */
Daniel Veillard416589a2003-02-17 17:25:42 +000056/* #define DEBUG_LIST 1 */
Daniel Veillard5add8682003-03-10 13:13:58 +000057/* #define DEBUG_INCLUDE */
Daniel Veillard6eadf632003-01-23 18:29:16 +000058
59#define UNBOUNDED (1 << 30)
60#define TODO \
61 xmlGenericError(xmlGenericErrorContext, \
62 "Unimplemented block at %s:%d\n", \
63 __FILE__, __LINE__);
64
65typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
66typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
67
68typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
69typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
70
Daniel Veillardd41f4f42003-01-29 21:07:52 +000071typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
72typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
73
Daniel Veillarda9d912d2003-02-01 17:43:10 +000074typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
75typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
76
Daniel Veillard6eadf632003-01-23 18:29:16 +000077typedef enum {
78 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
79 XML_RELAXNG_COMBINE_CHOICE, /* choice */
80 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
81} xmlRelaxNGCombine;
82
Daniel Veillard4c5cf702003-02-21 15:40:34 +000083typedef enum {
84 XML_RELAXNG_CONTENT_ERROR = -1,
85 XML_RELAXNG_CONTENT_EMPTY = 0,
86 XML_RELAXNG_CONTENT_SIMPLE,
87 XML_RELAXNG_CONTENT_COMPLEX
88} xmlRelaxNGContentType;
89
Daniel Veillard6eadf632003-01-23 18:29:16 +000090typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
91typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
92
93struct _xmlRelaxNGGrammar {
94 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
95 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
96 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
97 xmlRelaxNGDefinePtr start; /* <start> content */
98 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000099 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000100 xmlHashTablePtr defs; /* define* */
101 xmlHashTablePtr refs; /* references */
102};
103
104
Daniel Veillard6eadf632003-01-23 18:29:16 +0000105typedef enum {
Daniel Veillard77648bb2003-02-20 15:03:22 +0000106 XML_RELAXNG_NOOP = -1, /* a no operation from simplification */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000107 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
108 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +0000109 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000110 XML_RELAXNG_TEXT, /* textual content */
111 XML_RELAXNG_ELEMENT, /* an element */
112 XML_RELAXNG_DATATYPE, /* extenal data type definition */
Daniel Veillard8fe98712003-02-19 00:19:14 +0000113 XML_RELAXNG_PARAM, /* extenal data type parameter */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000114 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
115 XML_RELAXNG_LIST, /* a list of patterns */
116 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
117 XML_RELAXNG_DEF, /* a definition */
118 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000119 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000120 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillardfd573f12003-03-16 17:52:32 +0000121 XML_RELAXNG_OPTIONAL, /* optional patterns */
122 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000123 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
124 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
125 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000126 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
Daniel Veillardfd573f12003-03-16 17:52:32 +0000127 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000128} xmlRelaxNGType;
129
Daniel Veillardfd573f12003-03-16 17:52:32 +0000130#define IS_NULLABLE 1
131#define IS_NOT_NULLABLE 2
132#define IS_INDETERMINIST 4
Daniel Veillard1564e6e2003-03-15 21:30:25 +0000133
Daniel Veillard6eadf632003-01-23 18:29:16 +0000134struct _xmlRelaxNGDefine {
135 xmlRelaxNGType type; /* the type of definition */
Daniel Veillard1564e6e2003-03-15 21:30:25 +0000136 xmlNodePtr node; /* the node in the source */
Daniel Veillardfd573f12003-03-16 17:52:32 +0000137 xmlChar *name; /* the element local name if present */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000138 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000139 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000140 void *data; /* data lib or specific pointer */
Daniel Veillardfd573f12003-03-16 17:52:32 +0000141 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000142 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000143 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
Daniel Veillardfd573f12003-03-16 17:52:32 +0000144 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000145 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000146 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
Daniel Veillardfd573f12003-03-16 17:52:32 +0000147 short depth; /* used for the cycle detection */
148 short flags; /* define related flags */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000149};
150
151/**
152 * _xmlRelaxNG:
153 *
154 * A RelaxNGs definition
155 */
156struct _xmlRelaxNG {
157 xmlRelaxNGGrammarPtr topgrammar;
158 xmlDocPtr doc;
159
160 xmlHashTablePtr defs; /* define */
161 xmlHashTablePtr refs; /* references */
Daniel Veillardc482e262003-02-26 14:48:48 +0000162 xmlRelaxNGDocumentPtr documents; /* all the documents loaded */
163 xmlRelaxNGIncludePtr includes; /* all the includes loaded */
Daniel Veillard419a7682003-02-03 23:22:49 +0000164 int defNr; /* number of defines used */
165 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000166 void *_private; /* unused by the library for users or bindings */
167};
168
Daniel Veillard77648bb2003-02-20 15:03:22 +0000169#define XML_RELAXNG_IN_ATTRIBUTE (1 << 0)
170#define XML_RELAXNG_IN_ONEORMORE (1 << 1)
171#define XML_RELAXNG_IN_LIST (1 << 2)
172#define XML_RELAXNG_IN_DATAEXCEPT (1 << 3)
173#define XML_RELAXNG_IN_START (1 << 4)
174#define XML_RELAXNG_IN_OOMGROUP (1 << 5)
175#define XML_RELAXNG_IN_OOMINTERLEAVE (1 << 6)
176#define XML_RELAXNG_IN_EXTERNALREF (1 << 7)
Daniel Veillardc5312d72003-02-21 17:14:10 +0000177#define XML_RELAXNG_IN_ANYEXCEPT (1 << 8)
178#define XML_RELAXNG_IN_NSEXCEPT (1 << 9)
Daniel Veillard6eadf632003-01-23 18:29:16 +0000179
180struct _xmlRelaxNGParserCtxt {
181 void *userData; /* user specific data block */
182 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
183 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
Daniel Veillard42f12e92003-03-07 18:32:59 +0000184 xmlRelaxNGValidErr err;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000185
186 xmlRelaxNGPtr schema; /* The schema in use */
187 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000188 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000189 int flags; /* parser flags */
190 int nbErrors; /* number of errors at parse time */
191 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000192 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000193 xmlRelaxNGDefinePtr def; /* the current define */
194
195 int nbInterleaves;
196 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000197
Daniel Veillardc482e262003-02-26 14:48:48 +0000198 xmlRelaxNGDocumentPtr documents; /* all the documents loaded */
199 xmlRelaxNGIncludePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000200 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000201 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000202
Daniel Veillard419a7682003-02-03 23:22:49 +0000203 int defNr; /* number of defines used */
204 int defMax; /* number of defines aloocated */
205 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
206
Daniel Veillard6eadf632003-01-23 18:29:16 +0000207 const char *buffer;
208 int size;
209
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000210 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000211 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000212 int docNr; /* Depth of the parsing stack */
213 int docMax; /* Max depth of the parsing stack */
214 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000215
216 /* the include stack */
Daniel Veillardd4310742003-02-18 21:12:46 +0000217 xmlRelaxNGIncludePtr inc; /* Current parsed include */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000218 int incNr; /* Depth of the include parsing stack */
219 int incMax; /* Max depth of the parsing stack */
220 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000221};
222
223#define FLAGS_IGNORABLE 1
224#define FLAGS_NEGATIVE 2
225
226/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000227 * xmlRelaxNGInterleaveGroup:
228 *
229 * A RelaxNGs partition set associated to lists of definitions
230 */
231typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
232typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
233struct _xmlRelaxNGInterleaveGroup {
234 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
235 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000236 xmlRelaxNGDefinePtr *attrs; /* the array of attributes definitions */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000237};
238
239/**
240 * xmlRelaxNGPartitions:
241 *
242 * A RelaxNGs partition associated to an interleave group
243 */
244typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
245typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
246struct _xmlRelaxNGPartition {
247 int nbgroups; /* number of groups in the partitions */
248 xmlRelaxNGInterleaveGroupPtr *groups;
249};
250
251/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000252 * xmlRelaxNGValidState:
253 *
254 * A RelaxNGs validation state
255 */
256#define MAX_ATTR 20
257typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
258typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
259struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000260 xmlNodePtr node; /* the current node */
261 xmlNodePtr seq; /* the sequence of children left to validate */
262 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000263 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000264 xmlChar *value; /* the value when operating on string */
265 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000266 xmlAttrPtr attrs[1]; /* the array of attributes */
267};
268
269/**
Daniel Veillardfd573f12003-03-16 17:52:32 +0000270 * xmlRelaxNGStates:
271 *
272 * A RelaxNGs container for validation state
273 */
274typedef struct _xmlRelaxNGStates xmlRelaxNGStates;
275typedef xmlRelaxNGStates *xmlRelaxNGStatesPtr;
276struct _xmlRelaxNGStates {
277 int nbState; /* the number of states */
278 int maxState; /* the size of the array */
279 xmlRelaxNGValidStatePtr *tabState;
280};
281
282/**
Daniel Veillard42f12e92003-03-07 18:32:59 +0000283 * xmlRelaxNGValidError:
284 *
285 * A RelaxNGs validation error
286 */
287typedef struct _xmlRelaxNGValidError xmlRelaxNGValidError;
288typedef xmlRelaxNGValidError *xmlRelaxNGValidErrorPtr;
289struct _xmlRelaxNGValidError {
290 xmlRelaxNGValidErr err; /* the error number */
291 xmlNodePtr node; /* the current node */
292 xmlNodePtr seq; /* the current child */
293 const xmlChar * arg1; /* first arg */
294 const xmlChar * arg2; /* second arg */
295};
296
297/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000298 * xmlRelaxNGValidCtxt:
299 *
300 * A RelaxNGs validation context
301 */
302
303struct _xmlRelaxNGValidCtxt {
304 void *userData; /* user specific data block */
305 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
306 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
307
308 xmlRelaxNGPtr schema; /* The schema in use */
309 xmlDocPtr doc; /* the document being validated */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000310 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000311 int depth; /* validation depth */
Daniel Veillard42f12e92003-03-07 18:32:59 +0000312
313 /*
314 * Errors accumulated in branches may have to be stacked to be
315 * provided back when it's sure they affect validation.
316 */
317 xmlRelaxNGValidErrorPtr err; /* Last error */
318 int errNr; /* Depth of the error stack */
319 int errMax; /* Max depth of the error stack */
320 xmlRelaxNGValidErrorPtr errTab; /* stack of errors */
Daniel Veillard1564e6e2003-03-15 21:30:25 +0000321
Daniel Veillardfd573f12003-03-16 17:52:32 +0000322 xmlRelaxNGValidStatePtr state; /* the current validation state */
323 xmlRelaxNGStatesPtr states; /* the accumulated state list */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000324};
325
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000326/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000327 * xmlRelaxNGInclude:
328 *
329 * Structure associated to a RelaxNGs document element
330 */
331struct _xmlRelaxNGInclude {
Daniel Veillardc482e262003-02-26 14:48:48 +0000332 xmlRelaxNGIncludePtr next; /* keep a chain of includes */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000333 xmlChar *href; /* the normalized href value */
334 xmlDocPtr doc; /* the associated XML document */
335 xmlRelaxNGDefinePtr content;/* the definitions */
336 xmlRelaxNGPtr schema; /* the schema */
337};
338
339/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000340 * xmlRelaxNGDocument:
341 *
342 * Structure associated to a RelaxNGs document element
343 */
344struct _xmlRelaxNGDocument {
Daniel Veillardc482e262003-02-26 14:48:48 +0000345 xmlRelaxNGDocumentPtr next; /* keep a chain of documents */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000346 xmlChar *href; /* the normalized href value */
347 xmlDocPtr doc; /* the associated XML document */
348 xmlRelaxNGDefinePtr content;/* the definitions */
349 xmlRelaxNGPtr schema; /* the schema */
350};
351
Daniel Veillard3ebc7d42003-02-24 17:17:58 +0000352
Daniel Veillard6eadf632003-01-23 18:29:16 +0000353/************************************************************************
354 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000355 * Preliminary type checking interfaces *
356 * *
357 ************************************************************************/
358/**
359 * xmlRelaxNGTypeHave:
360 * @data: data needed for the library
361 * @type: the type name
362 * @value: the value to check
363 *
364 * Function provided by a type library to check if a type is exported
365 *
366 * Returns 1 if yes, 0 if no and -1 in case of error.
367 */
368typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
369
370/**
371 * xmlRelaxNGTypeCheck:
372 * @data: data needed for the library
373 * @type: the type name
374 * @value: the value to check
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000375 * @result: place to store the result if needed
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000376 *
377 * Function provided by a type library to check if a value match a type
378 *
379 * Returns 1 if yes, 0 if no and -1 in case of error.
380 */
381typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000382 const xmlChar *value, void **result);
383
384/**
385 * xmlRelaxNGFacetCheck:
386 * @data: data needed for the library
387 * @type: the type name
388 * @facet: the facet name
389 * @val: the facet value
390 * @strval: the string value
391 * @value: the value to check
392 *
393 * Function provided by a type library to check a value facet
394 *
395 * Returns 1 if yes, 0 if no and -1 in case of error.
396 */
397typedef int (*xmlRelaxNGFacetCheck) (void *data, const xmlChar *type,
398 const xmlChar *facet, const xmlChar *val,
399 const xmlChar *strval, void *value);
400
401/**
402 * xmlRelaxNGTypeFree:
403 * @data: data needed for the library
404 * @result: the value to free
405 *
406 * Function provided by a type library to free a returned result
407 */
408typedef void (*xmlRelaxNGTypeFree) (void *data, void *result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000409
410/**
411 * xmlRelaxNGTypeCompare:
412 * @data: data needed for the library
413 * @type: the type name
414 * @value1: the first value
415 * @value2: the second value
416 *
417 * Function provided by a type library to compare two values accordingly
418 * to a type.
419 *
420 * Returns 1 if yes, 0 if no and -1 in case of error.
421 */
422typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
423 const xmlChar *value1,
424 const xmlChar *value2);
425typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
426typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
427struct _xmlRelaxNGTypeLibrary {
428 const xmlChar *namespace; /* the datatypeLibrary value */
429 void *data; /* data needed for the library */
430 xmlRelaxNGTypeHave have; /* the export function */
431 xmlRelaxNGTypeCheck check; /* the checking function */
432 xmlRelaxNGTypeCompare comp; /* the compare function */
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000433 xmlRelaxNGFacetCheck facet; /* the facet check function */
434 xmlRelaxNGTypeFree freef; /* the freeing function */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000435};
436
437/************************************************************************
438 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000439 * Allocation functions *
440 * *
441 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000442static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
443static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000444static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillardc482e262003-02-26 14:48:48 +0000445static void xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema);
Daniel Veillardfd573f12003-03-16 17:52:32 +0000446static int xmlRelaxNGEqualValidState(
447 xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
448 xmlRelaxNGValidStatePtr state1,
449 xmlRelaxNGValidStatePtr state2);
450static void xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000451
452/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000453 * xmlRelaxNGFreeDocument:
454 * @docu: a document structure
455 *
456 * Deallocate a RelaxNG document structure.
457 */
458static void
459xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
460{
461 if (docu == NULL)
462 return;
463
464 if (docu->href != NULL)
465 xmlFree(docu->href);
466 if (docu->doc != NULL)
467 xmlFreeDoc(docu->doc);
468 if (docu->schema != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000469 xmlRelaxNGFreeInnerSchema(docu->schema);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000470 xmlFree(docu);
471}
472
473/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000474 * xmlRelaxNGFreeDocumentList:
475 * @docu: a list of document structure
476 *
477 * Deallocate a RelaxNG document structures.
478 */
479static void
480xmlRelaxNGFreeDocumentList(xmlRelaxNGDocumentPtr docu)
481{
482 xmlRelaxNGDocumentPtr next;
483 while (docu != NULL) {
484 next = docu->next;
485 xmlRelaxNGFreeDocument(docu);
486 docu = next;
487 }
488}
489
490/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000491 * xmlRelaxNGFreeInclude:
492 * @incl: a include structure
493 *
494 * Deallocate a RelaxNG include structure.
495 */
496static void
497xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
498{
499 if (incl == NULL)
500 return;
501
502 if (incl->href != NULL)
503 xmlFree(incl->href);
504 if (incl->doc != NULL)
505 xmlFreeDoc(incl->doc);
506 if (incl->schema != NULL)
507 xmlRelaxNGFree(incl->schema);
508 xmlFree(incl);
509}
510
511/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000512 * xmlRelaxNGFreeIncludeList:
513 * @incl: a include structure list
514 *
515 * Deallocate a RelaxNG include structure.
516 */
517static void
518xmlRelaxNGFreeIncludeList(xmlRelaxNGIncludePtr incl)
519{
520 xmlRelaxNGIncludePtr next;
521 while (incl != NULL) {
522 next = incl->next;
523 xmlRelaxNGFreeInclude(incl);
524 incl = next;
525 }
526}
527
528/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000529 * xmlRelaxNGNewRelaxNG:
530 * @ctxt: a Relax-NG validation context (optional)
531 *
532 * Allocate a new RelaxNG structure.
533 *
534 * Returns the newly allocated structure or NULL in case or error
535 */
536static xmlRelaxNGPtr
537xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
538{
539 xmlRelaxNGPtr ret;
540
541 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
542 if (ret == NULL) {
543 if ((ctxt != NULL) && (ctxt->error != NULL))
544 ctxt->error(ctxt->userData, "Out of memory\n");
545 ctxt->nbErrors++;
546 return (NULL);
547 }
548 memset(ret, 0, sizeof(xmlRelaxNG));
549
550 return (ret);
551}
552
553/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000554 * xmlRelaxNGFreeInnerSchema:
555 * @schema: a schema structure
556 *
557 * Deallocate a RelaxNG schema structure.
558 */
559static void
560xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema)
561{
562 if (schema == NULL)
563 return;
564
565 if (schema->doc != NULL)
566 xmlFreeDoc(schema->doc);
567 if (schema->defTab != NULL) {
568 int i;
569
570 for (i = 0;i < schema->defNr;i++)
571 xmlRelaxNGFreeDefine(schema->defTab[i]);
572 xmlFree(schema->defTab);
573 }
574
575 xmlFree(schema);
576}
577
578/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000579 * xmlRelaxNGFree:
580 * @schema: a schema structure
581 *
582 * Deallocate a RelaxNG structure.
583 */
584void
585xmlRelaxNGFree(xmlRelaxNGPtr schema)
586{
587 if (schema == NULL)
588 return;
589
Daniel Veillard6eadf632003-01-23 18:29:16 +0000590 if (schema->topgrammar != NULL)
591 xmlRelaxNGFreeGrammar(schema->topgrammar);
592 if (schema->doc != NULL)
593 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000594 if (schema->documents != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000595 xmlRelaxNGFreeDocumentList(schema->documents);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000596 if (schema->includes != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000597 xmlRelaxNGFreeIncludeList(schema->includes);
Daniel Veillard419a7682003-02-03 23:22:49 +0000598 if (schema->defTab != NULL) {
599 int i;
600
601 for (i = 0;i < schema->defNr;i++)
602 xmlRelaxNGFreeDefine(schema->defTab[i]);
603 xmlFree(schema->defTab);
604 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000605
606 xmlFree(schema);
607}
608
609/**
610 * xmlRelaxNGNewGrammar:
611 * @ctxt: a Relax-NG validation context (optional)
612 *
613 * Allocate a new RelaxNG grammar.
614 *
615 * Returns the newly allocated structure or NULL in case or error
616 */
617static xmlRelaxNGGrammarPtr
618xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
619{
620 xmlRelaxNGGrammarPtr ret;
621
622 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
623 if (ret == NULL) {
624 if ((ctxt != NULL) && (ctxt->error != NULL))
625 ctxt->error(ctxt->userData, "Out of memory\n");
626 ctxt->nbErrors++;
627 return (NULL);
628 }
629 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
630
631 return (ret);
632}
633
634/**
635 * xmlRelaxNGFreeGrammar:
636 * @grammar: a grammar structure
637 *
638 * Deallocate a RelaxNG grammar structure.
639 */
640static void
641xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
642{
643 if (grammar == NULL)
644 return;
645
Daniel Veillardc482e262003-02-26 14:48:48 +0000646 if (grammar->children != NULL) {
647 xmlRelaxNGFreeGrammar(grammar->children);
648 }
Daniel Veillard419a7682003-02-03 23:22:49 +0000649 if (grammar->next != NULL) {
650 xmlRelaxNGFreeGrammar(grammar->next);
651 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000652 if (grammar->refs != NULL) {
653 xmlHashFree(grammar->refs, NULL);
654 }
655 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000656 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000657 }
658
659 xmlFree(grammar);
660}
661
662/**
663 * xmlRelaxNGNewDefine:
664 * @ctxt: a Relax-NG validation context
665 * @node: the node in the input document.
666 *
667 * Allocate a new RelaxNG define.
668 *
669 * Returns the newly allocated structure or NULL in case or error
670 */
671static xmlRelaxNGDefinePtr
Daniel Veillardfd573f12003-03-16 17:52:32 +0000672xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
Daniel Veillard6eadf632003-01-23 18:29:16 +0000673{
674 xmlRelaxNGDefinePtr ret;
675
Daniel Veillard419a7682003-02-03 23:22:49 +0000676 if (ctxt->defMax == 0) {
677 ctxt->defMax = 16;
678 ctxt->defNr = 0;
679 ctxt->defTab = (xmlRelaxNGDefinePtr *)
680 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
681 if (ctxt->defTab == NULL) {
682 if ((ctxt != NULL) && (ctxt->error != NULL))
683 ctxt->error(ctxt->userData, "Out of memory\n");
684 ctxt->nbErrors++;
685 return (NULL);
686 }
687 } else if (ctxt->defMax <= ctxt->defNr) {
688 xmlRelaxNGDefinePtr *tmp;
689 ctxt->defMax *= 2;
690 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
691 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
692 if (tmp == NULL) {
693 if ((ctxt != NULL) && (ctxt->error != NULL))
694 ctxt->error(ctxt->userData, "Out of memory\n");
695 ctxt->nbErrors++;
696 return (NULL);
697 }
698 ctxt->defTab = tmp;
699 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000700 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
701 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000702 if ((ctxt != NULL) && (ctxt->error != NULL))
703 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000704 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000705 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000706 }
707 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000708 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000709 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000710 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000711 return (ret);
712}
713
714/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000715 * xmlRelaxNGFreePartition:
716 * @partitions: a partition set structure
717 *
718 * Deallocate RelaxNG partition set structures.
719 */
720static void
721xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
722 xmlRelaxNGInterleaveGroupPtr group;
723 int j;
724
725 if (partitions != NULL) {
726 if (partitions->groups != NULL) {
727 for (j = 0;j < partitions->nbgroups;j++) {
728 group = partitions->groups[j];
729 if (group != NULL) {
730 if (group->defs != NULL)
731 xmlFree(group->defs);
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000732 if (group->attrs != NULL)
733 xmlFree(group->attrs);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000734 xmlFree(group);
735 }
736 }
737 xmlFree(partitions->groups);
738 }
739 xmlFree(partitions);
740 }
741}
742/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000743 * xmlRelaxNGFreeDefine:
744 * @define: a define structure
745 *
746 * Deallocate a RelaxNG define structure.
747 */
748static void
749xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
750{
751 if (define == NULL)
752 return;
753
Daniel Veillard419a7682003-02-03 23:22:49 +0000754 if ((define->data != NULL) &&
755 (define->type == XML_RELAXNG_INTERLEAVE))
756 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000757 if (define->name != NULL)
758 xmlFree(define->name);
759 if (define->ns != NULL)
760 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000761 if (define->value != NULL)
762 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000763 xmlFree(define);
764}
765
766/**
Daniel Veillardfd573f12003-03-16 17:52:32 +0000767 * xmlRelaxNGNewStates:
768 * @ctxt: a Relax-NG validation context
769 * @size: the default size for the container
770 *
771 * Allocate a new RelaxNG validation state container
772 * TODO: keep a pool in the ctxt
773 *
774 * Returns the newly allocated structure or NULL in case or error
775 */
776static xmlRelaxNGStatesPtr
777xmlRelaxNGNewStates(xmlRelaxNGValidCtxtPtr ctxt, int size)
778{
779 xmlRelaxNGStatesPtr ret;
780
781 if (size < 16) size = 16;
782
783 ret = (xmlRelaxNGStatesPtr) xmlMalloc(sizeof(xmlRelaxNGStates) +
784 (size - 1) * sizeof(xmlRelaxNGValidStatePtr));
785 if (ret == NULL) {
786 if ((ctxt != NULL) && (ctxt->error != NULL))
787 ctxt->error(ctxt->userData, "Out of memory\n");
788 return (NULL);
789 }
790 ret->nbState = 0;
791 ret->maxState = size;
792 ret->tabState = (xmlRelaxNGValidStatePtr *) xmlMalloc(
793 (size) * sizeof(xmlRelaxNGValidStatePtr));
794 if (ret->tabState == NULL) {
795 if ((ctxt != NULL) && (ctxt->error != NULL))
796 ctxt->error(ctxt->userData, "Out of memory\n");
797 xmlFree(ret->tabState);
798 return (NULL);
799 }
800 return(ret);
801}
802
803/**
804 * xmlRelaxNGAddState:
805 * @ctxt: a Relax-NG validation context
806 * @states: the states container
807 * @state: the validation state
808 *
809 * Add a RelaxNG validation state to the container
810 *
811 * Return 1 in case of success and 0 if this is a duplicate and -1 on error
812 */
813static int
814xmlRelaxNGAddStates(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGStatesPtr states,
815 xmlRelaxNGValidStatePtr state)
816{
817 int i;
818
819 if (state == NULL) {
820 return(-1);
821 }
822 if (states->nbState >= states->maxState) {
823 xmlRelaxNGValidStatePtr *tmp;
824 int size;
825
826 size = states->maxState * 2;
827 tmp = (xmlRelaxNGValidStatePtr *) xmlRealloc(states->tabState,
828 (size) * sizeof(xmlRelaxNGValidStatePtr));
829 if (tmp == NULL) {
830 if ((ctxt != NULL) && (ctxt->error != NULL))
831 ctxt->error(ctxt->userData, "Out of memory\n");
832 return(-1);
833 }
834 states->tabState = tmp;
835 states->maxState = size;
836 }
837 for (i = 0;i < states->nbState;i++) {
838 if (xmlRelaxNGEqualValidState(ctxt, state, states->tabState[i])) {
839 xmlRelaxNGFreeValidState(state);
840 return(0);
841 }
842 }
843 states->tabState[states->nbState++] = state;
844 return(1);
845}
846
847/**
848 * xmlRelaxNGFreeStates:
849 * @ctxt: a Relax-NG validation context
850 * @states: teh container
851 *
852 * Free a RelaxNG validation state container
853 * TODO: keep a pool in the ctxt
854 */
855static void
856xmlRelaxNGFreeStates(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
857 xmlRelaxNGStatesPtr states)
858{
859 if (states != NULL) {
860 xmlFree(states->tabState);
861 xmlFree(states);
862 }
863}
864
865/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000866 * xmlRelaxNGNewValidState:
867 * @ctxt: a Relax-NG validation context
868 * @node: the current node or NULL for the document
869 *
870 * Allocate a new RelaxNG validation state
Daniel Veillard1564e6e2003-03-15 21:30:25 +0000871 * TODO: keep a pool in the ctxt
Daniel Veillard6eadf632003-01-23 18:29:16 +0000872 *
873 * Returns the newly allocated structure or NULL in case or error
874 */
875static xmlRelaxNGValidStatePtr
876xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
877{
878 xmlRelaxNGValidStatePtr ret;
879 xmlAttrPtr attr;
880 xmlAttrPtr attrs[MAX_ATTR];
881 int nbAttrs = 0;
882 xmlNodePtr root = NULL;
883
884 if (node == NULL) {
885 root = xmlDocGetRootElement(ctxt->doc);
886 if (root == NULL)
887 return(NULL);
888 } else {
889 attr = node->properties;
890 while (attr != NULL) {
891 if (nbAttrs < MAX_ATTR)
892 attrs[nbAttrs++] = attr;
893 else
894 nbAttrs++;
895 attr = attr->next;
896 }
897 }
898
899 if (nbAttrs < MAX_ATTR)
900 attrs[nbAttrs] = NULL;
901 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
902 nbAttrs * sizeof(xmlAttrPtr));
903 if (ret == NULL) {
904 if ((ctxt != NULL) && (ctxt->error != NULL))
905 ctxt->error(ctxt->userData, "Out of memory\n");
906 return (NULL);
907 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000908 ret->value = NULL;
909 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000910 if (node == NULL) {
911 ret->node = (xmlNodePtr) ctxt->doc;
912 ret->seq = root;
913 ret->nbAttrs = 0;
914 } else {
915 ret->node = node;
916 ret->seq = node->children;
917 ret->nbAttrs = nbAttrs;
918 if (nbAttrs > 0) {
919 if (nbAttrs < MAX_ATTR) {
920 memcpy(&(ret->attrs[0]), attrs,
921 sizeof(xmlAttrPtr) * (nbAttrs + 1));
922 } else {
923 attr = node->properties;
924 nbAttrs = 0;
925 while (attr != NULL) {
926 ret->attrs[nbAttrs++] = attr;
927 attr = attr->next;
928 }
929 ret->attrs[nbAttrs] = NULL;
930 }
931 }
932 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000933 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000934 return (ret);
935}
936
937/**
Daniel Veillardfd573f12003-03-16 17:52:32 +0000938 * xmlRelaxNGCopyValidState:
939 * @ctxt: a Relax-NG validation context
940 * @state: a validation state
941 *
942 * Copy the validation state
943 *
944 * Returns the newly allocated structure or NULL in case or error
945 */
946static xmlRelaxNGValidStatePtr
947xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
948 xmlRelaxNGValidStatePtr state)
949{
950 xmlRelaxNGValidStatePtr ret;
951 unsigned int size;
952
953 if (state == NULL)
954 return(NULL);
955
956 size = sizeof(xmlRelaxNGValidState) +
957 state->nbAttrs * sizeof(xmlAttrPtr);
958 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
959 if (ret == NULL) {
960 if ((ctxt != NULL) && (ctxt->error != NULL))
961 ctxt->error(ctxt->userData, "Out of memory\n");
962 return (NULL);
963 }
964 memcpy(ret, state, size);
965 return(ret);
966}
967
968/**
969 * xmlRelaxNGEqualValidState:
970 * @ctxt: a Relax-NG validation context
971 * @state1: a validation state
972 * @state2: a validation state
973 *
974 * Compare the validation states for equality
975 *
976 * Returns 1 if equald, 0 otherwise
977 */
978static int
979xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
980 xmlRelaxNGValidStatePtr state1,
981 xmlRelaxNGValidStatePtr state2)
982{
983 int i;
984
985 if ((state1 == NULL) || (state2 == NULL))
986 return(0);
987 if (state1 == state2)
988 return(1);
989 if (state1->node != state2->node)
990 return(0);
991 if (state1->seq != state2->seq)
992 return(0);
993 if (state1->nbAttrLeft != state2->nbAttrLeft)
994 return(0);
995 if (state1->nbAttrs != state2->nbAttrs)
996 return(0);
997 if (state1->endvalue != state2->endvalue)
998 return(0);
999 if ((state1->value != state2->value) &&
1000 (!xmlStrEqual(state1->value, state2->value)))
1001 return(0);
1002 for (i = 0;i < state1->nbAttrs;i++) {
1003 if (state1->attrs[i] != state2->attrs[i])
1004 return(0);
1005 }
1006 return(1);
1007}
1008
1009/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00001010 * xmlRelaxNGFreeValidState:
1011 * @state: a validation state structure
1012 *
1013 * Deallocate a RelaxNG validation state structure.
Daniel Veillardfd573f12003-03-16 17:52:32 +00001014 * TODO: keep a pool in the ctxt
Daniel Veillard6eadf632003-01-23 18:29:16 +00001015 */
1016static void
1017xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
1018{
1019 if (state == NULL)
1020 return;
1021
1022 xmlFree(state);
1023}
1024
1025/************************************************************************
1026 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001027 * Document functions *
1028 * *
1029 ************************************************************************/
1030static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
1031 xmlDocPtr doc);
1032
1033/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001034 * xmlRelaxNGIncludePush:
1035 * @ctxt: the parser context
1036 * @value: the element doc
1037 *
1038 * Pushes a new include on top of the include stack
1039 *
1040 * Returns 0 in case of error, the index in the stack otherwise
1041 */
1042static int
1043xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
1044 xmlRelaxNGIncludePtr value)
1045{
1046 if (ctxt->incTab == NULL) {
1047 ctxt->incMax = 4;
1048 ctxt->incNr = 0;
1049 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
1050 ctxt->incMax * sizeof(ctxt->incTab[0]));
1051 if (ctxt->incTab == NULL) {
1052 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1053 return (0);
1054 }
1055 }
1056 if (ctxt->incNr >= ctxt->incMax) {
1057 ctxt->incMax *= 2;
1058 ctxt->incTab =
1059 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
1060 ctxt->incMax *
1061 sizeof(ctxt->incTab[0]));
1062 if (ctxt->incTab == NULL) {
1063 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1064 return (0);
1065 }
1066 }
1067 ctxt->incTab[ctxt->incNr] = value;
1068 ctxt->inc = value;
1069 return (ctxt->incNr++);
1070}
1071
1072/**
1073 * xmlRelaxNGIncludePop:
1074 * @ctxt: the parser context
1075 *
1076 * Pops the top include from the include stack
1077 *
1078 * Returns the include just removed
1079 */
1080static xmlRelaxNGIncludePtr
1081xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
1082{
1083 xmlRelaxNGIncludePtr ret;
1084
1085 if (ctxt->incNr <= 0)
1086 return (0);
1087 ctxt->incNr--;
1088 if (ctxt->incNr > 0)
1089 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
1090 else
1091 ctxt->inc = NULL;
1092 ret = ctxt->incTab[ctxt->incNr];
1093 ctxt->incTab[ctxt->incNr] = 0;
1094 return (ret);
1095}
1096
1097/**
Daniel Veillard5add8682003-03-10 13:13:58 +00001098 * xmlRelaxNGRemoveRedefine:
1099 * @ctxt: the parser context
1100 * @URL: the normalized URL
1101 * @target: the included target
1102 * @name: the define name to eliminate
1103 *
1104 * Applies the elimination algorithm of 4.7
1105 *
1106 * Returns 0 in case of error, 1 in case of success.
1107 */
1108static int
1109xmlRelaxNGRemoveRedefine(xmlRelaxNGParserCtxtPtr ctxt,
1110 const xmlChar *URL ATTRIBUTE_UNUSED,
1111 xmlNodePtr target, const xmlChar *name) {
1112 int found = 0;
1113 xmlNodePtr tmp, tmp2;
1114 xmlChar *name2;
1115
1116#ifdef DEBUG_INCLUDE
1117 xmlGenericError(xmlGenericErrorContext,
1118 "Elimination of <include> %s from %s\n", name, URL);
1119#endif
1120 tmp = target;
1121 while (tmp != NULL) {
1122 tmp2 = tmp->next;
1123 if ((name == NULL) && (IS_RELAXNG(tmp, "start"))) {
1124 found = 1;
1125 xmlUnlinkNode(tmp);
1126 xmlFreeNode(tmp);
1127 } else if ((name != NULL) && (IS_RELAXNG(tmp, "define"))) {
1128 name2 = xmlGetProp(tmp, BAD_CAST "name");
1129 xmlRelaxNGNormExtSpace(name2);
1130 if (name2 != NULL) {
1131 if (xmlStrEqual(name, name2)) {
1132 found = 1;
1133 xmlUnlinkNode(tmp);
1134 xmlFreeNode(tmp);
1135 }
1136 xmlFree(name2);
1137 }
1138 } else if (IS_RELAXNG(tmp, "include")) {
1139 xmlChar *href = NULL;
1140 xmlRelaxNGDocumentPtr inc = tmp->_private;
1141
1142 if ((inc != NULL) && (inc->doc != NULL) &&
1143 (inc->doc->children != NULL)) {
1144
1145 if (xmlStrEqual(inc->doc->children->name, BAD_CAST "grammar")) {
1146#ifdef DEBUG_INCLUDE
1147 href = xmlGetProp(tmp, BAD_CAST "href");
1148#endif
1149 if (xmlRelaxNGRemoveRedefine(ctxt, href,
1150 inc->doc->children->children, name) == 1) {
1151 found = 1;
1152 }
1153 if (href != NULL)
1154 xmlFree(href);
1155 }
1156 }
1157 }
1158 tmp = tmp2;
1159 }
1160 return(found);
1161}
1162
1163/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001164 * xmlRelaxNGLoadInclude:
1165 * @ctxt: the parser context
1166 * @URL: the normalized URL
1167 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +00001168 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001169 *
1170 * First lookup if the document is already loaded into the parser context,
1171 * check against recursion. If not found the resource is loaded and
1172 * the content is preprocessed before being returned back to the caller.
1173 *
1174 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
1175 */
1176static xmlRelaxNGIncludePtr
1177xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +00001178 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001179 xmlRelaxNGIncludePtr ret = NULL;
1180 xmlDocPtr doc;
1181 int i;
Daniel Veillard5add8682003-03-10 13:13:58 +00001182 xmlNodePtr root, cur;
1183
1184#ifdef DEBUG_INCLUDE
1185 xmlGenericError(xmlGenericErrorContext,
1186 "xmlRelaxNGLoadInclude(%s)\n", URL);
1187#endif
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001188
1189 /*
1190 * check against recursion in the stack
1191 */
1192 for (i = 0;i < ctxt->incNr;i++) {
1193 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
1194 if (ctxt->error != NULL)
1195 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00001196 "Detected an Include recursion for %s\n",
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001197 URL);
1198 ctxt->nbErrors++;
1199 return(NULL);
1200 }
1201 }
1202
1203 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001204 * load the document
1205 */
1206 doc = xmlParseFile((const char *) URL);
1207 if (doc == NULL) {
1208 if (ctxt->error != NULL)
1209 ctxt->error(ctxt->userData,
1210 "xmlRelaxNG: could not load %s\n", URL);
1211 ctxt->nbErrors++;
1212 return (NULL);
1213 }
1214
Daniel Veillard5add8682003-03-10 13:13:58 +00001215#ifdef DEBUG_INCLUDE
1216 xmlGenericError(xmlGenericErrorContext,
1217 "Parsed %s Okay\n", URL);
1218#endif
1219
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001220 /*
1221 * Allocate the document structures and register it first.
1222 */
1223 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
1224 if (ret == NULL) {
1225 if (ctxt->error != NULL)
1226 ctxt->error(ctxt->userData,
1227 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1228 ctxt->nbErrors++;
1229 xmlFreeDoc(doc);
1230 return (NULL);
1231 }
1232 memset(ret, 0, sizeof(xmlRelaxNGInclude));
1233 ret->doc = doc;
1234 ret->href = xmlStrdup(URL);
Daniel Veillardc482e262003-02-26 14:48:48 +00001235 ret->next = ctxt->includes;
1236 ctxt->includes = ret;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001237
1238 /*
Daniel Veillard416589a2003-02-17 17:25:42 +00001239 * transmit the ns if needed
1240 */
1241 if (ns != NULL) {
1242 root = xmlDocGetRootElement(doc);
1243 if (root != NULL) {
1244 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1245 xmlSetProp(root, BAD_CAST"ns", ns);
1246 }
1247 }
1248 }
1249
1250 /*
Daniel Veillardc482e262003-02-26 14:48:48 +00001251 * push it on the stack
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001252 */
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001253 xmlRelaxNGIncludePush(ctxt, ret);
1254
1255 /*
1256 * Some preprocessing of the document content, this include recursing
1257 * in the include stack.
1258 */
Daniel Veillard5add8682003-03-10 13:13:58 +00001259#ifdef DEBUG_INCLUDE
1260 xmlGenericError(xmlGenericErrorContext,
1261 "cleanup of %s\n", URL);
1262#endif
1263
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001264 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1265 if (doc == NULL) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001266 ctxt->inc = NULL;
1267 return(NULL);
1268 }
1269
1270 /*
1271 * Pop up the include from the stack
1272 */
1273 xmlRelaxNGIncludePop(ctxt);
1274
Daniel Veillard5add8682003-03-10 13:13:58 +00001275#ifdef DEBUG_INCLUDE
1276 xmlGenericError(xmlGenericErrorContext,
1277 "Checking of %s\n", URL);
1278#endif
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001279 /*
1280 * Check that the top element is a grammar
1281 */
1282 root = xmlDocGetRootElement(doc);
1283 if (root == NULL) {
1284 if (ctxt->error != NULL)
1285 ctxt->error(ctxt->userData,
1286 "xmlRelaxNG: included document is empty %s\n", URL);
1287 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001288 return (NULL);
1289 }
1290 if (!IS_RELAXNG(root, "grammar")) {
1291 if (ctxt->error != NULL)
1292 ctxt->error(ctxt->userData,
1293 "xmlRelaxNG: included document %s root is not a grammar\n",
1294 URL);
1295 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001296 return (NULL);
1297 }
1298
1299 /*
1300 * Elimination of redefined rules in the include.
1301 */
1302 cur = node->children;
1303 while (cur != NULL) {
1304 if (IS_RELAXNG(cur, "start")) {
1305 int found = 0;
1306
Daniel Veillard5add8682003-03-10 13:13:58 +00001307 found = xmlRelaxNGRemoveRedefine(ctxt, URL, root->children, NULL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001308 if (!found) {
1309 if (ctxt->error != NULL)
1310 ctxt->error(ctxt->userData,
1311 "xmlRelaxNG: include %s has a start but not the included grammar\n",
1312 URL);
1313 ctxt->nbErrors++;
1314 }
1315 } else if (IS_RELAXNG(cur, "define")) {
Daniel Veillard5add8682003-03-10 13:13:58 +00001316 xmlChar *name;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001317
1318 name = xmlGetProp(cur, BAD_CAST "name");
1319 if (name == NULL) {
1320 if (ctxt->error != NULL)
1321 ctxt->error(ctxt->userData,
1322 "xmlRelaxNG: include %s has define without name\n",
1323 URL);
1324 ctxt->nbErrors++;
1325 } else {
Daniel Veillard5add8682003-03-10 13:13:58 +00001326 int found;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001327
Daniel Veillardd2298792003-02-14 16:54:11 +00001328 xmlRelaxNGNormExtSpace(name);
Daniel Veillard5add8682003-03-10 13:13:58 +00001329 found = xmlRelaxNGRemoveRedefine(ctxt, URL,
1330 root->children, name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001331 if (!found) {
1332 if (ctxt->error != NULL)
1333 ctxt->error(ctxt->userData,
1334 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1335 URL, name);
1336 ctxt->nbErrors++;
1337 }
1338 xmlFree(name);
1339 }
1340 }
1341 cur = cur->next;
1342 }
1343
1344
1345 return(ret);
1346}
1347
1348/**
Daniel Veillard42f12e92003-03-07 18:32:59 +00001349 * xmlRelaxNGValidErrorPush:
1350 * @ctxt: the validation context
1351 * @err: the error code
1352 * @arg1: the first string argument
1353 * @arg2: the second string argument
1354 *
1355 * Pushes a new error on top of the error stack
1356 *
1357 * Returns 0 in case of error, the index in the stack otherwise
1358 */
1359static int
1360xmlRelaxNGValidErrorPush(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err,
1361 const xmlChar *arg1, const xmlChar *arg2)
1362{
1363 xmlRelaxNGValidErrorPtr cur;
1364 if (ctxt->errTab == NULL) {
1365 ctxt->errMax = 8;
1366 ctxt->errNr = 0;
1367 ctxt->errTab = (xmlRelaxNGValidErrorPtr) xmlMalloc(
1368 ctxt->errMax * sizeof(xmlRelaxNGValidError));
1369 if (ctxt->errTab == NULL) {
1370 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1371 return (0);
1372 }
1373 }
1374 if (ctxt->errNr >= ctxt->errMax) {
1375 ctxt->errMax *= 2;
1376 ctxt->errTab =
1377 (xmlRelaxNGValidErrorPtr) xmlRealloc(ctxt->errTab,
1378 ctxt->errMax * sizeof(xmlRelaxNGValidError));
1379 if (ctxt->errTab == NULL) {
1380 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1381 return (0);
1382 }
1383 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00001384 if ((ctxt->err != NULL) &&
1385 (ctxt->err->node == ctxt->state->node) &&
1386 (ctxt->err->err == err))
1387 return(ctxt->errNr);
Daniel Veillard42f12e92003-03-07 18:32:59 +00001388 cur = &ctxt->errTab[ctxt->errNr];
1389 cur->err = err;
1390 cur->arg1 = arg1;
1391 cur->arg2 = arg2;
1392 if (ctxt->state != NULL) {
1393 cur->node = ctxt->state->node;
1394 cur->seq = ctxt->state->seq;
1395 } else {
1396 cur->node = NULL;
1397 cur->seq = NULL;
1398 }
1399 ctxt->err = cur;
1400 return (ctxt->errNr++);
1401}
1402
1403/**
1404 * xmlRelaxNGValidErrorPop:
1405 * @ctxt: the validation context
1406 *
1407 * Pops the top error from the error stack
1408 *
1409 * Returns the error just removed
1410 */
1411static xmlRelaxNGValidErrorPtr
1412xmlRelaxNGValidErrorPop(xmlRelaxNGValidCtxtPtr ctxt)
1413{
1414 xmlRelaxNGValidErrorPtr ret;
1415
1416 if (ctxt->errNr <= 0)
1417 return (NULL);
1418 ctxt->errNr--;
1419 if (ctxt->errNr > 0)
1420 ctxt->err = &ctxt->errTab[ctxt->errNr - 1];
1421 else
1422 ctxt->err = NULL;
1423 ret = &ctxt->errTab[ctxt->errNr];
1424 return (ret);
1425}
1426
Daniel Veillard42f12e92003-03-07 18:32:59 +00001427/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001428 * xmlRelaxNGDocumentPush:
1429 * @ctxt: the parser context
1430 * @value: the element doc
1431 *
1432 * Pushes a new doc on top of the doc stack
1433 *
1434 * Returns 0 in case of error, the index in the stack otherwise
1435 */
1436static int
1437xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1438 xmlRelaxNGDocumentPtr value)
1439{
1440 if (ctxt->docTab == NULL) {
1441 ctxt->docMax = 4;
1442 ctxt->docNr = 0;
1443 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1444 ctxt->docMax * sizeof(ctxt->docTab[0]));
1445 if (ctxt->docTab == NULL) {
1446 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1447 return (0);
1448 }
1449 }
1450 if (ctxt->docNr >= ctxt->docMax) {
1451 ctxt->docMax *= 2;
1452 ctxt->docTab =
1453 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1454 ctxt->docMax *
1455 sizeof(ctxt->docTab[0]));
1456 if (ctxt->docTab == NULL) {
1457 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1458 return (0);
1459 }
1460 }
1461 ctxt->docTab[ctxt->docNr] = value;
1462 ctxt->doc = value;
1463 return (ctxt->docNr++);
1464}
1465
1466/**
1467 * xmlRelaxNGDocumentPop:
1468 * @ctxt: the parser context
1469 *
1470 * Pops the top doc from the doc stack
1471 *
1472 * Returns the doc just removed
1473 */
1474static xmlRelaxNGDocumentPtr
1475xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1476{
1477 xmlRelaxNGDocumentPtr ret;
1478
1479 if (ctxt->docNr <= 0)
1480 return (0);
1481 ctxt->docNr--;
1482 if (ctxt->docNr > 0)
1483 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1484 else
1485 ctxt->doc = NULL;
1486 ret = ctxt->docTab[ctxt->docNr];
1487 ctxt->docTab[ctxt->docNr] = 0;
1488 return (ret);
1489}
1490
1491/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001492 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001493 * @ctxt: the parser context
1494 * @URL: the normalized URL
1495 * @ns: the inherited ns if any
1496 *
1497 * First lookup if the document is already loaded into the parser context,
1498 * check against recursion. If not found the resource is loaded and
1499 * the content is preprocessed before being returned back to the caller.
1500 *
1501 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1502 */
1503static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001504xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001505 const xmlChar *ns) {
1506 xmlRelaxNGDocumentPtr ret = NULL;
1507 xmlDocPtr doc;
1508 xmlNodePtr root;
1509 int i;
1510
1511 /*
1512 * check against recursion in the stack
1513 */
1514 for (i = 0;i < ctxt->docNr;i++) {
1515 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1516 if (ctxt->error != NULL)
1517 ctxt->error(ctxt->userData,
1518 "Detected an externalRef recursion for %s\n",
1519 URL);
1520 ctxt->nbErrors++;
1521 return(NULL);
1522 }
1523 }
1524
1525 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001526 * load the document
1527 */
1528 doc = xmlParseFile((const char *) URL);
1529 if (doc == NULL) {
1530 if (ctxt->error != NULL)
1531 ctxt->error(ctxt->userData,
1532 "xmlRelaxNG: could not load %s\n", URL);
1533 ctxt->nbErrors++;
1534 return (NULL);
1535 }
1536
1537 /*
1538 * Allocate the document structures and register it first.
1539 */
1540 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1541 if (ret == NULL) {
1542 if (ctxt->error != NULL)
1543 ctxt->error(ctxt->userData,
1544 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1545 ctxt->nbErrors++;
1546 xmlFreeDoc(doc);
1547 return (NULL);
1548 }
1549 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1550 ret->doc = doc;
1551 ret->href = xmlStrdup(URL);
Daniel Veillardc482e262003-02-26 14:48:48 +00001552 ret->next = ctxt->documents;
1553 ctxt->documents = ret;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001554
1555 /*
1556 * transmit the ns if needed
1557 */
1558 if (ns != NULL) {
1559 root = xmlDocGetRootElement(doc);
1560 if (root != NULL) {
1561 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1562 xmlSetProp(root, BAD_CAST"ns", ns);
1563 }
1564 }
1565 }
1566
1567 /*
1568 * push it on the stack and register it in the hash table
1569 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001570 xmlRelaxNGDocumentPush(ctxt, ret);
1571
1572 /*
1573 * Some preprocessing of the document content
1574 */
1575 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1576 if (doc == NULL) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001577 ctxt->doc = NULL;
1578 return(NULL);
1579 }
1580
1581 xmlRelaxNGDocumentPop(ctxt);
1582
1583 return(ret);
1584}
1585
1586/************************************************************************
1587 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001588 * Error functions *
1589 * *
1590 ************************************************************************/
1591
Daniel Veillard42f12e92003-03-07 18:32:59 +00001592#define VALID_ERR(a) xmlRelaxNGAddValidError(ctxt, a, NULL, NULL);
1593#define VALID_ERR2(a, b) xmlRelaxNGAddValidError(ctxt, a, b, NULL);
1594#define VALID_ERR3(a, b, c) xmlRelaxNGAddValidError(ctxt, a, b, c);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001595
Daniel Veillardfd573f12003-03-16 17:52:32 +00001596#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001597static const char *
1598xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1599 if (def == NULL)
1600 return("none");
1601 switch(def->type) {
1602 case XML_RELAXNG_EMPTY: return("empty");
1603 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1604 case XML_RELAXNG_EXCEPT: return("except");
1605 case XML_RELAXNG_TEXT: return("text");
1606 case XML_RELAXNG_ELEMENT: return("element");
1607 case XML_RELAXNG_DATATYPE: return("datatype");
1608 case XML_RELAXNG_VALUE: return("value");
1609 case XML_RELAXNG_LIST: return("list");
1610 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1611 case XML_RELAXNG_DEF: return("def");
1612 case XML_RELAXNG_REF: return("ref");
1613 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1614 case XML_RELAXNG_PARENTREF: return("parentRef");
Daniel Veillardfd573f12003-03-16 17:52:32 +00001615 case XML_RELAXNG_OPTIONAL: return("optional");
1616 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
Daniel Veillard231d7912003-02-09 14:22:17 +00001617 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1618 case XML_RELAXNG_CHOICE: return("choice");
1619 case XML_RELAXNG_GROUP: return("group");
1620 case XML_RELAXNG_INTERLEAVE: return("interleave");
1621 case XML_RELAXNG_START: return("start");
Daniel Veillard1564e6e2003-03-15 21:30:25 +00001622 case XML_RELAXNG_NOOP: return("noop");
Daniel Veillard1564e6e2003-03-15 21:30:25 +00001623 case XML_RELAXNG_PARAM: return("param");
Daniel Veillard231d7912003-02-09 14:22:17 +00001624 }
1625 return("unknown");
1626}
Daniel Veillardfd573f12003-03-16 17:52:32 +00001627#endif
Daniel Veillardd2298792003-02-14 16:54:11 +00001628
Daniel Veillard6eadf632003-01-23 18:29:16 +00001629/**
Daniel Veillard42f12e92003-03-07 18:32:59 +00001630 * xmlRelaxNGGetErrorString:
1631 * @err: the error code
1632 * @arg1: the first string argument
1633 * @arg2: the second string argument
Daniel Veillard6eadf632003-01-23 18:29:16 +00001634 *
Daniel Veillard42f12e92003-03-07 18:32:59 +00001635 * computes a formatted error string for the given error code and args
1636 *
1637 * Returns the error string, it must be deallocated by the caller
1638 */
1639static xmlChar *
Daniel Veillardfd573f12003-03-16 17:52:32 +00001640xmlRelaxNGGetErrorString(xmlRelaxNGValidErr err, const xmlChar *arg1,
1641 const xmlChar *arg2) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00001642 char msg[1000];
1643
1644 if (arg1 == NULL)
Daniel Veillardfd573f12003-03-16 17:52:32 +00001645 arg1 = BAD_CAST "";
Daniel Veillard42f12e92003-03-07 18:32:59 +00001646 if (arg2 == NULL)
Daniel Veillardfd573f12003-03-16 17:52:32 +00001647 arg2 = BAD_CAST "";
Daniel Veillard42f12e92003-03-07 18:32:59 +00001648
1649 msg[0] = 0;
1650 switch (err) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00001651 case XML_RELAXNG_OK:
1652 return(NULL);
1653 case XML_RELAXNG_ERR_MEMORY:
1654 return(xmlCharStrdup("out of memory"));
Daniel Veillard42f12e92003-03-07 18:32:59 +00001655 case XML_RELAXNG_ERR_TYPE:
Daniel Veillardfd573f12003-03-16 17:52:32 +00001656 snprintf(msg, 1000, "failed to validate type %s", arg1);
1657 break;
1658 case XML_RELAXNG_ERR_TYPEVAL:
1659 snprintf(msg, 1000, "Type %s doesn't allow value %s", arg1, arg2);
1660 break;
1661 case XML_RELAXNG_ERR_TYPECMP:
1662 snprintf(msg, 1000, "failed to compare type %s", arg1);
1663 break;
1664 case XML_RELAXNG_ERR_NOSTATE:
1665 return(xmlCharStrdup("Internal error: no state"));
1666 case XML_RELAXNG_ERR_NODEFINE:
1667 return(xmlCharStrdup("Internal error: no define"));
1668 case XML_RELAXNG_ERR_LISTEXTRA:
1669 snprintf(msg, 1000, "Extra data in list: %s", arg1);
1670 break;
1671 case XML_RELAXNG_ERR_INTERNODATA:
1672 return(xmlCharStrdup("Internal: interleave block has no data"));
1673 case XML_RELAXNG_ERR_INTERSEQ:
1674 return(xmlCharStrdup("Invalid sequence in interleave"));
1675 case XML_RELAXNG_ERR_INTEREXTRA:
1676 snprintf(msg, 1000, "Extra element %s in interleave", arg1);
1677 break;
1678 case XML_RELAXNG_ERR_ELEMNAME:
1679 snprintf(msg, 1000, "Expecting element %s, got %s", arg1, arg2);
1680 break;
1681 case XML_RELAXNG_ERR_ELEMNONS:
1682 snprintf(msg, 1000, "Expecting a namespace for element %s", arg1);
1683 break;
1684 case XML_RELAXNG_ERR_ELEMWRONGNS:
1685 snprintf(msg, 1000, "Element %s has wrong namespace: expecting %s",
1686 arg1, arg2);
1687 break;
1688 case XML_RELAXNG_ERR_ELEMEXTRANS:
1689 snprintf(msg, 1000, "Expecting no namespace for element %s", arg1);
1690 break;
1691 case XML_RELAXNG_ERR_ELEMNOTEMPTY:
1692 snprintf(msg, 1000, "Expecting element %s to be empty", arg1);
1693 break;
1694 case XML_RELAXNG_ERR_NOELEM:
1695 snprintf(msg, 1000, "Expecting an element %s, got nothing", arg1);
1696 break;
1697 case XML_RELAXNG_ERR_NOTELEM:
1698 return(xmlCharStrdup("Expecting an element got text"));
1699 case XML_RELAXNG_ERR_ATTRVALID:
1700 snprintf(msg, 1000, "Element %s failed to validate attributes",
1701 arg1);
1702 break;
1703 case XML_RELAXNG_ERR_CONTENTVALID:
1704 snprintf(msg, 1000, "Element %s failed to validate content",
1705 arg1);
1706 break;
1707 case XML_RELAXNG_ERR_EXTRACONTENT:
1708 snprintf(msg, 1000, "Element %s has extra content: %s",
1709 arg1, arg2);
1710 break;
1711 case XML_RELAXNG_ERR_INVALIDATTR:
1712 snprintf(msg, 1000, "Invalid attribute %s for element %s",
1713 arg1, arg2);
1714 break;
1715 case XML_RELAXNG_ERR_DATAELEM:
1716 snprintf(msg, 1000, "Datatype element %s has child elements",
1717 arg1);
1718 break;
1719 case XML_RELAXNG_ERR_VALELEM:
1720 snprintf(msg, 1000, "Value element %s has child elements",
1721 arg1);
1722 break;
1723 case XML_RELAXNG_ERR_LISTELEM:
1724 snprintf(msg, 1000, "List element %s has child elements",
1725 arg1);
1726 break;
1727 case XML_RELAXNG_ERR_DATATYPE:
1728 snprintf(msg, 1000, "Error validating datatype %s",
1729 arg1);
1730 break;
1731 case XML_RELAXNG_ERR_VALUE:
1732 snprintf(msg, 1000, "Error validating value %s",
1733 arg1);
1734 break;
1735 case XML_RELAXNG_ERR_LIST:
1736 return(xmlCharStrdup("Error validating list"));
1737 case XML_RELAXNG_ERR_NOGRAMMAR:
1738 return(xmlCharStrdup("No top grammar defined"));
1739 case XML_RELAXNG_ERR_EXTRADATA:
1740 return(xmlCharStrdup("Extra data in the document"));
1741 default:
1742 TODO
Daniel Veillard42f12e92003-03-07 18:32:59 +00001743 }
1744 if (msg[0] == 0) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00001745 snprintf(msg, 1000, "Unknown error code %d", err);
Daniel Veillard42f12e92003-03-07 18:32:59 +00001746 }
1747 msg[1000] = 0;
Daniel Veillardfd573f12003-03-16 17:52:32 +00001748 return(xmlStrdup((xmlChar *) msg));
Daniel Veillard42f12e92003-03-07 18:32:59 +00001749}
1750
1751/**
1752 * xmlRelaxNGValidErrorContext:
1753 * @ctxt: the validation context
1754 * @node: the node
1755 * @child: the node child generating the problem.
1756 *
1757 * Dump informations about the kocation of the error in the instance
Daniel Veillard6eadf632003-01-23 18:29:16 +00001758 */
1759static void
Daniel Veillard42f12e92003-03-07 18:32:59 +00001760xmlRelaxNGValidErrorContext(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node,
1761 xmlNodePtr child)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001762{
1763 int line = 0;
1764 const xmlChar *file = NULL;
1765 const xmlChar *name = NULL;
1766 const char *type = "error";
1767
1768 if ((ctxt == NULL) || (ctxt->error == NULL))
1769 return;
1770
1771 if (child != NULL)
1772 node = child;
1773
1774 if (node != NULL) {
1775 if ((node->type == XML_DOCUMENT_NODE) ||
1776 (node->type == XML_HTML_DOCUMENT_NODE)) {
1777 xmlDocPtr doc = (xmlDocPtr) node;
1778
1779 file = doc->URL;
1780 } else {
1781 /*
1782 * Try to find contextual informations to report
1783 */
1784 if (node->type == XML_ELEMENT_NODE) {
1785 line = (int) node->content;
1786 } else if ((node->prev != NULL) &&
1787 (node->prev->type == XML_ELEMENT_NODE)) {
1788 line = (int) node->prev->content;
1789 } else if ((node->parent != NULL) &&
1790 (node->parent->type == XML_ELEMENT_NODE)) {
1791 line = (int) node->parent->content;
1792 }
1793 if ((node->doc != NULL) && (node->doc->URL != NULL))
1794 file = node->doc->URL;
1795 if (node->name != NULL)
1796 name = node->name;
1797 }
1798 }
1799
Daniel Veillard42f12e92003-03-07 18:32:59 +00001800 type = "RNG validity error";
Daniel Veillard6eadf632003-01-23 18:29:16 +00001801
1802 if ((file != NULL) && (line != 0) && (name != NULL))
1803 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1804 type, file, line, name);
1805 else if ((file != NULL) && (name != NULL))
1806 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1807 type, file, name);
1808 else if ((file != NULL) && (line != 0))
1809 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1810 else if (file != NULL)
1811 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1812 else if (name != NULL)
1813 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1814 else
1815 ctxt->error(ctxt->userData, "%s\n", type);
1816}
Daniel Veillard42f12e92003-03-07 18:32:59 +00001817
1818/**
1819 * xmlRelaxNGShowValidError:
1820 * @ctxt: the validation context
1821 * @err: the error number
1822 * @node: the node
1823 * @child: the node child generating the problem.
1824 * @arg1: the first argument
1825 * @arg2: the second argument
1826 *
1827 * Show a validation error.
1828 */
1829static void
1830xmlRelaxNGShowValidError(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err,
1831 xmlNodePtr node, xmlNodePtr child,
1832 const xmlChar *arg1, const xmlChar *arg2)
1833{
1834 xmlChar *msg;
1835
1836 if (ctxt->error == NULL)
1837 return;
1838
1839 msg = xmlRelaxNGGetErrorString(err, arg1, arg2);
1840 if (msg == NULL)
1841 return;
1842
1843 xmlRelaxNGValidErrorContext(ctxt, node, child);
1844 ctxt->error(ctxt->userData, "%s\n", msg);
1845 xmlFree(msg);
1846}
1847
1848/**
1849 * xmlRelaxNGDumpValidError:
1850 * @ctxt: the validation context
1851 *
1852 * Show all validation error over a given index.
1853 */
1854static void
1855xmlRelaxNGDumpValidError(xmlRelaxNGValidCtxtPtr ctxt) {
1856 int i;
1857 xmlRelaxNGValidErrorPtr err;
1858
1859 for (i = 0;i < ctxt->errNr;i++) {
1860 err = &ctxt->errTab[i];
1861 xmlRelaxNGShowValidError(ctxt, err->err, err->node, err->seq,
1862 err->arg1, err->arg2);
1863 }
1864 ctxt->errNr = 0;
1865}
1866/**
1867 * xmlRelaxNGAddValidError:
1868 * @ctxt: the validation context
1869 * @err: the error number
1870 * @arg1: the first argument
1871 * @arg2: the second argument
1872 *
1873 * Register a validation error, either generating it if it's sure
1874 * or stacking it for later handling if unsure.
1875 */
1876static void
1877xmlRelaxNGAddValidError(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err,
1878 const xmlChar *arg1, const xmlChar *arg2)
1879{
1880 if ((ctxt == NULL) || (ctxt->error == NULL))
1881 return;
1882
1883 /*
1884 * generate the error directly
1885 */
1886 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) {
1887 xmlNodePtr node, seq;
1888 /*
1889 * Flush first any stacked error which might be the
1890 * real cause of the problem.
1891 */
1892 if (ctxt->errNr != 0)
1893 xmlRelaxNGDumpValidError(ctxt);
1894 if (ctxt->state != NULL) {
1895 node = ctxt->state->node;
1896 seq = ctxt->state->seq;
1897 } else {
1898 node = seq = NULL;
1899 }
1900 xmlRelaxNGShowValidError(ctxt, err, node, seq, arg1, arg2);
1901 }
1902 /*
1903 * Stack the error for later processing if needed
1904 */
1905 else {
1906 xmlRelaxNGValidErrorPush(ctxt, err, arg1, arg2);
1907 }
1908}
1909
Daniel Veillard6eadf632003-01-23 18:29:16 +00001910
1911/************************************************************************
1912 * *
1913 * Type library hooks *
1914 * *
1915 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001916static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1917 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001918
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001919/**
1920 * xmlRelaxNGSchemaTypeHave:
1921 * @data: data needed for the library
1922 * @type: the type name
1923 *
1924 * Check if the given type is provided by
1925 * the W3C XMLSchema Datatype library.
1926 *
1927 * Returns 1 if yes, 0 if no and -1 in case of error.
1928 */
1929static int
1930xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001931 const xmlChar *type) {
1932 xmlSchemaTypePtr typ;
1933
1934 if (type == NULL)
1935 return(-1);
1936 typ = xmlSchemaGetPredefinedType(type,
1937 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1938 if (typ == NULL)
1939 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001940 return(1);
1941}
1942
1943/**
1944 * xmlRelaxNGSchemaTypeCheck:
1945 * @data: data needed for the library
1946 * @type: the type name
1947 * @value: the value to check
1948 *
1949 * Check if the given type and value are validated by
1950 * the W3C XMLSchema Datatype library.
1951 *
1952 * Returns 1 if yes, 0 if no and -1 in case of error.
1953 */
1954static int
1955xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001956 const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001957 const xmlChar *value,
1958 void **result) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001959 xmlSchemaTypePtr typ;
1960 int ret;
1961
1962 /*
1963 * TODO: the type should be cached ab provided back, interface subject
1964 * to changes.
1965 * TODO: handle facets, may require an additional interface and keep
1966 * the value returned from the validation.
1967 */
1968 if ((type == NULL) || (value == NULL))
1969 return(-1);
1970 typ = xmlSchemaGetPredefinedType(type,
1971 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1972 if (typ == NULL)
1973 return(-1);
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001974 ret = xmlSchemaValidatePredefinedType(typ, value,
1975 (xmlSchemaValPtr *) result);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001976 if (ret == 0)
1977 return(1);
1978 if (ret > 0)
1979 return(0);
1980 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001981}
1982
1983/**
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001984 * xmlRelaxNGSchemaFacetCheck:
1985 * @data: data needed for the library
1986 * @type: the type name
1987 * @facet: the facet name
1988 * @val: the facet value
1989 * @strval: the string value
1990 * @value: the value to check
1991 *
1992 * Function provided by a type library to check a value facet
1993 *
1994 * Returns 1 if yes, 0 if no and -1 in case of error.
1995 */
1996static int
Daniel Veillard42f12e92003-03-07 18:32:59 +00001997xmlRelaxNGSchemaFacetCheck (void *data ATTRIBUTE_UNUSED, const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001998 const xmlChar *facetname, const xmlChar *val,
1999 const xmlChar *strval, void *value) {
2000 xmlSchemaFacetPtr facet;
2001 xmlSchemaTypePtr typ;
2002 int ret;
2003
2004 if ((type == NULL) || (strval == NULL))
2005 return(-1);
2006 typ = xmlSchemaGetPredefinedType(type,
2007 BAD_CAST "http://www.w3.org/2001/XMLSchema");
2008 if (typ == NULL)
2009 return(-1);
2010
2011 facet = xmlSchemaNewFacet();
2012 if (facet == NULL)
2013 return(-1);
2014
2015 if (xmlStrEqual(facetname, BAD_CAST "minInclusive")) {
2016 facet->type = XML_SCHEMA_FACET_MININCLUSIVE;
2017 } else if (xmlStrEqual(facetname, BAD_CAST "minExclusive")) {
2018 facet->type = XML_SCHEMA_FACET_MINEXCLUSIVE;
2019 } else if (xmlStrEqual(facetname, BAD_CAST "maxInclusive")) {
2020 facet->type = XML_SCHEMA_FACET_MAXINCLUSIVE;
2021 } else if (xmlStrEqual(facetname, BAD_CAST "maxExclusive")) {
2022 facet->type = XML_SCHEMA_FACET_MAXEXCLUSIVE;
2023 } else if (xmlStrEqual(facetname, BAD_CAST "totalDigits")) {
2024 facet->type = XML_SCHEMA_FACET_TOTALDIGITS;
2025 } else if (xmlStrEqual(facetname, BAD_CAST "fractionDigits")) {
2026 facet->type = XML_SCHEMA_FACET_FRACTIONDIGITS;
2027 } else if (xmlStrEqual(facetname, BAD_CAST "pattern")) {
2028 facet->type = XML_SCHEMA_FACET_PATTERN;
2029 } else if (xmlStrEqual(facetname, BAD_CAST "enumeration")) {
2030 facet->type = XML_SCHEMA_FACET_ENUMERATION;
2031 } else if (xmlStrEqual(facetname, BAD_CAST "whiteSpace")) {
2032 facet->type = XML_SCHEMA_FACET_WHITESPACE;
2033 } else if (xmlStrEqual(facetname, BAD_CAST "length")) {
2034 facet->type = XML_SCHEMA_FACET_LENGTH;
2035 } else if (xmlStrEqual(facetname, BAD_CAST "maxLength")) {
2036 facet->type = XML_SCHEMA_FACET_MAXLENGTH;
2037 } else if (xmlStrEqual(facetname, BAD_CAST "minLength")) {
2038 facet->type = XML_SCHEMA_FACET_MINLENGTH;
2039 } else {
2040 xmlSchemaFreeFacet(facet);
2041 return(-1);
2042 }
2043 facet->value = xmlStrdup(val);
2044 ret = xmlSchemaCheckFacet(facet, typ, NULL, type);
2045 if (ret != 0) {
2046 xmlSchemaFreeFacet(facet);
2047 return(-1);
2048 }
2049 ret = xmlSchemaValidateFacet(typ, facet, strval, value);
2050 xmlSchemaFreeFacet(facet);
2051 if (ret != 0)
2052 return(-1);
2053 return(0);
2054}
2055
2056/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002057 * xmlRelaxNGSchemaTypeCompare:
2058 * @data: data needed for the library
2059 * @type: the type name
2060 * @value1: the first value
2061 * @value2: the second value
2062 *
2063 * Compare two values accordingly a type from the W3C XMLSchema
2064 * Datatype library.
2065 *
2066 * Returns 1 if yes, 0 if no and -1 in case of error.
2067 */
2068static int
2069xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
2070 const xmlChar *type ATTRIBUTE_UNUSED,
2071 const xmlChar *value1 ATTRIBUTE_UNUSED,
2072 const xmlChar *value2 ATTRIBUTE_UNUSED) {
2073 TODO
2074 return(1);
2075}
2076
2077/**
2078 * xmlRelaxNGDefaultTypeHave:
2079 * @data: data needed for the library
2080 * @type: the type name
2081 *
2082 * Check if the given type is provided by
2083 * the default datatype library.
2084 *
2085 * Returns 1 if yes, 0 if no and -1 in case of error.
2086 */
2087static int
2088xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
2089 if (type == NULL)
2090 return(-1);
2091 if (xmlStrEqual(type, BAD_CAST "string"))
2092 return(1);
2093 if (xmlStrEqual(type, BAD_CAST "token"))
2094 return(1);
2095 return(0);
2096}
2097
2098/**
2099 * xmlRelaxNGDefaultTypeCheck:
2100 * @data: data needed for the library
2101 * @type: the type name
2102 * @value: the value to check
2103 *
2104 * Check if the given type and value are validated by
2105 * the default datatype library.
2106 *
2107 * Returns 1 if yes, 0 if no and -1 in case of error.
2108 */
2109static int
2110xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
2111 const xmlChar *type ATTRIBUTE_UNUSED,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002112 const xmlChar *value ATTRIBUTE_UNUSED,
2113 void **result ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00002114 if (value == NULL)
2115 return(-1);
2116 if (xmlStrEqual(type, BAD_CAST "string"))
2117 return(1);
2118 if (xmlStrEqual(type, BAD_CAST "token")) {
Daniel Veillardd4310742003-02-18 21:12:46 +00002119 return(1);
2120 }
2121
2122 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002123}
2124
2125/**
2126 * xmlRelaxNGDefaultTypeCompare:
2127 * @data: data needed for the library
2128 * @type: the type name
2129 * @value1: the first value
2130 * @value2: the second value
2131 *
2132 * Compare two values accordingly a type from the default
2133 * datatype library.
2134 *
2135 * Returns 1 if yes, 0 if no and -1 in case of error.
2136 */
2137static int
2138xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
2139 const xmlChar *type ATTRIBUTE_UNUSED,
2140 const xmlChar *value1 ATTRIBUTE_UNUSED,
2141 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00002142 int ret = -1;
2143
2144 if (xmlStrEqual(type, BAD_CAST "string")) {
2145 ret = xmlStrEqual(value1, value2);
2146 } else if (xmlStrEqual(type, BAD_CAST "token")) {
2147 if (!xmlStrEqual(value1, value2)) {
2148 xmlChar *nval, *nvalue;
2149
2150 /*
2151 * TODO: trivial optimizations are possible by
2152 * computing at compile-time
2153 */
2154 nval = xmlRelaxNGNormalize(NULL, value1);
2155 nvalue = xmlRelaxNGNormalize(NULL, value2);
2156
Daniel Veillardd4310742003-02-18 21:12:46 +00002157 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00002158 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00002159 else if (xmlStrEqual(nval, nvalue))
2160 ret = 1;
2161 else
2162 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00002163 if (nval != NULL)
2164 xmlFree(nval);
2165 if (nvalue != NULL)
2166 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00002167 } else
2168 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00002169 }
2170 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002171}
2172
2173static int xmlRelaxNGTypeInitialized = 0;
2174static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
2175
2176/**
2177 * xmlRelaxNGFreeTypeLibrary:
2178 * @lib: the type library structure
2179 * @namespace: the URI bound to the library
2180 *
2181 * Free the structure associated to the type library
2182 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00002183static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002184xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
2185 const xmlChar *namespace ATTRIBUTE_UNUSED) {
2186 if (lib == NULL)
2187 return;
2188 if (lib->namespace != NULL)
2189 xmlFree((xmlChar *)lib->namespace);
2190 xmlFree(lib);
2191}
2192
2193/**
2194 * xmlRelaxNGRegisterTypeLibrary:
2195 * @namespace: the URI bound to the library
2196 * @data: data associated to the library
2197 * @have: the provide function
2198 * @check: the checking function
2199 * @comp: the comparison function
2200 *
2201 * Register a new type library
2202 *
2203 * Returns 0 in case of success and -1 in case of error.
2204 */
2205static int
2206xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
2207 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002208 xmlRelaxNGTypeCompare comp, xmlRelaxNGFacetCheck facet,
2209 xmlRelaxNGTypeFree freef) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002210 xmlRelaxNGTypeLibraryPtr lib;
2211 int ret;
2212
2213 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
2214 (check == NULL) || (comp == NULL))
2215 return(-1);
2216 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
2217 xmlGenericError(xmlGenericErrorContext,
2218 "Relax-NG types library '%s' already registered\n",
2219 namespace);
2220 return(-1);
2221 }
2222 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
2223 if (lib == NULL) {
2224 xmlGenericError(xmlGenericErrorContext,
2225 "Relax-NG types library '%s' malloc() failed\n",
2226 namespace);
2227 return (-1);
2228 }
2229 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
2230 lib->namespace = xmlStrdup(namespace);
2231 lib->data = data;
2232 lib->have = have;
2233 lib->comp = comp;
2234 lib->check = check;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002235 lib->facet = facet;
2236 lib->freef = freef;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002237 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
2238 if (ret < 0) {
2239 xmlGenericError(xmlGenericErrorContext,
2240 "Relax-NG types library failed to register '%s'\n",
2241 namespace);
2242 xmlRelaxNGFreeTypeLibrary(lib, namespace);
2243 return(-1);
2244 }
2245 return(0);
2246}
2247
2248/**
2249 * xmlRelaxNGInitTypes:
2250 *
2251 * Initilize the default type libraries.
2252 *
2253 * Returns 0 in case of success and -1 in case of error.
2254 */
2255static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00002256xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002257 if (xmlRelaxNGTypeInitialized != 0)
2258 return(0);
2259 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
2260 if (xmlRelaxNGRegisteredTypes == NULL) {
2261 xmlGenericError(xmlGenericErrorContext,
2262 "Failed to allocate sh table for Relax-NG types\n");
2263 return(-1);
2264 }
2265 xmlRelaxNGRegisterTypeLibrary(
2266 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
2267 NULL,
2268 xmlRelaxNGSchemaTypeHave,
2269 xmlRelaxNGSchemaTypeCheck,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002270 xmlRelaxNGSchemaTypeCompare,
2271 xmlRelaxNGSchemaFacetCheck,
2272 (xmlRelaxNGTypeFree) xmlSchemaFreeValue);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002273 xmlRelaxNGRegisterTypeLibrary(
2274 xmlRelaxNGNs,
2275 NULL,
2276 xmlRelaxNGDefaultTypeHave,
2277 xmlRelaxNGDefaultTypeCheck,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002278 xmlRelaxNGDefaultTypeCompare,
2279 NULL,
2280 NULL);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002281 xmlRelaxNGTypeInitialized = 1;
2282 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002283}
2284
2285/**
2286 * xmlRelaxNGCleanupTypes:
2287 *
2288 * Cleanup the default Schemas type library associated to RelaxNG
2289 */
2290void
2291xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002292 if (xmlRelaxNGTypeInitialized == 0)
2293 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002294 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002295 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
2296 xmlRelaxNGFreeTypeLibrary);
2297 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002298}
2299
2300/************************************************************************
2301 * *
2302 * Parsing functions *
2303 * *
2304 ************************************************************************/
2305
2306static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
2307 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
2308static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
2309 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
2310static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00002311 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002312static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
2313 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002314static xmlRelaxNGPtr xmlRelaxNGParseDocument(
2315 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002316static int xmlRelaxNGParseGrammarContent(
2317 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002318static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
2319 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2320 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00002321static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
2322 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillardfd573f12003-03-16 17:52:32 +00002323static int xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
2324 xmlRelaxNGDefinePtr define, xmlNodePtr elem);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002325
2326
2327#define IS_BLANK_NODE(n) \
Daniel Veillard39eb88b2003-03-11 11:21:28 +00002328 ((((n)->type == XML_TEXT_NODE) || \
2329 ((n)->type == XML_CDATA_SECTION_NODE)) && \
2330 (xmlRelaxNGIsBlank((n)->content)))
Daniel Veillard6eadf632003-01-23 18:29:16 +00002331
2332/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00002333 * xmlRelaxNGIsNullable:
2334 * @define: the definition to verify
2335 *
2336 * Check if a definition is nullable.
2337 *
2338 * Returns 1 if yes, 0 if no and -1 in case of error
2339 */
2340static int
2341xmlRelaxNGIsNullable(xmlRelaxNGDefinePtr define) {
2342 int ret;
2343 if (define == NULL)
2344 return(-1);
2345
2346 if (define->flags & IS_NULLABLE)
2347 return(1);
2348 if (define->flags & IS_NOT_NULLABLE)
2349 return(0);
2350 switch (define->type) {
2351 case XML_RELAXNG_EMPTY:
2352 case XML_RELAXNG_TEXT:
2353 ret = 1; break;
2354 case XML_RELAXNG_NOOP:
2355 case XML_RELAXNG_DEF:
2356 case XML_RELAXNG_REF:
2357 case XML_RELAXNG_EXTERNALREF:
2358 case XML_RELAXNG_PARENTREF:
2359 case XML_RELAXNG_ONEORMORE:
2360 ret = xmlRelaxNGIsNullable(define->content);
2361 break;
2362 case XML_RELAXNG_EXCEPT:
2363 case XML_RELAXNG_NOT_ALLOWED:
2364 case XML_RELAXNG_ELEMENT:
2365 case XML_RELAXNG_DATATYPE:
2366 case XML_RELAXNG_PARAM:
2367 case XML_RELAXNG_VALUE:
2368 case XML_RELAXNG_LIST:
2369 case XML_RELAXNG_ATTRIBUTE:
2370 ret = 0; break;
2371 case XML_RELAXNG_CHOICE: {
2372 xmlRelaxNGDefinePtr list = define->content;
2373
2374 while (list != NULL) {
2375 ret = xmlRelaxNGIsNullable(list);
2376 if (ret != 0)
2377 goto done;
2378 list = list->next;
2379 }
2380 ret = 0; break;
2381 }
2382 case XML_RELAXNG_START:
2383 case XML_RELAXNG_INTERLEAVE:
2384 case XML_RELAXNG_GROUP: {
2385 xmlRelaxNGDefinePtr list = define->content;
2386
2387 while (list != NULL) {
2388 ret = xmlRelaxNGIsNullable(list);
2389 if (ret != 1)
2390 goto done;
2391 list = list->next;
2392 }
2393 return(1);
2394 }
2395 default:
2396 return(-1);
2397 }
2398done:
2399 if (ret == 0)
2400 define->flags |= IS_NOT_NULLABLE;
2401 if (ret == 1)
2402 define->flags |= IS_NULLABLE;
2403 return(ret);
2404}
2405
2406/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002407 * xmlRelaxNGIsBlank:
2408 * @str: a string
2409 *
2410 * Check if a string is ignorable c.f. 4.2. Whitespace
2411 *
2412 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
2413 */
2414static int
2415xmlRelaxNGIsBlank(xmlChar *str) {
2416 if (str == NULL)
2417 return(1);
2418 while (*str != 0) {
2419 if (!(IS_BLANK(*str))) return(0);
2420 str++;
2421 }
2422 return(1);
2423}
2424
Daniel Veillard6eadf632003-01-23 18:29:16 +00002425/**
2426 * xmlRelaxNGGetDataTypeLibrary:
2427 * @ctxt: a Relax-NG parser context
2428 * @node: the current data or value element
2429 *
2430 * Applies algorithm from 4.3. datatypeLibrary attribute
2431 *
2432 * Returns the datatypeLibary value or NULL if not found
2433 */
2434static xmlChar *
2435xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2436 xmlNodePtr node) {
2437 xmlChar *ret, *escape;
2438
Daniel Veillard6eadf632003-01-23 18:29:16 +00002439 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
2440 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
2441 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002442 if (ret[0] == 0) {
2443 xmlFree(ret);
2444 return(NULL);
2445 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002446 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00002447 if (escape == NULL) {
2448 return(ret);
2449 }
2450 xmlFree(ret);
2451 return(escape);
2452 }
2453 }
2454 node = node->parent;
2455 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00002456 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
2457 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002458 if (ret[0] == 0) {
2459 xmlFree(ret);
2460 return(NULL);
2461 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00002462 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
2463 if (escape == NULL) {
2464 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002465 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00002466 xmlFree(ret);
2467 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002468 }
2469 node = node->parent;
2470 }
2471 return(NULL);
2472}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002473
2474/**
Daniel Veillardedc91922003-01-26 00:52:04 +00002475 * xmlRelaxNGParseValue:
2476 * @ctxt: a Relax-NG parser context
2477 * @node: the data node.
2478 *
2479 * parse the content of a RelaxNG value node.
2480 *
2481 * Returns the definition pointer or NULL in case of error
2482 */
2483static xmlRelaxNGDefinePtr
2484xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2485 xmlRelaxNGDefinePtr def = NULL;
2486 xmlRelaxNGTypeLibraryPtr lib;
2487 xmlChar *type;
2488 xmlChar *library;
2489 int tmp;
2490
Daniel Veillardfd573f12003-03-16 17:52:32 +00002491 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillardedc91922003-01-26 00:52:04 +00002492 if (def == NULL)
2493 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00002494 def->type = XML_RELAXNG_VALUE;
Daniel Veillardedc91922003-01-26 00:52:04 +00002495
2496 type = xmlGetProp(node, BAD_CAST "type");
2497 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002498 xmlRelaxNGNormExtSpace(type);
2499 if (xmlValidateNCName(type, 0)) {
2500 if (ctxt->error != NULL)
2501 ctxt->error(ctxt->userData,
2502 "value type '%s' is not an NCName\n",
2503 type);
2504 ctxt->nbErrors++;
2505 }
Daniel Veillardedc91922003-01-26 00:52:04 +00002506 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
2507 if (library == NULL)
2508 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
2509
2510 def->name = type;
2511 def->ns = library;
2512
2513 lib = (xmlRelaxNGTypeLibraryPtr)
2514 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
2515 if (lib == NULL) {
2516 if (ctxt->error != NULL)
2517 ctxt->error(ctxt->userData,
2518 "Use of unregistered type library '%s'\n",
2519 library);
2520 ctxt->nbErrors++;
2521 def->data = NULL;
2522 } else {
2523 def->data = lib;
2524 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002525 if (ctxt->error != NULL)
2526 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00002527 "Internal error with type library '%s': no 'have'\n",
2528 library);
2529 ctxt->nbErrors++;
2530 } else {
2531 tmp = lib->have(lib->data, def->name);
2532 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002533 if (ctxt->error != NULL)
2534 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00002535 "Error type '%s' is not exported by type library '%s'\n",
2536 def->name, library);
2537 ctxt->nbErrors++;
2538 }
2539 }
2540 }
2541 }
2542 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00002543 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillard39eb88b2003-03-11 11:21:28 +00002544 } else if (((node->children->type != XML_TEXT_NODE) &&
2545 (node->children->type != XML_CDATA_SECTION_NODE)) ||
Daniel Veillardedc91922003-01-26 00:52:04 +00002546 (node->children->next != NULL)) {
2547 if (ctxt->error != NULL)
2548 ctxt->error(ctxt->userData,
2549 "Expecting a single text value for <value>content\n");
2550 ctxt->nbErrors++;
2551 } else {
2552 def->value = xmlNodeGetContent(node);
2553 if (def->value == NULL) {
2554 if (ctxt->error != NULL)
2555 ctxt->error(ctxt->userData,
2556 "Element <value> has no content\n");
2557 ctxt->nbErrors++;
2558 }
2559 }
2560 /* TODO check ahead of time that the value is okay per the type */
2561 return(def);
2562}
2563
2564/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002565 * xmlRelaxNGParseData:
2566 * @ctxt: a Relax-NG parser context
2567 * @node: the data node.
2568 *
2569 * parse the content of a RelaxNG data node.
2570 *
2571 * Returns the definition pointer or NULL in case of error
2572 */
2573static xmlRelaxNGDefinePtr
2574xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002575 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002576 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002577 xmlRelaxNGTypeLibraryPtr lib;
2578 xmlChar *type;
2579 xmlChar *library;
2580 xmlNodePtr content;
2581 int tmp;
2582
2583 type = xmlGetProp(node, BAD_CAST "type");
2584 if (type == NULL) {
2585 if (ctxt->error != NULL)
2586 ctxt->error(ctxt->userData,
2587 "data has no type\n");
2588 ctxt->nbErrors++;
2589 return(NULL);
2590 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002591 xmlRelaxNGNormExtSpace(type);
2592 if (xmlValidateNCName(type, 0)) {
2593 if (ctxt->error != NULL)
2594 ctxt->error(ctxt->userData,
2595 "data type '%s' is not an NCName\n",
2596 type);
2597 ctxt->nbErrors++;
2598 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002599 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
2600 if (library == NULL)
2601 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
2602
Daniel Veillardfd573f12003-03-16 17:52:32 +00002603 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002604 if (def == NULL) {
2605 xmlFree(type);
2606 return(NULL);
2607 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00002608 def->type = XML_RELAXNG_DATATYPE;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002609 def->name = type;
2610 def->ns = library;
2611
2612 lib = (xmlRelaxNGTypeLibraryPtr)
2613 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
2614 if (lib == NULL) {
2615 if (ctxt->error != NULL)
2616 ctxt->error(ctxt->userData,
2617 "Use of unregistered type library '%s'\n",
2618 library);
2619 ctxt->nbErrors++;
2620 def->data = NULL;
2621 } else {
2622 def->data = lib;
2623 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002624 if (ctxt->error != NULL)
2625 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002626 "Internal error with type library '%s': no 'have'\n",
2627 library);
2628 ctxt->nbErrors++;
2629 } else {
2630 tmp = lib->have(lib->data, def->name);
2631 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002632 if (ctxt->error != NULL)
2633 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002634 "Error type '%s' is not exported by type library '%s'\n",
2635 def->name, library);
2636 ctxt->nbErrors++;
2637 }
2638 }
2639 }
2640 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00002641
2642 /*
2643 * Handle optional params
2644 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002645 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002646 if (!xmlStrEqual(content->name, BAD_CAST "param"))
2647 break;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002648 if (xmlStrEqual(library,
2649 BAD_CAST"http://relaxng.org/ns/structure/1.0")) {
2650 if (ctxt->error != NULL)
2651 ctxt->error(ctxt->userData,
2652 "Type library '%s' does not allow type parameters\n",
2653 library);
2654 ctxt->nbErrors++;
2655 content = content->next;
2656 while ((content != NULL) &&
2657 (xmlStrEqual(content->name, BAD_CAST "param")))
2658 content = content->next;
2659 } else {
Daniel Veillardfd573f12003-03-16 17:52:32 +00002660 param = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002661 if (param != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00002662 param->type = XML_RELAXNG_PARAM;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002663 param->name = xmlGetProp(content, BAD_CAST "name");
2664 if (param->name == NULL) {
2665 if (ctxt->error != NULL)
2666 ctxt->error(ctxt->userData,
2667 "param has no name\n");
2668 ctxt->nbErrors++;
2669 }
2670 param->value = xmlNodeGetContent(content);
2671 if (lastparam == NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00002672 def->attrs = lastparam = param;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002673 } else {
2674 lastparam->next = param;
2675 lastparam = param;
2676 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002677 if (lib != NULL) {
2678 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00002679 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002680 content = content->next;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002681 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002682 }
Daniel Veillard416589a2003-02-17 17:25:42 +00002683 /*
2684 * Handle optional except
2685 */
2686 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
2687 xmlNodePtr child;
2688 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
2689
Daniel Veillardfd573f12003-03-16 17:52:32 +00002690 except = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard416589a2003-02-17 17:25:42 +00002691 if (except == NULL) {
2692 return(def);
2693 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00002694 except->type = XML_RELAXNG_EXCEPT;
Daniel Veillard416589a2003-02-17 17:25:42 +00002695 child = content->children;
2696 if (last == NULL) {
2697 def->content = except;
2698 } else {
2699 last->next = except;
2700 }
2701 if (child == NULL) {
2702 if (ctxt->error != NULL)
2703 ctxt->error(ctxt->userData,
2704 "except has no content\n");
2705 ctxt->nbErrors++;
2706 }
2707 while (child != NULL) {
2708 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
2709 if (tmp2 != NULL) {
2710 if (last2 == NULL) {
2711 except->content = last2 = tmp2;
2712 } else {
2713 last2->next = tmp2;
2714 last2 = tmp2;
2715 }
2716 }
2717 child = child->next;
2718 }
2719 content = content->next;
2720 }
2721 /*
2722 * Check there is no unhandled data
2723 */
2724 if (content != NULL) {
2725 if (ctxt->error != NULL)
2726 ctxt->error(ctxt->userData,
2727 "Element data has unexpected content %s\n", content->name);
2728 ctxt->nbErrors++;
2729 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002730
2731 return(def);
2732}
2733
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002734static const xmlChar *invalidName = BAD_CAST "\1";
2735
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002736/**
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002737 * xmlRelaxNGCompareNameClasses:
2738 * @defs1: the first element/attribute defs
2739 * @defs2: the second element/attribute defs
2740 * @name: the restriction on the name
2741 * @ns: the restriction on the namespace
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002742 *
2743 * Compare the 2 lists of element definitions. The comparison is
2744 * that if both lists do not accept the same QNames, it returns 1
2745 * If the 2 lists can accept the same QName the comparison returns 0
2746 *
2747 * Returns 1 disttinct, 0 if equal
2748 */
2749static int
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002750xmlRelaxNGCompareNameClasses(xmlRelaxNGDefinePtr def1,
2751 xmlRelaxNGDefinePtr def2) {
2752 int ret = 1;
2753 xmlNode node;
2754 xmlNs ns;
2755 xmlRelaxNGValidCtxt ctxt;
2756 ctxt.flags = FLAGS_IGNORABLE;
2757
Daniel Veillard42f12e92003-03-07 18:32:59 +00002758 memset(&ctxt, 0, sizeof(xmlRelaxNGValidCtxt));
2759
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002760 if ((def1->type == XML_RELAXNG_ELEMENT) ||
2761 (def1->type == XML_RELAXNG_ATTRIBUTE)) {
2762 if (def2->type == XML_RELAXNG_TEXT)
2763 return(1);
2764 if (def1->name != NULL) {
2765 node.name = def1->name;
2766 } else {
2767 node.name = invalidName;
2768 }
2769 node.ns = &ns;
2770 if (def1->ns != NULL) {
2771 if (def1->ns[0] == 0) {
2772 node.ns = NULL;
2773 } else {
2774 ns.href = def1->ns;
2775 }
2776 } else {
2777 ns.href = invalidName;
2778 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00002779 if (xmlRelaxNGElementMatch(&ctxt, def2, &node)) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002780 if (def1->nameClass != NULL) {
2781 ret = xmlRelaxNGCompareNameClasses(def1->nameClass, def2);
2782 } else {
2783 ret = 0;
2784 }
2785 } else {
2786 ret = 1;
2787 }
2788 } else if (def1->type == XML_RELAXNG_TEXT) {
2789 if (def2->type == XML_RELAXNG_TEXT)
2790 return(0);
2791 return(1);
2792 } else if (def1->type == XML_RELAXNG_EXCEPT) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002793 TODO
2794 ret = 0;
2795 } else {
2796 TODO
2797 ret = 0;
2798 }
2799 if (ret == 0)
2800 return(ret);
2801 if ((def2->type == XML_RELAXNG_ELEMENT) ||
2802 (def2->type == XML_RELAXNG_ATTRIBUTE)) {
2803 if (def2->name != NULL) {
2804 node.name = def2->name;
2805 } else {
2806 node.name = invalidName;
2807 }
2808 node.ns = &ns;
2809 if (def2->ns != NULL) {
2810 if (def2->ns[0] == 0) {
2811 node.ns = NULL;
2812 } else {
2813 ns.href = def2->ns;
2814 }
2815 } else {
2816 ns.href = invalidName;
2817 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00002818 if (xmlRelaxNGElementMatch(&ctxt, def1, &node)) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002819 if (def2->nameClass != NULL) {
2820 ret = xmlRelaxNGCompareNameClasses(def2->nameClass, def1);
2821 } else {
2822 ret = 0;
2823 }
2824 } else {
2825 ret = 1;
2826 }
2827 } else {
2828 TODO
2829 ret = 0;
2830 }
2831
2832 return(ret);
2833}
2834
2835/**
2836 * xmlRelaxNGCompareElemDefLists:
2837 * @ctxt: a Relax-NG parser context
2838 * @defs1: the first list of element/attribute defs
2839 * @defs2: the second list of element/attribute defs
2840 *
2841 * Compare the 2 lists of element or attribute definitions. The comparison
2842 * is that if both lists do not accept the same QNames, it returns 1
2843 * If the 2 lists can accept the same QName the comparison returns 0
2844 *
2845 * Returns 1 disttinct, 0 if equal
2846 */
2847static int
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002848xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2849 xmlRelaxNGDefinePtr *def1,
2850 xmlRelaxNGDefinePtr *def2) {
2851 xmlRelaxNGDefinePtr *basedef2 = def2;
2852
Daniel Veillard154877e2003-01-30 12:17:05 +00002853 if ((def1 == NULL) || (def2 == NULL))
2854 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002855 if ((*def1 == NULL) || (*def2 == NULL))
2856 return(1);
2857 while (*def1 != NULL) {
2858 while ((*def2) != NULL) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002859 if (xmlRelaxNGCompareNameClasses(*def1, *def2) == 0)
2860 return(0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002861 def2++;
2862 }
2863 def2 = basedef2;
2864 def1++;
2865 }
2866 return(1);
2867}
2868
2869/**
2870 * xmlRelaxNGGetElements:
2871 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002872 * @def: the definition definition
2873 * @eora: gather elements (0) or attributes (1)
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002874 *
2875 * Compute the list of top elements a definition can generate
2876 *
2877 * Returns a list of elements or NULL if none was found.
2878 */
2879static xmlRelaxNGDefinePtr *
2880xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002881 xmlRelaxNGDefinePtr def,
2882 int eora) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00002883 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002884 int len = 0;
2885 int max = 0;
2886
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002887 /*
2888 * Don't run that check in case of error. Infinite recursion
2889 * becomes possible.
2890 */
2891 if (ctxt->nbErrors != 0)
2892 return(NULL);
2893
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002894 parent = NULL;
2895 cur = def;
2896 while (cur != NULL) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002897 if (((eora == 0) && ((cur->type == XML_RELAXNG_ELEMENT) ||
2898 (cur->type == XML_RELAXNG_TEXT))) ||
2899 ((eora == 1) && (cur->type == XML_RELAXNG_ATTRIBUTE))) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002900 if (ret == NULL) {
2901 max = 10;
2902 ret = (xmlRelaxNGDefinePtr *)
2903 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2904 if (ret == NULL) {
2905 if (ctxt->error != NULL)
2906 ctxt->error(ctxt->userData,
2907 "Out of memory in element search\n");
2908 ctxt->nbErrors++;
2909 return(NULL);
2910 }
2911 } else if (max <= len) {
2912 max *= 2;
2913 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2914 if (ret == NULL) {
2915 if (ctxt->error != NULL)
2916 ctxt->error(ctxt->userData,
2917 "Out of memory in element search\n");
2918 ctxt->nbErrors++;
2919 return(NULL);
2920 }
2921 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002922 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002923 ret[len] = NULL;
2924 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2925 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2926 (cur->type == XML_RELAXNG_GROUP) ||
2927 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardfd573f12003-03-16 17:52:32 +00002928 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2929 (cur->type == XML_RELAXNG_OPTIONAL) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002930 (cur->type == XML_RELAXNG_REF) ||
2931 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002932 /*
2933 * Don't go within elements or attributes or string values.
2934 * Just gather the element top list
2935 */
2936 if (cur->content != NULL) {
2937 parent = cur;
2938 cur = cur->content;
Daniel Veillardfd573f12003-03-16 17:52:32 +00002939 tmp = cur;
2940 while (tmp != NULL) {
2941 tmp->parent = parent;
2942 tmp = tmp->next;
2943 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002944 continue;
2945 }
2946 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002947 if (cur == def)
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002948 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002949 if (cur->next != NULL) {
2950 cur = cur->next;
2951 continue;
2952 }
2953 do {
2954 cur = cur->parent;
2955 if (cur == NULL) break;
2956 if (cur == def) return(ret);
2957 if (cur->next != NULL) {
2958 cur = cur->next;
2959 break;
2960 }
2961 } while (cur != NULL);
2962 }
2963 return(ret);
2964}
2965
2966/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00002967 * xmlRelaxNGCheckChoiceDeterminism:
2968 * @ctxt: a Relax-NG parser context
2969 * @def: the choice definition
2970 *
2971 * Also used to find indeterministic pattern in choice
2972 */
2973static void
2974xmlRelaxNGCheckChoiceDeterminism(xmlRelaxNGParserCtxtPtr ctxt,
2975 xmlRelaxNGDefinePtr def) {
2976 xmlRelaxNGDefinePtr **list;
2977 xmlRelaxNGDefinePtr cur;
2978 int nbchild = 0, i, j, ret;
2979 int is_nullable = 0;
2980 int is_indeterminist = 0;
2981
2982 if ((def == NULL) ||
2983 (def->type != XML_RELAXNG_CHOICE))
2984 return;
2985
2986 /*
2987 * Don't run that check in case of error. Infinite recursion
2988 * becomes possible.
2989 */
2990 if (ctxt->nbErrors != 0)
2991 return;
2992
2993 is_nullable = xmlRelaxNGIsNullable(def);
2994
2995 cur = def->content;
2996 while (cur != NULL) {
2997 nbchild++;
2998 cur = cur->next;
2999 }
3000
3001 list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
3002 sizeof(xmlRelaxNGDefinePtr *));
3003 if (list == NULL) {
3004 if (ctxt->error != NULL)
3005 ctxt->error(ctxt->userData,
3006 "Out of memory in choice computation\n");
3007 ctxt->nbErrors++;
3008 return;
3009 }
3010 i = 0;
3011 cur = def->content;
3012 while (cur != NULL) {
3013 list[i] = xmlRelaxNGGetElements(ctxt, cur, 0);
3014 i++;
3015 cur = cur->next;
3016 }
3017
3018 for (i = 0;i < nbchild;i++) {
3019 if (list[i] == NULL)
3020 continue;
3021 for (j = 0;j < i;j++) {
3022 if (list[j] == NULL)
3023 continue;
3024 ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
3025 if (ret == 0) {
3026 is_indeterminist = 1;
3027 }
3028 }
3029 }
3030 for (i = 0;i < nbchild;i++) {
3031 if (list[i] != NULL)
3032 xmlFree(list[i]);
3033 }
3034
3035 xmlFree(list);
3036 if (is_indeterminist) {
3037 def->flags |= IS_INDETERMINIST;
3038 }
3039}
3040
3041/**
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003042 * xmlRelaxNGCheckGroupAttrs:
3043 * @ctxt: a Relax-NG parser context
3044 * @def: the group definition
3045 *
3046 * Detects violations of rule 7.3
3047 */
3048static void
3049xmlRelaxNGCheckGroupAttrs(xmlRelaxNGParserCtxtPtr ctxt,
3050 xmlRelaxNGDefinePtr def) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003051 xmlRelaxNGDefinePtr **list;
3052 xmlRelaxNGDefinePtr cur;
3053 int nbchild = 0, i, j, ret;
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003054
3055 if ((def == NULL) ||
3056 ((def->type != XML_RELAXNG_GROUP) &&
Daniel Veillardfd573f12003-03-16 17:52:32 +00003057 (def->type != XML_RELAXNG_ELEMENT)))
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003058 return;
3059
3060 /*
3061 * Don't run that check in case of error. Infinite recursion
3062 * becomes possible.
3063 */
3064 if (ctxt->nbErrors != 0)
3065 return;
3066
Daniel Veillardfd573f12003-03-16 17:52:32 +00003067 cur = def->attrs;
3068 while (cur != NULL) {
3069 nbchild++;
3070 cur = cur->next;
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003071 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003072 cur = def->content;
3073 while (cur != NULL) {
3074 nbchild++;
3075 cur = cur->next;
3076 }
3077
3078 list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
3079 sizeof(xmlRelaxNGDefinePtr *));
3080 if (list == NULL) {
3081 if (ctxt->error != NULL)
3082 ctxt->error(ctxt->userData,
3083 "Out of memory in group computation\n");
3084 ctxt->nbErrors++;
3085 return;
3086 }
3087 i = 0;
3088 cur = def->attrs;
3089 while (cur != NULL) {
3090 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
3091 i++;
3092 cur = cur->next;
3093 }
3094 cur = def->content;
3095 while (cur != NULL) {
3096 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
3097 i++;
3098 cur = cur->next;
3099 }
3100
3101 for (i = 0;i < nbchild;i++) {
3102 if (list[i] == NULL)
3103 continue;
3104 for (j = 0;j < i;j++) {
3105 if (list[j] == NULL)
3106 continue;
3107 ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
3108 if (ret == 0) {
3109 if (ctxt->error != NULL)
3110 ctxt->error(ctxt->userData,
3111 "Attributes conflicts in group\n");
3112 ctxt->nbErrors++;
3113 }
3114 }
3115 }
3116 for (i = 0;i < nbchild;i++) {
3117 if (list[i] != NULL)
3118 xmlFree(list[i]);
3119 }
3120
3121 xmlFree(list);
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003122}
3123
3124/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00003125 * xmlRelaxNGComputeInterleaves:
3126 * @def: the interleave definition
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003127 * @ctxt: a Relax-NG parser context
Daniel Veillardfd573f12003-03-16 17:52:32 +00003128 * @name: the definition name
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003129 *
Daniel Veillardfd573f12003-03-16 17:52:32 +00003130 * A lot of work for preprocessing interleave definitions
3131 * is potentially needed to get a decent execution speed at runtime
3132 * - trying to get a total order on the element nodes generated
3133 * by the interleaves, order the list of interleave definitions
3134 * following that order.
3135 * - if <text/> is used to handle mixed content, it is better to
3136 * flag this in the define and simplify the runtime checking
3137 * algorithm
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003138 */
3139static void
Daniel Veillardfd573f12003-03-16 17:52:32 +00003140xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
3141 xmlRelaxNGParserCtxtPtr ctxt,
3142 xmlChar *name ATTRIBUTE_UNUSED) {
3143 xmlRelaxNGDefinePtr cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003144
Daniel Veillardfd573f12003-03-16 17:52:32 +00003145 xmlRelaxNGPartitionPtr partitions = NULL;
3146 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
3147 xmlRelaxNGInterleaveGroupPtr group;
3148 int i,j,ret;
3149 int nbgroups = 0;
3150 int nbchild = 0;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003151
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003152 /*
3153 * Don't run that check in case of error. Infinite recursion
3154 * becomes possible.
3155 */
3156 if (ctxt->nbErrors != 0)
3157 return;
3158
Daniel Veillardfd573f12003-03-16 17:52:32 +00003159#ifdef DEBUG_INTERLEAVE
3160 xmlGenericError(xmlGenericErrorContext,
3161 "xmlRelaxNGComputeInterleaves(%s)\n",
3162 name);
3163#endif
3164 cur = def->content;
3165 while (cur != NULL) {
3166 nbchild++;
3167 cur = cur->next;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003168 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003169
3170#ifdef DEBUG_INTERLEAVE
3171 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
3172#endif
3173 groups = (xmlRelaxNGInterleaveGroupPtr *)
3174 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
3175 if (groups == NULL)
3176 goto error;
3177 cur = def->content;
3178 while (cur != NULL) {
3179 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
3180 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
3181 if (groups[nbgroups] == NULL)
3182 goto error;
3183 groups[nbgroups]->rule = cur;
3184 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur, 0);
3185 groups[nbgroups]->attrs = xmlRelaxNGGetElements(ctxt, cur, 1);
3186 nbgroups++;
3187 cur = cur->next;
3188 }
3189#ifdef DEBUG_INTERLEAVE
3190 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
3191#endif
3192
3193 /*
3194 * Let's check that all rules makes a partitions according to 7.4
3195 */
3196 partitions = (xmlRelaxNGPartitionPtr)
3197 xmlMalloc(sizeof(xmlRelaxNGPartition));
3198 if (partitions == NULL)
3199 goto error;
3200 partitions->nbgroups = nbgroups;
3201 for (i = 0;i < nbgroups;i++) {
3202 group = groups[i];
3203 for (j = i+1;j < nbgroups;j++) {
3204 if (groups[j] == NULL)
3205 continue;
3206 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
3207 groups[j]->defs);
3208 if (ret == 0) {
3209 if (ctxt->error != NULL)
3210 ctxt->error(ctxt->userData,
3211 "Element or text conflicts in interleave\n");
3212 ctxt->nbErrors++;
3213 }
3214 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->attrs,
3215 groups[j]->attrs);
3216 if (ret == 0) {
3217 if (ctxt->error != NULL)
3218 ctxt->error(ctxt->userData,
3219 "Attributes conflicts in interleave\n");
3220 ctxt->nbErrors++;
3221 }
3222 }
3223 }
3224 partitions->groups = groups;
3225
3226 /*
3227 * and save the partition list back in the def
3228 */
3229 def->data = partitions;
3230 return;
3231
3232error:
3233 if (ctxt->error != NULL)
3234 ctxt->error(ctxt->userData,
3235 "Out of memory in interleave computation\n");
3236 ctxt->nbErrors++;
3237 if (groups != NULL) {
3238 for (i = 0;i < nbgroups;i++)
3239 if (groups[i] != NULL) {
3240 if (groups[i]->defs != NULL)
3241 xmlFree(groups[i]->defs);
3242 xmlFree(groups[i]);
3243 }
3244 xmlFree(groups);
3245 }
3246 xmlRelaxNGFreePartition(partitions);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003247}
3248
3249/**
3250 * xmlRelaxNGParseInterleave:
3251 * @ctxt: a Relax-NG parser context
3252 * @node: the data node.
3253 *
3254 * parse the content of a RelaxNG interleave node.
3255 *
3256 * Returns the definition pointer or NULL in case of error
3257 */
3258static xmlRelaxNGDefinePtr
3259xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3260 xmlRelaxNGDefinePtr def = NULL;
Daniel Veillardfd573f12003-03-16 17:52:32 +00003261 xmlRelaxNGDefinePtr last = NULL, cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003262 xmlNodePtr child;
3263
Daniel Veillardfd573f12003-03-16 17:52:32 +00003264 def = xmlRelaxNGNewDefine(ctxt, node);
3265 if (def == NULL) {
3266 return(NULL);
3267 }
3268 def->type = XML_RELAXNG_INTERLEAVE;
3269
3270 if (ctxt->interleaves == NULL)
3271 ctxt->interleaves = xmlHashCreate(10);
3272 if (ctxt->interleaves == NULL) {
3273 if (ctxt->error != NULL)
3274 ctxt->error(ctxt->userData,
3275 "Failed to create interleaves hash table\n");
3276 ctxt->nbErrors++;
3277 } else {
3278 char name[32];
3279
3280 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
3281 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
3282 if (ctxt->error != NULL)
3283 ctxt->error(ctxt->userData,
3284 "Failed to add %s to hash table\n", name);
3285 ctxt->nbErrors++;
3286 }
3287 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003288 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00003289 if (child == NULL) {
3290 if (ctxt->error != NULL)
3291 ctxt->error(ctxt->userData, "Element interleave is empty\n");
3292 ctxt->nbErrors++;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00003293 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003294 while (child != NULL) {
3295 if (IS_RELAXNG(child, "element")) {
3296 cur = xmlRelaxNGParseElement(ctxt, child);
3297 } else {
3298 cur = xmlRelaxNGParsePattern(ctxt, child);
3299 }
3300 if (cur != NULL) {
3301 cur->parent = def;
3302 if (last == NULL) {
3303 def->content = last = cur;
3304 } else {
3305 last->next = cur;
3306 last = cur;
3307 }
3308 }
3309 child = child->next;
3310 }
3311
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003312 return(def);
3313}
Daniel Veillard6eadf632003-01-23 18:29:16 +00003314
3315/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003316 * xmlRelaxNGParseInclude:
3317 * @ctxt: a Relax-NG parser context
3318 * @node: the include node
3319 *
3320 * Integrate the content of an include node in the current grammar
3321 *
3322 * Returns 0 in case of success or -1 in case of error
3323 */
3324static int
3325xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3326 xmlRelaxNGIncludePtr incl;
3327 xmlNodePtr root;
3328 int ret = 0, tmp;
3329
3330 incl = node->_private;
3331 if (incl == NULL) {
3332 if (ctxt->error != NULL)
3333 ctxt->error(ctxt->userData,
3334 "Include node has no data\n");
3335 ctxt->nbErrors++;
3336 return(-1);
3337 }
3338 root = xmlDocGetRootElement(incl->doc);
3339 if (root == NULL) {
3340 if (ctxt->error != NULL)
3341 ctxt->error(ctxt->userData,
3342 "Include document is empty\n");
3343 ctxt->nbErrors++;
3344 return(-1);
3345 }
3346 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
3347 if (ctxt->error != NULL)
3348 ctxt->error(ctxt->userData,
3349 "Include document root is not a grammar\n");
3350 ctxt->nbErrors++;
3351 return(-1);
3352 }
3353
3354 /*
3355 * Merge the definition from both the include and the internal list
3356 */
3357 if (root->children != NULL) {
3358 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
3359 if (tmp != 0)
3360 ret = -1;
3361 }
3362 if (node->children != NULL) {
3363 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
3364 if (tmp != 0)
3365 ret = -1;
3366 }
3367 return(ret);
3368}
3369
3370/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00003371 * xmlRelaxNGParseDefine:
3372 * @ctxt: a Relax-NG parser context
3373 * @node: the define node
3374 *
3375 * parse the content of a RelaxNG define element node.
3376 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003377 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00003378 */
3379static int
3380xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3381 xmlChar *name;
3382 int ret = 0, tmp;
3383 xmlRelaxNGDefinePtr def;
3384 const xmlChar *olddefine;
3385
3386 name = xmlGetProp(node, BAD_CAST "name");
3387 if (name == NULL) {
3388 if (ctxt->error != NULL)
3389 ctxt->error(ctxt->userData,
3390 "define has no name\n");
3391 ctxt->nbErrors++;
3392 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003393 xmlRelaxNGNormExtSpace(name);
3394 if (xmlValidateNCName(name, 0)) {
3395 if (ctxt->error != NULL)
3396 ctxt->error(ctxt->userData,
3397 "define name '%s' is not an NCName\n",
3398 name);
3399 ctxt->nbErrors++;
3400 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003401 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00003402 if (def == NULL) {
3403 xmlFree(name);
3404 return(-1);
3405 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003406 def->type = XML_RELAXNG_DEF;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003407 def->name = name;
3408 if (node->children == NULL) {
3409 if (ctxt->error != NULL)
3410 ctxt->error(ctxt->userData,
3411 "define has no children\n");
3412 ctxt->nbErrors++;
3413 } else {
3414 olddefine = ctxt->define;
3415 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00003416 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00003417 ctxt->define = olddefine;
3418 }
3419 if (ctxt->grammar->defs == NULL)
3420 ctxt->grammar->defs = xmlHashCreate(10);
3421 if (ctxt->grammar->defs == NULL) {
3422 if (ctxt->error != NULL)
3423 ctxt->error(ctxt->userData,
3424 "Could not create definition hash\n");
3425 ctxt->nbErrors++;
3426 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003427 } else {
3428 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
3429 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00003430 xmlRelaxNGDefinePtr prev;
3431
3432 prev = xmlHashLookup(ctxt->grammar->defs, name);
3433 if (prev == NULL) {
3434 if (ctxt->error != NULL)
3435 ctxt->error(ctxt->userData,
3436 "Internal error on define aggregation of %s\n",
3437 name);
3438 ctxt->nbErrors++;
3439 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00003440 } else {
3441 while (prev->nextHash != NULL)
3442 prev = prev->nextHash;
3443 prev->nextHash = def;
3444 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003445 }
3446 }
3447 }
3448 return(ret);
3449}
3450
3451/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00003452 * xmlRelaxNGProcessExternalRef:
3453 * @ctxt: the parser context
3454 * @node: the externlRef node
3455 *
3456 * Process and compile an externlRef node
3457 *
3458 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
3459 */
3460static xmlRelaxNGDefinePtr
3461xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3462 xmlRelaxNGDocumentPtr docu;
3463 xmlNodePtr root, tmp;
3464 xmlChar *ns;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003465 int newNs = 0, oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003466 xmlRelaxNGDefinePtr def;
3467
3468 docu = node->_private;
3469 if (docu != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003470 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillardfebcca42003-02-16 15:44:18 +00003471 if (def == NULL)
3472 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003473 def->type = XML_RELAXNG_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003474
3475 if (docu->content == NULL) {
3476 /*
3477 * Then do the parsing for good
3478 */
3479 root = xmlDocGetRootElement(docu->doc);
3480 if (root == NULL) {
3481 if (ctxt->error != NULL)
3482 ctxt->error(ctxt->userData,
3483 "xmlRelaxNGParse: %s is empty\n",
3484 ctxt->URL);
3485 ctxt->nbErrors++;
3486 return (NULL);
3487 }
3488 /*
3489 * ns transmission rules
3490 */
3491 ns = xmlGetProp(root, BAD_CAST "ns");
3492 if (ns == NULL) {
3493 tmp = node;
3494 while ((tmp != NULL) &&
3495 (tmp->type == XML_ELEMENT_NODE)) {
3496 ns = xmlGetProp(tmp, BAD_CAST "ns");
3497 if (ns != NULL) {
3498 break;
3499 }
3500 tmp = tmp->parent;
3501 }
3502 if (ns != NULL) {
3503 xmlSetProp(root, BAD_CAST "ns", ns);
3504 newNs = 1;
3505 xmlFree(ns);
3506 }
3507 } else {
3508 xmlFree(ns);
3509 }
3510
3511 /*
3512 * Parsing to get a precompiled schemas.
3513 */
Daniel Veillard77648bb2003-02-20 15:03:22 +00003514 oldflags = ctxt->flags;
3515 ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003516 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard77648bb2003-02-20 15:03:22 +00003517 ctxt->flags = oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003518 if ((docu->schema != NULL) &&
3519 (docu->schema->topgrammar != NULL)) {
3520 docu->content = docu->schema->topgrammar->start;
3521 }
3522
3523 /*
3524 * the externalRef may be reused in a different ns context
3525 */
3526 if (newNs == 1) {
3527 xmlUnsetProp(root, BAD_CAST "ns");
3528 }
3529 }
3530 def->content = docu->content;
3531 } else {
3532 def = NULL;
3533 }
3534 return(def);
3535}
3536
3537/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003538 * xmlRelaxNGParsePattern:
3539 * @ctxt: a Relax-NG parser context
3540 * @node: the pattern node.
3541 *
3542 * parse the content of a RelaxNG pattern node.
3543 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00003544 * Returns the definition pointer or NULL in case of error or if no
3545 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003546 */
3547static xmlRelaxNGDefinePtr
3548xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3549 xmlRelaxNGDefinePtr def = NULL;
3550
Daniel Veillardd2298792003-02-14 16:54:11 +00003551 if (node == NULL) {
3552 return(NULL);
3553 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003554 if (IS_RELAXNG(node, "element")) {
3555 def = xmlRelaxNGParseElement(ctxt, node);
3556 } else if (IS_RELAXNG(node, "attribute")) {
3557 def = xmlRelaxNGParseAttribute(ctxt, node);
3558 } else if (IS_RELAXNG(node, "empty")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003559 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003560 if (def == NULL)
3561 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003562 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00003563 if (node->children != NULL) {
3564 if (ctxt->error != NULL)
3565 ctxt->error(ctxt->userData, "empty: had a child node\n");
3566 ctxt->nbErrors++;
3567 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003568 } else if (IS_RELAXNG(node, "text")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003569 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003570 if (def == NULL)
3571 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003572 def->type = XML_RELAXNG_TEXT;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003573 if (node->children != NULL) {
3574 if (ctxt->error != NULL)
3575 ctxt->error(ctxt->userData, "text: had a child node\n");
3576 ctxt->nbErrors++;
3577 }
3578 } else if (IS_RELAXNG(node, "zeroOrMore")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003579 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003580 if (def == NULL)
3581 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003582 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00003583 if (node->children == NULL) {
3584 if (ctxt->error != NULL)
3585 ctxt->error(ctxt->userData,
3586 "Element %s is empty\n", node->name);
3587 ctxt->nbErrors++;
3588 } else {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003589 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillardd2298792003-02-14 16:54:11 +00003590 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003591 } else if (IS_RELAXNG(node, "oneOrMore")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003592 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003593 if (def == NULL)
3594 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003595 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00003596 if (node->children == NULL) {
3597 if (ctxt->error != NULL)
3598 ctxt->error(ctxt->userData,
3599 "Element %s is empty\n", node->name);
3600 ctxt->nbErrors++;
3601 } else {
3602 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
3603 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003604 } else if (IS_RELAXNG(node, "optional")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003605 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003606 if (def == NULL)
3607 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003608 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00003609 if (node->children == NULL) {
3610 if (ctxt->error != NULL)
3611 ctxt->error(ctxt->userData,
3612 "Element %s is empty\n", node->name);
3613 ctxt->nbErrors++;
3614 } else {
3615 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
3616 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003617 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003618 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003619 if (def == NULL)
3620 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003621 def->type = XML_RELAXNG_CHOICE;
3622 if (node->children == NULL) {
3623 if (ctxt->error != NULL)
3624 ctxt->error(ctxt->userData,
3625 "Element %s is empty\n", node->name);
3626 ctxt->nbErrors++;
3627 } else {
3628 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3629 }
3630 } else if (IS_RELAXNG(node, "group")) {
3631 def = xmlRelaxNGNewDefine(ctxt, node);
3632 if (def == NULL)
3633 return(NULL);
3634 def->type = XML_RELAXNG_GROUP;
3635 if (node->children == NULL) {
3636 if (ctxt->error != NULL)
3637 ctxt->error(ctxt->userData,
3638 "Element %s is empty\n", node->name);
3639 ctxt->nbErrors++;
3640 } else {
3641 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3642 }
3643 } else if (IS_RELAXNG(node, "ref")) {
3644 def = xmlRelaxNGNewDefine(ctxt, node);
3645 if (def == NULL)
3646 return(NULL);
3647 def->type = XML_RELAXNG_REF;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003648 def->name = xmlGetProp(node, BAD_CAST "name");
3649 if (def->name == NULL) {
3650 if (ctxt->error != NULL)
3651 ctxt->error(ctxt->userData,
3652 "ref has no name\n");
3653 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003654 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003655 xmlRelaxNGNormExtSpace(def->name);
3656 if (xmlValidateNCName(def->name, 0)) {
3657 if (ctxt->error != NULL)
3658 ctxt->error(ctxt->userData,
3659 "ref name '%s' is not an NCName\n",
3660 def->name);
3661 ctxt->nbErrors++;
3662 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003663 }
3664 if (node->children != NULL) {
3665 if (ctxt->error != NULL)
3666 ctxt->error(ctxt->userData,
3667 "ref is not empty\n");
3668 ctxt->nbErrors++;
3669 }
3670 if (ctxt->grammar->refs == NULL)
3671 ctxt->grammar->refs = xmlHashCreate(10);
3672 if (ctxt->grammar->refs == NULL) {
3673 if (ctxt->error != NULL)
3674 ctxt->error(ctxt->userData,
3675 "Could not create references hash\n");
3676 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003677 def = NULL;
3678 } else {
3679 int tmp;
3680
3681 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
3682 if (tmp < 0) {
3683 xmlRelaxNGDefinePtr prev;
3684
3685 prev = (xmlRelaxNGDefinePtr)
3686 xmlHashLookup(ctxt->grammar->refs, def->name);
3687 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00003688 if (def->name != NULL) {
3689 if (ctxt->error != NULL)
3690 ctxt->error(ctxt->userData,
3691 "Error refs definitions '%s'\n",
3692 def->name);
3693 } else {
3694 if (ctxt->error != NULL)
3695 ctxt->error(ctxt->userData,
3696 "Error refs definitions\n");
3697 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003698 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003699 def = NULL;
3700 } else {
3701 def->nextHash = prev->nextHash;
3702 prev->nextHash = def;
3703 }
3704 }
3705 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00003706 } else if (IS_RELAXNG(node, "data")) {
3707 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardedc91922003-01-26 00:52:04 +00003708 } else if (IS_RELAXNG(node, "value")) {
3709 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003710 } else if (IS_RELAXNG(node, "list")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003711 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003712 if (def == NULL)
3713 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003714 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00003715 if (node->children == NULL) {
3716 if (ctxt->error != NULL)
3717 ctxt->error(ctxt->userData,
3718 "Element %s is empty\n", node->name);
3719 ctxt->nbErrors++;
3720 } else {
3721 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3722 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003723 } else if (IS_RELAXNG(node, "interleave")) {
3724 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003725 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00003726 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003727 } else if (IS_RELAXNG(node, "notAllowed")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003728 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003729 if (def == NULL)
3730 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003731 def->type = XML_RELAXNG_NOT_ALLOWED;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003732 if (node->children != NULL) {
3733 if (ctxt->error != NULL)
3734 ctxt->error(ctxt->userData,
3735 "xmlRelaxNGParse: notAllowed element is not empty\n");
3736 ctxt->nbErrors++;
3737 }
Daniel Veillard419a7682003-02-03 23:22:49 +00003738 } else if (IS_RELAXNG(node, "grammar")) {
3739 xmlRelaxNGGrammarPtr grammar, old;
3740 xmlRelaxNGGrammarPtr oldparent;
3741
Daniel Veillardc482e262003-02-26 14:48:48 +00003742#ifdef DEBUG_GRAMMAR
3743 xmlGenericError(xmlGenericErrorContext, "Found <grammar> pattern\n");
3744#endif
3745
Daniel Veillard419a7682003-02-03 23:22:49 +00003746 oldparent = ctxt->parentgrammar;
3747 old = ctxt->grammar;
3748 ctxt->parentgrammar = old;
3749 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3750 if (old != NULL) {
3751 ctxt->grammar = old;
3752 ctxt->parentgrammar = oldparent;
Daniel Veillardc482e262003-02-26 14:48:48 +00003753#if 0
Daniel Veillard419a7682003-02-03 23:22:49 +00003754 if (grammar != NULL) {
3755 grammar->next = old->next;
3756 old->next = grammar;
3757 }
Daniel Veillardc482e262003-02-26 14:48:48 +00003758#endif
Daniel Veillard419a7682003-02-03 23:22:49 +00003759 }
3760 if (grammar != NULL)
3761 def = grammar->start;
3762 else
3763 def = NULL;
3764 } else if (IS_RELAXNG(node, "parentRef")) {
3765 if (ctxt->parentgrammar == NULL) {
3766 if (ctxt->error != NULL)
3767 ctxt->error(ctxt->userData,
3768 "Use of parentRef without a parent grammar\n");
3769 ctxt->nbErrors++;
3770 return(NULL);
3771 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003772 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard419a7682003-02-03 23:22:49 +00003773 if (def == NULL)
3774 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003775 def->type = XML_RELAXNG_PARENTREF;
Daniel Veillard419a7682003-02-03 23:22:49 +00003776 def->name = xmlGetProp(node, BAD_CAST "name");
3777 if (def->name == NULL) {
3778 if (ctxt->error != NULL)
3779 ctxt->error(ctxt->userData,
3780 "parentRef has no name\n");
3781 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00003782 } else {
3783 xmlRelaxNGNormExtSpace(def->name);
3784 if (xmlValidateNCName(def->name, 0)) {
3785 if (ctxt->error != NULL)
3786 ctxt->error(ctxt->userData,
3787 "parentRef name '%s' is not an NCName\n",
3788 def->name);
3789 ctxt->nbErrors++;
3790 }
Daniel Veillard419a7682003-02-03 23:22:49 +00003791 }
3792 if (node->children != NULL) {
3793 if (ctxt->error != NULL)
3794 ctxt->error(ctxt->userData,
3795 "parentRef is not empty\n");
3796 ctxt->nbErrors++;
3797 }
3798 if (ctxt->parentgrammar->refs == NULL)
3799 ctxt->parentgrammar->refs = xmlHashCreate(10);
3800 if (ctxt->parentgrammar->refs == NULL) {
3801 if (ctxt->error != NULL)
3802 ctxt->error(ctxt->userData,
3803 "Could not create references hash\n");
3804 ctxt->nbErrors++;
3805 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00003806 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00003807 int tmp;
3808
3809 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
3810 if (tmp < 0) {
3811 xmlRelaxNGDefinePtr prev;
3812
3813 prev = (xmlRelaxNGDefinePtr)
3814 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
3815 if (prev == NULL) {
3816 if (ctxt->error != NULL)
3817 ctxt->error(ctxt->userData,
3818 "Internal error parentRef definitions '%s'\n",
3819 def->name);
3820 ctxt->nbErrors++;
3821 def = NULL;
3822 } else {
3823 def->nextHash = prev->nextHash;
3824 prev->nextHash = def;
3825 }
3826 }
3827 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003828 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003829 if (node->children == NULL) {
3830 if (ctxt->error != NULL)
3831 ctxt->error(ctxt->userData,
3832 "Mixed is empty\n");
3833 ctxt->nbErrors++;
3834 def = NULL;
3835 } else {
3836 def = xmlRelaxNGParseInterleave(ctxt, node);
3837 if (def != NULL) {
3838 xmlRelaxNGDefinePtr tmp;
3839
3840 if ((def->content != NULL) && (def->content->next != NULL)) {
3841 tmp = xmlRelaxNGNewDefine(ctxt, node);
3842 if (tmp != NULL) {
3843 tmp->type = XML_RELAXNG_GROUP;
3844 tmp->content = def->content;
3845 def->content = tmp;
3846 }
3847 }
3848
3849 tmp = xmlRelaxNGNewDefine(ctxt, node);
3850 if (tmp == NULL)
3851 return(def);
3852 tmp->type = XML_RELAXNG_TEXT;
3853 tmp->next = def->content;
3854 def->content = tmp;
3855 }
3856 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003857 } else {
3858 if (ctxt->error != NULL)
3859 ctxt->error(ctxt->userData,
3860 "Unexpected node %s is not a pattern\n",
3861 node->name);
3862 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003863 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003864 }
3865 return(def);
3866}
3867
3868/**
3869 * xmlRelaxNGParseAttribute:
3870 * @ctxt: a Relax-NG parser context
3871 * @node: the element node
3872 *
3873 * parse the content of a RelaxNG attribute node.
3874 *
3875 * Returns the definition pointer or NULL in case of error.
3876 */
3877static xmlRelaxNGDefinePtr
3878xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00003879 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003880 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003881 int old_flags;
3882
Daniel Veillardfd573f12003-03-16 17:52:32 +00003883 ret = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003884 if (ret == NULL)
3885 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003886 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003887 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003888 child = node->children;
3889 if (child == NULL) {
3890 if (ctxt->error != NULL)
3891 ctxt->error(ctxt->userData,
3892 "xmlRelaxNGParseattribute: attribute has no children\n");
3893 ctxt->nbErrors++;
3894 return(ret);
3895 }
3896 old_flags = ctxt->flags;
3897 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003898 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3899 if (cur != NULL)
3900 child = child->next;
3901
Daniel Veillardd2298792003-02-14 16:54:11 +00003902 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003903 cur = xmlRelaxNGParsePattern(ctxt, child);
3904 if (cur != NULL) {
3905 switch (cur->type) {
3906 case XML_RELAXNG_EMPTY:
3907 case XML_RELAXNG_NOT_ALLOWED:
3908 case XML_RELAXNG_TEXT:
3909 case XML_RELAXNG_ELEMENT:
3910 case XML_RELAXNG_DATATYPE:
3911 case XML_RELAXNG_VALUE:
3912 case XML_RELAXNG_LIST:
3913 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003914 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003915 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003916 case XML_RELAXNG_DEF:
3917 case XML_RELAXNG_ONEORMORE:
Daniel Veillardfd573f12003-03-16 17:52:32 +00003918 case XML_RELAXNG_ZEROORMORE:
3919 case XML_RELAXNG_OPTIONAL:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003920 case XML_RELAXNG_CHOICE:
3921 case XML_RELAXNG_GROUP:
3922 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard77648bb2003-02-20 15:03:22 +00003923 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00003924 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003925 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003926 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003927 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003928 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003929 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00003930 if (ctxt->error != NULL)
3931 ctxt->error(ctxt->userData,
3932 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003933 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003934 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003935 case XML_RELAXNG_NOOP:
3936 TODO
3937 if (ctxt->error != NULL)
3938 ctxt->error(ctxt->userData,
3939 "Internal error, noop found\n");
3940 ctxt->nbErrors++;
3941 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003942 }
3943 }
3944 child = child->next;
3945 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003946 if (child != NULL) {
3947 if (ctxt->error != NULL)
3948 ctxt->error(ctxt->userData, "attribute has multiple children\n");
3949 ctxt->nbErrors++;
3950 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003951 ctxt->flags = old_flags;
3952 return(ret);
3953}
3954
3955/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003956 * xmlRelaxNGParseExceptNameClass:
3957 * @ctxt: a Relax-NG parser context
3958 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00003959 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003960 *
3961 * parse the content of a RelaxNG nameClass node.
3962 *
3963 * Returns the definition pointer or NULL in case of error.
3964 */
3965static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00003966xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
3967 xmlNodePtr node, int attr) {
3968 xmlRelaxNGDefinePtr ret, cur, last = NULL;
3969 xmlNodePtr child;
3970
Daniel Veillardd2298792003-02-14 16:54:11 +00003971 if (!IS_RELAXNG(node, "except")) {
3972 if (ctxt->error != NULL)
3973 ctxt->error(ctxt->userData,
3974 "Expecting an except node\n");
3975 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00003976 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00003977 }
3978 if (node->next != NULL) {
3979 if (ctxt->error != NULL)
3980 ctxt->error(ctxt->userData,
3981 "exceptNameClass allows only a single except node\n");
3982 ctxt->nbErrors++;
3983 }
Daniel Veillard144fae12003-02-03 13:17:57 +00003984 if (node->children == NULL) {
3985 if (ctxt->error != NULL)
3986 ctxt->error(ctxt->userData,
3987 "except has no content\n");
3988 ctxt->nbErrors++;
3989 return(NULL);
3990 }
3991
Daniel Veillardfd573f12003-03-16 17:52:32 +00003992 ret = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard144fae12003-02-03 13:17:57 +00003993 if (ret == NULL)
3994 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003995 ret->type = XML_RELAXNG_EXCEPT;
Daniel Veillard144fae12003-02-03 13:17:57 +00003996 child = node->children;
3997 while (child != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003998 cur = xmlRelaxNGNewDefine(ctxt, child);
Daniel Veillard144fae12003-02-03 13:17:57 +00003999 if (cur == NULL)
4000 break;
4001 if (attr)
4002 cur->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004003 else
4004 cur->type = XML_RELAXNG_ELEMENT;
Daniel Veillard144fae12003-02-03 13:17:57 +00004005
Daniel Veillard419a7682003-02-03 23:22:49 +00004006 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00004007 if (last == NULL) {
4008 ret->content = cur;
4009 } else {
4010 last->next = cur;
4011 }
4012 last = cur;
4013 }
4014 child = child->next;
4015 }
4016
4017 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004018}
4019
4020/**
4021 * xmlRelaxNGParseNameClass:
4022 * @ctxt: a Relax-NG parser context
4023 * @node: the nameClass node
4024 * @def: the current definition
4025 *
4026 * parse the content of a RelaxNG nameClass node.
4027 *
4028 * Returns the definition pointer or NULL in case of error.
4029 */
4030static xmlRelaxNGDefinePtr
4031xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
4032 xmlRelaxNGDefinePtr def) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004033 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004034 xmlChar *val;
4035
Daniel Veillard2e9b1652003-02-19 13:29:45 +00004036 ret = def;
4037 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
4038 (IS_RELAXNG(node, "nsName"))) {
4039 if ((def->type != XML_RELAXNG_ELEMENT) &&
4040 (def->type != XML_RELAXNG_ATTRIBUTE)) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004041 ret = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard2e9b1652003-02-19 13:29:45 +00004042 if (ret == NULL)
4043 return(NULL);
4044 ret->parent = def;
4045 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
4046 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004047 else
4048 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard2e9b1652003-02-19 13:29:45 +00004049 }
4050 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004051 if (IS_RELAXNG(node, "name")) {
4052 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00004053 xmlRelaxNGNormExtSpace(val);
4054 if (xmlValidateNCName(val, 0)) {
4055 if (ctxt->error != NULL) {
4056 if (node->parent != NULL)
4057 ctxt->error(ctxt->userData,
4058 "Element %s name '%s' is not an NCName\n",
4059 node->parent->name, val);
4060 else
4061 ctxt->error(ctxt->userData,
4062 "name '%s' is not an NCName\n",
4063 val);
4064 }
4065 ctxt->nbErrors++;
4066 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004067 ret->name = val;
4068 val = xmlGetProp(node, BAD_CAST "ns");
4069 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00004070 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
4071 (val != NULL) &&
4072 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
4073 ctxt->error(ctxt->userData,
4074 "Attribute with namespace '%s' is not allowed\n",
4075 val);
4076 ctxt->nbErrors++;
4077 }
4078 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
4079 (val != NULL) &&
4080 (val[0] == 0) &&
4081 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
4082 ctxt->error(ctxt->userData,
4083 "Attribute with QName 'xmlns' is not allowed\n",
4084 val);
4085 ctxt->nbErrors++;
4086 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004087 } else if (IS_RELAXNG(node, "anyName")) {
4088 ret->name = NULL;
4089 ret->ns = NULL;
4090 if (node->children != NULL) {
4091 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00004092 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
4093 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004094 }
4095 } else if (IS_RELAXNG(node, "nsName")) {
4096 ret->name = NULL;
4097 ret->ns = xmlGetProp(node, BAD_CAST "ns");
4098 if (ret->ns == NULL) {
4099 if (ctxt->error != NULL)
4100 ctxt->error(ctxt->userData,
4101 "nsName has no ns attribute\n");
4102 ctxt->nbErrors++;
4103 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004104 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
4105 (ret->ns != NULL) &&
4106 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
4107 ctxt->error(ctxt->userData,
4108 "Attribute with namespace '%s' is not allowed\n",
4109 ret->ns);
4110 ctxt->nbErrors++;
4111 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004112 if (node->children != NULL) {
4113 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00004114 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
4115 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004116 }
4117 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004118 xmlNodePtr child;
4119 xmlRelaxNGDefinePtr last = NULL;
4120
4121 ret = xmlRelaxNGNewDefine(ctxt, node);
4122 if (ret == NULL)
4123 return(NULL);
4124 ret->parent = def;
4125 ret->type = XML_RELAXNG_CHOICE;
4126
Daniel Veillardd2298792003-02-14 16:54:11 +00004127 if (node->children == NULL) {
4128 if (ctxt->error != NULL)
4129 ctxt->error(ctxt->userData,
4130 "Element choice is empty\n");
4131 ctxt->nbErrors++;
4132 } else {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004133
4134 child = node->children;
4135 while (child != NULL) {
4136 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
4137 if (tmp != NULL) {
4138 if (last == NULL) {
4139 last = ret->nameClass = tmp;
4140 } else {
4141 last->next = tmp;
4142 last = tmp;
4143 }
4144 }
4145 child = child->next;
4146 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004147 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004148 } else {
4149 if (ctxt->error != NULL)
4150 ctxt->error(ctxt->userData,
Daniel Veillardfd573f12003-03-16 17:52:32 +00004151 "expecting name, anyName, nsName or choice : got %s\n",
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004152 node->name);
4153 ctxt->nbErrors++;
4154 return(NULL);
4155 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00004156 if (ret != def) {
4157 if (def->nameClass == NULL) {
4158 def->nameClass = ret;
4159 } else {
4160 tmp = def->nameClass;
4161 while (tmp->next != NULL) {
4162 tmp = tmp->next;
4163 }
4164 tmp->next = ret;
4165 }
4166 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004167 return(ret);
4168}
4169
4170/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004171 * xmlRelaxNGParseElement:
4172 * @ctxt: a Relax-NG parser context
4173 * @node: the element node
4174 *
4175 * parse the content of a RelaxNG element node.
4176 *
4177 * Returns the definition pointer or NULL in case of error.
4178 */
4179static xmlRelaxNGDefinePtr
4180xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4181 xmlRelaxNGDefinePtr ret, cur, last;
4182 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004183 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004184
Daniel Veillardfd573f12003-03-16 17:52:32 +00004185 ret = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004186 if (ret == NULL)
4187 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00004188 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004189 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004190 child = node->children;
4191 if (child == NULL) {
4192 if (ctxt->error != NULL)
4193 ctxt->error(ctxt->userData,
4194 "xmlRelaxNGParseElement: element has no children\n");
4195 ctxt->nbErrors++;
4196 return(ret);
4197 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004198 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
4199 if (cur != NULL)
4200 child = child->next;
4201
Daniel Veillard6eadf632003-01-23 18:29:16 +00004202 if (child == NULL) {
4203 if (ctxt->error != NULL)
4204 ctxt->error(ctxt->userData,
4205 "xmlRelaxNGParseElement: element has no content\n");
4206 ctxt->nbErrors++;
4207 return(ret);
4208 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004209 olddefine = ctxt->define;
4210 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004211 last = NULL;
4212 while (child != NULL) {
4213 cur = xmlRelaxNGParsePattern(ctxt, child);
4214 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004215 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004216 switch (cur->type) {
4217 case XML_RELAXNG_EMPTY:
4218 case XML_RELAXNG_NOT_ALLOWED:
4219 case XML_RELAXNG_TEXT:
4220 case XML_RELAXNG_ELEMENT:
4221 case XML_RELAXNG_DATATYPE:
4222 case XML_RELAXNG_VALUE:
4223 case XML_RELAXNG_LIST:
4224 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00004225 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004226 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004227 case XML_RELAXNG_DEF:
Daniel Veillardfd573f12003-03-16 17:52:32 +00004228 case XML_RELAXNG_ZEROORMORE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004229 case XML_RELAXNG_ONEORMORE:
Daniel Veillardfd573f12003-03-16 17:52:32 +00004230 case XML_RELAXNG_OPTIONAL:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004231 case XML_RELAXNG_CHOICE:
4232 case XML_RELAXNG_GROUP:
4233 case XML_RELAXNG_INTERLEAVE:
4234 if (last == NULL) {
4235 ret->content = last = cur;
4236 } else {
4237 if ((last->type == XML_RELAXNG_ELEMENT) &&
4238 (ret->content == last)) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004239 ret->content = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004240 if (ret->content != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004241 ret->content->type = XML_RELAXNG_GROUP;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004242 ret->content->content = last;
4243 } else {
4244 ret->content = last;
4245 }
4246 }
4247 last->next = cur;
4248 last = cur;
4249 }
4250 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004251 case XML_RELAXNG_ATTRIBUTE:
4252 cur->next = ret->attrs;
4253 ret->attrs = cur;
4254 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004255 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00004256 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00004257 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004258 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004259 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004260 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004261 case XML_RELAXNG_NOOP:
4262 TODO
4263 if (ctxt->error != NULL)
4264 ctxt->error(ctxt->userData,
4265 "Internal error, noop found\n");
4266 ctxt->nbErrors++;
4267 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004268 }
4269 }
4270 child = child->next;
4271 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004272 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004273 return(ret);
4274}
4275
4276/**
4277 * xmlRelaxNGParsePatterns:
4278 * @ctxt: a Relax-NG parser context
4279 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00004280 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00004281 *
4282 * parse the content of a RelaxNG start node.
4283 *
4284 * Returns the definition pointer or NULL in case of error.
4285 */
4286static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00004287xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
4288 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004289 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004290
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004291 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004292 while (nodes != NULL) {
4293 if (IS_RELAXNG(nodes, "element")) {
4294 cur = xmlRelaxNGParseElement(ctxt, nodes);
4295 if (def == NULL) {
4296 def = last = cur;
4297 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00004298 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
4299 (def == last)) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004300 def = xmlRelaxNGNewDefine(ctxt, nodes);
4301 def->type = XML_RELAXNG_GROUP;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004302 def->content = last;
4303 }
4304 last->next = cur;
4305 last = cur;
4306 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004307 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004308 } else {
4309 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00004310 if (cur != NULL) {
4311 if (def == NULL) {
4312 def = last = cur;
4313 } else {
4314 last->next = cur;
4315 last = cur;
4316 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004317 }
4318 }
4319 nodes = nodes->next;
4320 }
4321 return(def);
4322}
4323
4324/**
4325 * xmlRelaxNGParseStart:
4326 * @ctxt: a Relax-NG parser context
4327 * @nodes: start children nodes
4328 *
4329 * parse the content of a RelaxNG start node.
4330 *
4331 * Returns 0 in case of success, -1 in case of error
4332 */
4333static int
4334xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
4335 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004336 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004337
Daniel Veillardd2298792003-02-14 16:54:11 +00004338 if (nodes == NULL) {
4339 if (ctxt->error != NULL)
4340 ctxt->error(ctxt->userData,
4341 "start has no children\n");
4342 ctxt->nbErrors++;
4343 return(-1);
4344 }
4345 if (IS_RELAXNG(nodes, "empty")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004346 def = xmlRelaxNGNewDefine(ctxt, nodes);
Daniel Veillardd2298792003-02-14 16:54:11 +00004347 if (def == NULL)
4348 return(-1);
Daniel Veillardfd573f12003-03-16 17:52:32 +00004349 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00004350 if (nodes->children != NULL) {
4351 if (ctxt->error != NULL)
4352 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004353 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004354 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004355 } else if (IS_RELAXNG(nodes, "notAllowed")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004356 def = xmlRelaxNGNewDefine(ctxt, nodes);
Daniel Veillardd2298792003-02-14 16:54:11 +00004357 if (def == NULL)
4358 return(-1);
Daniel Veillardfd573f12003-03-16 17:52:32 +00004359 def->type = XML_RELAXNG_NOT_ALLOWED;
Daniel Veillardd2298792003-02-14 16:54:11 +00004360 if (nodes->children != NULL) {
4361 if (ctxt->error != NULL)
4362 ctxt->error(ctxt->userData,
4363 "element notAllowed is not empty\n");
4364 ctxt->nbErrors++;
4365 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004366 } else {
4367 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00004368 }
4369 if (ctxt->grammar->start != NULL) {
4370 last = ctxt->grammar->start;
4371 while (last->next != NULL)
4372 last = last->next;
4373 last->next = def;
4374 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004375 ctxt->grammar->start = def;
4376 }
4377 nodes = nodes->next;
4378 if (nodes != NULL) {
4379 if (ctxt->error != NULL)
4380 ctxt->error(ctxt->userData,
4381 "start more than one children\n");
4382 ctxt->nbErrors++;
4383 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004384 }
4385 return(ret);
4386}
4387
4388/**
4389 * xmlRelaxNGParseGrammarContent:
4390 * @ctxt: a Relax-NG parser context
4391 * @nodes: grammar children nodes
4392 *
4393 * parse the content of a RelaxNG grammar node.
4394 *
4395 * Returns 0 in case of success, -1 in case of error
4396 */
4397static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004398xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00004399{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004400 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004401
4402 if (nodes == NULL) {
4403 if (ctxt->error != NULL)
4404 ctxt->error(ctxt->userData,
4405 "grammar has no children\n");
4406 ctxt->nbErrors++;
4407 return(-1);
4408 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004409 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004410 if (IS_RELAXNG(nodes, "start")) {
4411 if (nodes->children == NULL) {
4412 if (ctxt->error != NULL)
4413 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00004414 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004415 ctxt->nbErrors++;
4416 } else {
4417 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
4418 if (tmp != 0)
4419 ret = -1;
4420 }
4421 } else if (IS_RELAXNG(nodes, "define")) {
4422 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
4423 if (tmp != 0)
4424 ret = -1;
4425 } else if (IS_RELAXNG(nodes, "include")) {
4426 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
4427 if (tmp != 0)
4428 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004429 } else {
4430 if (ctxt->error != NULL)
4431 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004432 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004433 ctxt->nbErrors++;
4434 ret = -1;
4435 }
4436 nodes = nodes->next;
4437 }
4438 return (ret);
4439}
4440
4441/**
4442 * xmlRelaxNGCheckReference:
4443 * @ref: the ref
4444 * @ctxt: a Relax-NG parser context
4445 * @name: the name associated to the defines
4446 *
4447 * Applies the 4.17. combine attribute rule for all the define
4448 * element of a given grammar using the same name.
4449 */
4450static void
4451xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
4452 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
4453 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004454 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004455
4456 grammar = ctxt->grammar;
4457 if (grammar == NULL) {
4458 if (ctxt->error != NULL)
4459 ctxt->error(ctxt->userData,
4460 "Internal error: no grammar in CheckReference %s\n",
4461 name);
4462 ctxt->nbErrors++;
4463 return;
4464 }
4465 if (ref->content != NULL) {
4466 if (ctxt->error != NULL)
4467 ctxt->error(ctxt->userData,
4468 "Internal error: reference has content in CheckReference %s\n",
4469 name);
4470 ctxt->nbErrors++;
4471 return;
4472 }
4473 if (grammar->defs != NULL) {
4474 def = xmlHashLookup(grammar->defs, name);
4475 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00004476 cur = ref;
4477 while (cur != NULL) {
4478 cur->content = def;
4479 cur = cur->nextHash;
4480 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004481 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00004482 if (ctxt->error != NULL)
4483 ctxt->error(ctxt->userData,
4484 "Reference %s has no matching definition\n",
4485 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004486 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004487 }
Daniel Veillardd4310742003-02-18 21:12:46 +00004488 } else {
4489 if (ctxt->error != NULL)
4490 ctxt->error(ctxt->userData,
4491 "Reference %s has no matching definition\n",
4492 name);
4493 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004494 }
4495 /*
4496 * TODO: make a closure and verify there is no loop !
4497 */
4498}
4499
4500/**
4501 * xmlRelaxNGCheckCombine:
4502 * @define: the define(s) list
4503 * @ctxt: a Relax-NG parser context
4504 * @name: the name associated to the defines
4505 *
4506 * Applies the 4.17. combine attribute rule for all the define
4507 * element of a given grammar using the same name.
4508 */
4509static void
4510xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
4511 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
4512 xmlChar *combine;
4513 int choiceOrInterleave = -1;
4514 int missing = 0;
4515 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
4516
4517 if (define->nextHash == NULL)
4518 return;
4519 cur = define;
4520 while (cur != NULL) {
4521 combine = xmlGetProp(cur->node, BAD_CAST "combine");
4522 if (combine != NULL) {
4523 if (xmlStrEqual(combine, BAD_CAST "choice")) {
4524 if (choiceOrInterleave == -1)
4525 choiceOrInterleave = 1;
4526 else if (choiceOrInterleave == 0) {
4527 if (ctxt->error != NULL)
4528 ctxt->error(ctxt->userData,
4529 "Defines for %s use both 'choice' and 'interleave'\n",
4530 name);
4531 ctxt->nbErrors++;
4532 }
Daniel Veillard154877e2003-01-30 12:17:05 +00004533 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004534 if (choiceOrInterleave == -1)
4535 choiceOrInterleave = 0;
4536 else if (choiceOrInterleave == 1) {
4537 if (ctxt->error != NULL)
4538 ctxt->error(ctxt->userData,
4539 "Defines for %s use both 'choice' and 'interleave'\n",
4540 name);
4541 ctxt->nbErrors++;
4542 }
4543 } else {
4544 if (ctxt->error != NULL)
4545 ctxt->error(ctxt->userData,
4546 "Defines for %s use unknown combine value '%s''\n",
4547 name, combine);
4548 ctxt->nbErrors++;
4549 }
4550 xmlFree(combine);
4551 } else {
4552 if (missing == 0)
4553 missing = 1;
4554 else {
4555 if (ctxt->error != NULL)
4556 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00004557 "Some defines for %s needs the combine attribute\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00004558 name);
4559 ctxt->nbErrors++;
4560 }
4561 }
4562
4563 cur = cur->nextHash;
4564 }
4565#ifdef DEBUG
4566 xmlGenericError(xmlGenericErrorContext,
4567 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
4568 name, choiceOrInterleave);
4569#endif
4570 if (choiceOrInterleave == -1)
4571 choiceOrInterleave = 0;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004572 cur = xmlRelaxNGNewDefine(ctxt, define->node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004573 if (cur == NULL)
4574 return;
4575 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00004576 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004577 else
4578 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004579 tmp = define;
4580 last = NULL;
4581 while (tmp != NULL) {
4582 if (tmp->content != NULL) {
4583 if (tmp->content->next != NULL) {
4584 /*
4585 * we need first to create a wrapper.
4586 */
Daniel Veillardfd573f12003-03-16 17:52:32 +00004587 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004588 if (tmp2 == NULL)
4589 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004590 tmp2->type = XML_RELAXNG_GROUP;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004591 tmp2->content = tmp->content;
4592 } else {
4593 tmp2 = tmp->content;
4594 }
4595 if (last == NULL) {
4596 cur->content = tmp2;
4597 } else {
4598 last->next = tmp2;
4599 }
4600 last = tmp2;
4601 tmp->content = NULL;
4602 }
4603 tmp = tmp->nextHash;
4604 }
4605 define->content = cur;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004606 if (choiceOrInterleave == 0) {
4607 if (ctxt->interleaves == NULL)
4608 ctxt->interleaves = xmlHashCreate(10);
4609 if (ctxt->interleaves == NULL) {
4610 if (ctxt->error != NULL)
4611 ctxt->error(ctxt->userData,
4612 "Failed to create interleaves hash table\n");
4613 ctxt->nbErrors++;
4614 } else {
4615 char tmpname[32];
4616
4617 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
4618 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
4619 if (ctxt->error != NULL)
4620 ctxt->error(ctxt->userData,
4621 "Failed to add %s to hash table\n", tmpname);
4622 ctxt->nbErrors++;
4623 }
4624 }
4625 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004626}
4627
4628/**
4629 * xmlRelaxNGCombineStart:
4630 * @ctxt: a Relax-NG parser context
4631 * @grammar: the grammar
4632 *
4633 * Applies the 4.17. combine rule for all the start
4634 * element of a given grammar.
4635 */
4636static void
4637xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
4638 xmlRelaxNGGrammarPtr grammar) {
4639 xmlRelaxNGDefinePtr starts;
4640 xmlChar *combine;
4641 int choiceOrInterleave = -1;
4642 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004643 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004644
Daniel Veillard2df2de22003-02-17 23:34:33 +00004645 starts = grammar->start;
4646 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00004647 return;
4648 cur = starts;
4649 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00004650 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
4651 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
4652 combine = NULL;
4653 if (ctxt->error != NULL)
4654 ctxt->error(ctxt->userData,
4655 "Internal error: start element not found\n");
4656 ctxt->nbErrors++;
4657 } else {
4658 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
4659 }
4660
Daniel Veillard6eadf632003-01-23 18:29:16 +00004661 if (combine != NULL) {
4662 if (xmlStrEqual(combine, BAD_CAST "choice")) {
4663 if (choiceOrInterleave == -1)
4664 choiceOrInterleave = 1;
4665 else if (choiceOrInterleave == 0) {
4666 if (ctxt->error != NULL)
4667 ctxt->error(ctxt->userData,
4668 "<start> use both 'choice' and 'interleave'\n");
4669 ctxt->nbErrors++;
4670 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00004671 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004672 if (choiceOrInterleave == -1)
4673 choiceOrInterleave = 0;
4674 else if (choiceOrInterleave == 1) {
4675 if (ctxt->error != NULL)
4676 ctxt->error(ctxt->userData,
4677 "<start> use both 'choice' and 'interleave'\n");
4678 ctxt->nbErrors++;
4679 }
4680 } else {
4681 if (ctxt->error != NULL)
4682 ctxt->error(ctxt->userData,
4683 "<start> uses unknown combine value '%s''\n", combine);
4684 ctxt->nbErrors++;
4685 }
4686 xmlFree(combine);
4687 } else {
4688 if (missing == 0)
4689 missing = 1;
4690 else {
4691 if (ctxt->error != NULL)
4692 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00004693 "Some <start> element miss the combine attribute\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +00004694 ctxt->nbErrors++;
4695 }
4696 }
4697
Daniel Veillard2df2de22003-02-17 23:34:33 +00004698 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004699 }
4700#ifdef DEBUG
4701 xmlGenericError(xmlGenericErrorContext,
4702 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
4703 choiceOrInterleave);
4704#endif
4705 if (choiceOrInterleave == -1)
4706 choiceOrInterleave = 0;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004707 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004708 if (cur == NULL)
4709 return;
4710 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00004711 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004712 else
4713 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004714 cur->content = grammar->start;
4715 grammar->start = cur;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004716 if (choiceOrInterleave == 0) {
4717 if (ctxt->interleaves == NULL)
4718 ctxt->interleaves = xmlHashCreate(10);
4719 if (ctxt->interleaves == NULL) {
4720 if (ctxt->error != NULL)
4721 ctxt->error(ctxt->userData,
4722 "Failed to create interleaves hash table\n");
4723 ctxt->nbErrors++;
4724 } else {
4725 char tmpname[32];
4726
4727 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
4728 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
4729 if (ctxt->error != NULL)
4730 ctxt->error(ctxt->userData,
4731 "Failed to add %s to hash table\n", tmpname);
4732 ctxt->nbErrors++;
4733 }
4734 }
4735 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004736}
4737
4738/**
Daniel Veillardd4310742003-02-18 21:12:46 +00004739 * xmlRelaxNGCheckCycles:
4740 * @ctxt: a Relax-NG parser context
4741 * @nodes: grammar children nodes
4742 * @depth: the counter
4743 *
4744 * Check for cycles.
4745 *
4746 * Returns 0 if check passed, and -1 in case of error
4747 */
4748static int
4749xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
4750 xmlRelaxNGDefinePtr cur, int depth) {
4751 int ret = 0;
4752
4753 while ((ret == 0) && (cur != NULL)) {
4754 if ((cur->type == XML_RELAXNG_REF) ||
4755 (cur->type == XML_RELAXNG_PARENTREF)) {
4756 if (cur->depth == -1) {
4757 cur->depth = depth;
4758 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
4759 cur->depth = -2;
4760 } else if (depth == cur->depth) {
4761 if (ctxt->error != NULL)
4762 ctxt->error(ctxt->userData,
4763 "Detected a cycle in %s references\n", cur->name);
4764 ctxt->nbErrors++;
4765 return(-1);
4766 }
4767 } else if (cur->type == XML_RELAXNG_ELEMENT) {
4768 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
4769 } else {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004770 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
Daniel Veillardd4310742003-02-18 21:12:46 +00004771 }
4772 cur = cur->next;
4773 }
4774 return(ret);
4775}
4776
4777/**
Daniel Veillard77648bb2003-02-20 15:03:22 +00004778 * xmlRelaxNGTryUnlink:
4779 * @ctxt: a Relax-NG parser context
4780 * @cur: the definition to unlink
4781 * @parent: the parent definition
4782 * @prev: the previous sibling definition
4783 *
4784 * Try to unlink a definition. If not possble make it a NOOP
4785 *
4786 * Returns the new prev definition
4787 */
4788static xmlRelaxNGDefinePtr
4789xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
4790 xmlRelaxNGDefinePtr cur,
4791 xmlRelaxNGDefinePtr parent,
4792 xmlRelaxNGDefinePtr prev) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004793 if (prev != NULL) {
4794 prev->next = cur->next;
4795 } else {
4796 if (parent != NULL) {
4797 if (parent->content == cur)
4798 parent->content = cur->next;
4799 else if (parent->attrs == cur)
4800 parent->attrs = cur->next;
4801 else if (parent->nameClass == cur)
4802 parent->nameClass = cur->next;
4803 } else {
4804 cur->type = XML_RELAXNG_NOOP;
4805 prev = cur;
4806 }
4807 }
4808 return(prev);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004809}
4810
4811/**
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004812 * xmlRelaxNGSimplify:
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004813 * @ctxt: a Relax-NG parser context
4814 * @nodes: grammar children nodes
4815 *
4816 * Check for simplification of empty and notAllowed
4817 */
4818static void
4819xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
4820 xmlRelaxNGDefinePtr cur,
4821 xmlRelaxNGDefinePtr parent) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004822 xmlRelaxNGDefinePtr prev = NULL;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004823
Daniel Veillardfd573f12003-03-16 17:52:32 +00004824 while (cur != NULL) {
4825 if ((cur->type == XML_RELAXNG_REF) ||
4826 (cur->type == XML_RELAXNG_PARENTREF)) {
4827 if (cur->depth != -3) {
4828 cur->depth = -3;
4829 xmlRelaxNGSimplify(ctxt, cur->content, cur);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004830 }
4831 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004832 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004833 if ((parent != NULL) &&
4834 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4835 (parent->type == XML_RELAXNG_LIST) ||
4836 (parent->type == XML_RELAXNG_GROUP) ||
4837 (parent->type == XML_RELAXNG_INTERLEAVE) ||
Daniel Veillardfd573f12003-03-16 17:52:32 +00004838 (parent->type == XML_RELAXNG_ONEORMORE) ||
4839 (parent->type == XML_RELAXNG_ZEROORMORE))) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004840 parent->type = XML_RELAXNG_NOT_ALLOWED;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004841 break;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004842 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004843 if ((parent != NULL) &&
Daniel Veillardfd573f12003-03-16 17:52:32 +00004844 (parent->type == XML_RELAXNG_CHOICE)) {
4845 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
4846 } else
4847 prev = cur;
4848 } else if (cur->type == XML_RELAXNG_EMPTY){
4849 cur->parent = parent;
4850 if ((parent != NULL) &&
4851 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4852 (parent->type == XML_RELAXNG_ZEROORMORE))) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004853 parent->type = XML_RELAXNG_EMPTY;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004854 break;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004855 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00004856 if ((parent != NULL) &&
4857 ((parent->type == XML_RELAXNG_GROUP) ||
4858 (parent->type == XML_RELAXNG_INTERLEAVE))) {
4859 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
4860 } else
4861 prev = cur;
4862 } else {
4863 cur->parent = parent;
4864 if (cur->content != NULL)
4865 xmlRelaxNGSimplify(ctxt, cur->content, cur);
4866 if (cur->attrs != NULL)
4867 xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
4868 if (cur->nameClass != NULL)
4869 xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
4870 /*
4871 * This may result in a simplification
4872 */
4873 if ((cur->type == XML_RELAXNG_GROUP) ||
4874 (cur->type == XML_RELAXNG_INTERLEAVE)) {
4875 if (cur->content == NULL)
4876 cur->type = XML_RELAXNG_EMPTY;
4877 else if (cur->content->next == NULL) {
4878 if ((parent == NULL) && (prev == NULL)) {
4879 cur->type = XML_RELAXNG_NOOP;
4880 } else if (prev == NULL) {
4881 parent->content = cur->content;
4882 cur->content->next = cur->next;
4883 cur = cur->content;
4884 } else {
4885 cur->content->next = cur->next;
4886 prev->next = cur->content;
4887 cur = cur->content;
4888 }
Daniel Veillard1564e6e2003-03-15 21:30:25 +00004889 }
4890 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00004891 /*
4892 * the current node may have been transformed back
4893 */
4894 if ((cur->type == XML_RELAXNG_EXCEPT) &&
4895 (cur->content != NULL) &&
4896 (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
4897 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
4898 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
4899 if ((parent != NULL) &&
4900 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4901 (parent->type == XML_RELAXNG_LIST) ||
4902 (parent->type == XML_RELAXNG_GROUP) ||
4903 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4904 (parent->type == XML_RELAXNG_ONEORMORE) ||
4905 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4906 parent->type = XML_RELAXNG_NOT_ALLOWED;
4907 break;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00004908 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00004909 if ((parent != NULL) &&
4910 (parent->type == XML_RELAXNG_CHOICE)) {
4911 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
4912 } else
4913 prev = cur;
4914 } else if (cur->type == XML_RELAXNG_EMPTY){
4915 if ((parent != NULL) &&
4916 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4917 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4918 parent->type = XML_RELAXNG_EMPTY;
4919 break;
4920 }
4921 if ((parent != NULL) &&
4922 ((parent->type == XML_RELAXNG_GROUP) ||
4923 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4924 (parent->type == XML_RELAXNG_CHOICE))) {
4925 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
4926 } else
4927 prev = cur;
4928 } else {
4929 prev = cur;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00004930 }
Daniel Veillard1564e6e2003-03-15 21:30:25 +00004931 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00004932 cur = cur->next;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004933 }
4934}
4935
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004936/**
4937 * xmlRelaxNGGroupContentType:
4938 * @ct1: the first content type
4939 * @ct2: the second content type
4940 *
4941 * Try to group 2 content types
4942 *
4943 * Returns the content type
4944 */
4945static xmlRelaxNGContentType
4946xmlRelaxNGGroupContentType(xmlRelaxNGContentType ct1,
4947 xmlRelaxNGContentType ct2) {
4948 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4949 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4950 return(XML_RELAXNG_CONTENT_ERROR);
4951 if (ct1 == XML_RELAXNG_CONTENT_EMPTY)
4952 return(ct2);
4953 if (ct2 == XML_RELAXNG_CONTENT_EMPTY)
4954 return(ct1);
4955 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) &&
4956 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4957 return(XML_RELAXNG_CONTENT_COMPLEX);
4958 return(XML_RELAXNG_CONTENT_ERROR);
4959}
4960
4961/**
4962 * xmlRelaxNGMaxContentType:
4963 * @ct1: the first content type
4964 * @ct2: the second content type
4965 *
4966 * Compute the max content-type
4967 *
4968 * Returns the content type
4969 */
4970static xmlRelaxNGContentType
4971xmlRelaxNGMaxContentType(xmlRelaxNGContentType ct1,
4972 xmlRelaxNGContentType ct2) {
4973 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4974 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4975 return(XML_RELAXNG_CONTENT_ERROR);
4976 if ((ct1 == XML_RELAXNG_CONTENT_SIMPLE) ||
4977 (ct2 == XML_RELAXNG_CONTENT_SIMPLE))
4978 return(XML_RELAXNG_CONTENT_SIMPLE);
4979 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) ||
4980 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4981 return(XML_RELAXNG_CONTENT_COMPLEX);
4982 return(XML_RELAXNG_CONTENT_EMPTY);
4983}
Daniel Veillard77648bb2003-02-20 15:03:22 +00004984
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004985/**
4986 * xmlRelaxNGCheckRules:
4987 * @ctxt: a Relax-NG parser context
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004988 * @cur: the current definition
Daniel Veillard77648bb2003-02-20 15:03:22 +00004989 * @flags: some accumulated flags
Daniel Veillardfd573f12003-03-16 17:52:32 +00004990 * @ptype: the parent type
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004991 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004992 * Check for rules in section 7.1 and 7.2
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004993 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004994 * Returns the content type of @cur
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004995 */
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004996static xmlRelaxNGContentType
Daniel Veillardfd573f12003-03-16 17:52:32 +00004997xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
4998 xmlRelaxNGDefinePtr cur, int flags,
4999 xmlRelaxNGType ptype) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00005000 int nflags = flags;
Daniel Veillardfd573f12003-03-16 17:52:32 +00005001 xmlRelaxNGContentType ret, tmp, val = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005002
Daniel Veillardfd573f12003-03-16 17:52:32 +00005003 while (cur != NULL) {
5004 ret = XML_RELAXNG_CONTENT_EMPTY;
5005 if ((cur->type == XML_RELAXNG_REF) ||
5006 (cur->type == XML_RELAXNG_PARENTREF)) {
5007 if (flags & XML_RELAXNG_IN_LIST) {
5008 if (ctxt->error != NULL)
5009 ctxt->error(ctxt->userData,
5010 "Found forbidden pattern list//ref\n");
5011 ctxt->nbErrors++;
5012 }
5013 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5014 if (ctxt->error != NULL)
5015 ctxt->error(ctxt->userData,
5016 "Found forbidden pattern data/except//ref\n");
5017 ctxt->nbErrors++;
5018 }
5019 if (cur->depth > -4) {
5020 cur->depth = -4;
5021 ret = xmlRelaxNGCheckRules(ctxt, cur->content,
5022 flags, cur->type);
5023 cur->depth = ret - 15 ;
5024 } else if (cur->depth == -4) {
5025 ret = XML_RELAXNG_CONTENT_COMPLEX;
5026 } else {
5027 ret = (xmlRelaxNGContentType) cur->depth + 15;
5028 }
5029 } else if (cur->type == XML_RELAXNG_ELEMENT) {
5030 /*
5031 * The 7.3 Attribute derivation rule for groups is plugged there
5032 */
5033 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
5034 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5035 if (ctxt->error != NULL)
5036 ctxt->error(ctxt->userData,
5037 "Found forbidden pattern data/except//element(ref)\n");
5038 ctxt->nbErrors++;
5039 }
5040 if (flags & XML_RELAXNG_IN_LIST) {
5041 if (ctxt->error != NULL)
5042 ctxt->error(ctxt->userData,
5043 "Found forbidden pattern list//element(ref)\n");
5044 ctxt->nbErrors++;
5045 }
5046 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
5047 if (ctxt->error != NULL)
5048 ctxt->error(ctxt->userData,
5049 "Found forbidden pattern attribute//element(ref)\n");
5050 ctxt->nbErrors++;
5051 }
5052 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
5053 if (ctxt->error != NULL)
5054 ctxt->error(ctxt->userData,
Daniel Veillard463a5472003-02-27 21:30:32 +00005055 "Found forbidden pattern attribute//element(ref)\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00005056 ctxt->nbErrors++;
5057 }
5058 /*
5059 * reset since in the simple form elements are only child
5060 * of grammar/define
5061 */
5062 nflags = 0;
5063 ret = xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags, cur->type);
5064 if (ret != XML_RELAXNG_CONTENT_EMPTY) {
5065 if (ctxt->error != NULL)
5066 ctxt->error(ctxt->userData,
5067 "Element %s attributes have a content type error\n",
5068 cur->name);
5069 ctxt->nbErrors++;
5070 }
5071 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5072 if (ret == XML_RELAXNG_CONTENT_ERROR) {
5073 if (ctxt->error != NULL)
5074 ctxt->error(ctxt->userData,
5075 "Element %s has a content type error\n",
5076 cur->name);
5077 ctxt->nbErrors++;
5078 } else {
5079 ret = XML_RELAXNG_CONTENT_COMPLEX;
5080 }
5081 } else if (cur->type == XML_RELAXNG_ATTRIBUTE) {
5082 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
5083 if (ctxt->error != NULL)
5084 ctxt->error(ctxt->userData,
5085 "Found forbidden pattern attribute//attribute\n");
5086 ctxt->nbErrors++;
5087 }
5088 if (flags & XML_RELAXNG_IN_LIST) {
5089 if (ctxt->error != NULL)
5090 ctxt->error(ctxt->userData,
5091 "Found forbidden pattern list//attribute\n");
5092 ctxt->nbErrors++;
5093 }
5094 if (flags & XML_RELAXNG_IN_OOMGROUP) {
5095 if (ctxt->error != NULL)
5096 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00005097 "Found forbidden pattern oneOrMore//group//attribute\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00005098 ctxt->nbErrors++;
5099 }
5100 if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) {
5101 if (ctxt->error != NULL)
5102 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00005103 "Found forbidden pattern oneOrMore//interleave//attribute\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00005104 ctxt->nbErrors++;
5105 }
5106 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5107 if (ctxt->error != NULL)
5108 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00005109 "Found forbidden pattern data/except//attribute\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00005110 ctxt->nbErrors++;
5111 }
5112 if (flags & XML_RELAXNG_IN_START) {
5113 if (ctxt->error != NULL)
5114 ctxt->error(ctxt->userData,
5115 "Found forbidden pattern start//attribute\n");
5116 ctxt->nbErrors++;
5117 }
Daniel Veillard1564e6e2003-03-15 21:30:25 +00005118 if ((!(flags & XML_RELAXNG_IN_ONEORMORE)) && (cur->name == NULL)) {
5119 if (cur->ns == NULL) {
5120 if (ctxt->error != NULL)
5121 ctxt->error(ctxt->userData,
5122 "Found anyName attribute without oneOrMore ancestor\n");
5123 ctxt->nbErrors++;
5124 } else {
5125 if (ctxt->error != NULL)
5126 ctxt->error(ctxt->userData,
5127 "Found nsName attribute without oneOrMore ancestor\n");
5128 ctxt->nbErrors++;
5129 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00005130 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00005131 nflags = flags | XML_RELAXNG_IN_ATTRIBUTE;
5132 xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5133 ret = XML_RELAXNG_CONTENT_EMPTY;
5134 } else if ((cur->type == XML_RELAXNG_ONEORMORE) ||
5135 (cur->type == XML_RELAXNG_ZEROORMORE)) {
5136 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5137 if (ctxt->error != NULL)
5138 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00005139 "Found forbidden pattern data/except//oneOrMore\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00005140 ctxt->nbErrors++;
5141 }
5142 if (flags & XML_RELAXNG_IN_START) {
5143 if (ctxt->error != NULL)
5144 ctxt->error(ctxt->userData,
5145 "Found forbidden pattern start//oneOrMore\n");
5146 ctxt->nbErrors++;
5147 }
5148 nflags = flags | XML_RELAXNG_IN_ONEORMORE;
5149 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5150 ret = xmlRelaxNGGroupContentType(ret, ret);
5151 } else if (cur->type == XML_RELAXNG_LIST) {
5152 if (flags & XML_RELAXNG_IN_LIST) {
5153 if (ctxt->error != NULL)
5154 ctxt->error(ctxt->userData,
5155 "Found forbidden pattern list//list\n");
5156 ctxt->nbErrors++;
5157 }
5158 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5159 if (ctxt->error != NULL)
5160 ctxt->error(ctxt->userData,
5161 "Found forbidden pattern data/except//list\n");
5162 ctxt->nbErrors++;
5163 }
5164 if (flags & XML_RELAXNG_IN_START) {
5165 if (ctxt->error != NULL)
5166 ctxt->error(ctxt->userData,
5167 "Found forbidden pattern start//list\n");
5168 ctxt->nbErrors++;
5169 }
5170 nflags = flags | XML_RELAXNG_IN_LIST;
5171 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5172 } else if (cur->type == XML_RELAXNG_GROUP) {
5173 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5174 if (ctxt->error != NULL)
5175 ctxt->error(ctxt->userData,
5176 "Found forbidden pattern data/except//group\n");
5177 ctxt->nbErrors++;
5178 }
5179 if (flags & XML_RELAXNG_IN_START) {
5180 if (ctxt->error != NULL)
5181 ctxt->error(ctxt->userData,
5182 "Found forbidden pattern start//group\n");
5183 ctxt->nbErrors++;
5184 }
5185 if (flags & XML_RELAXNG_IN_ONEORMORE)
5186 nflags = flags | XML_RELAXNG_IN_OOMGROUP;
5187 else
5188 nflags = flags;
5189 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5190 /*
5191 * The 7.3 Attribute derivation rule for groups is plugged there
5192 */
5193 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
5194 } else if (cur->type == XML_RELAXNG_INTERLEAVE) {
5195 if (flags & XML_RELAXNG_IN_LIST) {
5196 if (ctxt->error != NULL)
5197 ctxt->error(ctxt->userData,
5198 "Found forbidden pattern list//interleave\n");
5199 ctxt->nbErrors++;
5200 }
5201 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5202 if (ctxt->error != NULL)
5203 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00005204 "Found forbidden pattern data/except//interleave\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00005205 ctxt->nbErrors++;
5206 }
5207 if (flags & XML_RELAXNG_IN_START) {
5208 if (ctxt->error != NULL)
5209 ctxt->error(ctxt->userData,
5210 "Found forbidden pattern start//interleave\n");
5211 ctxt->nbErrors++;
5212 }
5213 if (flags & XML_RELAXNG_IN_ONEORMORE)
5214 nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE;
5215 else
5216 nflags = flags;
5217 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5218 } else if (cur->type == XML_RELAXNG_EXCEPT) {
5219 if ((cur->parent != NULL) &&
5220 (cur->parent->type == XML_RELAXNG_DATATYPE))
5221 nflags = flags | XML_RELAXNG_IN_DATAEXCEPT;
5222 else
5223 nflags = flags;
5224 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5225 } else if (cur->type == XML_RELAXNG_DATATYPE) {
5226 if (flags & XML_RELAXNG_IN_START) {
5227 if (ctxt->error != NULL)
5228 ctxt->error(ctxt->userData,
5229 "Found forbidden pattern start//data\n");
5230 ctxt->nbErrors++;
5231 }
5232 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
5233 ret = XML_RELAXNG_CONTENT_SIMPLE;
5234 } else if (cur->type == XML_RELAXNG_VALUE) {
5235 if (flags & XML_RELAXNG_IN_START) {
5236 if (ctxt->error != NULL)
5237 ctxt->error(ctxt->userData,
5238 "Found forbidden pattern start//value\n");
5239 ctxt->nbErrors++;
5240 }
5241 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
5242 ret = XML_RELAXNG_CONTENT_SIMPLE;
5243 } else if (cur->type == XML_RELAXNG_TEXT) {
5244 if (flags & XML_RELAXNG_IN_LIST) {
5245 if (ctxt->error != NULL)
5246 ctxt->error(ctxt->userData,
5247 "Found forbidden pattern list//text\n");
5248 ctxt->nbErrors++;
5249 }
5250 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5251 if (ctxt->error != NULL)
5252 ctxt->error(ctxt->userData,
5253 "Found forbidden pattern data/except//text\n");
5254 ctxt->nbErrors++;
5255 }
5256 if (flags & XML_RELAXNG_IN_START) {
5257 if (ctxt->error != NULL)
5258 ctxt->error(ctxt->userData,
5259 "Found forbidden pattern start//text\n");
5260 ctxt->nbErrors++;
5261 }
5262 ret = XML_RELAXNG_CONTENT_COMPLEX;
5263 } else if (cur->type == XML_RELAXNG_EMPTY) {
5264 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5265 if (ctxt->error != NULL)
5266 ctxt->error(ctxt->userData,
5267 "Found forbidden pattern data/except//empty\n");
5268 ctxt->nbErrors++;
5269 }
5270 if (flags & XML_RELAXNG_IN_START) {
5271 if (ctxt->error != NULL)
5272 ctxt->error(ctxt->userData,
5273 "Found forbidden pattern start//empty\n");
5274 ctxt->nbErrors++;
5275 }
5276 ret = XML_RELAXNG_CONTENT_EMPTY;
5277 } else if (cur->type == XML_RELAXNG_CHOICE) {
5278 xmlRelaxNGCheckChoiceDeterminism(ctxt, cur);
5279 ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
5280 } else {
5281 ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
5282 }
5283 cur = cur->next;
5284 if (ptype == XML_RELAXNG_GROUP) {
5285 val = xmlRelaxNGGroupContentType(val, ret);
5286 } else if (ptype == XML_RELAXNG_INTERLEAVE) {
5287 tmp = xmlRelaxNGGroupContentType(val, ret);
5288 if (tmp != XML_RELAXNG_CONTENT_ERROR)
5289 tmp = xmlRelaxNGMaxContentType(val, ret);
5290 } else if (ptype == XML_RELAXNG_CHOICE) {
5291 val = xmlRelaxNGMaxContentType(val, ret);
5292 } else if (ptype == XML_RELAXNG_LIST) {
5293 val = XML_RELAXNG_CONTENT_SIMPLE;
5294 } else if (ptype == XML_RELAXNG_EXCEPT) {
5295 if (ret == XML_RELAXNG_CONTENT_ERROR)
5296 val = XML_RELAXNG_CONTENT_ERROR;
5297 else
5298 val = XML_RELAXNG_CONTENT_SIMPLE;
5299 } else {
5300 val = xmlRelaxNGGroupContentType(val, ret);
5301 }
5302
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005303 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00005304 return(val);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005305}
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005306
5307/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005308 * xmlRelaxNGParseGrammar:
5309 * @ctxt: a Relax-NG parser context
5310 * @nodes: grammar children nodes
5311 *
5312 * parse a Relax-NG <grammar> node
5313 *
5314 * Returns the internal xmlRelaxNGGrammarPtr built or
5315 * NULL in case of error
5316 */
5317static xmlRelaxNGGrammarPtr
5318xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
5319 xmlRelaxNGGrammarPtr ret, tmp, old;
5320
Daniel Veillardc482e262003-02-26 14:48:48 +00005321#ifdef DEBUG_GRAMMAR
5322 xmlGenericError(xmlGenericErrorContext, "Parsing a new grammar\n");
5323#endif
5324
Daniel Veillard6eadf632003-01-23 18:29:16 +00005325 ret = xmlRelaxNGNewGrammar(ctxt);
5326 if (ret == NULL)
5327 return(NULL);
5328
5329 /*
5330 * Link the new grammar in the tree
5331 */
5332 ret->parent = ctxt->grammar;
5333 if (ctxt->grammar != NULL) {
5334 tmp = ctxt->grammar->children;
5335 if (tmp == NULL) {
5336 ctxt->grammar->children = ret;
5337 } else {
5338 while (tmp->next != NULL)
5339 tmp = tmp->next;
5340 tmp->next = ret;
5341 }
5342 }
5343
5344 old = ctxt->grammar;
5345 ctxt->grammar = ret;
5346 xmlRelaxNGParseGrammarContent(ctxt, nodes);
5347 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00005348 if (ctxt->grammar == NULL) {
5349 if (ctxt->error != NULL)
5350 ctxt->error(ctxt->userData,
5351 "Failed to parse <grammar> content\n");
5352 ctxt->nbErrors++;
5353 } else if (ctxt->grammar->start == NULL) {
5354 if (ctxt->error != NULL)
5355 ctxt->error(ctxt->userData,
5356 "Element <grammar> has no <start>\n");
5357 ctxt->nbErrors++;
5358 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005359
5360 /*
5361 * Apply 4.17 mergingd rules to defines and starts
5362 */
5363 xmlRelaxNGCombineStart(ctxt, ret);
5364 if (ret->defs != NULL) {
5365 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
5366 ctxt);
5367 }
5368
5369 /*
5370 * link together defines and refs in this grammar
5371 */
5372 if (ret->refs != NULL) {
5373 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
5374 ctxt);
5375 }
5376 ctxt->grammar = old;
5377 return(ret);
5378}
5379
5380/**
5381 * xmlRelaxNGParseDocument:
5382 * @ctxt: a Relax-NG parser context
5383 * @node: the root node of the RelaxNG schema
5384 *
5385 * parse a Relax-NG definition resource and build an internal
5386 * xmlRelaxNG struture which can be used to validate instances.
5387 *
5388 * Returns the internal XML RelaxNG structure built or
5389 * NULL in case of error
5390 */
5391static xmlRelaxNGPtr
5392xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
5393 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005394 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00005395 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005396
5397 if ((ctxt == NULL) || (node == NULL))
5398 return (NULL);
5399
5400 schema = xmlRelaxNGNewRelaxNG(ctxt);
5401 if (schema == NULL)
5402 return(NULL);
5403
Daniel Veillard276be4a2003-01-24 01:03:34 +00005404 olddefine = ctxt->define;
5405 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005406 if (IS_RELAXNG(node, "grammar")) {
5407 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
5408 } else {
Daniel Veillardc482e262003-02-26 14:48:48 +00005409 xmlRelaxNGGrammarPtr tmp, ret;
5410
5411 schema->topgrammar = ret = xmlRelaxNGNewGrammar(ctxt);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005412 if (schema->topgrammar == NULL) {
5413 return(schema);
5414 }
Daniel Veillardc482e262003-02-26 14:48:48 +00005415 /*
5416 * Link the new grammar in the tree
5417 */
5418 ret->parent = ctxt->grammar;
5419 if (ctxt->grammar != NULL) {
5420 tmp = ctxt->grammar->children;
5421 if (tmp == NULL) {
5422 ctxt->grammar->children = ret;
5423 } else {
5424 while (tmp->next != NULL)
5425 tmp = tmp->next;
5426 tmp->next = ret;
5427 }
5428 }
Daniel Veillarde431a272003-01-29 23:02:33 +00005429 old = ctxt->grammar;
Daniel Veillardc482e262003-02-26 14:48:48 +00005430 ctxt->grammar = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005431 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00005432 if (old != NULL)
5433 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005434 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005435 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00005436 if (schema->topgrammar->start != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00005437 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
Daniel Veillard77648bb2003-02-20 15:03:22 +00005438 if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00005439 xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
5440 while ((schema->topgrammar->start != NULL) &&
5441 (schema->topgrammar->start->type == XML_RELAXNG_NOOP) &&
5442 (schema->topgrammar->start->next != NULL))
5443 schema->topgrammar->start = schema->topgrammar->start->content;
5444 xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start,
5445 XML_RELAXNG_IN_START, XML_RELAXNG_NOOP);
Daniel Veillard77648bb2003-02-20 15:03:22 +00005446 }
Daniel Veillardd4310742003-02-18 21:12:46 +00005447 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005448
5449#ifdef DEBUG
5450 if (schema == NULL)
5451 xmlGenericError(xmlGenericErrorContext,
5452 "xmlRelaxNGParseDocument() failed\n");
5453#endif
5454
5455 return (schema);
5456}
5457
5458/************************************************************************
5459 * *
5460 * Reading RelaxNGs *
5461 * *
5462 ************************************************************************/
5463
5464/**
5465 * xmlRelaxNGNewParserCtxt:
5466 * @URL: the location of the schema
5467 *
5468 * Create an XML RelaxNGs parse context for that file/resource expected
5469 * to contain an XML RelaxNGs file.
5470 *
5471 * Returns the parser context or NULL in case of error
5472 */
5473xmlRelaxNGParserCtxtPtr
5474xmlRelaxNGNewParserCtxt(const char *URL) {
5475 xmlRelaxNGParserCtxtPtr ret;
5476
5477 if (URL == NULL)
5478 return(NULL);
5479
5480 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
5481 if (ret == NULL) {
5482 xmlGenericError(xmlGenericErrorContext,
5483 "Failed to allocate new schama parser context for %s\n", URL);
5484 return (NULL);
5485 }
5486 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
5487 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005488 ret->error = xmlGenericError;
5489 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005490 return (ret);
5491}
5492
5493/**
5494 * xmlRelaxNGNewMemParserCtxt:
5495 * @buffer: a pointer to a char array containing the schemas
5496 * @size: the size of the array
5497 *
5498 * Create an XML RelaxNGs parse context for that memory buffer expected
5499 * to contain an XML RelaxNGs file.
5500 *
5501 * Returns the parser context or NULL in case of error
5502 */
5503xmlRelaxNGParserCtxtPtr
5504xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
5505 xmlRelaxNGParserCtxtPtr ret;
5506
5507 if ((buffer == NULL) || (size <= 0))
5508 return(NULL);
5509
5510 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
5511 if (ret == NULL) {
5512 xmlGenericError(xmlGenericErrorContext,
5513 "Failed to allocate new schama parser context\n");
5514 return (NULL);
5515 }
5516 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
5517 ret->buffer = buffer;
5518 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005519 ret->error = xmlGenericError;
5520 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005521 return (ret);
5522}
5523
5524/**
5525 * xmlRelaxNGFreeParserCtxt:
5526 * @ctxt: the schema parser context
5527 *
5528 * Free the resources associated to the schema parser context
5529 */
5530void
5531xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
5532 if (ctxt == NULL)
5533 return;
5534 if (ctxt->URL != NULL)
5535 xmlFree(ctxt->URL);
5536 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005537 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005538 if (ctxt->interleaves != NULL)
5539 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005540 if (ctxt->documents != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +00005541 xmlRelaxNGFreeDocumentList(ctxt->documents);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005542 if (ctxt->includes != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +00005543 xmlRelaxNGFreeIncludeList(ctxt->includes);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005544 if (ctxt->docTab != NULL)
5545 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005546 if (ctxt->incTab != NULL)
5547 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00005548 if (ctxt->defTab != NULL) {
5549 int i;
5550
5551 for (i = 0;i < ctxt->defNr;i++)
5552 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
5553 xmlFree(ctxt->defTab);
5554 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005555 xmlFree(ctxt);
5556}
5557
Daniel Veillard6eadf632003-01-23 18:29:16 +00005558/**
Daniel Veillardd2298792003-02-14 16:54:11 +00005559 * xmlRelaxNGNormExtSpace:
5560 * @value: a value
5561 *
5562 * Removes the leading and ending spaces of the value
5563 * The string is modified "in situ"
5564 */
5565static void
5566xmlRelaxNGNormExtSpace(xmlChar *value) {
5567 xmlChar *start = value;
5568 xmlChar *cur = value;
5569 if (value == NULL)
5570 return;
5571
5572 while (IS_BLANK(*cur)) cur++;
5573 if (cur == start) {
5574 do {
5575 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5576 if (*cur == 0)
5577 return;
5578 start = cur;
5579 while (IS_BLANK(*cur)) cur++;
5580 if (*cur == 0) {
5581 *start = 0;
5582 return;
5583 }
5584 } while (1);
5585 } else {
5586 do {
5587 while ((*cur != 0) && (!IS_BLANK(*cur)))
5588 *start++ = *cur++;
5589 if (*cur == 0) {
5590 *start = 0;
5591 return;
5592 }
5593 /* don't try to normalize the inner spaces */
5594 while (IS_BLANK(*cur)) cur++;
5595 *start++ = *cur++;
5596 if (*cur == 0) {
5597 *start = 0;
5598 return;
5599 }
5600 } while (1);
5601 }
5602}
5603
5604/**
5605 * xmlRelaxNGCheckAttributes:
5606 * @ctxt: a Relax-NG parser context
5607 * @node: a Relax-NG node
5608 *
5609 * Check all the attributes on the given node
5610 */
5611static void
5612xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
5613 xmlAttrPtr cur, next;
5614
5615 cur = node->properties;
5616 while (cur != NULL) {
5617 next = cur->next;
5618 if ((cur->ns == NULL) ||
5619 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
5620 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
5621 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
5622 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
5623 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
5624 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00005625 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00005626 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
5627 if (ctxt->error != NULL)
5628 ctxt->error(ctxt->userData,
5629 "Attribute %s is not allowed on %s\n",
5630 cur->name, node->name);
5631 ctxt->nbErrors++;
5632 }
5633 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
5634 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
5635 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
5636 if (ctxt->error != NULL)
5637 ctxt->error(ctxt->userData,
5638 "Attribute %s is not allowed on %s\n",
5639 cur->name, node->name);
5640 ctxt->nbErrors++;
5641 }
5642 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
5643 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
5644 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
5645 if (ctxt->error != NULL)
5646 ctxt->error(ctxt->userData,
5647 "Attribute %s is not allowed on %s\n",
5648 cur->name, node->name);
5649 ctxt->nbErrors++;
5650 }
5651 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
5652 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
5653 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
5654 if (ctxt->error != NULL)
5655 ctxt->error(ctxt->userData,
5656 "Attribute %s is not allowed on %s\n",
5657 cur->name, node->name);
5658 ctxt->nbErrors++;
5659 }
5660 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
5661 xmlChar *val;
5662 xmlURIPtr uri;
5663
5664 val = xmlNodeListGetString(node->doc, cur->children, 1);
5665 if (val != NULL) {
5666 if (val[0] != 0) {
5667 uri = xmlParseURI((const char *) val);
5668 if (uri == NULL) {
5669 if (ctxt->error != NULL)
5670 ctxt->error(ctxt->userData,
5671 "Attribute %s contains invalid URI %s\n",
5672 cur->name, val);
5673 ctxt->nbErrors++;
5674 } else {
5675 if (uri->scheme == NULL) {
5676 if (ctxt->error != NULL)
5677 ctxt->error(ctxt->userData,
5678 "Attribute %s URI %s is not absolute\n",
5679 cur->name, val);
5680 ctxt->nbErrors++;
5681 }
5682 if (uri->fragment != NULL) {
5683 if (ctxt->error != NULL)
5684 ctxt->error(ctxt->userData,
5685 "Attribute %s URI %s has a fragment ID\n",
5686 cur->name, val);
5687 ctxt->nbErrors++;
5688 }
5689 xmlFreeURI(uri);
5690 }
5691 }
5692 xmlFree(val);
5693 }
5694 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
5695 if (ctxt->error != NULL)
5696 ctxt->error(ctxt->userData,
5697 "Unknown attribute %s on %s\n",
5698 cur->name, node->name);
5699 ctxt->nbErrors++;
5700 }
5701 }
5702 cur = next;
5703 }
5704}
5705
5706/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00005707 * xmlRelaxNGCleanupTree:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005708 * @ctxt: a Relax-NG parser context
Daniel Veillardc5312d72003-02-21 17:14:10 +00005709 * @root: an xmlNodePtr subtree
Daniel Veillard6eadf632003-01-23 18:29:16 +00005710 *
Daniel Veillardfd573f12003-03-16 17:52:32 +00005711 * Cleanup the subtree from unwanted nodes for parsing, resolve
5712 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00005713 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005714static void
Daniel Veillardfd573f12003-03-16 17:52:32 +00005715xmlRelaxNGCleanupTree(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr root) {
Daniel Veillardc5312d72003-02-21 17:14:10 +00005716 xmlNodePtr cur, delete;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005717
Daniel Veillard6eadf632003-01-23 18:29:16 +00005718 delete = NULL;
5719 cur = root;
5720 while (cur != NULL) {
5721 if (delete != NULL) {
5722 xmlUnlinkNode(delete);
5723 xmlFreeNode(delete);
5724 delete = NULL;
5725 }
5726 if (cur->type == XML_ELEMENT_NODE) {
5727 /*
5728 * Simplification 4.1. Annotations
5729 */
5730 if ((cur->ns == NULL) ||
5731 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00005732 if ((cur->parent != NULL) &&
5733 (cur->parent->type == XML_ELEMENT_NODE) &&
5734 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
5735 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
5736 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
5737 if (ctxt->error != NULL)
5738 ctxt->error(ctxt->userData,
5739 "element %s doesn't allow foreign elements\n",
5740 cur->parent->name);
5741 ctxt->nbErrors++;
5742 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005743 delete = cur;
5744 goto skip_children;
Daniel Veillardfd573f12003-03-16 17:52:32 +00005745 } else {
5746 xmlRelaxNGCleanupAttributes(ctxt, cur);
5747 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
5748 xmlChar *href, *ns, *base, *URL;
5749 xmlRelaxNGDocumentPtr docu;
5750 xmlNodePtr tmp;
5751
5752 ns = xmlGetProp(cur, BAD_CAST "ns");
5753 if (ns == NULL) {
5754 tmp = cur->parent;
5755 while ((tmp != NULL) &&
5756 (tmp->type == XML_ELEMENT_NODE)) {
5757 ns = xmlGetProp(tmp, BAD_CAST "ns");
5758 if (ns != NULL)
5759 break;
5760 tmp = tmp->parent;
5761 }
5762 }
5763 href = xmlGetProp(cur, BAD_CAST "href");
5764 if (href == NULL) {
5765 if (ctxt->error != NULL)
5766 ctxt->error(ctxt->userData,
5767 "xmlRelaxNGParse: externalRef has no href attribute\n");
5768 ctxt->nbErrors++;
5769 delete = cur;
5770 goto skip_children;
5771 }
5772 base = xmlNodeGetBase(cur->doc, cur);
5773 URL = xmlBuildURI(href, base);
5774 if (URL == NULL) {
5775 if (ctxt->error != NULL)
5776 ctxt->error(ctxt->userData,
5777 "Failed to compute URL for externalRef %s\n", href);
5778 ctxt->nbErrors++;
5779 if (href != NULL)
5780 xmlFree(href);
5781 if (base != NULL)
5782 xmlFree(base);
5783 delete = cur;
5784 goto skip_children;
5785 }
5786 if (href != NULL)
5787 xmlFree(href);
5788 if (base != NULL)
5789 xmlFree(base);
5790 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
5791 if (docu == NULL) {
5792 if (ctxt->error != NULL)
5793 ctxt->error(ctxt->userData,
5794 "Failed to load externalRef %s\n", URL);
5795 ctxt->nbErrors++;
5796 xmlFree(URL);
5797 delete = cur;
5798 goto skip_children;
5799 }
5800 if (ns != NULL)
5801 xmlFree(ns);
5802 xmlFree(URL);
5803 cur->_private = docu;
5804 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
5805 xmlChar *href, *ns, *base, *URL;
5806 xmlRelaxNGIncludePtr incl;
5807 xmlNodePtr tmp;
5808
5809 href = xmlGetProp(cur, BAD_CAST "href");
5810 if (href == NULL) {
5811 if (ctxt->error != NULL)
5812 ctxt->error(ctxt->userData,
5813 "xmlRelaxNGParse: include has no href attribute\n");
5814 ctxt->nbErrors++;
5815 delete = cur;
5816 goto skip_children;
5817 }
5818 base = xmlNodeGetBase(cur->doc, cur);
5819 URL = xmlBuildURI(href, base);
5820 if (URL == NULL) {
5821 if (ctxt->error != NULL)
5822 ctxt->error(ctxt->userData,
5823 "Failed to compute URL for include %s\n", href);
5824 ctxt->nbErrors++;
5825 if (href != NULL)
5826 xmlFree(href);
5827 if (base != NULL)
5828 xmlFree(base);
5829 delete = cur;
5830 goto skip_children;
5831 }
5832 if (href != NULL)
5833 xmlFree(href);
5834 if (base != NULL)
5835 xmlFree(base);
5836 ns = xmlGetProp(cur, BAD_CAST "ns");
5837 if (ns == NULL) {
5838 tmp = cur->parent;
5839 while ((tmp != NULL) &&
5840 (tmp->type == XML_ELEMENT_NODE)) {
5841 ns = xmlGetProp(tmp, BAD_CAST "ns");
5842 if (ns != NULL)
5843 break;
5844 tmp = tmp->parent;
5845 }
5846 }
5847 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
5848 if (ns != NULL)
5849 xmlFree(ns);
5850 if (incl == NULL) {
5851 if (ctxt->error != NULL)
5852 ctxt->error(ctxt->userData,
5853 "Failed to load include %s\n", URL);
5854 ctxt->nbErrors++;
5855 xmlFree(URL);
5856 delete = cur;
5857 goto skip_children;
5858 }
5859 xmlFree(URL);
5860 cur->_private = incl;
5861 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
5862 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
5863 xmlChar *name, *ns;
5864 xmlNodePtr text = NULL;
5865
5866 /*
5867 * Simplification 4.8. name attribute of element
5868 * and attribute elements
5869 */
5870 name = xmlGetProp(cur, BAD_CAST "name");
5871 if (name != NULL) {
5872 if (cur->children == NULL) {
5873 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
5874 name);
5875 } else {
5876 xmlNodePtr node;
5877 node = xmlNewNode(cur->ns, BAD_CAST "name");
5878 if (node != NULL) {
5879 xmlAddPrevSibling(cur->children, node);
5880 text = xmlNewText(name);
5881 xmlAddChild(node, text);
5882 text = node;
5883 }
5884 }
5885 if (text == NULL) {
5886 if (ctxt->error != NULL)
5887 ctxt->error(ctxt->userData,
5888 "Failed to create a name %s element\n", name);
5889 ctxt->nbErrors++;
5890 }
5891 xmlUnsetProp(cur, BAD_CAST "name");
5892 xmlFree(name);
5893 ns = xmlGetProp(cur, BAD_CAST "ns");
5894 if (ns != NULL) {
5895 if (text != NULL) {
5896 xmlSetProp(text, BAD_CAST "ns", ns);
5897 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
5898 }
5899 xmlFree(ns);
5900 } else if (xmlStrEqual(cur->name,
5901 BAD_CAST "attribute")) {
5902 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
5903 }
5904 }
5905 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
5906 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
5907 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
5908 /*
5909 * Simplification 4.8. name attribute of element
5910 * and attribute elements
5911 */
5912 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
5913 xmlNodePtr node;
5914 xmlChar *ns = NULL;
5915
5916 node = cur->parent;
5917 while ((node != NULL) &&
5918 (node->type == XML_ELEMENT_NODE)) {
5919 ns = xmlGetProp(node, BAD_CAST "ns");
5920 if (ns != NULL) {
5921 break;
5922 }
5923 node = node->parent;
5924 }
5925 if (ns == NULL) {
5926 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
5927 } else {
5928 xmlSetProp(cur, BAD_CAST "ns", ns);
5929 xmlFree(ns);
5930 }
5931 }
5932 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
5933 xmlChar *name, *local, *prefix;
5934
5935 /*
5936 * Simplification: 4.10. QNames
5937 */
5938 name = xmlNodeGetContent(cur);
5939 if (name != NULL) {
5940 local = xmlSplitQName2(name, &prefix);
5941 if (local != NULL) {
5942 xmlNsPtr ns;
5943
5944 ns = xmlSearchNs(cur->doc, cur, prefix);
5945 if (ns == NULL) {
5946 if (ctxt->error != NULL)
5947 ctxt->error(ctxt->userData,
5948 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
5949 ctxt->nbErrors++;
5950 } else {
5951 xmlSetProp(cur, BAD_CAST "ns", ns->href);
5952 xmlNodeSetContent(cur, local);
5953 }
5954 xmlFree(local);
5955 xmlFree(prefix);
5956 }
5957 xmlFree(name);
5958 }
5959 }
5960 /*
5961 * 4.16
5962 */
5963 if (xmlStrEqual(cur->name, BAD_CAST "nsName")) {
5964 if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5965 if (ctxt->error != NULL)
5966 ctxt->error(ctxt->userData,
5967 "Found nsName/except//nsName forbidden construct\n");
5968 ctxt->nbErrors++;
5969 }
5970 }
5971 } else if ((xmlStrEqual(cur->name, BAD_CAST "except")) &&
5972 (cur != root)) {
5973 int oldflags = ctxt->flags;
5974
5975 /*
5976 * 4.16
5977 */
5978 if ((cur->parent != NULL) &&
5979 (xmlStrEqual(cur->parent->name, BAD_CAST "anyName"))) {
5980 ctxt->flags |= XML_RELAXNG_IN_ANYEXCEPT;
5981 xmlRelaxNGCleanupTree(ctxt, cur);
5982 ctxt->flags = oldflags;
5983 goto skip_children;
5984 } else if ((cur->parent != NULL) &&
5985 (xmlStrEqual(cur->parent->name, BAD_CAST "nsName"))) {
5986 ctxt->flags |= XML_RELAXNG_IN_NSEXCEPT;
5987 xmlRelaxNGCleanupTree(ctxt, cur);
5988 ctxt->flags = oldflags;
5989 goto skip_children;
5990 }
5991 } else if (xmlStrEqual(cur->name, BAD_CAST "anyName")) {
5992 /*
5993 * 4.16
5994 */
5995 if (ctxt->flags & XML_RELAXNG_IN_ANYEXCEPT) {
5996 if (ctxt->error != NULL)
5997 ctxt->error(ctxt->userData,
5998 "Found anyName/except//anyName forbidden construct\n");
5999 ctxt->nbErrors++;
6000 } else if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
6001 if (ctxt->error != NULL)
6002 ctxt->error(ctxt->userData,
6003 "Found nsName/except//anyName forbidden construct\n");
6004 ctxt->nbErrors++;
6005 }
6006 }
6007 /*
6008 * Thisd is not an else since "include" is transformed
6009 * into a div
6010 */
6011 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
6012 xmlChar *ns;
6013 xmlNodePtr child, ins, tmp;
6014
6015 /*
6016 * implements rule 4.11
6017 */
6018
6019 ns = xmlGetProp(cur, BAD_CAST "ns");
6020
6021 child = cur->children;
6022 ins = cur;
6023 while (child != NULL) {
6024 if (ns != NULL) {
6025 if (!xmlHasProp(child, BAD_CAST "ns")) {
6026 xmlSetProp(child, BAD_CAST "ns", ns);
6027 }
6028 }
6029 tmp = child->next;
6030 xmlUnlinkNode(child);
6031 ins = xmlAddNextSibling(ins, child);
6032 child = tmp;
6033 }
6034 if (ns != NULL)
6035 xmlFree(ns);
6036 delete = cur;
6037 goto skip_children;
6038 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006039 }
6040 }
6041 /*
6042 * Simplification 4.2 whitespaces
6043 */
Daniel Veillard39eb88b2003-03-11 11:21:28 +00006044 else if ((cur->type == XML_TEXT_NODE) ||
6045 (cur->type == XML_CDATA_SECTION_NODE)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006046 if (IS_BLANK_NODE(cur)) {
6047 if (cur->parent->type == XML_ELEMENT_NODE) {
6048 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
6049 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
6050 delete = cur;
6051 } else {
6052 delete = cur;
6053 goto skip_children;
6054 }
6055 }
Daniel Veillard39eb88b2003-03-11 11:21:28 +00006056 } else {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006057 delete = cur;
6058 goto skip_children;
6059 }
6060
6061 /*
6062 * Skip to next node
6063 */
6064 if (cur->children != NULL) {
6065 if ((cur->children->type != XML_ENTITY_DECL) &&
6066 (cur->children->type != XML_ENTITY_REF_NODE) &&
6067 (cur->children->type != XML_ENTITY_NODE)) {
6068 cur = cur->children;
6069 continue;
6070 }
6071 }
6072skip_children:
6073 if (cur->next != NULL) {
6074 cur = cur->next;
6075 continue;
6076 }
6077
6078 do {
6079 cur = cur->parent;
6080 if (cur == NULL)
6081 break;
6082 if (cur == root) {
6083 cur = NULL;
6084 break;
6085 }
6086 if (cur->next != NULL) {
6087 cur = cur->next;
6088 break;
6089 }
6090 } while (cur != NULL);
6091 }
6092 if (delete != NULL) {
6093 xmlUnlinkNode(delete);
6094 xmlFreeNode(delete);
6095 delete = NULL;
6096 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00006097}
Daniel Veillard6eadf632003-01-23 18:29:16 +00006098
Daniel Veillardc5312d72003-02-21 17:14:10 +00006099/**
6100 * xmlRelaxNGCleanupDoc:
6101 * @ctxt: a Relax-NG parser context
6102 * @doc: an xmldocPtr document pointer
6103 *
6104 * Cleanup the document from unwanted nodes for parsing, resolve
6105 * Include and externalRef lookups.
6106 *
6107 * Returns the cleaned up document or NULL in case of error
6108 */
6109static xmlDocPtr
6110xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
6111 xmlNodePtr root;
6112
6113 /*
6114 * Extract the root
6115 */
6116 root = xmlDocGetRootElement(doc);
6117 if (root == NULL) {
6118 if (ctxt->error != NULL)
6119 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
6120 ctxt->URL);
6121 ctxt->nbErrors++;
6122 return (NULL);
6123 }
6124 xmlRelaxNGCleanupTree(ctxt, root);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006125 return(doc);
6126}
6127
6128/**
6129 * xmlRelaxNGParse:
6130 * @ctxt: a Relax-NG parser context
6131 *
6132 * parse a schema definition resource and build an internal
6133 * XML Shema struture which can be used to validate instances.
6134 * *WARNING* this interface is highly subject to change
6135 *
6136 * Returns the internal XML RelaxNG structure built from the resource or
6137 * NULL in case of error
6138 */
6139xmlRelaxNGPtr
6140xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
6141{
6142 xmlRelaxNGPtr ret = NULL;
6143 xmlDocPtr doc;
6144 xmlNodePtr root;
6145
6146 xmlRelaxNGInitTypes();
6147
6148 if (ctxt == NULL)
6149 return (NULL);
6150
6151 /*
6152 * First step is to parse the input document into an DOM/Infoset
6153 */
6154 if (ctxt->URL != NULL) {
6155 doc = xmlParseFile((const char *) ctxt->URL);
6156 if (doc == NULL) {
6157 if (ctxt->error != NULL)
6158 ctxt->error(ctxt->userData,
6159 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
6160 ctxt->nbErrors++;
6161 return (NULL);
6162 }
6163 } else if (ctxt->buffer != NULL) {
6164 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
6165 if (doc == NULL) {
6166 if (ctxt->error != NULL)
6167 ctxt->error(ctxt->userData,
6168 "xmlRelaxNGParse: could not parse schemas\n");
6169 ctxt->nbErrors++;
6170 return (NULL);
6171 }
6172 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
6173 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
6174 } else {
6175 if (ctxt->error != NULL)
6176 ctxt->error(ctxt->userData,
6177 "xmlRelaxNGParse: nothing to parse\n");
6178 ctxt->nbErrors++;
6179 return (NULL);
6180 }
6181 ctxt->document = doc;
6182
6183 /*
6184 * Some preprocessing of the document content
6185 */
6186 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
6187 if (doc == NULL) {
6188 xmlFreeDoc(ctxt->document);
6189 ctxt->document = NULL;
6190 return(NULL);
6191 }
6192
Daniel Veillard6eadf632003-01-23 18:29:16 +00006193 /*
6194 * Then do the parsing for good
6195 */
6196 root = xmlDocGetRootElement(doc);
6197 if (root == NULL) {
6198 if (ctxt->error != NULL)
6199 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
6200 ctxt->URL);
6201 ctxt->nbErrors++;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006202 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006203 return (NULL);
6204 }
6205 ret = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006206 if (ret == NULL) {
6207 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006208 return(NULL);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006209 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006210
6211 /*
Daniel Veillardfd573f12003-03-16 17:52:32 +00006212 * Check the ref/defines links
6213 */
6214 /*
6215 * try to preprocess interleaves
6216 */
6217 if (ctxt->interleaves != NULL) {
6218 xmlHashScan(ctxt->interleaves,
6219 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
6220 }
6221
6222 /*
Daniel Veillard6eadf632003-01-23 18:29:16 +00006223 * if there was a parsing error return NULL
6224 */
6225 if (ctxt->nbErrors > 0) {
6226 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006227 ctxt->document = NULL;
6228 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006229 return(NULL);
6230 }
6231
6232 /*
6233 * Transfer the pointer for cleanup at the schema level.
6234 */
6235 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006236 ctxt->document = NULL;
6237 ret->documents = ctxt->documents;
6238 ctxt->documents = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006239
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006240 ret->includes = ctxt->includes;
6241 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00006242 ret->defNr = ctxt->defNr;
6243 ret->defTab = ctxt->defTab;
6244 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006245
6246 return (ret);
6247}
6248
6249/**
6250 * xmlRelaxNGSetParserErrors:
6251 * @ctxt: a Relax-NG validation context
6252 * @err: the error callback
6253 * @warn: the warning callback
6254 * @ctx: contextual data for the callbacks
6255 *
6256 * Set the callback functions used to handle errors for a validation context
6257 */
6258void
6259xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
6260 xmlRelaxNGValidityErrorFunc err,
6261 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
6262 if (ctxt == NULL)
6263 return;
6264 ctxt->error = err;
6265 ctxt->warning = warn;
6266 ctxt->userData = ctx;
6267}
6268/************************************************************************
6269 * *
6270 * Dump back a compiled form *
6271 * *
6272 ************************************************************************/
6273static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
6274
6275/**
6276 * xmlRelaxNGDumpDefines:
6277 * @output: the file output
6278 * @defines: a list of define structures
6279 *
6280 * Dump a RelaxNG structure back
6281 */
6282static void
6283xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
6284 while (defines != NULL) {
6285 xmlRelaxNGDumpDefine(output, defines);
6286 defines = defines->next;
6287 }
6288}
6289
6290/**
6291 * xmlRelaxNGDumpDefine:
6292 * @output: the file output
6293 * @define: a define structure
6294 *
6295 * Dump a RelaxNG structure back
6296 */
6297static void
6298xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
6299 if (define == NULL)
6300 return;
6301 switch(define->type) {
6302 case XML_RELAXNG_EMPTY:
6303 fprintf(output, "<empty/>\n");
6304 break;
6305 case XML_RELAXNG_NOT_ALLOWED:
6306 fprintf(output, "<notAllowed/>\n");
6307 break;
6308 case XML_RELAXNG_TEXT:
6309 fprintf(output, "<text/>\n");
6310 break;
6311 case XML_RELAXNG_ELEMENT:
6312 fprintf(output, "<element>\n");
6313 if (define->name != NULL) {
6314 fprintf(output, "<name");
6315 if (define->ns != NULL)
6316 fprintf(output, " ns=\"%s\"", define->ns);
6317 fprintf(output, ">%s</name>\n", define->name);
6318 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00006319 xmlRelaxNGDumpDefines(output, define->attrs);
6320 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006321 fprintf(output, "</element>\n");
6322 break;
6323 case XML_RELAXNG_LIST:
6324 fprintf(output, "<list>\n");
6325 xmlRelaxNGDumpDefines(output, define->content);
6326 fprintf(output, "</list>\n");
6327 break;
6328 case XML_RELAXNG_ONEORMORE:
6329 fprintf(output, "<oneOrMore>\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006330 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006331 fprintf(output, "</oneOrMore>\n");
6332 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00006333 case XML_RELAXNG_ZEROORMORE:
6334 fprintf(output, "<zeroOrMore>\n");
6335 xmlRelaxNGDumpDefines(output, define->content);
6336 fprintf(output, "</zeroOrMore>\n");
6337 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006338 case XML_RELAXNG_CHOICE:
6339 fprintf(output, "<choice>\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006340 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006341 fprintf(output, "</choice>\n");
6342 break;
6343 case XML_RELAXNG_GROUP:
6344 fprintf(output, "<group>\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006345 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006346 fprintf(output, "</group>\n");
6347 break;
6348 case XML_RELAXNG_INTERLEAVE:
6349 fprintf(output, "<interleave>\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006350 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006351 fprintf(output, "</interleave>\n");
6352 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00006353 case XML_RELAXNG_OPTIONAL:
6354 fprintf(output, "<optional>\n");
6355 xmlRelaxNGDumpDefines(output, define->content);
6356 fprintf(output, "</optional>\n");
6357 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006358 case XML_RELAXNG_ATTRIBUTE:
6359 fprintf(output, "<attribute>\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006360 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006361 fprintf(output, "</attribute>\n");
6362 break;
6363 case XML_RELAXNG_DEF:
6364 fprintf(output, "<define");
6365 if (define->name != NULL)
6366 fprintf(output, " name=\"%s\"", define->name);
6367 fprintf(output, ">\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006368 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006369 fprintf(output, "</define>\n");
6370 break;
6371 case XML_RELAXNG_REF:
6372 fprintf(output, "<ref");
6373 if (define->name != NULL)
6374 fprintf(output, " name=\"%s\"", define->name);
6375 fprintf(output, ">\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006376 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006377 fprintf(output, "</ref>\n");
6378 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00006379 case XML_RELAXNG_PARENTREF:
6380 fprintf(output, "<parentRef");
6381 if (define->name != NULL)
6382 fprintf(output, " name=\"%s\"", define->name);
6383 fprintf(output, ">\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006384 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard419a7682003-02-03 23:22:49 +00006385 fprintf(output, "</parentRef>\n");
6386 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006387 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00006388 fprintf(output, "<externalRef>");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006389 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillarde431a272003-01-29 23:02:33 +00006390 fprintf(output, "</externalRef>\n");
6391 break;
6392 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006393 case XML_RELAXNG_VALUE:
Daniel Veillardfd573f12003-03-16 17:52:32 +00006394 TODO
Daniel Veillard6eadf632003-01-23 18:29:16 +00006395 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006396 case XML_RELAXNG_START:
Daniel Veillardfd573f12003-03-16 17:52:32 +00006397 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00006398 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006399 TODO
6400 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00006401 case XML_RELAXNG_NOOP:
Daniel Veillardfd573f12003-03-16 17:52:32 +00006402 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard77648bb2003-02-20 15:03:22 +00006403 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006404 }
6405}
6406
6407/**
6408 * xmlRelaxNGDumpGrammar:
6409 * @output: the file output
6410 * @grammar: a grammar structure
6411 * @top: is this a top grammar
6412 *
6413 * Dump a RelaxNG structure back
6414 */
6415static void
6416xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
6417{
6418 if (grammar == NULL)
6419 return;
6420
6421 fprintf(output, "<grammar");
6422 if (top)
6423 fprintf(output,
6424 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
6425 switch(grammar->combine) {
6426 case XML_RELAXNG_COMBINE_UNDEFINED:
6427 break;
6428 case XML_RELAXNG_COMBINE_CHOICE:
6429 fprintf(output, " combine=\"choice\"");
6430 break;
6431 case XML_RELAXNG_COMBINE_INTERLEAVE:
6432 fprintf(output, " combine=\"interleave\"");
6433 break;
6434 default:
6435 fprintf(output, " <!-- invalid combine value -->");
6436 }
6437 fprintf(output, ">\n");
6438 if (grammar->start == NULL) {
6439 fprintf(output, " <!-- grammar had no start -->");
6440 } else {
6441 fprintf(output, "<start>\n");
6442 xmlRelaxNGDumpDefine(output, grammar->start);
6443 fprintf(output, "</start>\n");
6444 }
6445 /* TODO ? Dump the defines ? */
6446 fprintf(output, "</grammar>\n");
6447}
6448
6449/**
6450 * xmlRelaxNGDump:
6451 * @output: the file output
6452 * @schema: a schema structure
6453 *
6454 * Dump a RelaxNG structure back
6455 */
6456void
6457xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
6458{
6459 if (schema == NULL) {
6460 fprintf(output, "RelaxNG empty or failed to compile\n");
6461 return;
6462 }
6463 fprintf(output, "RelaxNG: ");
6464 if (schema->doc == NULL) {
6465 fprintf(output, "no document\n");
6466 } else if (schema->doc->URL != NULL) {
6467 fprintf(output, "%s\n", schema->doc->URL);
6468 } else {
6469 fprintf(output, "\n");
6470 }
6471 if (schema->topgrammar == NULL) {
6472 fprintf(output, "RelaxNG has no top grammar\n");
6473 return;
6474 }
6475 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
6476}
6477
Daniel Veillardfebcca42003-02-16 15:44:18 +00006478/**
6479 * xmlRelaxNGDumpTree:
6480 * @output: the file output
6481 * @schema: a schema structure
6482 *
6483 * Dump the transformed RelaxNG tree.
6484 */
6485void
6486xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
6487{
6488 if (schema == NULL) {
6489 fprintf(output, "RelaxNG empty or failed to compile\n");
6490 return;
6491 }
6492 if (schema->doc == NULL) {
6493 fprintf(output, "no document\n");
6494 } else {
6495 xmlDocDump(output, schema->doc);
6496 }
6497}
6498
Daniel Veillard6eadf632003-01-23 18:29:16 +00006499/************************************************************************
6500 * *
6501 * Validation implementation *
6502 * *
6503 ************************************************************************/
Daniel Veillardfd573f12003-03-16 17:52:32 +00006504static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
6505 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006506static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
6507 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006508
6509/**
6510 * xmlRelaxNGSkipIgnored:
6511 * @ctxt: a schema validation context
6512 * @node: the top node.
6513 *
6514 * Skip ignorable nodes in that context
6515 *
6516 * Returns the new sibling or NULL in case of error.
6517 */
6518static xmlNodePtr
6519xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
6520 xmlNodePtr node) {
6521 /*
6522 * TODO complete and handle entities
6523 */
6524 while ((node != NULL) &&
6525 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006526 (node->type == XML_PI_NODE) ||
Daniel Veillard39eb88b2003-03-11 11:21:28 +00006527 (((node->type == XML_TEXT_NODE) ||
6528 (node->type == XML_CDATA_SECTION_NODE)) &&
Daniel Veillard6eadf632003-01-23 18:29:16 +00006529 (IS_BLANK_NODE(node))))) {
6530 node = node->next;
6531 }
6532 return(node);
6533}
6534
6535/**
Daniel Veillardedc91922003-01-26 00:52:04 +00006536 * xmlRelaxNGNormalize:
6537 * @ctxt: a schema validation context
6538 * @str: the string to normalize
6539 *
6540 * Implements the normalizeWhiteSpace( s ) function from
6541 * section 6.2.9 of the spec
6542 *
6543 * Returns the new string or NULL in case of error.
6544 */
6545static xmlChar *
6546xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
6547 xmlChar *ret, *p;
6548 const xmlChar *tmp;
6549 int len;
6550
6551 if (str == NULL)
6552 return(NULL);
6553 tmp = str;
6554 while (*tmp != 0) tmp++;
6555 len = tmp - str;
6556
6557 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
6558 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00006559 if (ctxt != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006560 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardea3f3982003-01-26 19:45:18 +00006561 } else {
6562 xmlGenericError(xmlGenericErrorContext,
6563 "xmlRelaxNGNormalize: out of memory\n");
6564 }
Daniel Veillardedc91922003-01-26 00:52:04 +00006565 return(NULL);
6566 }
6567 p = ret;
6568 while (IS_BLANK(*str)) str++;
6569 while (*str != 0) {
6570 if (IS_BLANK(*str)) {
6571 while (IS_BLANK(*str)) str++;
6572 if (*str == 0)
6573 break;
6574 *p++ = ' ';
6575 } else
6576 *p++ = *str++;
6577 }
6578 *p = 0;
6579 return(ret);
6580}
6581
6582/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006583 * xmlRelaxNGValidateDatatype:
6584 * @ctxt: a Relax-NG validation context
6585 * @value: the string value
6586 * @type: the datatype definition
6587 *
6588 * Validate the given value against the dataype
6589 *
6590 * Returns 0 if the validation succeeded or an error code.
6591 */
6592static int
6593xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
6594 xmlRelaxNGDefinePtr define) {
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006595 int ret, tmp;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006596 xmlRelaxNGTypeLibraryPtr lib;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006597 void *result = NULL;
6598 xmlRelaxNGDefinePtr cur;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006599
6600 if ((define == NULL) || (define->data == NULL)) {
6601 return(-1);
6602 }
6603 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006604 if (lib->check != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00006605 if ((define->attrs != NULL) &&
6606 (define->attrs->type == XML_RELAXNG_PARAM)) {
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006607 ret = lib->check(lib->data, define->name, value, &result);
6608 } else {
6609 ret = lib->check(lib->data, define->name, value, NULL);
6610 }
6611 } else
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006612 ret = -1;
6613 if (ret < 0) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006614 VALID_ERR2(XML_RELAXNG_ERR_TYPE, define->name);
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006615 if ((result != NULL) && (lib != NULL) && (lib->freef != NULL))
6616 lib->freef(lib->data, result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006617 return(-1);
6618 } else if (ret == 1) {
6619 ret = 0;
6620 } else {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006621 VALID_ERR3(XML_RELAXNG_ERR_TYPEVAL, define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006622 ret = -1;
6623 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00006624 cur = define->attrs;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006625 while ((ret == 0) && (cur != NULL) && (cur->type == XML_RELAXNG_PARAM)) {
6626 if (lib->facet != NULL) {
6627 tmp = lib->facet(lib->data, define->name, cur->name,
6628 cur->value, value, result);
6629 if (tmp != 0)
6630 ret = -1;
6631 }
6632 cur = cur->next;
6633 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006634 if ((ret == 0) && (define->content != NULL)) {
6635 const xmlChar *oldvalue, *oldendvalue;
6636
6637 oldvalue = ctxt->state->value;
6638 oldendvalue = ctxt->state->endvalue;
6639 ctxt->state->value = (xmlChar *) value;
6640 ctxt->state->endvalue = NULL;
6641 ret = xmlRelaxNGValidateValue(ctxt, define->content);
6642 ctxt->state->value = (xmlChar *) oldvalue;
6643 ctxt->state->endvalue = (xmlChar *) oldendvalue;
6644 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006645 if ((result != NULL) && (lib != NULL) && (lib->freef != NULL))
6646 lib->freef(lib->data, result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006647 return(ret);
6648}
6649
6650/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006651 * xmlRelaxNGNextValue:
6652 * @ctxt: a Relax-NG validation context
6653 *
6654 * Skip to the next value when validating within a list
6655 *
6656 * Returns 0 if the operation succeeded or an error code.
6657 */
6658static int
6659xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
6660 xmlChar *cur;
6661
6662 cur = ctxt->state->value;
6663 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
6664 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006665 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006666 return(0);
6667 }
6668 while (*cur != 0) cur++;
6669 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
6670 if (cur == ctxt->state->endvalue)
6671 ctxt->state->value = NULL;
6672 else
6673 ctxt->state->value = cur;
6674 return(0);
6675}
6676
6677/**
6678 * xmlRelaxNGValidateValueList:
6679 * @ctxt: a Relax-NG validation context
6680 * @defines: the list of definitions to verify
6681 *
6682 * Validate the given set of definitions for the current value
6683 *
6684 * Returns 0 if the validation succeeded or an error code.
6685 */
6686static int
6687xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
6688 xmlRelaxNGDefinePtr defines) {
6689 int ret = 0;
6690
6691 while (defines != NULL) {
6692 ret = xmlRelaxNGValidateValue(ctxt, defines);
6693 if (ret != 0)
6694 break;
6695 defines = defines->next;
6696 }
6697 return(ret);
6698}
6699
6700/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006701 * xmlRelaxNGValidateValue:
6702 * @ctxt: a Relax-NG validation context
6703 * @define: the definition to verify
6704 *
6705 * Validate the given definition for the current value
6706 *
6707 * Returns 0 if the validation succeeded or an error code.
6708 */
6709static int
6710xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
6711 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00006712 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006713 xmlChar *value;
6714
6715 value = ctxt->state->value;
6716 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006717 case XML_RELAXNG_EMPTY: {
6718 if ((value != NULL) && (value[0] != 0)) {
6719 int idx = 0;
6720
6721 while (IS_BLANK(value[idx]))
6722 idx++;
6723 if (value[idx] != 0)
6724 ret = -1;
6725 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006726 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00006727 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006728 case XML_RELAXNG_TEXT:
6729 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00006730 case XML_RELAXNG_VALUE: {
6731 if (!xmlStrEqual(value, define->value)) {
6732 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00006733 xmlRelaxNGTypeLibraryPtr lib;
6734
6735 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
6736 if ((lib != NULL) && (lib->comp != NULL))
6737 ret = lib->comp(lib->data, define->name, value,
6738 define->value);
6739 else
6740 ret = -1;
6741 if (ret < 0) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006742 VALID_ERR2(XML_RELAXNG_ERR_TYPECMP, define->name);
Daniel Veillardea3f3982003-01-26 19:45:18 +00006743 return(-1);
6744 } else if (ret == 1) {
6745 ret = 0;
6746 } else {
6747 ret = -1;
6748 }
Daniel Veillardedc91922003-01-26 00:52:04 +00006749 } else {
6750 xmlChar *nval, *nvalue;
6751
6752 /*
6753 * TODO: trivial optimizations are possible by
6754 * computing at compile-time
6755 */
6756 nval = xmlRelaxNGNormalize(ctxt, define->value);
6757 nvalue = xmlRelaxNGNormalize(ctxt, value);
6758
Daniel Veillardea3f3982003-01-26 19:45:18 +00006759 if ((nval == NULL) || (nvalue == NULL) ||
6760 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00006761 ret = -1;
6762 if (nval != NULL)
6763 xmlFree(nval);
6764 if (nvalue != NULL)
6765 xmlFree(nvalue);
6766 }
6767 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006768 if (ret == 0)
6769 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00006770 break;
6771 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006772 case XML_RELAXNG_DATATYPE: {
6773 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
6774 if (ret == 0)
6775 xmlRelaxNGNextValue(ctxt);
6776
6777 break;
6778 }
6779 case XML_RELAXNG_CHOICE: {
Daniel Veillardfd573f12003-03-16 17:52:32 +00006780 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006781 xmlChar *oldvalue;
6782
6783 oldflags = ctxt->flags;
6784 ctxt->flags |= FLAGS_IGNORABLE;
6785
6786 oldvalue = ctxt->state->value;
Daniel Veillardfd573f12003-03-16 17:52:32 +00006787 while (list != NULL) {
6788 ret = xmlRelaxNGValidateValue(ctxt, list);
6789 if (ret == 0) {
6790 break;
6791 }
6792 ctxt->state->value = oldvalue;
6793 list = list->next;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006794 }
6795 ctxt->flags = oldflags;
Daniel Veillard42f12e92003-03-07 18:32:59 +00006796 if (ret != 0) {
6797 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
6798 xmlRelaxNGDumpValidError(ctxt);
6799 } else {
6800 ctxt->errNr = 0;
6801 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006802 if (ret == 0)
6803 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006804 break;
6805 }
6806 case XML_RELAXNG_LIST: {
Daniel Veillardfd573f12003-03-16 17:52:32 +00006807 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006808 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00006809#ifdef DEBUG_LIST
6810 int nb_values = 0;
6811#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006812
6813 oldvalue = ctxt->state->value;
6814 oldend = ctxt->state->endvalue;
6815
6816 val = xmlStrdup(oldvalue);
6817 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006818 val = xmlStrdup(BAD_CAST "");
6819 }
6820 if (val == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006821 VALID_ERR(XML_RELAXNG_ERR_NOSTATE);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006822 return(-1);
6823 }
6824 cur = val;
6825 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006826 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006827 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006828 cur++;
6829#ifdef DEBUG_LIST
6830 nb_values++;
6831#endif
6832 while (IS_BLANK(*cur))
6833 *cur++ = 0;
6834 } else
6835 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006836 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006837#ifdef DEBUG_LIST
6838 xmlGenericError(xmlGenericErrorContext,
6839 "list value: '%s' found %d items\n", oldvalue, nb_values);
6840 nb_values = 0;
6841#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006842 ctxt->state->endvalue = cur;
6843 cur = val;
6844 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00006845
Daniel Veillardfd573f12003-03-16 17:52:32 +00006846 ctxt->state->value = cur;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00006847
Daniel Veillardfd573f12003-03-16 17:52:32 +00006848 while (list != NULL) {
6849 if (ctxt->state->value == ctxt->state->endvalue)
6850 ctxt->state->value = NULL;
6851 ret = xmlRelaxNGValidateValue(ctxt, list);
6852 if (ret != 0) {
6853#ifdef DEBUG_LIST
6854 xmlGenericError(xmlGenericErrorContext,
6855 "Failed to validate value: '%s' with %d rule\n",
6856 ctxt->state->value, nb_values);
6857#endif
6858 break;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00006859 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00006860#ifdef DEBUG_LIST
6861 nb_values++;
6862#endif
6863 list = list->next;
6864 }
6865
6866 if ((ret == 0) && (ctxt->state->value != NULL) &&
6867 (ctxt->state->value != ctxt->state->endvalue)) {
6868 VALID_ERR2(XML_RELAXNG_ERR_LISTEXTRA, ctxt->state->value);
6869 ret = -1;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006870 }
6871 xmlFree(val);
6872 ctxt->state->value = oldvalue;
6873 ctxt->state->endvalue = oldend;
6874 break;
6875 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00006876 case XML_RELAXNG_ONEORMORE:
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006877 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6878 if (ret != 0) {
6879 break;
6880 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00006881 /* no break on purpose */
6882 case XML_RELAXNG_ZEROORMORE: {
6883 xmlChar *cur, *temp;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006884
6885 oldflags = ctxt->flags;
6886 ctxt->flags |= FLAGS_IGNORABLE;
6887 cur = ctxt->state->value;
6888 temp = NULL;
6889 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
6890 (temp != cur)) {
6891 temp = cur;
6892 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6893 if (ret != 0) {
6894 ctxt->state->value = temp;
6895 ret = 0;
6896 break;
6897 }
6898 cur = ctxt->state->value;
6899 }
6900 ctxt->flags = oldflags;
Daniel Veillard42f12e92003-03-07 18:32:59 +00006901 if (ret != 0) {
6902 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
6903 xmlRelaxNGDumpValidError(ctxt);
6904 } else {
6905 ctxt->errNr = 0;
6906 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006907 break;
6908 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006909 case XML_RELAXNG_EXCEPT: {
6910 xmlRelaxNGDefinePtr list;
6911
6912 list = define->content;
6913 while (list != NULL) {
6914 ret = xmlRelaxNGValidateValue(ctxt, list);
6915 if (ret == 0) {
6916 ret = -1;
6917 break;
6918 } else
6919 ret = 0;
6920 list = list->next;
6921 }
6922 break;
6923 }
Daniel Veillard463a5472003-02-27 21:30:32 +00006924 case XML_RELAXNG_DEF:
Daniel Veillardfd573f12003-03-16 17:52:32 +00006925 case XML_RELAXNG_GROUP: {
6926 xmlRelaxNGDefinePtr list;
6927
6928 list = define->content;
6929 while (list != NULL) {
6930 ret = xmlRelaxNGValidateValue(ctxt, list);
6931 if (ret != 0) {
6932 ret = -1;
6933 break;
6934 } else
6935 ret = 0;
6936 list = list->next;
6937 }
Daniel Veillardd4310742003-02-18 21:12:46 +00006938 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00006939 }
Daniel Veillard463a5472003-02-27 21:30:32 +00006940 case XML_RELAXNG_REF:
6941 case XML_RELAXNG_PARENTREF:
6942 ret = xmlRelaxNGValidateValue(ctxt, define->content);
6943 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006944 default:
6945 TODO
6946 ret = -1;
6947 }
6948 return(ret);
6949}
6950
6951/**
6952 * xmlRelaxNGValidateValueContent:
6953 * @ctxt: a Relax-NG validation context
6954 * @defines: the list of definitions to verify
6955 *
6956 * Validate the given definitions for the current value
6957 *
6958 * Returns 0 if the validation succeeded or an error code.
6959 */
6960static int
6961xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
6962 xmlRelaxNGDefinePtr defines) {
6963 int ret = 0;
6964
6965 while (defines != NULL) {
6966 ret = xmlRelaxNGValidateValue(ctxt, defines);
6967 if (ret != 0)
6968 break;
6969 defines = defines->next;
6970 }
6971 return(ret);
6972}
6973
6974/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00006975 * xmlRelaxNGAttributeMatch:
6976 * @ctxt: a Relax-NG validation context
6977 * @define: the definition to check
6978 * @prop: the attribute
6979 *
6980 * Check if the attribute matches the definition nameClass
6981 *
6982 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
6983 */
6984static int
6985xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
6986 xmlRelaxNGDefinePtr define,
6987 xmlAttrPtr prop) {
6988 int ret;
6989
6990 if (define->name != NULL) {
6991 if (!xmlStrEqual(define->name, prop->name))
6992 return(0);
6993 }
6994 if (define->ns != NULL) {
6995 if (define->ns[0] == 0) {
6996 if (prop->ns != NULL)
6997 return(0);
6998 } else {
6999 if ((prop->ns == NULL) ||
7000 (!xmlStrEqual(define->ns, prop->ns->href)))
7001 return(0);
7002 }
7003 }
7004 if (define->nameClass == NULL)
7005 return(1);
7006 define = define->nameClass;
7007 if (define->type == XML_RELAXNG_EXCEPT) {
7008 xmlRelaxNGDefinePtr list;
7009
7010 list = define->content;
7011 while (list != NULL) {
7012 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
7013 if (ret == 1)
7014 return(0);
7015 if (ret < 0)
7016 return(ret);
7017 list = list->next;
7018 }
7019 } else {
7020 TODO
7021 }
7022 return(1);
7023}
7024
7025/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00007026 * xmlRelaxNGValidateAttribute:
7027 * @ctxt: a Relax-NG validation context
7028 * @define: the definition to verify
7029 *
7030 * Validate the given attribute definition for that node
7031 *
7032 * Returns 0 if the validation succeeded or an error code.
7033 */
7034static int
7035xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
7036 xmlRelaxNGDefinePtr define) {
7037 int ret = 0, i;
7038 xmlChar *value, *oldvalue;
7039 xmlAttrPtr prop = NULL, tmp;
7040
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007041 if (ctxt->state->nbAttrLeft <= 0)
7042 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007043 if (define->name != NULL) {
7044 for (i = 0;i < ctxt->state->nbAttrs;i++) {
7045 tmp = ctxt->state->attrs[i];
7046 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
7047 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
7048 (tmp->ns == NULL)) ||
7049 ((tmp->ns != NULL) &&
7050 (xmlStrEqual(define->ns, tmp->ns->href)))) {
7051 prop = tmp;
7052 break;
7053 }
7054 }
7055 }
7056 if (prop != NULL) {
7057 value = xmlNodeListGetString(prop->doc, prop->children, 1);
7058 oldvalue = ctxt->state->value;
7059 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00007060 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007061 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00007062 if (ctxt->state->value != NULL)
7063 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007064 if (value != NULL)
7065 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00007066 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007067 if (ret == 0) {
7068 /*
7069 * flag the attribute as processed
7070 */
7071 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007072 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007073 }
7074 } else {
7075 ret = -1;
7076 }
7077#ifdef DEBUG
7078 xmlGenericError(xmlGenericErrorContext,
7079 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
7080#endif
7081 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007082 for (i = 0;i < ctxt->state->nbAttrs;i++) {
7083 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00007084 if ((tmp != NULL) &&
Daniel Veillardfd573f12003-03-16 17:52:32 +00007085 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007086 prop = tmp;
7087 break;
7088 }
7089 }
7090 if (prop != NULL) {
7091 value = xmlNodeListGetString(prop->doc, prop->children, 1);
7092 oldvalue = ctxt->state->value;
7093 ctxt->state->value = value;
7094 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00007095 if (ctxt->state->value != NULL)
7096 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007097 if (value != NULL)
7098 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00007099 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007100 if (ret == 0) {
7101 /*
7102 * flag the attribute as processed
7103 */
7104 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007105 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007106 }
7107 } else {
7108 ret = -1;
7109 }
7110#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00007111 if (define->ns != NULL) {
7112 xmlGenericError(xmlGenericErrorContext,
7113 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
7114 define->ns, ret);
7115 } else {
7116 xmlGenericError(xmlGenericErrorContext,
7117 "xmlRelaxNGValidateAttribute(anyName): %d\n",
7118 ret);
7119 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007120#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00007121 }
7122
7123 return(ret);
7124}
7125
7126/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00007127 * xmlRelaxNGValidateAttributeList:
7128 * @ctxt: a Relax-NG validation context
7129 * @define: the list of definition to verify
7130 *
7131 * Validate the given node against the list of attribute definitions
7132 *
7133 * Returns 0 if the validation succeeded or an error code.
7134 */
7135static int
7136xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
7137 xmlRelaxNGDefinePtr defines) {
7138 int ret = 0;
7139 while (defines != NULL) {
7140 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
7141 ret = -1;
7142 defines = defines->next;
7143 }
7144 return(ret);
7145}
7146
7147/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00007148 * xmlRelaxNGNodeMatchesList:
7149 * @node: the node
7150 * @list: a NULL terminated array of definitions
7151 *
7152 * Check if a node can be matched by one of the definitions
7153 *
7154 * Returns 1 if matches 0 otherwise
7155 */
7156static int
7157xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
7158 xmlRelaxNGDefinePtr cur;
7159 int i = 0;
7160
7161 if ((node == NULL) || (list == NULL))
7162 return(0);
7163
7164 cur = list[i++];
7165 while (cur != NULL) {
7166 if ((node->type == XML_ELEMENT_NODE) &&
7167 (cur->type == XML_RELAXNG_ELEMENT)) {
7168 if (cur->name == NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00007169 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
7170 if (node->ns == NULL)
7171 return(1);
7172 } else {
7173 if ((node->ns != NULL) &&
7174 (xmlStrEqual(node->ns->href, cur->ns)))
7175 return(1);
7176 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00007177 } else if (xmlStrEqual(cur->name, node->name)) {
7178 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
7179 if (node->ns == NULL)
7180 return(1);
7181 } else {
7182 if ((node->ns != NULL) &&
7183 (xmlStrEqual(node->ns->href, cur->ns)))
7184 return(1);
7185 }
7186 }
Daniel Veillard39eb88b2003-03-11 11:21:28 +00007187 } else if (((node->type == XML_TEXT_NODE) ||
7188 (node->type == XML_CDATA_SECTION_NODE)) &&
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00007189 (cur->type == XML_RELAXNG_TEXT)) {
7190 return(1);
7191 }
7192 cur = list[i++];
7193 }
7194 return(0);
7195}
7196
7197/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00007198 * xmlRelaxNGValidateInterleave:
7199 * @ctxt: a Relax-NG validation context
7200 * @define: the definition to verify
7201 *
7202 * Validate an interleave definition for a node.
7203 *
7204 * Returns 0 if the validation succeeded or an error code.
7205 */
7206static int
7207xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
7208 xmlRelaxNGDefinePtr define) {
7209 int ret = 0, i, nbgroups, left;
7210 int errNr = ctxt->errNr;
7211
7212 xmlRelaxNGValidStatePtr oldstate;
7213 xmlRelaxNGPartitionPtr partitions;
7214 xmlRelaxNGInterleaveGroupPtr group = NULL;
7215 xmlNodePtr cur, start, last = NULL, lastchg = NULL, lastelem;
7216 xmlNodePtr *list = NULL, *lasts = NULL;
7217
7218 if (define->data != NULL) {
7219 partitions = (xmlRelaxNGPartitionPtr) define->data;
7220 nbgroups = partitions->nbgroups;
7221 left = nbgroups;
7222 } else {
7223 VALID_ERR(XML_RELAXNG_ERR_INTERNODATA);
7224 return(-1);
7225 }
7226
7227 /*
7228 * Build arrays to store the first and last node of the chain
7229 * pertaining to each group
7230 */
7231 list = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
7232 if (list == NULL) {
7233 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
7234 return(-1);
7235 }
7236 memset(list, 0, nbgroups * sizeof(xmlNodePtr));
7237 lasts = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
7238 if (lasts == NULL) {
7239 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
7240 return(-1);
7241 }
7242 memset(lasts, 0, nbgroups * sizeof(xmlNodePtr));
7243
7244 /*
7245 * Walk the sequence of children finding the right group and
7246 * sorting them in sequences.
7247 */
7248 cur = ctxt->state->seq;
7249 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
7250 start = cur;
7251 while (cur != NULL) {
7252 ctxt->state->seq = cur;
7253 for (i = 0;i < nbgroups;i++) {
7254 group = partitions->groups[i];
7255 if (group == NULL)
7256 continue;
7257 if (xmlRelaxNGNodeMatchesList(cur, group->defs))
7258 break;
7259 }
7260 /*
7261 * We break as soon as an element not matched is found
7262 */
7263 if (i >= nbgroups) {
7264 break;
7265 }
7266 if (lasts[i] != NULL) {
7267 lasts[i]->next = cur;
7268 lasts[i] = cur;
7269 } else {
7270 list[i] = cur;
7271 lasts[i] = cur;
7272 }
7273 if (cur->next != NULL)
7274 lastchg = cur->next;
7275 else
7276 lastchg = cur;
7277 cur = xmlRelaxNGSkipIgnored(ctxt, cur->next);
7278 }
7279 if (ret != 0) {
7280 VALID_ERR(XML_RELAXNG_ERR_INTERSEQ);
7281 ret = -1;
7282 goto done;
7283 }
7284 lastelem = cur;
7285 oldstate = ctxt->state;
7286 for (i = 0;i < nbgroups;i++) {
7287 ctxt->state = xmlRelaxNGCopyValidState(ctxt, oldstate);
7288 group = partitions->groups[i];
7289 if (lasts[i] != NULL) {
7290 last = lasts[i]->next;
7291 lasts[i]->next = NULL;
7292 }
7293 ctxt->state->seq = list[i];
7294 ret = xmlRelaxNGValidateDefinition(ctxt, group->rule);
7295 if (ret != 0)
7296 break;
7297 if (ctxt->state != NULL) {
7298 cur = ctxt->state->seq;
7299 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
7300 xmlRelaxNGFreeValidState(oldstate);
7301 oldstate = ctxt->state;
7302 ctxt->state = NULL;
7303 if (cur != NULL) {
7304 VALID_ERR2(XML_RELAXNG_ERR_INTEREXTRA, cur->name);
7305 ret = -1;
7306 ctxt->state = oldstate;
7307 goto done;
7308 }
7309 } else if (ctxt->states != NULL) {
7310 int j;
7311 int found = 0;
7312
7313 for (j = 0;j < ctxt->states->nbState;j++) {
7314 cur = ctxt->states->tabState[j]->seq;
7315 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
7316 if (cur == NULL) {
7317 found = 1;
7318 break;
7319 }
7320 }
7321 if (ctxt->states->nbState > 0) {
7322 xmlRelaxNGFreeValidState(oldstate);
7323 oldstate = ctxt->states->tabState[ctxt->states->nbState - 1];
7324 }
7325 for (j = 0;j < ctxt->states->nbState - 1;j++) {
7326 xmlRelaxNGFreeValidState(ctxt->states->tabState[j]);
7327 }
7328 xmlRelaxNGFreeStates(ctxt, ctxt->states);
7329 ctxt->states = NULL;
7330 if (found == 0) {
7331 VALID_ERR2(XML_RELAXNG_ERR_INTEREXTRA, cur->name);
7332 ret = -1;
7333 ctxt->state = oldstate;
7334 goto done;
7335 }
7336 } else {
7337 ret = -1;
7338 break;
7339 }
7340 if (lasts[i] != NULL) {
7341 lasts[i]->next = last;
7342 }
7343 }
7344 if (ctxt->state != NULL)
7345 xmlRelaxNGFreeValidState(ctxt->state);
7346 ctxt->state = oldstate;
7347 ctxt->state->seq = lastelem;
7348 if (ret != 0) {
7349 VALID_ERR(XML_RELAXNG_ERR_INTERSEQ);
7350 ret = -1;
7351 goto done;
7352 }
7353
7354done:
7355 /*
7356 * builds the next links chain from the prev one
7357 */
7358 cur = lastchg;
7359 while (cur != NULL) {
7360 if ((cur == start) || (cur->prev == NULL))
7361 break;
7362 cur->prev->next = cur;
7363 cur = cur->prev;
7364 }
7365 if (ret == 0) {
7366 ctxt->errNr = errNr;
7367 }
7368
7369 xmlFree(list);
7370 xmlFree(lasts);
7371 return(ret);
7372}
7373
7374/**
7375 * xmlRelaxNGValidateDefinitionList:
7376 * @ctxt: a Relax-NG validation context
7377 * @define: the list of definition to verify
7378 *
7379 * Validate the given node content against the (list) of definitions
7380 *
7381 * Returns 0 if the validation succeeded or an error code.
7382 */
7383static int
7384xmlRelaxNGValidateDefinitionList(xmlRelaxNGValidCtxtPtr ctxt,
7385 xmlRelaxNGDefinePtr defines) {
7386 int ret = 0, res;
7387
7388
7389 while (defines != NULL) {
7390 if ((ctxt->state != NULL) || (ctxt->states != NULL)) {
7391 res = xmlRelaxNGValidateDefinition(ctxt, defines);
7392 if (res < 0)
7393 ret = -1;
7394 } else {
7395 VALID_ERR(XML_RELAXNG_ERR_NOSTATE);
7396 return(-1);
7397 }
7398 if (ret < 0)
7399 break;
7400 defines = defines->next;
7401 }
7402
7403 return(ret);
7404}
7405
7406/**
7407 * xmlRelaxNGElementMatch:
Daniel Veillard416589a2003-02-17 17:25:42 +00007408 * @ctxt: a Relax-NG validation context
7409 * @define: the definition to check
Daniel Veillardfd573f12003-03-16 17:52:32 +00007410 * @elem: the element
Daniel Veillard416589a2003-02-17 17:25:42 +00007411 *
Daniel Veillardfd573f12003-03-16 17:52:32 +00007412 * Check if the element matches the definition nameClass
Daniel Veillard416589a2003-02-17 17:25:42 +00007413 *
Daniel Veillardfd573f12003-03-16 17:52:32 +00007414 * Returns 1 if the element matches, 0 if no, or -1 in case of error
Daniel Veillard416589a2003-02-17 17:25:42 +00007415 */
7416static int
Daniel Veillardfd573f12003-03-16 17:52:32 +00007417xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
7418 xmlRelaxNGDefinePtr define,
7419 xmlNodePtr elem) {
Daniel Veillard416589a2003-02-17 17:25:42 +00007420 int ret, oldflags;
7421
Daniel Veillardfd573f12003-03-16 17:52:32 +00007422 if (define->name != NULL) {
7423 if (!xmlStrEqual(elem->name, define->name)) {
7424 VALID_ERR3(XML_RELAXNG_ERR_ELEMNAME, define->name, elem->name);
7425 return(0);
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007426 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007427 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00007428 if ((define->ns != NULL) && (define->ns[0] != 0)) {
7429 if (elem->ns == NULL) {
7430 VALID_ERR2(XML_RELAXNG_ERR_ELEMNONS,
7431 elem->name);
7432 return(0);
7433 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
7434 VALID_ERR3(XML_RELAXNG_ERR_ELEMWRONGNS,
7435 elem->name, define->ns);
7436 return(0);
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007437 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00007438 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
7439 (define->name == NULL)) {
7440 VALID_ERR2(XML_RELAXNG_ERR_ELEMEXTRANS,
7441 elem->name);
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007442 return(0);
Daniel Veillardfd573f12003-03-16 17:52:32 +00007443 } else if ((elem->ns != NULL) && (define->name != NULL)) {
7444 VALID_ERR2(XML_RELAXNG_ERR_ELEMEXTRANS,
7445 define->name);
7446 return(0);
7447 }
7448
7449 if (define->nameClass == NULL)
7450 return(1);
7451
7452 define = define->nameClass;
7453 if (define->type == XML_RELAXNG_EXCEPT) {
7454 xmlRelaxNGDefinePtr list;
7455 oldflags = ctxt->flags;
7456 ctxt->flags |= FLAGS_IGNORABLE;
7457
7458 list = define->content;
7459 while (list != NULL) {
7460 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
7461 if (ret == 1) {
7462 ctxt->flags = oldflags;
7463 return(0);
7464 }
7465 if (ret < 0) {
7466 ctxt->flags = oldflags;
7467 return(ret);
7468 }
7469 list = list->next;
7470 }
7471 ret = 1;
7472 ctxt->flags = oldflags;
7473 } else if (define->type == XML_RELAXNG_CHOICE) {
7474 xmlRelaxNGDefinePtr list;
7475
7476 oldflags = ctxt->flags;
7477 ctxt->flags |= FLAGS_IGNORABLE;
7478
7479 list = define->nameClass;
7480 while (list != NULL) {
7481 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
7482 if (ret == 1) {
7483 ctxt->flags = oldflags;
7484 return(1);
7485 }
7486 if (ret < 0) {
7487 ctxt->flags = oldflags;
7488 return(ret);
7489 }
7490 list = list->next;
7491 }
7492 if (ret != 0) {
7493 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7494 xmlRelaxNGDumpValidError(ctxt);
7495 } else {
7496 ctxt->errNr = 0;
7497 }
7498 ret = 0;
7499 ctxt->flags = oldflags;
7500 } else {
7501 TODO
7502 ret = -1;
7503 }
7504 return(ret);
7505}
7506
7507/**
7508 * xmlRelaxNGValidateElementEnd:
7509 * @ctxt: a Relax-NG validation context
7510 *
7511 * Validate the end of the element, implements check that
7512 * there is nothing left not consumed in the element content
7513 * or in the attribute list.
7514 *
7515 * Returns 0 if the validation succeeded or an error code.
7516 */
7517static int
7518xmlRelaxNGValidateElementEnd(xmlRelaxNGValidCtxtPtr ctxt) {
7519 int ret = 0, i;
7520 xmlRelaxNGValidStatePtr state;
7521
7522 state = ctxt->state;
7523 if (state->seq != NULL) {
7524 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
7525 if (state->seq != NULL) {
7526 VALID_ERR3(XML_RELAXNG_ERR_EXTRACONTENT,
7527 state->node->name, state->seq->name);
7528 ret = -1;
7529 }
7530 }
7531 for (i = 0;i < state->nbAttrs;i++) {
7532 if (state->attrs[i] != NULL) {
7533 VALID_ERR3(XML_RELAXNG_ERR_INVALIDATTR,
7534 state->attrs[i]->name, state->node->name);
7535 ret = -1;
7536 }
7537 }
7538 return(ret);
7539}
7540
7541/**
7542 * xmlRelaxNGValidateState:
7543 * @ctxt: a Relax-NG validation context
7544 * @define: the definition to verify
7545 *
7546 * Validate the current state against the definition
7547 *
7548 * Returns 0 if the validation succeeded or an error code.
7549 */
7550static int
7551xmlRelaxNGValidateState(xmlRelaxNGValidCtxtPtr ctxt,
7552 xmlRelaxNGDefinePtr define) {
7553 xmlNodePtr node;
7554 int ret = 0, i, tmp, oldflags, errNr;
7555 xmlRelaxNGValidStatePtr oldstate, state;
7556
7557 if (define == NULL) {
7558 VALID_ERR(XML_RELAXNG_ERR_NODEFINE);
7559 return(-1);
7560 }
7561
7562 if (ctxt->state != NULL) {
7563 node = ctxt->state->seq;
7564 } else {
7565 node = NULL;
7566 }
7567#ifdef DEBUG
7568 for (i = 0;i < ctxt->depth;i++)
7569 xmlGenericError(xmlGenericErrorContext, " ");
7570 xmlGenericError(xmlGenericErrorContext,
7571 "Start validating %s ", xmlRelaxNGDefName(define));
7572 if (define->name != NULL)
7573 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
7574 if ((node != NULL) && (node->name != NULL))
7575 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
7576 else
7577 xmlGenericError(xmlGenericErrorContext, "\n");
7578#endif
7579 ctxt->depth++;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007580 switch (define->type) {
7581 case XML_RELAXNG_EMPTY:
Daniel Veillardfd573f12003-03-16 17:52:32 +00007582 node = xmlRelaxNGSkipIgnored(ctxt, node);
7583 ret = 0;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007584 break;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007585 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillardfd573f12003-03-16 17:52:32 +00007586 ret = -1;
7587 break;
7588 case XML_RELAXNG_TEXT:
7589 while ((node != NULL) &&
7590 ((node->type == XML_TEXT_NODE) ||
7591 (node->type == XML_COMMENT_NODE) ||
7592 (node->type == XML_PI_NODE) ||
7593 (node->type == XML_CDATA_SECTION_NODE)))
7594 node = node->next;
7595 ctxt->state->seq = node;
7596 break;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007597 case XML_RELAXNG_ELEMENT:
Daniel Veillardfd573f12003-03-16 17:52:32 +00007598 errNr = ctxt->errNr;
7599 node = xmlRelaxNGSkipIgnored(ctxt, node);
7600 if (node == NULL) {
7601 VALID_ERR2(XML_RELAXNG_ERR_NOELEM, define->name);
7602 ret = -1;
7603 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7604 xmlRelaxNGDumpValidError(ctxt);
7605 break;
7606 }
7607 if (node->type != XML_ELEMENT_NODE) {
7608 VALID_ERR(XML_RELAXNG_ERR_NOTELEM);
7609 ret = -1;
7610 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7611 xmlRelaxNGDumpValidError(ctxt);
7612 break;
7613 }
7614 /*
7615 * This node was already validated successfully against
7616 * this definition.
7617 */
7618 if (node->_private == define) {
7619 ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
7620 break;
7621 }
7622
7623 ret = xmlRelaxNGElementMatch(ctxt, define, node);
7624 if (ret <= 0) {
7625 ret = -1;
7626 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7627 xmlRelaxNGDumpValidError(ctxt);
7628 break;
7629 }
7630 ret = 0;
7631 if (ctxt->errNr != 0) {
7632 while ((ctxt->err != NULL) &&
7633 (((ctxt->err->err == XML_RELAXNG_ERR_ELEMNAME) &&
7634 (xmlStrEqual(ctxt->err->arg2, node->name))) ||
7635 (ctxt->err->err == XML_RELAXNG_ERR_NOELEM) ||
7636 (ctxt->err->err == XML_RELAXNG_ERR_NOTELEM)))
7637 xmlRelaxNGValidErrorPop(ctxt);
7638 }
7639 errNr = ctxt->errNr;
7640
7641 state = xmlRelaxNGNewValidState(ctxt, node);
7642 if (state == NULL) {
7643 ret = -1;
7644 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7645 xmlRelaxNGDumpValidError(ctxt);
7646 break;
7647 }
7648
7649 oldstate = ctxt->state;
7650 ctxt->state = state;
7651 if (define->attrs != NULL) {
7652 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
7653 if (tmp != 0) {
7654 ret = -1;
7655 VALID_ERR2(XML_RELAXNG_ERR_ATTRVALID, node->name);
7656 }
7657 }
7658 if (define->content != NULL) {
7659 tmp = xmlRelaxNGValidateDefinitionList(ctxt, define->content);
7660 if (tmp != 0) {
7661 ret = -1;
7662 VALID_ERR2(XML_RELAXNG_ERR_CONTENTVALID, node->name);
7663 }
7664 }
7665 if (ctxt->states != NULL) {
7666 tmp = -1;
7667
7668 oldflags = ctxt->flags;
7669 ctxt->flags |= FLAGS_IGNORABLE;
7670
7671 for (i = 0;i < ctxt->states->nbState;i++) {
7672 state = ctxt->states->tabState[i];
7673 ctxt->state = state;
7674
7675 if (xmlRelaxNGValidateElementEnd(ctxt) == 0)
7676 tmp = 0;
7677 xmlRelaxNGFreeValidState(state);
7678 }
7679 xmlRelaxNGFreeStates(ctxt, ctxt->states);
7680 ctxt->flags = oldflags;
7681 ctxt->states = NULL;
7682 if ((ret == 0) && (tmp == -1))
7683 ret = -1;
7684 } else {
7685 state = ctxt->state;
7686 if (ret == 0)
7687 ret = xmlRelaxNGValidateElementEnd(ctxt);
7688 xmlRelaxNGFreeValidState(state);
7689 }
7690 ctxt->state = oldstate;
7691 if (oldstate != NULL)
7692 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
7693 if (ret == 0) {
7694 node->_private = define;
7695 }
7696 if (ret != 0) {
7697 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7698 xmlRelaxNGDumpValidError(ctxt);
7699 } else {
7700 ctxt->errNr = errNr;
7701 }
7702
7703#ifdef DEBUG
7704 xmlGenericError(xmlGenericErrorContext,
7705 "xmlRelaxNGValidateDefinition(): validated %s : %d",
7706 node->name, ret);
7707 if (oldstate == NULL)
7708 xmlGenericError(xmlGenericErrorContext, ": no state\n");
7709 else if (oldstate->seq == NULL)
7710 xmlGenericError(xmlGenericErrorContext, ": done\n");
7711 else if (oldstate->seq->type == XML_ELEMENT_NODE)
7712 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
7713 oldstate->seq->name);
7714 else
7715 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
7716 oldstate->seq->name, oldstate->seq->type);
7717#endif
7718 break;
7719 case XML_RELAXNG_OPTIONAL: {
7720 oldflags = ctxt->flags;
7721 ctxt->flags |= FLAGS_IGNORABLE;
7722 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
7723 ret = xmlRelaxNGValidateDefinitionList(ctxt, define->content);
7724 if (ret != 0) {
7725 if (ctxt->state != NULL)
7726 xmlRelaxNGFreeValidState(ctxt->state);
7727 ctxt->state = oldstate;
7728 ctxt->flags = oldflags;
7729 ret = 0;
7730 break;
7731 }
7732 if (ctxt->states != NULL) {
7733 xmlRelaxNGAddStates(ctxt, ctxt->states, oldstate);
7734 } else {
7735 ctxt->states = xmlRelaxNGNewStates(ctxt, 1);
7736 if (ctxt->states == NULL) {
7737 xmlRelaxNGFreeValidState(oldstate);
7738 ctxt->flags = oldflags;
7739 ret = -1;
7740 break;
7741 }
7742 xmlRelaxNGAddStates(ctxt, ctxt->states, oldstate);
7743 xmlRelaxNGAddStates(ctxt, ctxt->states, ctxt->state);
7744 ctxt->state = NULL;
7745 }
7746 ctxt->flags = oldflags;
7747 ret = 0;
7748 break;
7749 }
7750 case XML_RELAXNG_ONEORMORE:
7751 ret = xmlRelaxNGValidateDefinitionList(ctxt, define->content);
7752 if (ret != 0) {
7753 break;
7754 }
7755 /* no break on purpose */
7756 case XML_RELAXNG_ZEROORMORE: {
7757 int progress;
7758 xmlRelaxNGStatesPtr states = NULL, res = NULL;
7759 int base, j;
7760
7761 res = xmlRelaxNGNewStates(ctxt, 1);
7762 if (res == NULL) {
7763 ret = -1;
7764 break;
7765 }
7766 /*
7767 * All the input states are also exit states
7768 */
7769 if (ctxt->state != NULL) {
7770 xmlRelaxNGAddStates(ctxt, res,
7771 xmlRelaxNGCopyValidState(ctxt, ctxt->state));
7772 } else {
7773 for (j = 0;j < ctxt->states->nbState;j++) {
7774 xmlRelaxNGAddStates(ctxt, res,
7775 xmlRelaxNGCopyValidState(ctxt,
7776 ctxt->states->tabState[j]));
7777 }
7778 }
7779 oldflags = ctxt->flags;
7780 ctxt->flags |= FLAGS_IGNORABLE;
7781 do {
7782 progress = 0;
7783 base = res->nbState;
7784
7785 if (ctxt->states != NULL) {
7786 states = ctxt->states;
7787 for (i = 0;i < states->nbState;i++) {
7788 ctxt->state = states->tabState[i];
7789 ctxt->states = NULL;
7790 ret = xmlRelaxNGValidateDefinitionList(ctxt,
7791 define->content);
7792 if (ret == 0) {
7793 if (ctxt->state != NULL) {
7794 tmp = xmlRelaxNGAddStates(ctxt, res,
7795 ctxt->state);
7796 ctxt->state = NULL;
7797 if (tmp == 1)
7798 progress = 1;
7799 } else if (ctxt->states != NULL) {
7800 for (j = 0;j < ctxt->states->nbState;j++) {
7801 tmp = xmlRelaxNGAddStates(ctxt, res,
7802 ctxt->states->tabState[j]);
7803 if (tmp == 1)
7804 progress = 1;
7805 }
7806 xmlRelaxNGFreeStates(ctxt, ctxt->states);
7807 ctxt->states = NULL;
7808 }
7809 } else {
7810 if (ctxt->state != NULL) {
7811 xmlRelaxNGFreeValidState(ctxt->state);
7812 ctxt->state = NULL;
7813 }
7814 }
7815 }
7816 } else {
7817 ret = xmlRelaxNGValidateDefinitionList(ctxt,
7818 define->content);
7819 if (ret != 0) {
7820 xmlRelaxNGFreeValidState(ctxt->state);
7821 ctxt->state = NULL;
7822 } else {
7823 base = res->nbState;
7824 if (ctxt->state != NULL) {
7825 tmp = xmlRelaxNGAddStates(ctxt, res,
7826 ctxt->state);
7827 ctxt->state = NULL;
7828 if (tmp == 1)
7829 progress = 1;
7830 } else if (ctxt->states != NULL) {
7831 for (j = 0;j < ctxt->states->nbState;j++) {
7832 tmp = xmlRelaxNGAddStates(ctxt, res,
7833 ctxt->states->tabState[j]);
7834 if (tmp == 1)
7835 progress = 1;
7836 }
7837 if (states == NULL) {
7838 states = ctxt->states;
7839 } else {
7840 xmlRelaxNGFreeStates(ctxt, ctxt->states);
7841 }
7842 ctxt->states = NULL;
7843 }
7844 }
7845 }
7846 if (progress) {
7847 /*
7848 * Collect all the new nodes added at that step
7849 * and make them the new node set
7850 */
7851 if (res->nbState - base == 1) {
7852 ctxt->state = xmlRelaxNGCopyValidState(ctxt,
7853 res->tabState[base]);
7854 } else {
7855 if (states == NULL) {
7856 xmlRelaxNGNewStates(ctxt, res->nbState - base);
7857 }
7858 states->nbState = 0;
7859 for (i = base;i < res->nbState;i++)
7860 xmlRelaxNGAddStates(ctxt, states,
7861 xmlRelaxNGCopyValidState(ctxt,
7862 res->tabState[i]));
7863 ctxt->states = states;
7864 }
7865 }
7866 } while (progress == 1);
7867 if (states != NULL) {
7868 xmlRelaxNGFreeStates(ctxt, states);
7869 }
7870 ctxt->states = res;
7871 ctxt->flags = oldflags;
7872 ret = 0;
7873 break;
7874 }
7875 case XML_RELAXNG_CHOICE: {
7876 xmlRelaxNGDefinePtr list = define->content;
7877 xmlRelaxNGStatesPtr states = NULL;
7878
7879
7880 oldflags = ctxt->flags;
7881 errNr = ctxt->errNr;
7882 ctxt->flags |= FLAGS_IGNORABLE;
7883 node = xmlRelaxNGSkipIgnored(ctxt, node);
7884
7885 while (list != NULL) {
7886 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
7887 ret = xmlRelaxNGValidateDefinition(ctxt, list);
7888 if (ret == 0) {
7889 if (states == NULL) {
7890 states = xmlRelaxNGNewStates(ctxt, 1);
7891 }
7892 if (ctxt->state != NULL) {
7893 xmlRelaxNGAddStates(ctxt, states, ctxt->state);
7894 } else if (ctxt->states != NULL) {
7895 for (i = 0;i < ctxt->states->nbState;i++) {
7896 xmlRelaxNGAddStates(ctxt, states,
7897 ctxt->states->tabState[i]);
7898 }
7899 xmlRelaxNGFreeStates(ctxt, ctxt->states);
7900 ctxt->states = NULL;
7901 }
7902 } else {
7903 xmlRelaxNGFreeValidState(ctxt->state);
7904 }
7905 ctxt->state = oldstate;
7906 list = list->next;
7907 }
7908 if (states != NULL) {
7909 xmlRelaxNGFreeValidState(oldstate);
7910 ctxt->states = states;
7911 ctxt->state = NULL;
7912 ret = 0;
7913 } else {
7914 ctxt->states = NULL;
7915 }
7916 ctxt->flags = oldflags;
7917 if (ret != 0) {
7918 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7919 xmlRelaxNGDumpValidError(ctxt);
7920 } else if ((ctxt->flags & FLAGS_IGNORABLE) == 0) {
7921 ctxt->errNr = errNr;
7922 }
7923 break;
7924 }
7925 case XML_RELAXNG_DEF:
7926 case XML_RELAXNG_GROUP:
7927 ret = xmlRelaxNGValidateDefinitionList(ctxt, define->content);
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007928 break;
7929 case XML_RELAXNG_INTERLEAVE:
Daniel Veillardfd573f12003-03-16 17:52:32 +00007930 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007931 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00007932 case XML_RELAXNG_ATTRIBUTE:
7933 ret = xmlRelaxNGValidateAttribute(ctxt, define);
7934 break;
7935 case XML_RELAXNG_NOOP:
7936 case XML_RELAXNG_REF:
7937 case XML_RELAXNG_PARENTREF:
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007938 case XML_RELAXNG_EXTERNALREF:
Daniel Veillardfd573f12003-03-16 17:52:32 +00007939 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7940 break;
7941 case XML_RELAXNG_DATATYPE: {
7942 xmlNodePtr child;
7943 xmlChar *content = NULL;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007944
Daniel Veillardfd573f12003-03-16 17:52:32 +00007945 child = node;
7946 while (child != NULL) {
7947 if (child->type == XML_ELEMENT_NODE) {
7948 VALID_ERR2(XML_RELAXNG_ERR_DATAELEM,
7949 node->parent->name);
7950 ret = -1;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007951 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00007952 } else if ((child->type == XML_TEXT_NODE) ||
7953 (child->type == XML_CDATA_SECTION_NODE)) {
7954 content = xmlStrcat(content, child->content);
7955 }
7956 /* TODO: handle entities ... */
7957 child = child->next;
7958 }
7959 if (ret == -1) {
7960 if (content != NULL)
7961 xmlFree(content);
7962 break;
7963 }
7964 if (content == NULL) {
7965 content = xmlStrdup(BAD_CAST "");
7966 if (content == NULL) {
7967 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
7968 ret = -1;
7969 break;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007970 }
7971 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00007972 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
7973 if (ret == -1) {
7974 VALID_ERR2(XML_RELAXNG_ERR_DATATYPE, define->name);
7975 } else if (ret == 0) {
7976 ctxt->state->seq = NULL;
7977 }
7978 if (content != NULL)
7979 xmlFree(content);
7980 break;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007981 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00007982 case XML_RELAXNG_VALUE: {
7983 xmlChar *content = NULL;
7984 xmlChar *oldvalue;
7985 xmlNodePtr child;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007986
Daniel Veillardfd573f12003-03-16 17:52:32 +00007987 child = node;
7988 while (child != NULL) {
7989 if (child->type == XML_ELEMENT_NODE) {
7990 VALID_ERR2(XML_RELAXNG_ERR_VALELEM,
7991 node->parent->name);
7992 ret = -1;
7993 break;
7994 } else if ((child->type == XML_TEXT_NODE) ||
7995 (child->type == XML_CDATA_SECTION_NODE)) {
7996 content = xmlStrcat(content, child->content);
7997 }
7998 /* TODO: handle entities ... */
7999 child = child->next;
8000 }
8001 if (ret == -1) {
8002 if (content != NULL)
8003 xmlFree(content);
8004 break;
8005 }
8006 if (content == NULL) {
8007 content = xmlStrdup(BAD_CAST "");
8008 if (content == NULL) {
8009 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
8010 ret = -1;
8011 break;
8012 }
8013 }
8014 oldvalue = ctxt->state->value;
8015 ctxt->state->value = content;
8016 ret = xmlRelaxNGValidateValue(ctxt, define);
8017 ctxt->state->value = oldvalue;
8018 if (ret == -1) {
8019 VALID_ERR2(XML_RELAXNG_ERR_VALUE, define->name);
8020 } else if (ret == 0) {
8021 ctxt->state->seq = NULL;
8022 }
8023 if (content != NULL)
8024 xmlFree(content);
8025 break;
8026 }
8027 case XML_RELAXNG_LIST: {
8028 xmlChar *content;
8029 xmlNodePtr child;
8030 xmlChar *oldvalue, *oldendvalue;
8031 int len;
8032
8033 /*
8034 * Make sure it's only text nodes
8035 */
8036
8037 content = NULL;
8038 child = node;
8039 while (child != NULL) {
8040 if (child->type == XML_ELEMENT_NODE) {
8041 VALID_ERR2(XML_RELAXNG_ERR_LISTELEM,
8042 node->parent->name);
8043 ret = -1;
8044 break;
8045 } else if ((child->type == XML_TEXT_NODE) ||
8046 (child->type == XML_CDATA_SECTION_NODE)) {
8047 content = xmlStrcat(content, child->content);
8048 }
8049 /* TODO: handle entities ... */
8050 child = child->next;
8051 }
8052 if (ret == -1) {
8053 if (content != NULL)
8054 xmlFree(content);
8055 break;
8056 }
8057 if (content == NULL) {
8058 content = xmlStrdup(BAD_CAST "");
8059 if (content == NULL) {
8060 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
8061 ret = -1;
8062 break;
8063 }
8064 }
8065 len = xmlStrlen(content);
8066 oldvalue = ctxt->state->value;
8067 oldendvalue = ctxt->state->endvalue;
8068 ctxt->state->value = content;
8069 ctxt->state->endvalue = content + len;
8070 ret = xmlRelaxNGValidateValue(ctxt, define);
8071 ctxt->state->value = oldvalue;
8072 ctxt->state->endvalue = oldendvalue;
8073 if (ret == -1) {
8074 VALID_ERR(XML_RELAXNG_ERR_LIST);
8075 } else if ((ret == 0) && (node != NULL)) {
8076 ctxt->state->seq = node->next;
8077 }
8078 if (content != NULL)
8079 xmlFree(content);
8080 break;
8081 }
8082 case XML_RELAXNG_START:
8083 case XML_RELAXNG_EXCEPT:
8084 case XML_RELAXNG_PARAM:
8085 TODO
8086 ret = -1;
8087 break;
8088 }
8089 ctxt->depth--;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008090#ifdef DEBUG
Daniel Veillardfd573f12003-03-16 17:52:32 +00008091 for (i = 0;i < ctxt->depth;i++)
8092 xmlGenericError(xmlGenericErrorContext, " ");
8093 xmlGenericError(xmlGenericErrorContext,
8094 "Validating %s ", xmlRelaxNGDefName(define));
8095 if (define->name != NULL)
8096 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
8097 if (ret == 0)
8098 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
8099 else
8100 xmlGenericError(xmlGenericErrorContext, "failed\n");
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008101#endif
Daniel Veillardfd573f12003-03-16 17:52:32 +00008102 return(ret);
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008103}
8104
8105/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00008106 * xmlRelaxNGValidateDefinition:
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008107 * @ctxt: a Relax-NG validation context
8108 * @define: the definition to verify
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008109 *
Daniel Veillardfd573f12003-03-16 17:52:32 +00008110 * Validate the current node lists against the definition
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008111 *
Daniel Veillardfd573f12003-03-16 17:52:32 +00008112 * Returns 0 if the validation succeeded or an error code.
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008113 */
8114static int
Daniel Veillardfd573f12003-03-16 17:52:32 +00008115xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
8116 xmlRelaxNGDefinePtr define) {
8117 xmlRelaxNGStatesPtr states, res;
8118 int i, j, k, ret, oldflags;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008119
Daniel Veillardfd573f12003-03-16 17:52:32 +00008120 /*
8121 * We should NOT have both ctxt->state and ctxt->states
8122 */
8123 if ((ctxt->state != NULL) && (ctxt->states != NULL)) {
8124 TODO
8125 xmlRelaxNGFreeValidState(ctxt->state);
8126 ctxt->state = NULL;
8127 }
8128
8129 if ((ctxt->states == NULL) || (ctxt->states->nbState == 1)) {
8130 if (ctxt->states != NULL) {
8131 ctxt->state = ctxt->states->tabState[0];
8132 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8133 ctxt->states = NULL;
8134 }
8135 ret = xmlRelaxNGValidateState(ctxt, define);
8136 if ((ctxt->state != NULL) && (ctxt->states != NULL)) {
8137 TODO
8138 xmlRelaxNGFreeValidState(ctxt->state);
8139 ctxt->state = NULL;
8140 }
8141 if ((ctxt->states != NULL) && (ctxt->states->nbState == 1)) {
8142 ctxt->state = ctxt->states->tabState[0];
8143 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8144 ctxt->states = NULL;
8145 }
8146 return(ret);
8147 }
8148
8149 states = ctxt->states;
8150 ctxt->states = NULL;
8151 res = NULL;
8152 j = 0;
8153 oldflags = ctxt->flags;
8154 ctxt->flags |= FLAGS_IGNORABLE;
8155 for (i = 0;i < states->nbState;i++) {
8156 ctxt->state = states->tabState[i];
8157 ctxt->states = NULL;
8158 ret = xmlRelaxNGValidateState(ctxt, define);
8159 /*
8160 * We should NOT have both ctxt->state and ctxt->states
8161 */
8162 if ((ctxt->state != NULL) && (ctxt->states != NULL)) {
8163 TODO
8164 xmlRelaxNGFreeValidState(ctxt->state);
8165 ctxt->state = NULL;
8166 }
8167 if (ret == 0) {
8168 if (ctxt->states == NULL) {
8169 if (res != NULL) {
8170 /* add the state to the container */
8171 xmlRelaxNGAddStates(ctxt, res, ctxt->state);
8172 ctxt->state = NULL;
8173 } else {
8174 /* add the state directly in states */
8175 states->tabState[j++] = ctxt->state;
8176 ctxt->state = NULL;
8177 }
8178 } else {
8179 if (res == NULL) {
8180 /* make it the new container and copy other results */
8181 res = ctxt->states;
8182 ctxt->states = NULL;
8183 for (k = 0;k < j;k++)
8184 xmlRelaxNGAddStates(ctxt, res, states->tabState[k]);
8185 } else {
8186 /* add all the new results to res and reff the container */
8187 for (k = 0;k < ctxt->states->nbState;k++)
8188 xmlRelaxNGAddStates(ctxt, res,
8189 ctxt->states->tabState[k]);
8190 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8191 ctxt->states = NULL;
8192 }
8193 }
8194 } else {
8195 if (ctxt->state != NULL) {
8196 xmlRelaxNGFreeValidState(ctxt->state);
8197 ctxt->state = NULL;
8198 } else if (ctxt->states != NULL) {
8199 for (k = 0;k < ctxt->states->nbState;k++)
8200 xmlRelaxNGFreeValidState(ctxt->states->tabState[k]);
8201 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8202 ctxt->states = NULL;
8203 }
8204 }
8205 }
8206 ctxt->flags = oldflags;
8207 if (res != NULL) {
8208 xmlRelaxNGFreeStates(ctxt, states);
8209 ctxt->states = res;
8210 ret = 0;
8211 } else if (j > 1) {
8212 states->nbState = j;
8213 ctxt->states = states;
8214 ret =0;
8215 } else if (j == 1) {
8216 ctxt->state = states->tabState[0];
8217 xmlRelaxNGFreeStates(ctxt, states);
8218 ret = 0;
8219 } else {
8220 ret = -1;
8221 xmlRelaxNGFreeStates(ctxt, states);
8222 if (ctxt->states != NULL) {
8223 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8224 ctxt->states = NULL;
8225 }
8226 }
8227 if ((ctxt->state != NULL) && (ctxt->states != NULL)) {
8228 TODO
8229 xmlRelaxNGFreeValidState(ctxt->state);
8230 ctxt->state = NULL;
8231 }
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008232 return(ret);
8233}
8234
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008235/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00008236 * xmlRelaxNGValidateDocument:
8237 * @ctxt: a Relax-NG validation context
8238 * @doc: the document
8239 *
8240 * Validate the given document
8241 *
8242 * Returns 0 if the validation succeeded or an error code.
8243 */
8244static int
8245xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
8246 int ret;
8247 xmlRelaxNGPtr schema;
8248 xmlRelaxNGGrammarPtr grammar;
8249 xmlRelaxNGValidStatePtr state;
Daniel Veillardfd573f12003-03-16 17:52:32 +00008250 xmlNodePtr node;
Daniel Veillard6eadf632003-01-23 18:29:16 +00008251
8252 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
8253 return(-1);
8254
8255 schema = ctxt->schema;
8256 grammar = schema->topgrammar;
8257 if (grammar == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00008258 VALID_ERR(XML_RELAXNG_ERR_NOGRAMMAR);
Daniel Veillard6eadf632003-01-23 18:29:16 +00008259 return(-1);
8260 }
8261 state = xmlRelaxNGNewValidState(ctxt, NULL);
8262 ctxt->state = state;
8263 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008264 if ((ctxt->state != NULL) && (state->seq != NULL)) {
8265 state = ctxt->state;
Daniel Veillard6eadf632003-01-23 18:29:16 +00008266 node = state->seq;
8267 node = xmlRelaxNGSkipIgnored(ctxt, node);
8268 if (node != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00008269 if (ret != -1) {
8270 VALID_ERR(XML_RELAXNG_ERR_EXTRADATA);
8271 ret = -1;
8272 }
8273 }
8274 } else if (ctxt->states != NULL) {
8275 int i;
8276 int tmp = -1;
8277
8278 for (i = 0;i < ctxt->states->nbState;i++) {
8279 state = ctxt->states->tabState[i];
8280 node = state->seq;
8281 node = xmlRelaxNGSkipIgnored(ctxt, node);
8282 if (node == NULL)
8283 tmp = 0;
8284 xmlRelaxNGFreeValidState(state);
8285 }
8286 if (tmp == -1) {
8287 if (ret != -1) {
8288 VALID_ERR(XML_RELAXNG_ERR_EXTRADATA);
8289 ret = -1;
8290 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00008291 }
8292 }
8293 xmlRelaxNGFreeValidState(state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008294 if (ret != 0)
8295 xmlRelaxNGDumpValidError(ctxt);
Daniel Veillard6eadf632003-01-23 18:29:16 +00008296
8297 return(ret);
8298}
8299
Daniel Veillardfd573f12003-03-16 17:52:32 +00008300/************************************************************************
8301 * *
8302 * Validation interfaces *
8303 * *
8304 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +00008305/**
8306 * xmlRelaxNGNewValidCtxt:
8307 * @schema: a precompiled XML RelaxNGs
8308 *
8309 * Create an XML RelaxNGs validation context based on the given schema
8310 *
8311 * Returns the validation context or NULL in case of error
8312 */
8313xmlRelaxNGValidCtxtPtr
8314xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
8315 xmlRelaxNGValidCtxtPtr ret;
8316
8317 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
8318 if (ret == NULL) {
8319 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardfd573f12003-03-16 17:52:32 +00008320 "Failed to allocate new schema validation context\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +00008321 return (NULL);
8322 }
8323 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
8324 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00008325 ret->error = xmlGenericError;
8326 ret->userData = xmlGenericErrorContext;
Daniel Veillard42f12e92003-03-07 18:32:59 +00008327 ret->errNr = 0;
8328 ret->errMax = 0;
8329 ret->err = NULL;
8330 ret->errTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00008331 return (ret);
8332}
8333
8334/**
8335 * xmlRelaxNGFreeValidCtxt:
8336 * @ctxt: the schema validation context
8337 *
8338 * Free the resources associated to the schema validation context
8339 */
8340void
8341xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
8342 if (ctxt == NULL)
8343 return;
Daniel Veillardfd573f12003-03-16 17:52:32 +00008344 if (ctxt->states != NULL)
8345 xmlRelaxNGFreeStates(ctxt, ctxt->states);
Daniel Veillard42f12e92003-03-07 18:32:59 +00008346 if (ctxt->errTab != NULL)
8347 xmlFree(ctxt->errTab);
Daniel Veillard6eadf632003-01-23 18:29:16 +00008348 xmlFree(ctxt);
8349}
8350
8351/**
8352 * xmlRelaxNGSetValidErrors:
8353 * @ctxt: a Relax-NG validation context
8354 * @err: the error function
8355 * @warn: the warning function
8356 * @ctx: the functions context
8357 *
8358 * Set the error and warning callback informations
8359 */
8360void
8361xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
8362 xmlRelaxNGValidityErrorFunc err,
8363 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
8364 if (ctxt == NULL)
8365 return;
8366 ctxt->error = err;
8367 ctxt->warning = warn;
8368 ctxt->userData = ctx;
8369}
8370
8371/**
8372 * xmlRelaxNGValidateDoc:
8373 * @ctxt: a Relax-NG validation context
8374 * @doc: a parsed document tree
8375 *
8376 * Validate a document tree in memory.
8377 *
8378 * Returns 0 if the document is valid, a positive error code
8379 * number otherwise and -1 in case of internal or API error.
8380 */
8381int
8382xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
8383 int ret;
8384
8385 if ((ctxt == NULL) || (doc == NULL))
8386 return(-1);
8387
8388 ctxt->doc = doc;
8389
8390 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00008391 /*
8392 * TODO: build error codes
8393 */
8394 if (ret == -1)
8395 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00008396 return(ret);
8397}
8398
8399#endif /* LIBXML_SCHEMAS_ENABLED */
8400