blob: beb11bb5ea4332592b0dffdbc5aa351260c7ebc4 [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 Veillard952379b2003-03-17 15:37:12 +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 Veillard249d7bb2003-03-19 21:02:29 +0000133#define IS_MIXED 8
Daniel Veillard1564e6e2003-03-15 21:30:25 +0000134
Daniel Veillard6eadf632003-01-23 18:29:16 +0000135struct _xmlRelaxNGDefine {
136 xmlRelaxNGType type; /* the type of definition */
Daniel Veillard1564e6e2003-03-15 21:30:25 +0000137 xmlNodePtr node; /* the node in the source */
Daniel Veillardfd573f12003-03-16 17:52:32 +0000138 xmlChar *name; /* the element local name if present */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000139 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000140 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000141 void *data; /* data lib or specific pointer */
Daniel Veillardfd573f12003-03-16 17:52:32 +0000142 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000143 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000144 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
Daniel Veillardfd573f12003-03-16 17:52:32 +0000145 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000146 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000147 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
Daniel Veillardfd573f12003-03-16 17:52:32 +0000148 short depth; /* used for the cycle detection */
149 short flags; /* define related flags */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000150};
151
152/**
153 * _xmlRelaxNG:
154 *
155 * A RelaxNGs definition
156 */
157struct _xmlRelaxNG {
Daniel Veillardc3da18a2003-03-18 00:31:04 +0000158 void *_private; /* unused by the library for users or bindings */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000159 xmlRelaxNGGrammarPtr topgrammar;
160 xmlDocPtr doc;
161
Daniel Veillardc3da18a2003-03-18 00:31:04 +0000162 int idref; /* requires idref checking */
163
Daniel Veillard6eadf632003-01-23 18:29:16 +0000164 xmlHashTablePtr defs; /* define */
165 xmlHashTablePtr refs; /* references */
Daniel Veillardc482e262003-02-26 14:48:48 +0000166 xmlRelaxNGDocumentPtr documents; /* all the documents loaded */
167 xmlRelaxNGIncludePtr includes; /* all the includes loaded */
Daniel Veillard419a7682003-02-03 23:22:49 +0000168 int defNr; /* number of defines used */
169 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillardc3da18a2003-03-18 00:31:04 +0000170
Daniel Veillard6eadf632003-01-23 18:29:16 +0000171};
172
Daniel Veillard77648bb2003-02-20 15:03:22 +0000173#define XML_RELAXNG_IN_ATTRIBUTE (1 << 0)
174#define XML_RELAXNG_IN_ONEORMORE (1 << 1)
175#define XML_RELAXNG_IN_LIST (1 << 2)
176#define XML_RELAXNG_IN_DATAEXCEPT (1 << 3)
177#define XML_RELAXNG_IN_START (1 << 4)
178#define XML_RELAXNG_IN_OOMGROUP (1 << 5)
179#define XML_RELAXNG_IN_OOMINTERLEAVE (1 << 6)
180#define XML_RELAXNG_IN_EXTERNALREF (1 << 7)
Daniel Veillardc5312d72003-02-21 17:14:10 +0000181#define XML_RELAXNG_IN_ANYEXCEPT (1 << 8)
182#define XML_RELAXNG_IN_NSEXCEPT (1 << 9)
Daniel Veillard6eadf632003-01-23 18:29:16 +0000183
184struct _xmlRelaxNGParserCtxt {
185 void *userData; /* user specific data block */
186 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
187 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
Daniel Veillard42f12e92003-03-07 18:32:59 +0000188 xmlRelaxNGValidErr err;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000189
190 xmlRelaxNGPtr schema; /* The schema in use */
191 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000192 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000193 int flags; /* parser flags */
194 int nbErrors; /* number of errors at parse time */
195 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000196 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000197 xmlRelaxNGDefinePtr def; /* the current define */
198
199 int nbInterleaves;
200 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000201
Daniel Veillardc482e262003-02-26 14:48:48 +0000202 xmlRelaxNGDocumentPtr documents; /* all the documents loaded */
203 xmlRelaxNGIncludePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000204 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000205 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000206
Daniel Veillard419a7682003-02-03 23:22:49 +0000207 int defNr; /* number of defines used */
208 int defMax; /* number of defines aloocated */
209 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
210
Daniel Veillard6eadf632003-01-23 18:29:16 +0000211 const char *buffer;
212 int size;
213
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000214 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000215 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000216 int docNr; /* Depth of the parsing stack */
217 int docMax; /* Max depth of the parsing stack */
218 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000219
220 /* the include stack */
Daniel Veillardd4310742003-02-18 21:12:46 +0000221 xmlRelaxNGIncludePtr inc; /* Current parsed include */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000222 int incNr; /* Depth of the include parsing stack */
223 int incMax; /* Max depth of the parsing stack */
224 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillardc3da18a2003-03-18 00:31:04 +0000225
226 int idref; /* requires idref checking */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000227};
228
229#define FLAGS_IGNORABLE 1
230#define FLAGS_NEGATIVE 2
Daniel Veillard249d7bb2003-03-19 21:02:29 +0000231#define FLAGS_MIXED_CONTENT 4
Daniel Veillard6eadf632003-01-23 18:29:16 +0000232
233/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000234 * xmlRelaxNGInterleaveGroup:
235 *
236 * A RelaxNGs partition set associated to lists of definitions
237 */
238typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
239typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
240struct _xmlRelaxNGInterleaveGroup {
241 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
242 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000243 xmlRelaxNGDefinePtr *attrs; /* the array of attributes definitions */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000244};
245
246/**
247 * xmlRelaxNGPartitions:
248 *
249 * A RelaxNGs partition associated to an interleave group
250 */
251typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
252typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
253struct _xmlRelaxNGPartition {
254 int nbgroups; /* number of groups in the partitions */
255 xmlRelaxNGInterleaveGroupPtr *groups;
256};
257
258/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000259 * xmlRelaxNGValidState:
260 *
261 * A RelaxNGs validation state
262 */
263#define MAX_ATTR 20
264typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
265typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
266struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000267 xmlNodePtr node; /* the current node */
268 xmlNodePtr seq; /* the sequence of children left to validate */
269 int nbAttrs; /* the number of attributes */
Daniel Veillard798024a2003-03-19 10:36:09 +0000270 int maxAttrs; /* the size of attrs */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000271 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000272 xmlChar *value; /* the value when operating on string */
273 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard798024a2003-03-19 10:36:09 +0000274 xmlAttrPtr *attrs; /* the array of attributes */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000275};
276
277/**
Daniel Veillardfd573f12003-03-16 17:52:32 +0000278 * xmlRelaxNGStates:
279 *
280 * A RelaxNGs container for validation state
281 */
282typedef struct _xmlRelaxNGStates xmlRelaxNGStates;
283typedef xmlRelaxNGStates *xmlRelaxNGStatesPtr;
284struct _xmlRelaxNGStates {
285 int nbState; /* the number of states */
286 int maxState; /* the size of the array */
287 xmlRelaxNGValidStatePtr *tabState;
288};
289
Daniel Veillardc3da18a2003-03-18 00:31:04 +0000290#define ERROR_IS_DUP 1
Daniel Veillardfd573f12003-03-16 17:52:32 +0000291/**
Daniel Veillard42f12e92003-03-07 18:32:59 +0000292 * xmlRelaxNGValidError:
293 *
294 * A RelaxNGs validation error
295 */
296typedef struct _xmlRelaxNGValidError xmlRelaxNGValidError;
297typedef xmlRelaxNGValidError *xmlRelaxNGValidErrorPtr;
298struct _xmlRelaxNGValidError {
299 xmlRelaxNGValidErr err; /* the error number */
Daniel Veillardc3da18a2003-03-18 00:31:04 +0000300 int flags; /* flags */
Daniel Veillard42f12e92003-03-07 18:32:59 +0000301 xmlNodePtr node; /* the current node */
302 xmlNodePtr seq; /* the current child */
303 const xmlChar * arg1; /* first arg */
304 const xmlChar * arg2; /* second arg */
305};
306
307/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000308 * xmlRelaxNGValidCtxt:
309 *
310 * A RelaxNGs validation context
311 */
312
313struct _xmlRelaxNGValidCtxt {
314 void *userData; /* user specific data block */
315 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
316 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
317
318 xmlRelaxNGPtr schema; /* The schema in use */
319 xmlDocPtr doc; /* the document being validated */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000320 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000321 int depth; /* validation depth */
Daniel Veillardc3da18a2003-03-18 00:31:04 +0000322 int idref; /* requires idref checking */
Daniel Veillard42f12e92003-03-07 18:32:59 +0000323
324 /*
325 * Errors accumulated in branches may have to be stacked to be
326 * provided back when it's sure they affect validation.
327 */
328 xmlRelaxNGValidErrorPtr err; /* Last error */
329 int errNr; /* Depth of the error stack */
330 int errMax; /* Max depth of the error stack */
331 xmlRelaxNGValidErrorPtr errTab; /* stack of errors */
Daniel Veillard1564e6e2003-03-15 21:30:25 +0000332
Daniel Veillardfd573f12003-03-16 17:52:32 +0000333 xmlRelaxNGValidStatePtr state; /* the current validation state */
334 xmlRelaxNGStatesPtr states; /* the accumulated state list */
Daniel Veillard798024a2003-03-19 10:36:09 +0000335
336 xmlRelaxNGStatesPtr freeState; /* the pool of free valid states */
337 int freeStatesNr;
338 int freeStatesMax;
339 xmlRelaxNGStatesPtr *freeStates; /* the pool of free state groups */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000340};
341
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000342/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000343 * xmlRelaxNGInclude:
344 *
345 * Structure associated to a RelaxNGs document element
346 */
347struct _xmlRelaxNGInclude {
Daniel Veillardc482e262003-02-26 14:48:48 +0000348 xmlRelaxNGIncludePtr next; /* keep a chain of includes */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000349 xmlChar *href; /* the normalized href value */
350 xmlDocPtr doc; /* the associated XML document */
351 xmlRelaxNGDefinePtr content;/* the definitions */
352 xmlRelaxNGPtr schema; /* the schema */
353};
354
355/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000356 * xmlRelaxNGDocument:
357 *
358 * Structure associated to a RelaxNGs document element
359 */
360struct _xmlRelaxNGDocument {
Daniel Veillardc482e262003-02-26 14:48:48 +0000361 xmlRelaxNGDocumentPtr next; /* keep a chain of documents */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000362 xmlChar *href; /* the normalized href value */
363 xmlDocPtr doc; /* the associated XML document */
364 xmlRelaxNGDefinePtr content;/* the definitions */
365 xmlRelaxNGPtr schema; /* the schema */
366};
367
Daniel Veillard3ebc7d42003-02-24 17:17:58 +0000368
Daniel Veillard6eadf632003-01-23 18:29:16 +0000369/************************************************************************
370 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000371 * Preliminary type checking interfaces *
372 * *
373 ************************************************************************/
374/**
375 * xmlRelaxNGTypeHave:
376 * @data: data needed for the library
377 * @type: the type name
378 * @value: the value to check
379 *
380 * Function provided by a type library to check if a type is exported
381 *
382 * Returns 1 if yes, 0 if no and -1 in case of error.
383 */
384typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
385
386/**
387 * xmlRelaxNGTypeCheck:
388 * @data: data needed for the library
389 * @type: the type name
390 * @value: the value to check
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000391 * @result: place to store the result if needed
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000392 *
393 * Function provided by a type library to check if a value match a type
394 *
395 * Returns 1 if yes, 0 if no and -1 in case of error.
396 */
397typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
Daniel Veillardc3da18a2003-03-18 00:31:04 +0000398 const xmlChar *value, void **result,
399 xmlNodePtr node);
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000400
401/**
402 * xmlRelaxNGFacetCheck:
403 * @data: data needed for the library
404 * @type: the type name
405 * @facet: the facet name
406 * @val: the facet value
407 * @strval: the string value
408 * @value: the value to check
409 *
410 * Function provided by a type library to check a value facet
411 *
412 * Returns 1 if yes, 0 if no and -1 in case of error.
413 */
414typedef int (*xmlRelaxNGFacetCheck) (void *data, const xmlChar *type,
415 const xmlChar *facet, const xmlChar *val,
416 const xmlChar *strval, void *value);
417
418/**
419 * xmlRelaxNGTypeFree:
420 * @data: data needed for the library
421 * @result: the value to free
422 *
423 * Function provided by a type library to free a returned result
424 */
425typedef void (*xmlRelaxNGTypeFree) (void *data, void *result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000426
427/**
428 * xmlRelaxNGTypeCompare:
429 * @data: data needed for the library
430 * @type: the type name
431 * @value1: the first value
432 * @value2: the second value
433 *
434 * Function provided by a type library to compare two values accordingly
435 * to a type.
436 *
437 * Returns 1 if yes, 0 if no and -1 in case of error.
438 */
439typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
440 const xmlChar *value1,
441 const xmlChar *value2);
442typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
443typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
444struct _xmlRelaxNGTypeLibrary {
445 const xmlChar *namespace; /* the datatypeLibrary value */
446 void *data; /* data needed for the library */
447 xmlRelaxNGTypeHave have; /* the export function */
448 xmlRelaxNGTypeCheck check; /* the checking function */
449 xmlRelaxNGTypeCompare comp; /* the compare function */
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000450 xmlRelaxNGFacetCheck facet; /* the facet check function */
451 xmlRelaxNGTypeFree freef; /* the freeing function */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000452};
453
454/************************************************************************
455 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000456 * Allocation functions *
457 * *
458 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000459static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
460static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000461static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillardc482e262003-02-26 14:48:48 +0000462static void xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema);
Daniel Veillardfd573f12003-03-16 17:52:32 +0000463static int xmlRelaxNGEqualValidState(
464 xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
465 xmlRelaxNGValidStatePtr state1,
466 xmlRelaxNGValidStatePtr state2);
Daniel Veillard798024a2003-03-19 10:36:09 +0000467static void xmlRelaxNGFreeValidState(xmlRelaxNGValidCtxtPtr ctxt,
468 xmlRelaxNGValidStatePtr state);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000469
470/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000471 * xmlRelaxNGFreeDocument:
472 * @docu: a document structure
473 *
474 * Deallocate a RelaxNG document structure.
475 */
476static void
477xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
478{
479 if (docu == NULL)
480 return;
481
482 if (docu->href != NULL)
483 xmlFree(docu->href);
484 if (docu->doc != NULL)
485 xmlFreeDoc(docu->doc);
486 if (docu->schema != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000487 xmlRelaxNGFreeInnerSchema(docu->schema);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000488 xmlFree(docu);
489}
490
491/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000492 * xmlRelaxNGFreeDocumentList:
493 * @docu: a list of document structure
494 *
495 * Deallocate a RelaxNG document structures.
496 */
497static void
498xmlRelaxNGFreeDocumentList(xmlRelaxNGDocumentPtr docu)
499{
500 xmlRelaxNGDocumentPtr next;
501 while (docu != NULL) {
502 next = docu->next;
503 xmlRelaxNGFreeDocument(docu);
504 docu = next;
505 }
506}
507
508/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000509 * xmlRelaxNGFreeInclude:
510 * @incl: a include structure
511 *
512 * Deallocate a RelaxNG include structure.
513 */
514static void
515xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
516{
517 if (incl == NULL)
518 return;
519
520 if (incl->href != NULL)
521 xmlFree(incl->href);
522 if (incl->doc != NULL)
523 xmlFreeDoc(incl->doc);
524 if (incl->schema != NULL)
525 xmlRelaxNGFree(incl->schema);
526 xmlFree(incl);
527}
528
529/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000530 * xmlRelaxNGFreeIncludeList:
531 * @incl: a include structure list
532 *
533 * Deallocate a RelaxNG include structure.
534 */
535static void
536xmlRelaxNGFreeIncludeList(xmlRelaxNGIncludePtr incl)
537{
538 xmlRelaxNGIncludePtr next;
539 while (incl != NULL) {
540 next = incl->next;
541 xmlRelaxNGFreeInclude(incl);
542 incl = next;
543 }
544}
545
546/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000547 * xmlRelaxNGNewRelaxNG:
548 * @ctxt: a Relax-NG validation context (optional)
549 *
550 * Allocate a new RelaxNG structure.
551 *
552 * Returns the newly allocated structure or NULL in case or error
553 */
554static xmlRelaxNGPtr
555xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
556{
557 xmlRelaxNGPtr ret;
558
559 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
560 if (ret == NULL) {
561 if ((ctxt != NULL) && (ctxt->error != NULL))
562 ctxt->error(ctxt->userData, "Out of memory\n");
563 ctxt->nbErrors++;
564 return (NULL);
565 }
566 memset(ret, 0, sizeof(xmlRelaxNG));
567
568 return (ret);
569}
570
571/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000572 * xmlRelaxNGFreeInnerSchema:
573 * @schema: a schema structure
574 *
575 * Deallocate a RelaxNG schema structure.
576 */
577static void
578xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema)
579{
580 if (schema == NULL)
581 return;
582
583 if (schema->doc != NULL)
584 xmlFreeDoc(schema->doc);
585 if (schema->defTab != NULL) {
586 int i;
587
588 for (i = 0;i < schema->defNr;i++)
589 xmlRelaxNGFreeDefine(schema->defTab[i]);
590 xmlFree(schema->defTab);
591 }
592
593 xmlFree(schema);
594}
595
596/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000597 * xmlRelaxNGFree:
598 * @schema: a schema structure
599 *
600 * Deallocate a RelaxNG structure.
601 */
602void
603xmlRelaxNGFree(xmlRelaxNGPtr schema)
604{
605 if (schema == NULL)
606 return;
607
Daniel Veillard6eadf632003-01-23 18:29:16 +0000608 if (schema->topgrammar != NULL)
609 xmlRelaxNGFreeGrammar(schema->topgrammar);
610 if (schema->doc != NULL)
611 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000612 if (schema->documents != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000613 xmlRelaxNGFreeDocumentList(schema->documents);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000614 if (schema->includes != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000615 xmlRelaxNGFreeIncludeList(schema->includes);
Daniel Veillard419a7682003-02-03 23:22:49 +0000616 if (schema->defTab != NULL) {
617 int i;
618
619 for (i = 0;i < schema->defNr;i++)
620 xmlRelaxNGFreeDefine(schema->defTab[i]);
621 xmlFree(schema->defTab);
622 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000623
624 xmlFree(schema);
625}
626
627/**
628 * xmlRelaxNGNewGrammar:
629 * @ctxt: a Relax-NG validation context (optional)
630 *
631 * Allocate a new RelaxNG grammar.
632 *
633 * Returns the newly allocated structure or NULL in case or error
634 */
635static xmlRelaxNGGrammarPtr
636xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
637{
638 xmlRelaxNGGrammarPtr ret;
639
640 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
641 if (ret == NULL) {
642 if ((ctxt != NULL) && (ctxt->error != NULL))
643 ctxt->error(ctxt->userData, "Out of memory\n");
644 ctxt->nbErrors++;
645 return (NULL);
646 }
647 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
648
649 return (ret);
650}
651
652/**
653 * xmlRelaxNGFreeGrammar:
654 * @grammar: a grammar structure
655 *
656 * Deallocate a RelaxNG grammar structure.
657 */
658static void
659xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
660{
661 if (grammar == NULL)
662 return;
663
Daniel Veillardc482e262003-02-26 14:48:48 +0000664 if (grammar->children != NULL) {
665 xmlRelaxNGFreeGrammar(grammar->children);
666 }
Daniel Veillard419a7682003-02-03 23:22:49 +0000667 if (grammar->next != NULL) {
668 xmlRelaxNGFreeGrammar(grammar->next);
669 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000670 if (grammar->refs != NULL) {
671 xmlHashFree(grammar->refs, NULL);
672 }
673 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000674 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000675 }
676
677 xmlFree(grammar);
678}
679
680/**
681 * xmlRelaxNGNewDefine:
682 * @ctxt: a Relax-NG validation context
683 * @node: the node in the input document.
684 *
685 * Allocate a new RelaxNG define.
686 *
687 * Returns the newly allocated structure or NULL in case or error
688 */
689static xmlRelaxNGDefinePtr
Daniel Veillardfd573f12003-03-16 17:52:32 +0000690xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
Daniel Veillard6eadf632003-01-23 18:29:16 +0000691{
692 xmlRelaxNGDefinePtr ret;
693
Daniel Veillard419a7682003-02-03 23:22:49 +0000694 if (ctxt->defMax == 0) {
695 ctxt->defMax = 16;
696 ctxt->defNr = 0;
697 ctxt->defTab = (xmlRelaxNGDefinePtr *)
698 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
699 if (ctxt->defTab == NULL) {
700 if ((ctxt != NULL) && (ctxt->error != NULL))
701 ctxt->error(ctxt->userData, "Out of memory\n");
702 ctxt->nbErrors++;
703 return (NULL);
704 }
705 } else if (ctxt->defMax <= ctxt->defNr) {
706 xmlRelaxNGDefinePtr *tmp;
707 ctxt->defMax *= 2;
708 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
709 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
710 if (tmp == NULL) {
711 if ((ctxt != NULL) && (ctxt->error != NULL))
712 ctxt->error(ctxt->userData, "Out of memory\n");
713 ctxt->nbErrors++;
714 return (NULL);
715 }
716 ctxt->defTab = tmp;
717 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000718 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
719 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000720 if ((ctxt != NULL) && (ctxt->error != NULL))
721 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000722 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000723 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000724 }
725 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000726 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000727 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000728 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000729 return (ret);
730}
731
732/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000733 * xmlRelaxNGFreePartition:
734 * @partitions: a partition set structure
735 *
736 * Deallocate RelaxNG partition set structures.
737 */
738static void
739xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
740 xmlRelaxNGInterleaveGroupPtr group;
741 int j;
742
743 if (partitions != NULL) {
744 if (partitions->groups != NULL) {
745 for (j = 0;j < partitions->nbgroups;j++) {
746 group = partitions->groups[j];
747 if (group != NULL) {
748 if (group->defs != NULL)
749 xmlFree(group->defs);
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000750 if (group->attrs != NULL)
751 xmlFree(group->attrs);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000752 xmlFree(group);
753 }
754 }
755 xmlFree(partitions->groups);
756 }
757 xmlFree(partitions);
758 }
759}
760/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000761 * xmlRelaxNGFreeDefine:
762 * @define: a define structure
763 *
764 * Deallocate a RelaxNG define structure.
765 */
766static void
767xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
768{
769 if (define == NULL)
770 return;
771
Daniel Veillard419a7682003-02-03 23:22:49 +0000772 if ((define->data != NULL) &&
773 (define->type == XML_RELAXNG_INTERLEAVE))
774 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000775 if (define->name != NULL)
776 xmlFree(define->name);
777 if (define->ns != NULL)
778 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000779 if (define->value != NULL)
780 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000781 xmlFree(define);
782}
783
784/**
Daniel Veillardfd573f12003-03-16 17:52:32 +0000785 * xmlRelaxNGNewStates:
786 * @ctxt: a Relax-NG validation context
787 * @size: the default size for the container
788 *
789 * Allocate a new RelaxNG validation state container
790 * TODO: keep a pool in the ctxt
791 *
792 * Returns the newly allocated structure or NULL in case or error
793 */
794static xmlRelaxNGStatesPtr
795xmlRelaxNGNewStates(xmlRelaxNGValidCtxtPtr ctxt, int size)
796{
797 xmlRelaxNGStatesPtr ret;
798
Daniel Veillard798024a2003-03-19 10:36:09 +0000799 if ((ctxt != NULL) &&
800 (ctxt->freeState != NULL) &&
801 (ctxt->freeStatesNr > 0)) {
802 ctxt->freeStatesNr--;
803 ret = ctxt->freeStates[ctxt->freeStatesNr];
804 ret->nbState = 0;
805 return(ret);
806 }
Daniel Veillardfd573f12003-03-16 17:52:32 +0000807 if (size < 16) size = 16;
808
809 ret = (xmlRelaxNGStatesPtr) xmlMalloc(sizeof(xmlRelaxNGStates) +
810 (size - 1) * sizeof(xmlRelaxNGValidStatePtr));
811 if (ret == NULL) {
812 if ((ctxt != NULL) && (ctxt->error != NULL))
813 ctxt->error(ctxt->userData, "Out of memory\n");
814 return (NULL);
815 }
816 ret->nbState = 0;
817 ret->maxState = size;
818 ret->tabState = (xmlRelaxNGValidStatePtr *) xmlMalloc(
819 (size) * sizeof(xmlRelaxNGValidStatePtr));
820 if (ret->tabState == NULL) {
821 if ((ctxt != NULL) && (ctxt->error != NULL))
822 ctxt->error(ctxt->userData, "Out of memory\n");
823 xmlFree(ret->tabState);
824 return (NULL);
825 }
826 return(ret);
827}
828
829/**
Daniel Veillard798024a2003-03-19 10:36:09 +0000830 * xmlRelaxNGAddStateUniq:
831 * @ctxt: a Relax-NG validation context
832 * @states: the states container
833 * @state: the validation state
834 *
835 * Add a RelaxNG validation state to the container without checking
836 * for unicity.
837 *
838 * Return 1 in case of success and 0 if this is a duplicate and -1 on error
839 */
840static int
841xmlRelaxNGAddStatesUniq(xmlRelaxNGValidCtxtPtr ctxt,
842 xmlRelaxNGStatesPtr states,
843 xmlRelaxNGValidStatePtr state)
844{
845 if (state == NULL) {
846 return(-1);
847 }
848 if (states->nbState >= states->maxState) {
849 xmlRelaxNGValidStatePtr *tmp;
850 int size;
851
852 size = states->maxState * 2;
853 tmp = (xmlRelaxNGValidStatePtr *) xmlRealloc(states->tabState,
854 (size) * sizeof(xmlRelaxNGValidStatePtr));
855 if (tmp == NULL) {
856 if ((ctxt != NULL) && (ctxt->error != NULL))
857 ctxt->error(ctxt->userData, "Out of memory\n");
858 return(-1);
859 }
860 states->tabState = tmp;
861 states->maxState = size;
862 }
863 states->tabState[states->nbState++] = state;
864 return(1);
865}
866
867/**
Daniel Veillardfd573f12003-03-16 17:52:32 +0000868 * xmlRelaxNGAddState:
869 * @ctxt: a Relax-NG validation context
870 * @states: the states container
871 * @state: the validation state
872 *
873 * Add a RelaxNG validation state to the container
874 *
875 * Return 1 in case of success and 0 if this is a duplicate and -1 on error
876 */
877static int
878xmlRelaxNGAddStates(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGStatesPtr states,
879 xmlRelaxNGValidStatePtr state)
880{
881 int i;
882
883 if (state == NULL) {
884 return(-1);
885 }
886 if (states->nbState >= states->maxState) {
887 xmlRelaxNGValidStatePtr *tmp;
888 int size;
889
890 size = states->maxState * 2;
891 tmp = (xmlRelaxNGValidStatePtr *) xmlRealloc(states->tabState,
892 (size) * sizeof(xmlRelaxNGValidStatePtr));
893 if (tmp == NULL) {
894 if ((ctxt != NULL) && (ctxt->error != NULL))
895 ctxt->error(ctxt->userData, "Out of memory\n");
896 return(-1);
897 }
898 states->tabState = tmp;
899 states->maxState = size;
900 }
901 for (i = 0;i < states->nbState;i++) {
902 if (xmlRelaxNGEqualValidState(ctxt, state, states->tabState[i])) {
Daniel Veillard798024a2003-03-19 10:36:09 +0000903 xmlRelaxNGFreeValidState(ctxt, state);
Daniel Veillardfd573f12003-03-16 17:52:32 +0000904 return(0);
905 }
906 }
907 states->tabState[states->nbState++] = state;
908 return(1);
909}
910
911/**
912 * xmlRelaxNGFreeStates:
913 * @ctxt: a Relax-NG validation context
914 * @states: teh container
915 *
916 * Free a RelaxNG validation state container
Daniel Veillardfd573f12003-03-16 17:52:32 +0000917 */
918static void
Daniel Veillard798024a2003-03-19 10:36:09 +0000919xmlRelaxNGFreeStates(xmlRelaxNGValidCtxtPtr ctxt,
Daniel Veillardfd573f12003-03-16 17:52:32 +0000920 xmlRelaxNGStatesPtr states)
921{
Daniel Veillard798024a2003-03-19 10:36:09 +0000922 if (states == NULL)
923 return;
Daniel Veillard798024a2003-03-19 10:36:09 +0000924 if ((ctxt != NULL) && (ctxt->freeStates == NULL)) {
925 ctxt->freeStatesMax = 40;
926 ctxt->freeStatesNr = 0;
927 ctxt->freeStates = (xmlRelaxNGStatesPtr *)
928 xmlMalloc(ctxt->freeStatesMax * sizeof(xmlRelaxNGStatesPtr));
929 if (ctxt->freeStates == NULL) {
930 if ((ctxt != NULL) && (ctxt->error != NULL))
931 ctxt->error(ctxt->userData, "Out of memory\n");
932 }
933 } else if ((ctxt != NULL) && (ctxt->freeStatesNr >= ctxt->freeStatesMax)) {
934 xmlRelaxNGStatesPtr *tmp;
935
936 tmp = (xmlRelaxNGStatesPtr *) xmlRealloc(ctxt->freeStates,
937 2 * ctxt->freeStatesMax * sizeof(xmlRelaxNGStatesPtr));
938 if (tmp == NULL) {
939 if ((ctxt != NULL) && (ctxt->error != NULL))
940 ctxt->error(ctxt->userData, "Out of memory\n");
941 xmlFree(states->tabState);
942 xmlFree(states);
943 return;
944 }
945 ctxt->freeStates = tmp;
946 ctxt->freeStatesMax *= 2;
947 }
948 if ((ctxt == NULL) || (ctxt->freeState == NULL)) {
Daniel Veillardfd573f12003-03-16 17:52:32 +0000949 xmlFree(states->tabState);
950 xmlFree(states);
Daniel Veillard798024a2003-03-19 10:36:09 +0000951 } else {
952 ctxt->freeStates[ctxt->freeStatesNr++] = states;
Daniel Veillardfd573f12003-03-16 17:52:32 +0000953 }
954}
955
956/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000957 * xmlRelaxNGNewValidState:
958 * @ctxt: a Relax-NG validation context
959 * @node: the current node or NULL for the document
960 *
961 * Allocate a new RelaxNG validation state
962 *
963 * Returns the newly allocated structure or NULL in case or error
964 */
965static xmlRelaxNGValidStatePtr
966xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
967{
968 xmlRelaxNGValidStatePtr ret;
969 xmlAttrPtr attr;
970 xmlAttrPtr attrs[MAX_ATTR];
971 int nbAttrs = 0;
972 xmlNodePtr root = NULL;
973
974 if (node == NULL) {
975 root = xmlDocGetRootElement(ctxt->doc);
976 if (root == NULL)
977 return(NULL);
978 } else {
979 attr = node->properties;
980 while (attr != NULL) {
981 if (nbAttrs < MAX_ATTR)
982 attrs[nbAttrs++] = attr;
983 else
984 nbAttrs++;
985 attr = attr->next;
986 }
987 }
Daniel Veillard798024a2003-03-19 10:36:09 +0000988 if ((ctxt->freeState != NULL) &&
989 (ctxt->freeState->nbState > 0)) {
990 ctxt->freeState->nbState--;
991 ret = ctxt->freeState->tabState[ctxt->freeState->nbState];
992 } else {
993 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState));
994 if (ret == NULL) {
995 if ((ctxt != NULL) && (ctxt->error != NULL))
996 ctxt->error(ctxt->userData, "Out of memory\n");
997 return (NULL);
998 }
999 memset(ret, 0, sizeof(xmlRelaxNGValidState));
Daniel Veillard6eadf632003-01-23 18:29:16 +00001000 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001001 ret->value = NULL;
1002 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001003 if (node == NULL) {
1004 ret->node = (xmlNodePtr) ctxt->doc;
1005 ret->seq = root;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001006 } else {
1007 ret->node = node;
1008 ret->seq = node->children;
Daniel Veillard798024a2003-03-19 10:36:09 +00001009 }
1010 ret->nbAttrs = 0;
1011 if (nbAttrs > 0) {
1012 if (ret->attrs == NULL) {
1013 if (nbAttrs < 4) ret->maxAttrs = 4;
1014 else ret->maxAttrs = nbAttrs;
1015 ret->attrs = (xmlAttrPtr *) xmlMalloc(ret->maxAttrs *
1016 sizeof(xmlAttrPtr));
1017 if (ret->attrs == NULL) {
1018 if ((ctxt != NULL) && (ctxt->error != NULL))
1019 ctxt->error(ctxt->userData, "Out of memory\n");
1020 return (ret);
1021 }
1022 } else if (ret->maxAttrs < nbAttrs) {
1023 xmlAttrPtr *tmp;
1024
1025 tmp = (xmlAttrPtr *) xmlRealloc(ret->attrs, nbAttrs *
1026 sizeof(xmlAttrPtr));
1027 if (tmp == NULL) {
1028 if ((ctxt != NULL) && (ctxt->error != NULL))
1029 ctxt->error(ctxt->userData, "Out of memory\n");
1030 return (ret);
1031 }
1032 ret->attrs = tmp;
1033 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00001034 ret->nbAttrs = nbAttrs;
Daniel Veillard798024a2003-03-19 10:36:09 +00001035 if (nbAttrs < MAX_ATTR) {
1036 memcpy(ret->attrs, attrs, sizeof(xmlAttrPtr) * nbAttrs);
1037 } else {
1038 attr = node->properties;
1039 nbAttrs = 0;
1040 while (attr != NULL) {
1041 ret->attrs[nbAttrs++] = attr;
1042 attr = attr->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001043 }
1044 }
1045 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +00001046 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard798024a2003-03-19 10:36:09 +00001047 if (ret->node == NULL) {
1048 printf("pbm!\n");
1049 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00001050 return (ret);
1051}
1052
1053/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00001054 * xmlRelaxNGCopyValidState:
1055 * @ctxt: a Relax-NG validation context
1056 * @state: a validation state
1057 *
1058 * Copy the validation state
1059 *
1060 * Returns the newly allocated structure or NULL in case or error
1061 */
1062static xmlRelaxNGValidStatePtr
1063xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
1064 xmlRelaxNGValidStatePtr state)
1065{
1066 xmlRelaxNGValidStatePtr ret;
Daniel Veillard798024a2003-03-19 10:36:09 +00001067 unsigned int maxAttrs;
1068 xmlAttrPtr *attrs;
Daniel Veillardfd573f12003-03-16 17:52:32 +00001069
1070 if (state == NULL)
1071 return(NULL);
Daniel Veillard798024a2003-03-19 10:36:09 +00001072 if ((ctxt->freeState != NULL) &&
1073 (ctxt->freeState->nbState > 0)) {
1074 ctxt->freeState->nbState--;
1075 ret = ctxt->freeState->tabState[ctxt->freeState->nbState];
1076 } else {
1077 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState));
1078 if (ret == NULL) {
1079 if ((ctxt != NULL) && (ctxt->error != NULL))
1080 ctxt->error(ctxt->userData, "Out of memory\n");
1081 return (NULL);
1082 }
1083 memset(ret, 0, sizeof(xmlRelaxNGValidState));
Daniel Veillardfd573f12003-03-16 17:52:32 +00001084 }
Daniel Veillard798024a2003-03-19 10:36:09 +00001085 attrs = ret->attrs;
1086 maxAttrs = ret->maxAttrs;
1087 memcpy(ret, state, sizeof(xmlRelaxNGValidState));
1088 ret->attrs = attrs;
1089 ret->maxAttrs = maxAttrs;
1090 if (state->nbAttrs > 0) {
1091 if (ret->attrs == NULL) {
1092 ret->maxAttrs = state->maxAttrs;
1093 ret->attrs = (xmlAttrPtr *) xmlMalloc(ret->maxAttrs *
1094 sizeof(xmlAttrPtr));
1095 if (ret->attrs == NULL) {
1096 if ((ctxt != NULL) && (ctxt->error != NULL))
1097 ctxt->error(ctxt->userData, "Out of memory\n");
1098 ret->nbAttrs = 0;
1099 return (ret);
1100 }
1101 } else if (ret->maxAttrs < state->nbAttrs) {
1102 xmlAttrPtr *tmp;
1103
1104 tmp = (xmlAttrPtr *) xmlRealloc(ret->attrs, state->maxAttrs *
1105 sizeof(xmlAttrPtr));
1106 if (tmp == NULL) {
1107 if ((ctxt != NULL) && (ctxt->error != NULL))
1108 ctxt->error(ctxt->userData, "Out of memory\n");
1109 ret->nbAttrs = 0;
1110 return (ret);
1111 }
1112 ret->maxAttrs = state->maxAttrs;
1113 }
1114 memcpy(ret->attrs, state->attrs, state->nbAttrs * sizeof(xmlAttrPtr));
1115 }
1116 if (ret->node == NULL) {
1117 printf("pbm!\n");
1118 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00001119 return(ret);
1120}
1121
1122/**
1123 * xmlRelaxNGEqualValidState:
1124 * @ctxt: a Relax-NG validation context
1125 * @state1: a validation state
1126 * @state2: a validation state
1127 *
1128 * Compare the validation states for equality
1129 *
1130 * Returns 1 if equald, 0 otherwise
1131 */
1132static int
1133xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1134 xmlRelaxNGValidStatePtr state1,
1135 xmlRelaxNGValidStatePtr state2)
1136{
1137 int i;
1138
1139 if ((state1 == NULL) || (state2 == NULL))
1140 return(0);
1141 if (state1 == state2)
1142 return(1);
1143 if (state1->node != state2->node)
1144 return(0);
1145 if (state1->seq != state2->seq)
1146 return(0);
1147 if (state1->nbAttrLeft != state2->nbAttrLeft)
1148 return(0);
1149 if (state1->nbAttrs != state2->nbAttrs)
1150 return(0);
1151 if (state1->endvalue != state2->endvalue)
1152 return(0);
1153 if ((state1->value != state2->value) &&
1154 (!xmlStrEqual(state1->value, state2->value)))
1155 return(0);
1156 for (i = 0;i < state1->nbAttrs;i++) {
1157 if (state1->attrs[i] != state2->attrs[i])
1158 return(0);
1159 }
1160 return(1);
1161}
1162
1163/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00001164 * xmlRelaxNGFreeValidState:
1165 * @state: a validation state structure
1166 *
1167 * Deallocate a RelaxNG validation state structure.
1168 */
1169static void
Daniel Veillard798024a2003-03-19 10:36:09 +00001170xmlRelaxNGFreeValidState(xmlRelaxNGValidCtxtPtr ctxt,
1171 xmlRelaxNGValidStatePtr state)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001172{
1173 if (state == NULL)
1174 return;
1175
Daniel Veillard798024a2003-03-19 10:36:09 +00001176 if ((ctxt != NULL) && (ctxt->freeState == NULL)) {
1177 ctxt->freeState = xmlRelaxNGNewStates(ctxt, 40);
1178 }
1179 if ((ctxt == NULL) || (ctxt->freeState == NULL)) {
1180 if (state->attrs != NULL)
1181 xmlFree(state->attrs);
1182 xmlFree(state);
1183 } else {
1184 xmlRelaxNGAddStatesUniq(ctxt, ctxt->freeState, state);
1185 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00001186}
1187
1188/************************************************************************
1189 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001190 * Document functions *
1191 * *
1192 ************************************************************************/
1193static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
1194 xmlDocPtr doc);
1195
1196/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001197 * xmlRelaxNGIncludePush:
1198 * @ctxt: the parser context
1199 * @value: the element doc
1200 *
1201 * Pushes a new include on top of the include stack
1202 *
1203 * Returns 0 in case of error, the index in the stack otherwise
1204 */
1205static int
1206xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
1207 xmlRelaxNGIncludePtr value)
1208{
1209 if (ctxt->incTab == NULL) {
1210 ctxt->incMax = 4;
1211 ctxt->incNr = 0;
1212 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
1213 ctxt->incMax * sizeof(ctxt->incTab[0]));
1214 if (ctxt->incTab == NULL) {
1215 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1216 return (0);
1217 }
1218 }
1219 if (ctxt->incNr >= ctxt->incMax) {
1220 ctxt->incMax *= 2;
1221 ctxt->incTab =
1222 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
1223 ctxt->incMax *
1224 sizeof(ctxt->incTab[0]));
1225 if (ctxt->incTab == NULL) {
1226 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1227 return (0);
1228 }
1229 }
1230 ctxt->incTab[ctxt->incNr] = value;
1231 ctxt->inc = value;
1232 return (ctxt->incNr++);
1233}
1234
1235/**
1236 * xmlRelaxNGIncludePop:
1237 * @ctxt: the parser context
1238 *
1239 * Pops the top include from the include stack
1240 *
1241 * Returns the include just removed
1242 */
1243static xmlRelaxNGIncludePtr
1244xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
1245{
1246 xmlRelaxNGIncludePtr ret;
1247
1248 if (ctxt->incNr <= 0)
1249 return (0);
1250 ctxt->incNr--;
1251 if (ctxt->incNr > 0)
1252 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
1253 else
1254 ctxt->inc = NULL;
1255 ret = ctxt->incTab[ctxt->incNr];
1256 ctxt->incTab[ctxt->incNr] = 0;
1257 return (ret);
1258}
1259
1260/**
Daniel Veillard5add8682003-03-10 13:13:58 +00001261 * xmlRelaxNGRemoveRedefine:
1262 * @ctxt: the parser context
1263 * @URL: the normalized URL
1264 * @target: the included target
1265 * @name: the define name to eliminate
1266 *
1267 * Applies the elimination algorithm of 4.7
1268 *
1269 * Returns 0 in case of error, 1 in case of success.
1270 */
1271static int
1272xmlRelaxNGRemoveRedefine(xmlRelaxNGParserCtxtPtr ctxt,
1273 const xmlChar *URL ATTRIBUTE_UNUSED,
1274 xmlNodePtr target, const xmlChar *name) {
1275 int found = 0;
1276 xmlNodePtr tmp, tmp2;
1277 xmlChar *name2;
1278
1279#ifdef DEBUG_INCLUDE
Daniel Veillard952379b2003-03-17 15:37:12 +00001280 if (name == NULL)
1281 xmlGenericError(xmlGenericErrorContext,
1282 "Elimination of <include> start from %s\n", URL);
1283 else
1284 xmlGenericError(xmlGenericErrorContext,
1285 "Elimination of <include> define %s from %s\n", name, URL);
Daniel Veillard5add8682003-03-10 13:13:58 +00001286#endif
1287 tmp = target;
1288 while (tmp != NULL) {
1289 tmp2 = tmp->next;
1290 if ((name == NULL) && (IS_RELAXNG(tmp, "start"))) {
1291 found = 1;
1292 xmlUnlinkNode(tmp);
1293 xmlFreeNode(tmp);
1294 } else if ((name != NULL) && (IS_RELAXNG(tmp, "define"))) {
1295 name2 = xmlGetProp(tmp, BAD_CAST "name");
1296 xmlRelaxNGNormExtSpace(name2);
1297 if (name2 != NULL) {
1298 if (xmlStrEqual(name, name2)) {
1299 found = 1;
1300 xmlUnlinkNode(tmp);
1301 xmlFreeNode(tmp);
1302 }
1303 xmlFree(name2);
1304 }
1305 } else if (IS_RELAXNG(tmp, "include")) {
1306 xmlChar *href = NULL;
1307 xmlRelaxNGDocumentPtr inc = tmp->_private;
1308
1309 if ((inc != NULL) && (inc->doc != NULL) &&
1310 (inc->doc->children != NULL)) {
1311
1312 if (xmlStrEqual(inc->doc->children->name, BAD_CAST "grammar")) {
1313#ifdef DEBUG_INCLUDE
1314 href = xmlGetProp(tmp, BAD_CAST "href");
1315#endif
1316 if (xmlRelaxNGRemoveRedefine(ctxt, href,
1317 inc->doc->children->children, name) == 1) {
1318 found = 1;
1319 }
1320 if (href != NULL)
1321 xmlFree(href);
1322 }
1323 }
1324 }
1325 tmp = tmp2;
1326 }
1327 return(found);
1328}
1329
1330/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001331 * xmlRelaxNGLoadInclude:
1332 * @ctxt: the parser context
1333 * @URL: the normalized URL
1334 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +00001335 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001336 *
1337 * First lookup if the document is already loaded into the parser context,
1338 * check against recursion. If not found the resource is loaded and
1339 * the content is preprocessed before being returned back to the caller.
1340 *
1341 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
1342 */
1343static xmlRelaxNGIncludePtr
1344xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +00001345 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001346 xmlRelaxNGIncludePtr ret = NULL;
1347 xmlDocPtr doc;
1348 int i;
Daniel Veillard5add8682003-03-10 13:13:58 +00001349 xmlNodePtr root, cur;
1350
1351#ifdef DEBUG_INCLUDE
1352 xmlGenericError(xmlGenericErrorContext,
1353 "xmlRelaxNGLoadInclude(%s)\n", URL);
1354#endif
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001355
1356 /*
1357 * check against recursion in the stack
1358 */
1359 for (i = 0;i < ctxt->incNr;i++) {
1360 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
1361 if (ctxt->error != NULL)
1362 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00001363 "Detected an Include recursion for %s\n",
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001364 URL);
1365 ctxt->nbErrors++;
1366 return(NULL);
1367 }
1368 }
1369
1370 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001371 * load the document
1372 */
1373 doc = xmlParseFile((const char *) URL);
1374 if (doc == NULL) {
1375 if (ctxt->error != NULL)
1376 ctxt->error(ctxt->userData,
1377 "xmlRelaxNG: could not load %s\n", URL);
1378 ctxt->nbErrors++;
1379 return (NULL);
1380 }
1381
Daniel Veillard5add8682003-03-10 13:13:58 +00001382#ifdef DEBUG_INCLUDE
1383 xmlGenericError(xmlGenericErrorContext,
1384 "Parsed %s Okay\n", URL);
1385#endif
1386
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001387 /*
1388 * Allocate the document structures and register it first.
1389 */
1390 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
1391 if (ret == NULL) {
1392 if (ctxt->error != NULL)
1393 ctxt->error(ctxt->userData,
1394 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1395 ctxt->nbErrors++;
1396 xmlFreeDoc(doc);
1397 return (NULL);
1398 }
1399 memset(ret, 0, sizeof(xmlRelaxNGInclude));
1400 ret->doc = doc;
1401 ret->href = xmlStrdup(URL);
Daniel Veillardc482e262003-02-26 14:48:48 +00001402 ret->next = ctxt->includes;
1403 ctxt->includes = ret;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001404
1405 /*
Daniel Veillard416589a2003-02-17 17:25:42 +00001406 * transmit the ns if needed
1407 */
1408 if (ns != NULL) {
1409 root = xmlDocGetRootElement(doc);
1410 if (root != NULL) {
1411 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1412 xmlSetProp(root, BAD_CAST"ns", ns);
1413 }
1414 }
1415 }
1416
1417 /*
Daniel Veillardc482e262003-02-26 14:48:48 +00001418 * push it on the stack
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001419 */
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001420 xmlRelaxNGIncludePush(ctxt, ret);
1421
1422 /*
1423 * Some preprocessing of the document content, this include recursing
1424 * in the include stack.
1425 */
Daniel Veillard5add8682003-03-10 13:13:58 +00001426#ifdef DEBUG_INCLUDE
1427 xmlGenericError(xmlGenericErrorContext,
1428 "cleanup of %s\n", URL);
1429#endif
1430
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001431 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1432 if (doc == NULL) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001433 ctxt->inc = NULL;
1434 return(NULL);
1435 }
1436
1437 /*
1438 * Pop up the include from the stack
1439 */
1440 xmlRelaxNGIncludePop(ctxt);
1441
Daniel Veillard5add8682003-03-10 13:13:58 +00001442#ifdef DEBUG_INCLUDE
1443 xmlGenericError(xmlGenericErrorContext,
1444 "Checking of %s\n", URL);
1445#endif
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001446 /*
1447 * Check that the top element is a grammar
1448 */
1449 root = xmlDocGetRootElement(doc);
1450 if (root == NULL) {
1451 if (ctxt->error != NULL)
1452 ctxt->error(ctxt->userData,
1453 "xmlRelaxNG: included document is empty %s\n", URL);
1454 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001455 return (NULL);
1456 }
1457 if (!IS_RELAXNG(root, "grammar")) {
1458 if (ctxt->error != NULL)
1459 ctxt->error(ctxt->userData,
1460 "xmlRelaxNG: included document %s root is not a grammar\n",
1461 URL);
1462 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001463 return (NULL);
1464 }
1465
1466 /*
1467 * Elimination of redefined rules in the include.
1468 */
1469 cur = node->children;
1470 while (cur != NULL) {
1471 if (IS_RELAXNG(cur, "start")) {
1472 int found = 0;
1473
Daniel Veillard5add8682003-03-10 13:13:58 +00001474 found = xmlRelaxNGRemoveRedefine(ctxt, URL, root->children, NULL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001475 if (!found) {
1476 if (ctxt->error != NULL)
1477 ctxt->error(ctxt->userData,
1478 "xmlRelaxNG: include %s has a start but not the included grammar\n",
1479 URL);
1480 ctxt->nbErrors++;
1481 }
1482 } else if (IS_RELAXNG(cur, "define")) {
Daniel Veillard5add8682003-03-10 13:13:58 +00001483 xmlChar *name;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001484
1485 name = xmlGetProp(cur, BAD_CAST "name");
1486 if (name == NULL) {
1487 if (ctxt->error != NULL)
1488 ctxt->error(ctxt->userData,
1489 "xmlRelaxNG: include %s has define without name\n",
1490 URL);
1491 ctxt->nbErrors++;
1492 } else {
Daniel Veillard5add8682003-03-10 13:13:58 +00001493 int found;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001494
Daniel Veillardd2298792003-02-14 16:54:11 +00001495 xmlRelaxNGNormExtSpace(name);
Daniel Veillard5add8682003-03-10 13:13:58 +00001496 found = xmlRelaxNGRemoveRedefine(ctxt, URL,
1497 root->children, name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001498 if (!found) {
1499 if (ctxt->error != NULL)
1500 ctxt->error(ctxt->userData,
1501 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1502 URL, name);
1503 ctxt->nbErrors++;
1504 }
1505 xmlFree(name);
1506 }
1507 }
1508 cur = cur->next;
1509 }
1510
1511
1512 return(ret);
1513}
1514
1515/**
Daniel Veillard42f12e92003-03-07 18:32:59 +00001516 * xmlRelaxNGValidErrorPush:
1517 * @ctxt: the validation context
1518 * @err: the error code
1519 * @arg1: the first string argument
1520 * @arg2: the second string argument
Daniel Veillardc3da18a2003-03-18 00:31:04 +00001521 * @dup: arg need to be duplicated
Daniel Veillard42f12e92003-03-07 18:32:59 +00001522 *
1523 * Pushes a new error on top of the error stack
1524 *
1525 * Returns 0 in case of error, the index in the stack otherwise
1526 */
1527static int
1528xmlRelaxNGValidErrorPush(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err,
Daniel Veillardc3da18a2003-03-18 00:31:04 +00001529 const xmlChar *arg1, const xmlChar *arg2, int dup)
Daniel Veillard42f12e92003-03-07 18:32:59 +00001530{
1531 xmlRelaxNGValidErrorPtr cur;
1532 if (ctxt->errTab == NULL) {
1533 ctxt->errMax = 8;
1534 ctxt->errNr = 0;
1535 ctxt->errTab = (xmlRelaxNGValidErrorPtr) xmlMalloc(
1536 ctxt->errMax * sizeof(xmlRelaxNGValidError));
1537 if (ctxt->errTab == NULL) {
1538 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1539 return (0);
1540 }
1541 }
1542 if (ctxt->errNr >= ctxt->errMax) {
1543 ctxt->errMax *= 2;
1544 ctxt->errTab =
1545 (xmlRelaxNGValidErrorPtr) xmlRealloc(ctxt->errTab,
1546 ctxt->errMax * sizeof(xmlRelaxNGValidError));
1547 if (ctxt->errTab == NULL) {
1548 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1549 return (0);
1550 }
1551 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00001552 if ((ctxt->err != NULL) && (ctxt->state != NULL) &&
Daniel Veillardfd573f12003-03-16 17:52:32 +00001553 (ctxt->err->node == ctxt->state->node) &&
1554 (ctxt->err->err == err))
1555 return(ctxt->errNr);
Daniel Veillard42f12e92003-03-07 18:32:59 +00001556 cur = &ctxt->errTab[ctxt->errNr];
1557 cur->err = err;
Daniel Veillardc3da18a2003-03-18 00:31:04 +00001558 if (dup) {
1559 cur->arg1 = xmlStrdup(arg1);
1560 cur->arg2 = xmlStrdup(arg2);
1561 cur->flags = ERROR_IS_DUP;
1562 } else {
1563 cur->arg1 = arg1;
1564 cur->arg2 = arg2;
1565 cur->flags = 0;
1566 }
Daniel Veillard42f12e92003-03-07 18:32:59 +00001567 if (ctxt->state != NULL) {
1568 cur->node = ctxt->state->node;
1569 cur->seq = ctxt->state->seq;
1570 } else {
1571 cur->node = NULL;
1572 cur->seq = NULL;
1573 }
1574 ctxt->err = cur;
1575 return (ctxt->errNr++);
1576}
1577
1578/**
1579 * xmlRelaxNGValidErrorPop:
1580 * @ctxt: the validation context
1581 *
1582 * Pops the top error from the error stack
Daniel Veillard42f12e92003-03-07 18:32:59 +00001583 */
Daniel Veillardc3da18a2003-03-18 00:31:04 +00001584static void
Daniel Veillard42f12e92003-03-07 18:32:59 +00001585xmlRelaxNGValidErrorPop(xmlRelaxNGValidCtxtPtr ctxt)
1586{
Daniel Veillardc3da18a2003-03-18 00:31:04 +00001587 xmlRelaxNGValidErrorPtr cur;
Daniel Veillard42f12e92003-03-07 18:32:59 +00001588
1589 if (ctxt->errNr <= 0)
Daniel Veillardc3da18a2003-03-18 00:31:04 +00001590 return;
Daniel Veillard42f12e92003-03-07 18:32:59 +00001591 ctxt->errNr--;
1592 if (ctxt->errNr > 0)
1593 ctxt->err = &ctxt->errTab[ctxt->errNr - 1];
1594 else
1595 ctxt->err = NULL;
Daniel Veillardc3da18a2003-03-18 00:31:04 +00001596 cur = &ctxt->errTab[ctxt->errNr];
1597 if (cur->flags & ERROR_IS_DUP) {
Daniel Veillard249d7bb2003-03-19 21:02:29 +00001598 if (cur->arg1 != NULL)
1599 xmlFree((xmlChar *)cur->arg1);
Daniel Veillardc3da18a2003-03-18 00:31:04 +00001600 cur->arg1 = NULL;
Daniel Veillard249d7bb2003-03-19 21:02:29 +00001601 if (cur->arg2 != NULL)
1602 xmlFree((xmlChar *)cur->arg2);
Daniel Veillardc3da18a2003-03-18 00:31:04 +00001603 cur->arg2 = NULL;
1604 cur->flags = 0;
1605 }
Daniel Veillard42f12e92003-03-07 18:32:59 +00001606}
1607
Daniel Veillard42f12e92003-03-07 18:32:59 +00001608/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001609 * xmlRelaxNGDocumentPush:
1610 * @ctxt: the parser context
1611 * @value: the element doc
1612 *
1613 * Pushes a new doc on top of the doc stack
1614 *
1615 * Returns 0 in case of error, the index in the stack otherwise
1616 */
1617static int
1618xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1619 xmlRelaxNGDocumentPtr value)
1620{
1621 if (ctxt->docTab == NULL) {
1622 ctxt->docMax = 4;
1623 ctxt->docNr = 0;
1624 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1625 ctxt->docMax * sizeof(ctxt->docTab[0]));
1626 if (ctxt->docTab == NULL) {
1627 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1628 return (0);
1629 }
1630 }
1631 if (ctxt->docNr >= ctxt->docMax) {
1632 ctxt->docMax *= 2;
1633 ctxt->docTab =
1634 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1635 ctxt->docMax *
1636 sizeof(ctxt->docTab[0]));
1637 if (ctxt->docTab == NULL) {
1638 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1639 return (0);
1640 }
1641 }
1642 ctxt->docTab[ctxt->docNr] = value;
1643 ctxt->doc = value;
1644 return (ctxt->docNr++);
1645}
1646
1647/**
1648 * xmlRelaxNGDocumentPop:
1649 * @ctxt: the parser context
1650 *
1651 * Pops the top doc from the doc stack
1652 *
1653 * Returns the doc just removed
1654 */
1655static xmlRelaxNGDocumentPtr
1656xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1657{
1658 xmlRelaxNGDocumentPtr ret;
1659
1660 if (ctxt->docNr <= 0)
1661 return (0);
1662 ctxt->docNr--;
1663 if (ctxt->docNr > 0)
1664 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1665 else
1666 ctxt->doc = NULL;
1667 ret = ctxt->docTab[ctxt->docNr];
1668 ctxt->docTab[ctxt->docNr] = 0;
1669 return (ret);
1670}
1671
1672/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001673 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001674 * @ctxt: the parser context
1675 * @URL: the normalized URL
1676 * @ns: the inherited ns if any
1677 *
1678 * First lookup if the document is already loaded into the parser context,
1679 * check against recursion. If not found the resource is loaded and
1680 * the content is preprocessed before being returned back to the caller.
1681 *
1682 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1683 */
1684static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001685xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001686 const xmlChar *ns) {
1687 xmlRelaxNGDocumentPtr ret = NULL;
1688 xmlDocPtr doc;
1689 xmlNodePtr root;
1690 int i;
1691
1692 /*
1693 * check against recursion in the stack
1694 */
1695 for (i = 0;i < ctxt->docNr;i++) {
1696 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1697 if (ctxt->error != NULL)
1698 ctxt->error(ctxt->userData,
1699 "Detected an externalRef recursion for %s\n",
1700 URL);
1701 ctxt->nbErrors++;
1702 return(NULL);
1703 }
1704 }
1705
1706 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001707 * load the document
1708 */
1709 doc = xmlParseFile((const char *) URL);
1710 if (doc == NULL) {
1711 if (ctxt->error != NULL)
1712 ctxt->error(ctxt->userData,
1713 "xmlRelaxNG: could not load %s\n", URL);
1714 ctxt->nbErrors++;
1715 return (NULL);
1716 }
1717
1718 /*
1719 * Allocate the document structures and register it first.
1720 */
1721 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1722 if (ret == NULL) {
1723 if (ctxt->error != NULL)
1724 ctxt->error(ctxt->userData,
1725 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1726 ctxt->nbErrors++;
1727 xmlFreeDoc(doc);
1728 return (NULL);
1729 }
1730 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1731 ret->doc = doc;
1732 ret->href = xmlStrdup(URL);
Daniel Veillardc482e262003-02-26 14:48:48 +00001733 ret->next = ctxt->documents;
1734 ctxt->documents = ret;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001735
1736 /*
1737 * transmit the ns if needed
1738 */
1739 if (ns != NULL) {
1740 root = xmlDocGetRootElement(doc);
1741 if (root != NULL) {
1742 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1743 xmlSetProp(root, BAD_CAST"ns", ns);
1744 }
1745 }
1746 }
1747
1748 /*
1749 * push it on the stack and register it in the hash table
1750 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001751 xmlRelaxNGDocumentPush(ctxt, ret);
1752
1753 /*
1754 * Some preprocessing of the document content
1755 */
1756 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1757 if (doc == NULL) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001758 ctxt->doc = NULL;
1759 return(NULL);
1760 }
1761
1762 xmlRelaxNGDocumentPop(ctxt);
1763
1764 return(ret);
1765}
1766
1767/************************************************************************
1768 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001769 * Error functions *
1770 * *
1771 ************************************************************************/
1772
Daniel Veillardc3da18a2003-03-18 00:31:04 +00001773#define VALID_ERR(a) xmlRelaxNGAddValidError(ctxt, a, NULL, NULL, 0);
1774#define VALID_ERR2(a, b) xmlRelaxNGAddValidError(ctxt, a, b, NULL, 0);
1775#define VALID_ERR3(a, b, c) xmlRelaxNGAddValidError(ctxt, a, b, c, 0);
1776#define VALID_ERR2P(a, b) xmlRelaxNGAddValidError(ctxt, a, b, NULL, 1);
1777#define VALID_ERR3P(a, b, c) xmlRelaxNGAddValidError(ctxt, a, b, c, 1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001778
Daniel Veillardfd573f12003-03-16 17:52:32 +00001779#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001780static const char *
1781xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1782 if (def == NULL)
1783 return("none");
1784 switch(def->type) {
1785 case XML_RELAXNG_EMPTY: return("empty");
1786 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1787 case XML_RELAXNG_EXCEPT: return("except");
1788 case XML_RELAXNG_TEXT: return("text");
1789 case XML_RELAXNG_ELEMENT: return("element");
1790 case XML_RELAXNG_DATATYPE: return("datatype");
1791 case XML_RELAXNG_VALUE: return("value");
1792 case XML_RELAXNG_LIST: return("list");
1793 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1794 case XML_RELAXNG_DEF: return("def");
1795 case XML_RELAXNG_REF: return("ref");
1796 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1797 case XML_RELAXNG_PARENTREF: return("parentRef");
Daniel Veillardfd573f12003-03-16 17:52:32 +00001798 case XML_RELAXNG_OPTIONAL: return("optional");
1799 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
Daniel Veillard231d7912003-02-09 14:22:17 +00001800 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1801 case XML_RELAXNG_CHOICE: return("choice");
1802 case XML_RELAXNG_GROUP: return("group");
1803 case XML_RELAXNG_INTERLEAVE: return("interleave");
1804 case XML_RELAXNG_START: return("start");
Daniel Veillard1564e6e2003-03-15 21:30:25 +00001805 case XML_RELAXNG_NOOP: return("noop");
Daniel Veillard1564e6e2003-03-15 21:30:25 +00001806 case XML_RELAXNG_PARAM: return("param");
Daniel Veillard231d7912003-02-09 14:22:17 +00001807 }
1808 return("unknown");
1809}
Daniel Veillardfd573f12003-03-16 17:52:32 +00001810#endif
Daniel Veillardd2298792003-02-14 16:54:11 +00001811
Daniel Veillard6eadf632003-01-23 18:29:16 +00001812/**
Daniel Veillard42f12e92003-03-07 18:32:59 +00001813 * xmlRelaxNGGetErrorString:
1814 * @err: the error code
1815 * @arg1: the first string argument
1816 * @arg2: the second string argument
Daniel Veillard6eadf632003-01-23 18:29:16 +00001817 *
Daniel Veillard42f12e92003-03-07 18:32:59 +00001818 * computes a formatted error string for the given error code and args
1819 *
1820 * Returns the error string, it must be deallocated by the caller
1821 */
1822static xmlChar *
Daniel Veillardfd573f12003-03-16 17:52:32 +00001823xmlRelaxNGGetErrorString(xmlRelaxNGValidErr err, const xmlChar *arg1,
1824 const xmlChar *arg2) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00001825 char msg[1000];
1826
1827 if (arg1 == NULL)
Daniel Veillardfd573f12003-03-16 17:52:32 +00001828 arg1 = BAD_CAST "";
Daniel Veillard42f12e92003-03-07 18:32:59 +00001829 if (arg2 == NULL)
Daniel Veillardfd573f12003-03-16 17:52:32 +00001830 arg2 = BAD_CAST "";
Daniel Veillard42f12e92003-03-07 18:32:59 +00001831
1832 msg[0] = 0;
1833 switch (err) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00001834 case XML_RELAXNG_OK:
1835 return(NULL);
1836 case XML_RELAXNG_ERR_MEMORY:
1837 return(xmlCharStrdup("out of memory"));
Daniel Veillard42f12e92003-03-07 18:32:59 +00001838 case XML_RELAXNG_ERR_TYPE:
Daniel Veillardfd573f12003-03-16 17:52:32 +00001839 snprintf(msg, 1000, "failed to validate type %s", arg1);
1840 break;
1841 case XML_RELAXNG_ERR_TYPEVAL:
1842 snprintf(msg, 1000, "Type %s doesn't allow value %s", arg1, arg2);
1843 break;
Daniel Veillardc3da18a2003-03-18 00:31:04 +00001844 case XML_RELAXNG_ERR_DUPID:
1845 snprintf(msg, 1000, "ID %s redefined", arg1);
1846 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00001847 case XML_RELAXNG_ERR_TYPECMP:
1848 snprintf(msg, 1000, "failed to compare type %s", arg1);
1849 break;
1850 case XML_RELAXNG_ERR_NOSTATE:
1851 return(xmlCharStrdup("Internal error: no state"));
1852 case XML_RELAXNG_ERR_NODEFINE:
1853 return(xmlCharStrdup("Internal error: no define"));
Daniel Veillard952379b2003-03-17 15:37:12 +00001854 case XML_RELAXNG_ERR_INTERNAL:
1855 snprintf(msg, 1000, "Internal error: %s", arg1);
1856 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00001857 case XML_RELAXNG_ERR_LISTEXTRA:
1858 snprintf(msg, 1000, "Extra data in list: %s", arg1);
1859 break;
1860 case XML_RELAXNG_ERR_INTERNODATA:
1861 return(xmlCharStrdup("Internal: interleave block has no data"));
1862 case XML_RELAXNG_ERR_INTERSEQ:
1863 return(xmlCharStrdup("Invalid sequence in interleave"));
1864 case XML_RELAXNG_ERR_INTEREXTRA:
1865 snprintf(msg, 1000, "Extra element %s in interleave", arg1);
1866 break;
1867 case XML_RELAXNG_ERR_ELEMNAME:
1868 snprintf(msg, 1000, "Expecting element %s, got %s", arg1, arg2);
1869 break;
1870 case XML_RELAXNG_ERR_ELEMNONS:
1871 snprintf(msg, 1000, "Expecting a namespace for element %s", arg1);
1872 break;
1873 case XML_RELAXNG_ERR_ELEMWRONGNS:
1874 snprintf(msg, 1000, "Element %s has wrong namespace: expecting %s",
1875 arg1, arg2);
1876 break;
1877 case XML_RELAXNG_ERR_ELEMEXTRANS:
1878 snprintf(msg, 1000, "Expecting no namespace for element %s", arg1);
1879 break;
1880 case XML_RELAXNG_ERR_ELEMNOTEMPTY:
1881 snprintf(msg, 1000, "Expecting element %s to be empty", arg1);
1882 break;
1883 case XML_RELAXNG_ERR_NOELEM:
1884 snprintf(msg, 1000, "Expecting an element %s, got nothing", arg1);
1885 break;
1886 case XML_RELAXNG_ERR_NOTELEM:
1887 return(xmlCharStrdup("Expecting an element got text"));
1888 case XML_RELAXNG_ERR_ATTRVALID:
1889 snprintf(msg, 1000, "Element %s failed to validate attributes",
1890 arg1);
1891 break;
1892 case XML_RELAXNG_ERR_CONTENTVALID:
1893 snprintf(msg, 1000, "Element %s failed to validate content",
1894 arg1);
1895 break;
1896 case XML_RELAXNG_ERR_EXTRACONTENT:
1897 snprintf(msg, 1000, "Element %s has extra content: %s",
1898 arg1, arg2);
1899 break;
1900 case XML_RELAXNG_ERR_INVALIDATTR:
1901 snprintf(msg, 1000, "Invalid attribute %s for element %s",
1902 arg1, arg2);
1903 break;
1904 case XML_RELAXNG_ERR_DATAELEM:
1905 snprintf(msg, 1000, "Datatype element %s has child elements",
1906 arg1);
1907 break;
1908 case XML_RELAXNG_ERR_VALELEM:
1909 snprintf(msg, 1000, "Value element %s has child elements",
1910 arg1);
1911 break;
1912 case XML_RELAXNG_ERR_LISTELEM:
1913 snprintf(msg, 1000, "List element %s has child elements",
1914 arg1);
1915 break;
1916 case XML_RELAXNG_ERR_DATATYPE:
1917 snprintf(msg, 1000, "Error validating datatype %s",
1918 arg1);
1919 break;
1920 case XML_RELAXNG_ERR_VALUE:
1921 snprintf(msg, 1000, "Error validating value %s",
1922 arg1);
1923 break;
1924 case XML_RELAXNG_ERR_LIST:
1925 return(xmlCharStrdup("Error validating list"));
1926 case XML_RELAXNG_ERR_NOGRAMMAR:
1927 return(xmlCharStrdup("No top grammar defined"));
1928 case XML_RELAXNG_ERR_EXTRADATA:
1929 return(xmlCharStrdup("Extra data in the document"));
1930 default:
1931 TODO
Daniel Veillard42f12e92003-03-07 18:32:59 +00001932 }
1933 if (msg[0] == 0) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00001934 snprintf(msg, 1000, "Unknown error code %d", err);
Daniel Veillard42f12e92003-03-07 18:32:59 +00001935 }
1936 msg[1000] = 0;
Daniel Veillardfd573f12003-03-16 17:52:32 +00001937 return(xmlStrdup((xmlChar *) msg));
Daniel Veillard42f12e92003-03-07 18:32:59 +00001938}
1939
1940/**
1941 * xmlRelaxNGValidErrorContext:
1942 * @ctxt: the validation context
1943 * @node: the node
1944 * @child: the node child generating the problem.
1945 *
1946 * Dump informations about the kocation of the error in the instance
Daniel Veillard6eadf632003-01-23 18:29:16 +00001947 */
1948static void
Daniel Veillard42f12e92003-03-07 18:32:59 +00001949xmlRelaxNGValidErrorContext(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node,
1950 xmlNodePtr child)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001951{
1952 int line = 0;
1953 const xmlChar *file = NULL;
1954 const xmlChar *name = NULL;
1955 const char *type = "error";
1956
1957 if ((ctxt == NULL) || (ctxt->error == NULL))
1958 return;
1959
1960 if (child != NULL)
1961 node = child;
1962
1963 if (node != NULL) {
1964 if ((node->type == XML_DOCUMENT_NODE) ||
1965 (node->type == XML_HTML_DOCUMENT_NODE)) {
1966 xmlDocPtr doc = (xmlDocPtr) node;
1967
1968 file = doc->URL;
1969 } else {
1970 /*
1971 * Try to find contextual informations to report
1972 */
1973 if (node->type == XML_ELEMENT_NODE) {
1974 line = (int) node->content;
1975 } else if ((node->prev != NULL) &&
1976 (node->prev->type == XML_ELEMENT_NODE)) {
1977 line = (int) node->prev->content;
1978 } else if ((node->parent != NULL) &&
1979 (node->parent->type == XML_ELEMENT_NODE)) {
1980 line = (int) node->parent->content;
1981 }
1982 if ((node->doc != NULL) && (node->doc->URL != NULL))
1983 file = node->doc->URL;
1984 if (node->name != NULL)
1985 name = node->name;
1986 }
1987 }
1988
Daniel Veillard42f12e92003-03-07 18:32:59 +00001989 type = "RNG validity error";
Daniel Veillard6eadf632003-01-23 18:29:16 +00001990
1991 if ((file != NULL) && (line != 0) && (name != NULL))
1992 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1993 type, file, line, name);
1994 else if ((file != NULL) && (name != NULL))
1995 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1996 type, file, name);
1997 else if ((file != NULL) && (line != 0))
1998 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1999 else if (file != NULL)
2000 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
2001 else if (name != NULL)
2002 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
2003 else
2004 ctxt->error(ctxt->userData, "%s\n", type);
2005}
Daniel Veillard42f12e92003-03-07 18:32:59 +00002006
2007/**
2008 * xmlRelaxNGShowValidError:
2009 * @ctxt: the validation context
2010 * @err: the error number
2011 * @node: the node
2012 * @child: the node child generating the problem.
2013 * @arg1: the first argument
2014 * @arg2: the second argument
2015 *
2016 * Show a validation error.
2017 */
2018static void
2019xmlRelaxNGShowValidError(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err,
2020 xmlNodePtr node, xmlNodePtr child,
2021 const xmlChar *arg1, const xmlChar *arg2)
2022{
2023 xmlChar *msg;
2024
2025 if (ctxt->error == NULL)
2026 return;
2027
2028 msg = xmlRelaxNGGetErrorString(err, arg1, arg2);
2029 if (msg == NULL)
2030 return;
2031
2032 xmlRelaxNGValidErrorContext(ctxt, node, child);
2033 ctxt->error(ctxt->userData, "%s\n", msg);
2034 xmlFree(msg);
2035}
2036
2037/**
Daniel Veillard28c52ab2003-03-18 11:39:17 +00002038 * xmlRelaxNGPopErrors:
2039 * @ctxt: the validation context
2040 * @level: the error level in the stack
2041 *
2042 * pop and discard all errors until the given level is reached
2043 */
2044static void
2045xmlRelaxNGPopErrors(xmlRelaxNGValidCtxtPtr ctxt, int level) {
2046 int i;
2047 xmlRelaxNGValidErrorPtr err;
2048
2049 for (i = level;i < ctxt->errNr;i++) {
2050 err = &ctxt->errTab[i];
2051 if (err->flags & ERROR_IS_DUP) {
2052 if (err->arg1 != NULL)
2053 xmlFree((xmlChar *)err->arg1);
2054 err->arg1 = NULL;
2055 if (err->arg2 != NULL)
2056 xmlFree((xmlChar *)err->arg2);
2057 err->arg2 = NULL;
2058 err->flags = 0;
2059 }
2060 }
2061 ctxt->errNr = level;
2062}
2063/**
Daniel Veillard42f12e92003-03-07 18:32:59 +00002064 * xmlRelaxNGDumpValidError:
2065 * @ctxt: the validation context
2066 *
2067 * Show all validation error over a given index.
2068 */
2069static void
2070xmlRelaxNGDumpValidError(xmlRelaxNGValidCtxtPtr ctxt) {
2071 int i;
2072 xmlRelaxNGValidErrorPtr err;
2073
2074 for (i = 0;i < ctxt->errNr;i++) {
2075 err = &ctxt->errTab[i];
2076 xmlRelaxNGShowValidError(ctxt, err->err, err->node, err->seq,
2077 err->arg1, err->arg2);
Daniel Veillardc3da18a2003-03-18 00:31:04 +00002078 if (err->flags & ERROR_IS_DUP) {
2079 if (err->arg1 != NULL)
2080 xmlFree((xmlChar *)err->arg1);
2081 err->arg1 = NULL;
2082 if (err->arg2 != NULL)
2083 xmlFree((xmlChar *)err->arg2);
2084 err->arg2 = NULL;
2085 err->flags = 0;
2086 }
Daniel Veillard42f12e92003-03-07 18:32:59 +00002087 }
2088 ctxt->errNr = 0;
2089}
2090/**
2091 * xmlRelaxNGAddValidError:
2092 * @ctxt: the validation context
2093 * @err: the error number
2094 * @arg1: the first argument
2095 * @arg2: the second argument
Daniel Veillardc3da18a2003-03-18 00:31:04 +00002096 * @dup: need to dup the args
Daniel Veillard42f12e92003-03-07 18:32:59 +00002097 *
2098 * Register a validation error, either generating it if it's sure
2099 * or stacking it for later handling if unsure.
2100 */
2101static void
2102xmlRelaxNGAddValidError(xmlRelaxNGValidCtxtPtr ctxt, xmlRelaxNGValidErr err,
Daniel Veillardc3da18a2003-03-18 00:31:04 +00002103 const xmlChar *arg1, const xmlChar *arg2, int dup)
Daniel Veillard42f12e92003-03-07 18:32:59 +00002104{
2105 if ((ctxt == NULL) || (ctxt->error == NULL))
2106 return;
2107
2108 /*
2109 * generate the error directly
2110 */
2111 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) {
2112 xmlNodePtr node, seq;
2113 /*
2114 * Flush first any stacked error which might be the
2115 * real cause of the problem.
2116 */
2117 if (ctxt->errNr != 0)
2118 xmlRelaxNGDumpValidError(ctxt);
2119 if (ctxt->state != NULL) {
2120 node = ctxt->state->node;
2121 seq = ctxt->state->seq;
2122 } else {
2123 node = seq = NULL;
2124 }
2125 xmlRelaxNGShowValidError(ctxt, err, node, seq, arg1, arg2);
2126 }
2127 /*
2128 * Stack the error for later processing if needed
2129 */
2130 else {
Daniel Veillardc3da18a2003-03-18 00:31:04 +00002131 xmlRelaxNGValidErrorPush(ctxt, err, arg1, arg2, dup);
Daniel Veillard42f12e92003-03-07 18:32:59 +00002132 }
2133}
2134
Daniel Veillard6eadf632003-01-23 18:29:16 +00002135
2136/************************************************************************
2137 * *
2138 * Type library hooks *
2139 * *
2140 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00002141static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
2142 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002143
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002144/**
2145 * xmlRelaxNGSchemaTypeHave:
2146 * @data: data needed for the library
2147 * @type: the type name
2148 *
2149 * Check if the given type is provided by
2150 * the W3C XMLSchema Datatype library.
2151 *
2152 * Returns 1 if yes, 0 if no and -1 in case of error.
2153 */
2154static int
2155xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002156 const xmlChar *type) {
2157 xmlSchemaTypePtr typ;
2158
2159 if (type == NULL)
2160 return(-1);
2161 typ = xmlSchemaGetPredefinedType(type,
2162 BAD_CAST "http://www.w3.org/2001/XMLSchema");
2163 if (typ == NULL)
2164 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002165 return(1);
2166}
2167
2168/**
2169 * xmlRelaxNGSchemaTypeCheck:
2170 * @data: data needed for the library
2171 * @type: the type name
2172 * @value: the value to check
Daniel Veillardc3da18a2003-03-18 00:31:04 +00002173 * @node: the node
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002174 *
2175 * Check if the given type and value are validated by
2176 * the W3C XMLSchema Datatype library.
2177 *
2178 * Returns 1 if yes, 0 if no and -1 in case of error.
2179 */
2180static int
2181xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002182 const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002183 const xmlChar *value,
Daniel Veillardc3da18a2003-03-18 00:31:04 +00002184 void **result,
2185 xmlNodePtr node) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002186 xmlSchemaTypePtr typ;
2187 int ret;
2188
2189 /*
2190 * TODO: the type should be cached ab provided back, interface subject
2191 * to changes.
2192 * TODO: handle facets, may require an additional interface and keep
2193 * the value returned from the validation.
2194 */
2195 if ((type == NULL) || (value == NULL))
2196 return(-1);
2197 typ = xmlSchemaGetPredefinedType(type,
2198 BAD_CAST "http://www.w3.org/2001/XMLSchema");
2199 if (typ == NULL)
2200 return(-1);
Daniel Veillardc3da18a2003-03-18 00:31:04 +00002201 ret = xmlSchemaValPredefTypeNode(typ, value,
2202 (xmlSchemaValPtr *) result, node);
2203 if (ret == 2) /* special ID error code */
2204 return(2);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002205 if (ret == 0)
2206 return(1);
2207 if (ret > 0)
2208 return(0);
2209 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002210}
2211
2212/**
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002213 * xmlRelaxNGSchemaFacetCheck:
2214 * @data: data needed for the library
2215 * @type: the type name
2216 * @facet: the facet name
2217 * @val: the facet value
2218 * @strval: the string value
2219 * @value: the value to check
2220 *
2221 * Function provided by a type library to check a value facet
2222 *
2223 * Returns 1 if yes, 0 if no and -1 in case of error.
2224 */
2225static int
Daniel Veillard42f12e92003-03-07 18:32:59 +00002226xmlRelaxNGSchemaFacetCheck (void *data ATTRIBUTE_UNUSED, const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002227 const xmlChar *facetname, const xmlChar *val,
2228 const xmlChar *strval, void *value) {
2229 xmlSchemaFacetPtr facet;
2230 xmlSchemaTypePtr typ;
2231 int ret;
2232
2233 if ((type == NULL) || (strval == NULL))
2234 return(-1);
2235 typ = xmlSchemaGetPredefinedType(type,
2236 BAD_CAST "http://www.w3.org/2001/XMLSchema");
2237 if (typ == NULL)
2238 return(-1);
2239
2240 facet = xmlSchemaNewFacet();
2241 if (facet == NULL)
2242 return(-1);
2243
2244 if (xmlStrEqual(facetname, BAD_CAST "minInclusive")) {
2245 facet->type = XML_SCHEMA_FACET_MININCLUSIVE;
2246 } else if (xmlStrEqual(facetname, BAD_CAST "minExclusive")) {
2247 facet->type = XML_SCHEMA_FACET_MINEXCLUSIVE;
2248 } else if (xmlStrEqual(facetname, BAD_CAST "maxInclusive")) {
2249 facet->type = XML_SCHEMA_FACET_MAXINCLUSIVE;
2250 } else if (xmlStrEqual(facetname, BAD_CAST "maxExclusive")) {
2251 facet->type = XML_SCHEMA_FACET_MAXEXCLUSIVE;
2252 } else if (xmlStrEqual(facetname, BAD_CAST "totalDigits")) {
2253 facet->type = XML_SCHEMA_FACET_TOTALDIGITS;
2254 } else if (xmlStrEqual(facetname, BAD_CAST "fractionDigits")) {
2255 facet->type = XML_SCHEMA_FACET_FRACTIONDIGITS;
2256 } else if (xmlStrEqual(facetname, BAD_CAST "pattern")) {
2257 facet->type = XML_SCHEMA_FACET_PATTERN;
2258 } else if (xmlStrEqual(facetname, BAD_CAST "enumeration")) {
2259 facet->type = XML_SCHEMA_FACET_ENUMERATION;
2260 } else if (xmlStrEqual(facetname, BAD_CAST "whiteSpace")) {
2261 facet->type = XML_SCHEMA_FACET_WHITESPACE;
2262 } else if (xmlStrEqual(facetname, BAD_CAST "length")) {
2263 facet->type = XML_SCHEMA_FACET_LENGTH;
2264 } else if (xmlStrEqual(facetname, BAD_CAST "maxLength")) {
2265 facet->type = XML_SCHEMA_FACET_MAXLENGTH;
2266 } else if (xmlStrEqual(facetname, BAD_CAST "minLength")) {
2267 facet->type = XML_SCHEMA_FACET_MINLENGTH;
2268 } else {
2269 xmlSchemaFreeFacet(facet);
2270 return(-1);
2271 }
2272 facet->value = xmlStrdup(val);
2273 ret = xmlSchemaCheckFacet(facet, typ, NULL, type);
2274 if (ret != 0) {
2275 xmlSchemaFreeFacet(facet);
2276 return(-1);
2277 }
2278 ret = xmlSchemaValidateFacet(typ, facet, strval, value);
2279 xmlSchemaFreeFacet(facet);
2280 if (ret != 0)
2281 return(-1);
2282 return(0);
2283}
2284
2285/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002286 * xmlRelaxNGSchemaTypeCompare:
2287 * @data: data needed for the library
2288 * @type: the type name
2289 * @value1: the first value
2290 * @value2: the second value
2291 *
2292 * Compare two values accordingly a type from the W3C XMLSchema
2293 * Datatype library.
2294 *
2295 * Returns 1 if yes, 0 if no and -1 in case of error.
2296 */
2297static int
2298xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
2299 const xmlChar *type ATTRIBUTE_UNUSED,
2300 const xmlChar *value1 ATTRIBUTE_UNUSED,
2301 const xmlChar *value2 ATTRIBUTE_UNUSED) {
2302 TODO
2303 return(1);
2304}
2305
2306/**
2307 * xmlRelaxNGDefaultTypeHave:
2308 * @data: data needed for the library
2309 * @type: the type name
2310 *
2311 * Check if the given type is provided by
2312 * the default datatype library.
2313 *
2314 * Returns 1 if yes, 0 if no and -1 in case of error.
2315 */
2316static int
2317xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
2318 if (type == NULL)
2319 return(-1);
2320 if (xmlStrEqual(type, BAD_CAST "string"))
2321 return(1);
2322 if (xmlStrEqual(type, BAD_CAST "token"))
2323 return(1);
2324 return(0);
2325}
2326
2327/**
2328 * xmlRelaxNGDefaultTypeCheck:
2329 * @data: data needed for the library
2330 * @type: the type name
2331 * @value: the value to check
Daniel Veillardc3da18a2003-03-18 00:31:04 +00002332 * @node: the node
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002333 *
2334 * Check if the given type and value are validated by
2335 * the default datatype library.
2336 *
2337 * Returns 1 if yes, 0 if no and -1 in case of error.
2338 */
2339static int
2340xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
2341 const xmlChar *type ATTRIBUTE_UNUSED,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002342 const xmlChar *value ATTRIBUTE_UNUSED,
Daniel Veillardc3da18a2003-03-18 00:31:04 +00002343 void **result ATTRIBUTE_UNUSED,
2344 xmlNodePtr node ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00002345 if (value == NULL)
2346 return(-1);
2347 if (xmlStrEqual(type, BAD_CAST "string"))
2348 return(1);
2349 if (xmlStrEqual(type, BAD_CAST "token")) {
Daniel Veillardd4310742003-02-18 21:12:46 +00002350 return(1);
2351 }
2352
2353 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002354}
2355
2356/**
2357 * xmlRelaxNGDefaultTypeCompare:
2358 * @data: data needed for the library
2359 * @type: the type name
2360 * @value1: the first value
2361 * @value2: the second value
2362 *
2363 * Compare two values accordingly a type from the default
2364 * datatype library.
2365 *
2366 * Returns 1 if yes, 0 if no and -1 in case of error.
2367 */
2368static int
2369xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
2370 const xmlChar *type ATTRIBUTE_UNUSED,
2371 const xmlChar *value1 ATTRIBUTE_UNUSED,
2372 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00002373 int ret = -1;
2374
2375 if (xmlStrEqual(type, BAD_CAST "string")) {
2376 ret = xmlStrEqual(value1, value2);
2377 } else if (xmlStrEqual(type, BAD_CAST "token")) {
2378 if (!xmlStrEqual(value1, value2)) {
2379 xmlChar *nval, *nvalue;
2380
2381 /*
2382 * TODO: trivial optimizations are possible by
2383 * computing at compile-time
2384 */
2385 nval = xmlRelaxNGNormalize(NULL, value1);
2386 nvalue = xmlRelaxNGNormalize(NULL, value2);
2387
Daniel Veillardd4310742003-02-18 21:12:46 +00002388 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00002389 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00002390 else if (xmlStrEqual(nval, nvalue))
2391 ret = 1;
2392 else
2393 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00002394 if (nval != NULL)
2395 xmlFree(nval);
2396 if (nvalue != NULL)
2397 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00002398 } else
2399 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00002400 }
2401 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002402}
2403
2404static int xmlRelaxNGTypeInitialized = 0;
2405static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
2406
2407/**
2408 * xmlRelaxNGFreeTypeLibrary:
2409 * @lib: the type library structure
2410 * @namespace: the URI bound to the library
2411 *
2412 * Free the structure associated to the type library
2413 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00002414static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002415xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
2416 const xmlChar *namespace ATTRIBUTE_UNUSED) {
2417 if (lib == NULL)
2418 return;
2419 if (lib->namespace != NULL)
2420 xmlFree((xmlChar *)lib->namespace);
2421 xmlFree(lib);
2422}
2423
2424/**
2425 * xmlRelaxNGRegisterTypeLibrary:
2426 * @namespace: the URI bound to the library
2427 * @data: data associated to the library
2428 * @have: the provide function
2429 * @check: the checking function
2430 * @comp: the comparison function
2431 *
2432 * Register a new type library
2433 *
2434 * Returns 0 in case of success and -1 in case of error.
2435 */
2436static int
2437xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
2438 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002439 xmlRelaxNGTypeCompare comp, xmlRelaxNGFacetCheck facet,
2440 xmlRelaxNGTypeFree freef) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002441 xmlRelaxNGTypeLibraryPtr lib;
2442 int ret;
2443
2444 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
2445 (check == NULL) || (comp == NULL))
2446 return(-1);
2447 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
2448 xmlGenericError(xmlGenericErrorContext,
2449 "Relax-NG types library '%s' already registered\n",
2450 namespace);
2451 return(-1);
2452 }
2453 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
2454 if (lib == NULL) {
2455 xmlGenericError(xmlGenericErrorContext,
2456 "Relax-NG types library '%s' malloc() failed\n",
2457 namespace);
2458 return (-1);
2459 }
2460 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
2461 lib->namespace = xmlStrdup(namespace);
2462 lib->data = data;
2463 lib->have = have;
2464 lib->comp = comp;
2465 lib->check = check;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002466 lib->facet = facet;
2467 lib->freef = freef;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002468 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
2469 if (ret < 0) {
2470 xmlGenericError(xmlGenericErrorContext,
2471 "Relax-NG types library failed to register '%s'\n",
2472 namespace);
2473 xmlRelaxNGFreeTypeLibrary(lib, namespace);
2474 return(-1);
2475 }
2476 return(0);
2477}
2478
2479/**
2480 * xmlRelaxNGInitTypes:
2481 *
2482 * Initilize the default type libraries.
2483 *
2484 * Returns 0 in case of success and -1 in case of error.
2485 */
2486static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00002487xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002488 if (xmlRelaxNGTypeInitialized != 0)
2489 return(0);
2490 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
2491 if (xmlRelaxNGRegisteredTypes == NULL) {
2492 xmlGenericError(xmlGenericErrorContext,
2493 "Failed to allocate sh table for Relax-NG types\n");
2494 return(-1);
2495 }
2496 xmlRelaxNGRegisterTypeLibrary(
2497 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
2498 NULL,
2499 xmlRelaxNGSchemaTypeHave,
2500 xmlRelaxNGSchemaTypeCheck,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002501 xmlRelaxNGSchemaTypeCompare,
2502 xmlRelaxNGSchemaFacetCheck,
2503 (xmlRelaxNGTypeFree) xmlSchemaFreeValue);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002504 xmlRelaxNGRegisterTypeLibrary(
2505 xmlRelaxNGNs,
2506 NULL,
2507 xmlRelaxNGDefaultTypeHave,
2508 xmlRelaxNGDefaultTypeCheck,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002509 xmlRelaxNGDefaultTypeCompare,
2510 NULL,
2511 NULL);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002512 xmlRelaxNGTypeInitialized = 1;
2513 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002514}
2515
2516/**
2517 * xmlRelaxNGCleanupTypes:
2518 *
2519 * Cleanup the default Schemas type library associated to RelaxNG
2520 */
2521void
2522xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002523 if (xmlRelaxNGTypeInitialized == 0)
2524 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002525 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002526 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
2527 xmlRelaxNGFreeTypeLibrary);
2528 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002529}
2530
2531/************************************************************************
2532 * *
Daniel Veillard952379b2003-03-17 15:37:12 +00002533 * Compiling element content into regexp *
2534 * *
2535 * Sometime the element content can be compiled into a pure regexp, *
2536 * This allows a faster execution and streamability at that level *
2537 * *
2538 ************************************************************************/
2539
2540/**
2541 * xmlRelaxNGIsCompileable:
2542 * @define: the definition to check
2543 *
2544 * Check if a definition is nullable.
2545 *
2546 * Returns 1 if yes, 0 if no and -1 in case of error
2547 */
2548static int
2549xmlRelaxNGIsCompileable(xmlRelaxNGDefinePtr def) {
2550 if (def == NULL) {
2551 return(-1);
2552 }
2553 switch(def->type) {
2554 case XML_RELAXNG_REF:
2555 case XML_RELAXNG_EXTERNALREF:
2556 case XML_RELAXNG_PARENTREF:
2557 case XML_RELAXNG_NOOP:
2558 case XML_RELAXNG_START:
2559 return(xmlRelaxNGIsCompileable(def->content));
2560 case XML_RELAXNG_TEXT:
2561 case XML_RELAXNG_DATATYPE:
2562 case XML_RELAXNG_LIST:
2563 case XML_RELAXNG_PARAM:
2564 case XML_RELAXNG_VALUE:
2565
2566 case XML_RELAXNG_EMPTY:
2567 case XML_RELAXNG_ELEMENT:
2568 return(1);
2569 case XML_RELAXNG_OPTIONAL:
2570 case XML_RELAXNG_ZEROORMORE:
2571 case XML_RELAXNG_ONEORMORE:
2572 case XML_RELAXNG_CHOICE:
2573 case XML_RELAXNG_GROUP:
2574 case XML_RELAXNG_DEF: {
2575 xmlRelaxNGDefinePtr list;
2576 int ret;
2577
2578 list = def->content;
2579 while (list != NULL) {
2580 ret = xmlRelaxNGIsCompileable(list);
2581 if (ret != 1)
2582 return(ret);
2583 list = list->next;
2584 }
2585 return(1);
2586 }
2587 case XML_RELAXNG_EXCEPT:
2588 case XML_RELAXNG_ATTRIBUTE:
2589 case XML_RELAXNG_INTERLEAVE:
2590 return(0);
2591 case XML_RELAXNG_NOT_ALLOWED:
2592 return(-1);
2593 }
2594 return(-1);
2595}
2596
2597/************************************************************************
2598 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00002599 * Parsing functions *
2600 * *
2601 ************************************************************************/
2602
2603static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
2604 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
2605static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
2606 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
2607static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00002608 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002609static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
2610 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002611static xmlRelaxNGPtr xmlRelaxNGParseDocument(
2612 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002613static int xmlRelaxNGParseGrammarContent(
2614 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00002615static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
2616 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
2617 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00002618static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
2619 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillardfd573f12003-03-16 17:52:32 +00002620static int xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
2621 xmlRelaxNGDefinePtr define, xmlNodePtr elem);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002622
2623
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002624#define IS_BLANK_NODE(n) (xmlRelaxNGIsBlank((n)->content))
Daniel Veillard6eadf632003-01-23 18:29:16 +00002625
2626/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00002627 * xmlRelaxNGIsNullable:
2628 * @define: the definition to verify
2629 *
2630 * Check if a definition is nullable.
2631 *
2632 * Returns 1 if yes, 0 if no and -1 in case of error
2633 */
2634static int
2635xmlRelaxNGIsNullable(xmlRelaxNGDefinePtr define) {
2636 int ret;
2637 if (define == NULL)
2638 return(-1);
2639
2640 if (define->flags & IS_NULLABLE)
2641 return(1);
2642 if (define->flags & IS_NOT_NULLABLE)
2643 return(0);
2644 switch (define->type) {
2645 case XML_RELAXNG_EMPTY:
2646 case XML_RELAXNG_TEXT:
2647 ret = 1; break;
2648 case XML_RELAXNG_NOOP:
2649 case XML_RELAXNG_DEF:
2650 case XML_RELAXNG_REF:
2651 case XML_RELAXNG_EXTERNALREF:
2652 case XML_RELAXNG_PARENTREF:
2653 case XML_RELAXNG_ONEORMORE:
2654 ret = xmlRelaxNGIsNullable(define->content);
2655 break;
2656 case XML_RELAXNG_EXCEPT:
2657 case XML_RELAXNG_NOT_ALLOWED:
2658 case XML_RELAXNG_ELEMENT:
2659 case XML_RELAXNG_DATATYPE:
2660 case XML_RELAXNG_PARAM:
2661 case XML_RELAXNG_VALUE:
2662 case XML_RELAXNG_LIST:
2663 case XML_RELAXNG_ATTRIBUTE:
2664 ret = 0; break;
2665 case XML_RELAXNG_CHOICE: {
2666 xmlRelaxNGDefinePtr list = define->content;
2667
2668 while (list != NULL) {
2669 ret = xmlRelaxNGIsNullable(list);
2670 if (ret != 0)
2671 goto done;
2672 list = list->next;
2673 }
2674 ret = 0; break;
2675 }
2676 case XML_RELAXNG_START:
2677 case XML_RELAXNG_INTERLEAVE:
2678 case XML_RELAXNG_GROUP: {
2679 xmlRelaxNGDefinePtr list = define->content;
2680
2681 while (list != NULL) {
2682 ret = xmlRelaxNGIsNullable(list);
2683 if (ret != 1)
2684 goto done;
2685 list = list->next;
2686 }
2687 return(1);
2688 }
2689 default:
2690 return(-1);
2691 }
2692done:
2693 if (ret == 0)
2694 define->flags |= IS_NOT_NULLABLE;
2695 if (ret == 1)
2696 define->flags |= IS_NULLABLE;
2697 return(ret);
2698}
2699
2700/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002701 * xmlRelaxNGIsBlank:
2702 * @str: a string
2703 *
2704 * Check if a string is ignorable c.f. 4.2. Whitespace
2705 *
2706 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
2707 */
2708static int
2709xmlRelaxNGIsBlank(xmlChar *str) {
2710 if (str == NULL)
2711 return(1);
2712 while (*str != 0) {
2713 if (!(IS_BLANK(*str))) return(0);
2714 str++;
2715 }
2716 return(1);
2717}
2718
Daniel Veillard6eadf632003-01-23 18:29:16 +00002719/**
2720 * xmlRelaxNGGetDataTypeLibrary:
2721 * @ctxt: a Relax-NG parser context
2722 * @node: the current data or value element
2723 *
2724 * Applies algorithm from 4.3. datatypeLibrary attribute
2725 *
2726 * Returns the datatypeLibary value or NULL if not found
2727 */
2728static xmlChar *
2729xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2730 xmlNodePtr node) {
2731 xmlChar *ret, *escape;
2732
Daniel Veillard6eadf632003-01-23 18:29:16 +00002733 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
2734 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
2735 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002736 if (ret[0] == 0) {
2737 xmlFree(ret);
2738 return(NULL);
2739 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002740 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00002741 if (escape == NULL) {
2742 return(ret);
2743 }
2744 xmlFree(ret);
2745 return(escape);
2746 }
2747 }
2748 node = node->parent;
2749 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00002750 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
2751 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002752 if (ret[0] == 0) {
2753 xmlFree(ret);
2754 return(NULL);
2755 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00002756 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
2757 if (escape == NULL) {
2758 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002759 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00002760 xmlFree(ret);
2761 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00002762 }
2763 node = node->parent;
2764 }
2765 return(NULL);
2766}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002767
2768/**
Daniel Veillardedc91922003-01-26 00:52:04 +00002769 * xmlRelaxNGParseValue:
2770 * @ctxt: a Relax-NG parser context
2771 * @node: the data node.
2772 *
2773 * parse the content of a RelaxNG value node.
2774 *
2775 * Returns the definition pointer or NULL in case of error
2776 */
2777static xmlRelaxNGDefinePtr
2778xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2779 xmlRelaxNGDefinePtr def = NULL;
2780 xmlRelaxNGTypeLibraryPtr lib;
2781 xmlChar *type;
2782 xmlChar *library;
2783 int tmp;
2784
Daniel Veillardfd573f12003-03-16 17:52:32 +00002785 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillardedc91922003-01-26 00:52:04 +00002786 if (def == NULL)
2787 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00002788 def->type = XML_RELAXNG_VALUE;
Daniel Veillardedc91922003-01-26 00:52:04 +00002789
2790 type = xmlGetProp(node, BAD_CAST "type");
2791 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00002792 xmlRelaxNGNormExtSpace(type);
2793 if (xmlValidateNCName(type, 0)) {
2794 if (ctxt->error != NULL)
2795 ctxt->error(ctxt->userData,
2796 "value type '%s' is not an NCName\n",
2797 type);
2798 ctxt->nbErrors++;
2799 }
Daniel Veillardedc91922003-01-26 00:52:04 +00002800 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
2801 if (library == NULL)
2802 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
2803
2804 def->name = type;
2805 def->ns = library;
2806
2807 lib = (xmlRelaxNGTypeLibraryPtr)
2808 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
2809 if (lib == NULL) {
2810 if (ctxt->error != NULL)
2811 ctxt->error(ctxt->userData,
2812 "Use of unregistered type library '%s'\n",
2813 library);
2814 ctxt->nbErrors++;
2815 def->data = NULL;
2816 } else {
2817 def->data = lib;
2818 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002819 if (ctxt->error != NULL)
2820 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00002821 "Internal error with type library '%s': no 'have'\n",
2822 library);
2823 ctxt->nbErrors++;
2824 } else {
2825 tmp = lib->have(lib->data, def->name);
2826 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002827 if (ctxt->error != NULL)
2828 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00002829 "Error type '%s' is not exported by type library '%s'\n",
2830 def->name, library);
2831 ctxt->nbErrors++;
2832 }
2833 }
2834 }
2835 }
2836 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00002837 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillard39eb88b2003-03-11 11:21:28 +00002838 } else if (((node->children->type != XML_TEXT_NODE) &&
2839 (node->children->type != XML_CDATA_SECTION_NODE)) ||
Daniel Veillardedc91922003-01-26 00:52:04 +00002840 (node->children->next != NULL)) {
2841 if (ctxt->error != NULL)
2842 ctxt->error(ctxt->userData,
2843 "Expecting a single text value for <value>content\n");
2844 ctxt->nbErrors++;
2845 } else {
2846 def->value = xmlNodeGetContent(node);
2847 if (def->value == NULL) {
2848 if (ctxt->error != NULL)
2849 ctxt->error(ctxt->userData,
2850 "Element <value> has no content\n");
2851 ctxt->nbErrors++;
2852 }
2853 }
2854 /* TODO check ahead of time that the value is okay per the type */
2855 return(def);
2856}
2857
2858/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002859 * xmlRelaxNGParseData:
2860 * @ctxt: a Relax-NG parser context
2861 * @node: the data node.
2862 *
2863 * parse the content of a RelaxNG data node.
2864 *
2865 * Returns the definition pointer or NULL in case of error
2866 */
2867static xmlRelaxNGDefinePtr
2868xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002869 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002870 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002871 xmlRelaxNGTypeLibraryPtr lib;
2872 xmlChar *type;
2873 xmlChar *library;
2874 xmlNodePtr content;
2875 int tmp;
2876
2877 type = xmlGetProp(node, BAD_CAST "type");
2878 if (type == NULL) {
2879 if (ctxt->error != NULL)
2880 ctxt->error(ctxt->userData,
2881 "data has no type\n");
2882 ctxt->nbErrors++;
2883 return(NULL);
2884 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002885 xmlRelaxNGNormExtSpace(type);
2886 if (xmlValidateNCName(type, 0)) {
2887 if (ctxt->error != NULL)
2888 ctxt->error(ctxt->userData,
2889 "data type '%s' is not an NCName\n",
2890 type);
2891 ctxt->nbErrors++;
2892 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002893 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
2894 if (library == NULL)
2895 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
2896
Daniel Veillardfd573f12003-03-16 17:52:32 +00002897 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002898 if (def == NULL) {
2899 xmlFree(type);
2900 return(NULL);
2901 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00002902 def->type = XML_RELAXNG_DATATYPE;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002903 def->name = type;
2904 def->ns = library;
2905
2906 lib = (xmlRelaxNGTypeLibraryPtr)
2907 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
2908 if (lib == NULL) {
2909 if (ctxt->error != NULL)
2910 ctxt->error(ctxt->userData,
2911 "Use of unregistered type library '%s'\n",
2912 library);
2913 ctxt->nbErrors++;
2914 def->data = NULL;
2915 } else {
2916 def->data = lib;
2917 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002918 if (ctxt->error != NULL)
2919 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002920 "Internal error with type library '%s': no 'have'\n",
2921 library);
2922 ctxt->nbErrors++;
2923 } else {
2924 tmp = lib->have(lib->data, def->name);
2925 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002926 if (ctxt->error != NULL)
2927 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002928 "Error type '%s' is not exported by type library '%s'\n",
2929 def->name, library);
2930 ctxt->nbErrors++;
Daniel Veillardc3da18a2003-03-18 00:31:04 +00002931 } else if ((xmlStrEqual(library, BAD_CAST
2932 "http://www.w3.org/2001/XMLSchema-datatypes")) &&
2933 ((xmlStrEqual(def->name, BAD_CAST "IDREF")) ||
2934 (xmlStrEqual(def->name, BAD_CAST "IDREFS")))) {
2935 ctxt->idref = 1;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002936 }
2937 }
2938 }
2939 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00002940
2941 /*
2942 * Handle optional params
2943 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002944 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002945 if (!xmlStrEqual(content->name, BAD_CAST "param"))
2946 break;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002947 if (xmlStrEqual(library,
2948 BAD_CAST"http://relaxng.org/ns/structure/1.0")) {
2949 if (ctxt->error != NULL)
2950 ctxt->error(ctxt->userData,
2951 "Type library '%s' does not allow type parameters\n",
2952 library);
2953 ctxt->nbErrors++;
2954 content = content->next;
2955 while ((content != NULL) &&
2956 (xmlStrEqual(content->name, BAD_CAST "param")))
2957 content = content->next;
2958 } else {
Daniel Veillardfd573f12003-03-16 17:52:32 +00002959 param = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002960 if (param != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00002961 param->type = XML_RELAXNG_PARAM;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002962 param->name = xmlGetProp(content, BAD_CAST "name");
2963 if (param->name == NULL) {
2964 if (ctxt->error != NULL)
2965 ctxt->error(ctxt->userData,
2966 "param has no name\n");
2967 ctxt->nbErrors++;
2968 }
2969 param->value = xmlNodeGetContent(content);
2970 if (lastparam == NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00002971 def->attrs = lastparam = param;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002972 } else {
2973 lastparam->next = param;
2974 lastparam = param;
2975 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002976 if (lib != NULL) {
2977 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00002978 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002979 content = content->next;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002980 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002981 }
Daniel Veillard416589a2003-02-17 17:25:42 +00002982 /*
2983 * Handle optional except
2984 */
2985 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
2986 xmlNodePtr child;
2987 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
2988
Daniel Veillardfd573f12003-03-16 17:52:32 +00002989 except = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard416589a2003-02-17 17:25:42 +00002990 if (except == NULL) {
2991 return(def);
2992 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00002993 except->type = XML_RELAXNG_EXCEPT;
Daniel Veillard416589a2003-02-17 17:25:42 +00002994 child = content->children;
2995 if (last == NULL) {
2996 def->content = except;
2997 } else {
2998 last->next = except;
2999 }
3000 if (child == NULL) {
3001 if (ctxt->error != NULL)
3002 ctxt->error(ctxt->userData,
3003 "except has no content\n");
3004 ctxt->nbErrors++;
3005 }
3006 while (child != NULL) {
3007 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
3008 if (tmp2 != NULL) {
3009 if (last2 == NULL) {
3010 except->content = last2 = tmp2;
3011 } else {
3012 last2->next = tmp2;
3013 last2 = tmp2;
3014 }
3015 }
3016 child = child->next;
3017 }
3018 content = content->next;
3019 }
3020 /*
3021 * Check there is no unhandled data
3022 */
3023 if (content != NULL) {
3024 if (ctxt->error != NULL)
3025 ctxt->error(ctxt->userData,
3026 "Element data has unexpected content %s\n", content->name);
3027 ctxt->nbErrors++;
3028 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00003029
3030 return(def);
3031}
3032
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00003033static const xmlChar *invalidName = BAD_CAST "\1";
3034
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003035/**
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00003036 * xmlRelaxNGCompareNameClasses:
3037 * @defs1: the first element/attribute defs
3038 * @defs2: the second element/attribute defs
3039 * @name: the restriction on the name
3040 * @ns: the restriction on the namespace
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003041 *
3042 * Compare the 2 lists of element definitions. The comparison is
3043 * that if both lists do not accept the same QNames, it returns 1
3044 * If the 2 lists can accept the same QName the comparison returns 0
3045 *
3046 * Returns 1 disttinct, 0 if equal
3047 */
3048static int
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00003049xmlRelaxNGCompareNameClasses(xmlRelaxNGDefinePtr def1,
3050 xmlRelaxNGDefinePtr def2) {
3051 int ret = 1;
3052 xmlNode node;
3053 xmlNs ns;
3054 xmlRelaxNGValidCtxt ctxt;
3055 ctxt.flags = FLAGS_IGNORABLE;
3056
Daniel Veillard42f12e92003-03-07 18:32:59 +00003057 memset(&ctxt, 0, sizeof(xmlRelaxNGValidCtxt));
3058
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00003059 if ((def1->type == XML_RELAXNG_ELEMENT) ||
3060 (def1->type == XML_RELAXNG_ATTRIBUTE)) {
3061 if (def2->type == XML_RELAXNG_TEXT)
3062 return(1);
3063 if (def1->name != NULL) {
3064 node.name = def1->name;
3065 } else {
3066 node.name = invalidName;
3067 }
3068 node.ns = &ns;
3069 if (def1->ns != NULL) {
3070 if (def1->ns[0] == 0) {
3071 node.ns = NULL;
3072 } else {
3073 ns.href = def1->ns;
3074 }
3075 } else {
3076 ns.href = invalidName;
3077 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003078 if (xmlRelaxNGElementMatch(&ctxt, def2, &node)) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00003079 if (def1->nameClass != NULL) {
3080 ret = xmlRelaxNGCompareNameClasses(def1->nameClass, def2);
3081 } else {
3082 ret = 0;
3083 }
3084 } else {
3085 ret = 1;
3086 }
3087 } else if (def1->type == XML_RELAXNG_TEXT) {
3088 if (def2->type == XML_RELAXNG_TEXT)
3089 return(0);
3090 return(1);
3091 } else if (def1->type == XML_RELAXNG_EXCEPT) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00003092 TODO
3093 ret = 0;
3094 } else {
3095 TODO
3096 ret = 0;
3097 }
3098 if (ret == 0)
3099 return(ret);
3100 if ((def2->type == XML_RELAXNG_ELEMENT) ||
3101 (def2->type == XML_RELAXNG_ATTRIBUTE)) {
3102 if (def2->name != NULL) {
3103 node.name = def2->name;
3104 } else {
3105 node.name = invalidName;
3106 }
3107 node.ns = &ns;
3108 if (def2->ns != NULL) {
3109 if (def2->ns[0] == 0) {
3110 node.ns = NULL;
3111 } else {
3112 ns.href = def2->ns;
3113 }
3114 } else {
3115 ns.href = invalidName;
3116 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003117 if (xmlRelaxNGElementMatch(&ctxt, def1, &node)) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00003118 if (def2->nameClass != NULL) {
3119 ret = xmlRelaxNGCompareNameClasses(def2->nameClass, def1);
3120 } else {
3121 ret = 0;
3122 }
3123 } else {
3124 ret = 1;
3125 }
3126 } else {
3127 TODO
3128 ret = 0;
3129 }
3130
3131 return(ret);
3132}
3133
3134/**
3135 * xmlRelaxNGCompareElemDefLists:
3136 * @ctxt: a Relax-NG parser context
3137 * @defs1: the first list of element/attribute defs
3138 * @defs2: the second list of element/attribute defs
3139 *
3140 * Compare the 2 lists of element or attribute definitions. The comparison
3141 * is that if both lists do not accept the same QNames, it returns 1
3142 * If the 2 lists can accept the same QName the comparison returns 0
3143 *
3144 * Returns 1 disttinct, 0 if equal
3145 */
3146static int
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003147xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
3148 xmlRelaxNGDefinePtr *def1,
3149 xmlRelaxNGDefinePtr *def2) {
3150 xmlRelaxNGDefinePtr *basedef2 = def2;
3151
Daniel Veillard154877e2003-01-30 12:17:05 +00003152 if ((def1 == NULL) || (def2 == NULL))
3153 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003154 if ((*def1 == NULL) || (*def2 == NULL))
3155 return(1);
3156 while (*def1 != NULL) {
3157 while ((*def2) != NULL) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00003158 if (xmlRelaxNGCompareNameClasses(*def1, *def2) == 0)
3159 return(0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003160 def2++;
3161 }
3162 def2 = basedef2;
3163 def1++;
3164 }
3165 return(1);
3166}
3167
3168/**
3169 * xmlRelaxNGGetElements:
3170 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003171 * @def: the definition definition
3172 * @eora: gather elements (0) or attributes (1)
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003173 *
3174 * Compute the list of top elements a definition can generate
3175 *
3176 * Returns a list of elements or NULL if none was found.
3177 */
3178static xmlRelaxNGDefinePtr *
3179xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003180 xmlRelaxNGDefinePtr def,
3181 int eora) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003182 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003183 int len = 0;
3184 int max = 0;
3185
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003186 /*
3187 * Don't run that check in case of error. Infinite recursion
3188 * becomes possible.
3189 */
3190 if (ctxt->nbErrors != 0)
3191 return(NULL);
3192
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003193 parent = NULL;
3194 cur = def;
3195 while (cur != NULL) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003196 if (((eora == 0) && ((cur->type == XML_RELAXNG_ELEMENT) ||
3197 (cur->type == XML_RELAXNG_TEXT))) ||
3198 ((eora == 1) && (cur->type == XML_RELAXNG_ATTRIBUTE))) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003199 if (ret == NULL) {
3200 max = 10;
3201 ret = (xmlRelaxNGDefinePtr *)
3202 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
3203 if (ret == NULL) {
3204 if (ctxt->error != NULL)
3205 ctxt->error(ctxt->userData,
3206 "Out of memory in element search\n");
3207 ctxt->nbErrors++;
3208 return(NULL);
3209 }
3210 } else if (max <= len) {
3211 max *= 2;
3212 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
3213 if (ret == NULL) {
3214 if (ctxt->error != NULL)
3215 ctxt->error(ctxt->userData,
3216 "Out of memory in element search\n");
3217 ctxt->nbErrors++;
3218 return(NULL);
3219 }
3220 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00003221 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003222 ret[len] = NULL;
3223 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
3224 (cur->type == XML_RELAXNG_INTERLEAVE) ||
3225 (cur->type == XML_RELAXNG_GROUP) ||
3226 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardfd573f12003-03-16 17:52:32 +00003227 (cur->type == XML_RELAXNG_ZEROORMORE) ||
3228 (cur->type == XML_RELAXNG_OPTIONAL) ||
Daniel Veillard952379b2003-03-17 15:37:12 +00003229 (cur->type == XML_RELAXNG_PARENTREF) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00003230 (cur->type == XML_RELAXNG_REF) ||
3231 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003232 /*
3233 * Don't go within elements or attributes or string values.
3234 * Just gather the element top list
3235 */
3236 if (cur->content != NULL) {
3237 parent = cur;
3238 cur = cur->content;
Daniel Veillardfd573f12003-03-16 17:52:32 +00003239 tmp = cur;
3240 while (tmp != NULL) {
3241 tmp->parent = parent;
3242 tmp = tmp->next;
3243 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003244 continue;
3245 }
3246 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003247 if (cur == def)
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003248 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003249 if (cur->next != NULL) {
3250 cur = cur->next;
3251 continue;
3252 }
3253 do {
3254 cur = cur->parent;
3255 if (cur == NULL) break;
3256 if (cur == def) return(ret);
3257 if (cur->next != NULL) {
3258 cur = cur->next;
3259 break;
3260 }
3261 } while (cur != NULL);
3262 }
3263 return(ret);
3264}
3265
3266/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00003267 * xmlRelaxNGCheckChoiceDeterminism:
3268 * @ctxt: a Relax-NG parser context
3269 * @def: the choice definition
3270 *
3271 * Also used to find indeterministic pattern in choice
3272 */
3273static void
3274xmlRelaxNGCheckChoiceDeterminism(xmlRelaxNGParserCtxtPtr ctxt,
3275 xmlRelaxNGDefinePtr def) {
3276 xmlRelaxNGDefinePtr **list;
3277 xmlRelaxNGDefinePtr cur;
3278 int nbchild = 0, i, j, ret;
3279 int is_nullable = 0;
3280 int is_indeterminist = 0;
3281
3282 if ((def == NULL) ||
3283 (def->type != XML_RELAXNG_CHOICE))
3284 return;
3285
3286 /*
3287 * Don't run that check in case of error. Infinite recursion
3288 * becomes possible.
3289 */
3290 if (ctxt->nbErrors != 0)
3291 return;
3292
3293 is_nullable = xmlRelaxNGIsNullable(def);
3294
3295 cur = def->content;
3296 while (cur != NULL) {
3297 nbchild++;
3298 cur = cur->next;
3299 }
3300
3301 list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
3302 sizeof(xmlRelaxNGDefinePtr *));
3303 if (list == NULL) {
3304 if (ctxt->error != NULL)
3305 ctxt->error(ctxt->userData,
3306 "Out of memory in choice computation\n");
3307 ctxt->nbErrors++;
3308 return;
3309 }
3310 i = 0;
3311 cur = def->content;
3312 while (cur != NULL) {
3313 list[i] = xmlRelaxNGGetElements(ctxt, cur, 0);
3314 i++;
3315 cur = cur->next;
3316 }
3317
3318 for (i = 0;i < nbchild;i++) {
3319 if (list[i] == NULL)
3320 continue;
3321 for (j = 0;j < i;j++) {
3322 if (list[j] == NULL)
3323 continue;
3324 ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
3325 if (ret == 0) {
3326 is_indeterminist = 1;
3327 }
3328 }
3329 }
3330 for (i = 0;i < nbchild;i++) {
3331 if (list[i] != NULL)
3332 xmlFree(list[i]);
3333 }
3334
3335 xmlFree(list);
3336 if (is_indeterminist) {
3337 def->flags |= IS_INDETERMINIST;
3338 }
3339}
3340
3341/**
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003342 * xmlRelaxNGCheckGroupAttrs:
3343 * @ctxt: a Relax-NG parser context
3344 * @def: the group definition
3345 *
3346 * Detects violations of rule 7.3
3347 */
3348static void
3349xmlRelaxNGCheckGroupAttrs(xmlRelaxNGParserCtxtPtr ctxt,
3350 xmlRelaxNGDefinePtr def) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003351 xmlRelaxNGDefinePtr **list;
3352 xmlRelaxNGDefinePtr cur;
3353 int nbchild = 0, i, j, ret;
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003354
3355 if ((def == NULL) ||
3356 ((def->type != XML_RELAXNG_GROUP) &&
Daniel Veillardfd573f12003-03-16 17:52:32 +00003357 (def->type != XML_RELAXNG_ELEMENT)))
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003358 return;
3359
3360 /*
3361 * Don't run that check in case of error. Infinite recursion
3362 * becomes possible.
3363 */
3364 if (ctxt->nbErrors != 0)
3365 return;
3366
Daniel Veillardfd573f12003-03-16 17:52:32 +00003367 cur = def->attrs;
3368 while (cur != NULL) {
3369 nbchild++;
3370 cur = cur->next;
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003371 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003372 cur = def->content;
3373 while (cur != NULL) {
3374 nbchild++;
3375 cur = cur->next;
3376 }
3377
3378 list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
3379 sizeof(xmlRelaxNGDefinePtr *));
3380 if (list == NULL) {
3381 if (ctxt->error != NULL)
3382 ctxt->error(ctxt->userData,
3383 "Out of memory in group computation\n");
3384 ctxt->nbErrors++;
3385 return;
3386 }
3387 i = 0;
3388 cur = def->attrs;
3389 while (cur != NULL) {
3390 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
3391 i++;
3392 cur = cur->next;
3393 }
3394 cur = def->content;
3395 while (cur != NULL) {
3396 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
3397 i++;
3398 cur = cur->next;
3399 }
3400
3401 for (i = 0;i < nbchild;i++) {
3402 if (list[i] == NULL)
3403 continue;
3404 for (j = 0;j < i;j++) {
3405 if (list[j] == NULL)
3406 continue;
3407 ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
3408 if (ret == 0) {
3409 if (ctxt->error != NULL)
3410 ctxt->error(ctxt->userData,
3411 "Attributes conflicts in group\n");
3412 ctxt->nbErrors++;
3413 }
3414 }
3415 }
3416 for (i = 0;i < nbchild;i++) {
3417 if (list[i] != NULL)
3418 xmlFree(list[i]);
3419 }
3420
3421 xmlFree(list);
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003422}
3423
3424/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00003425 * xmlRelaxNGComputeInterleaves:
3426 * @def: the interleave definition
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003427 * @ctxt: a Relax-NG parser context
Daniel Veillardfd573f12003-03-16 17:52:32 +00003428 * @name: the definition name
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003429 *
Daniel Veillardfd573f12003-03-16 17:52:32 +00003430 * A lot of work for preprocessing interleave definitions
3431 * is potentially needed to get a decent execution speed at runtime
3432 * - trying to get a total order on the element nodes generated
3433 * by the interleaves, order the list of interleave definitions
3434 * following that order.
3435 * - if <text/> is used to handle mixed content, it is better to
3436 * flag this in the define and simplify the runtime checking
3437 * algorithm
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003438 */
3439static void
Daniel Veillardfd573f12003-03-16 17:52:32 +00003440xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
3441 xmlRelaxNGParserCtxtPtr ctxt,
3442 xmlChar *name ATTRIBUTE_UNUSED) {
3443 xmlRelaxNGDefinePtr cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003444
Daniel Veillardfd573f12003-03-16 17:52:32 +00003445 xmlRelaxNGPartitionPtr partitions = NULL;
3446 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
3447 xmlRelaxNGInterleaveGroupPtr group;
3448 int i,j,ret;
3449 int nbgroups = 0;
3450 int nbchild = 0;
Daniel Veillard249d7bb2003-03-19 21:02:29 +00003451 int is_mixed = 0;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003452
Daniel Veillard44e1dd02003-02-21 23:23:28 +00003453 /*
3454 * Don't run that check in case of error. Infinite recursion
3455 * becomes possible.
3456 */
3457 if (ctxt->nbErrors != 0)
3458 return;
3459
Daniel Veillardfd573f12003-03-16 17:52:32 +00003460#ifdef DEBUG_INTERLEAVE
3461 xmlGenericError(xmlGenericErrorContext,
3462 "xmlRelaxNGComputeInterleaves(%s)\n",
3463 name);
3464#endif
3465 cur = def->content;
3466 while (cur != NULL) {
3467 nbchild++;
3468 cur = cur->next;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003469 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003470
3471#ifdef DEBUG_INTERLEAVE
3472 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
3473#endif
3474 groups = (xmlRelaxNGInterleaveGroupPtr *)
3475 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
3476 if (groups == NULL)
3477 goto error;
3478 cur = def->content;
3479 while (cur != NULL) {
3480 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
3481 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
3482 if (groups[nbgroups] == NULL)
3483 goto error;
Daniel Veillard249d7bb2003-03-19 21:02:29 +00003484 if (cur->type == XML_RELAXNG_TEXT)
3485 is_mixed++;
Daniel Veillardfd573f12003-03-16 17:52:32 +00003486 groups[nbgroups]->rule = cur;
3487 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur, 0);
3488 groups[nbgroups]->attrs = xmlRelaxNGGetElements(ctxt, cur, 1);
3489 nbgroups++;
3490 cur = cur->next;
3491 }
3492#ifdef DEBUG_INTERLEAVE
3493 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
3494#endif
3495
3496 /*
3497 * Let's check that all rules makes a partitions according to 7.4
3498 */
3499 partitions = (xmlRelaxNGPartitionPtr)
3500 xmlMalloc(sizeof(xmlRelaxNGPartition));
3501 if (partitions == NULL)
3502 goto error;
3503 partitions->nbgroups = nbgroups;
3504 for (i = 0;i < nbgroups;i++) {
3505 group = groups[i];
3506 for (j = i+1;j < nbgroups;j++) {
3507 if (groups[j] == NULL)
3508 continue;
3509 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
3510 groups[j]->defs);
3511 if (ret == 0) {
3512 if (ctxt->error != NULL)
3513 ctxt->error(ctxt->userData,
3514 "Element or text conflicts in interleave\n");
3515 ctxt->nbErrors++;
3516 }
3517 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->attrs,
3518 groups[j]->attrs);
3519 if (ret == 0) {
3520 if (ctxt->error != NULL)
3521 ctxt->error(ctxt->userData,
3522 "Attributes conflicts in interleave\n");
3523 ctxt->nbErrors++;
3524 }
3525 }
3526 }
3527 partitions->groups = groups;
3528
3529 /*
3530 * and save the partition list back in the def
3531 */
3532 def->data = partitions;
Daniel Veillard249d7bb2003-03-19 21:02:29 +00003533 if (is_mixed != 0)
3534 def->flags |= IS_MIXED;
Daniel Veillardfd573f12003-03-16 17:52:32 +00003535 return;
3536
3537error:
3538 if (ctxt->error != NULL)
3539 ctxt->error(ctxt->userData,
3540 "Out of memory in interleave computation\n");
3541 ctxt->nbErrors++;
3542 if (groups != NULL) {
3543 for (i = 0;i < nbgroups;i++)
3544 if (groups[i] != NULL) {
3545 if (groups[i]->defs != NULL)
3546 xmlFree(groups[i]->defs);
3547 xmlFree(groups[i]);
3548 }
3549 xmlFree(groups);
3550 }
3551 xmlRelaxNGFreePartition(partitions);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003552}
3553
3554/**
3555 * xmlRelaxNGParseInterleave:
3556 * @ctxt: a Relax-NG parser context
3557 * @node: the data node.
3558 *
3559 * parse the content of a RelaxNG interleave node.
3560 *
3561 * Returns the definition pointer or NULL in case of error
3562 */
3563static xmlRelaxNGDefinePtr
3564xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3565 xmlRelaxNGDefinePtr def = NULL;
Daniel Veillardfd573f12003-03-16 17:52:32 +00003566 xmlRelaxNGDefinePtr last = NULL, cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003567 xmlNodePtr child;
3568
Daniel Veillardfd573f12003-03-16 17:52:32 +00003569 def = xmlRelaxNGNewDefine(ctxt, node);
3570 if (def == NULL) {
3571 return(NULL);
3572 }
3573 def->type = XML_RELAXNG_INTERLEAVE;
3574
3575 if (ctxt->interleaves == NULL)
3576 ctxt->interleaves = xmlHashCreate(10);
3577 if (ctxt->interleaves == NULL) {
3578 if (ctxt->error != NULL)
3579 ctxt->error(ctxt->userData,
3580 "Failed to create interleaves hash table\n");
3581 ctxt->nbErrors++;
3582 } else {
3583 char name[32];
3584
3585 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
3586 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
3587 if (ctxt->error != NULL)
3588 ctxt->error(ctxt->userData,
3589 "Failed to add %s to hash table\n", name);
3590 ctxt->nbErrors++;
3591 }
3592 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003593 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00003594 if (child == NULL) {
3595 if (ctxt->error != NULL)
3596 ctxt->error(ctxt->userData, "Element interleave is empty\n");
3597 ctxt->nbErrors++;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00003598 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003599 while (child != NULL) {
3600 if (IS_RELAXNG(child, "element")) {
3601 cur = xmlRelaxNGParseElement(ctxt, child);
3602 } else {
3603 cur = xmlRelaxNGParsePattern(ctxt, child);
3604 }
3605 if (cur != NULL) {
3606 cur->parent = def;
3607 if (last == NULL) {
3608 def->content = last = cur;
3609 } else {
3610 last->next = cur;
3611 last = cur;
3612 }
3613 }
3614 child = child->next;
3615 }
3616
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003617 return(def);
3618}
Daniel Veillard6eadf632003-01-23 18:29:16 +00003619
3620/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003621 * xmlRelaxNGParseInclude:
3622 * @ctxt: a Relax-NG parser context
3623 * @node: the include node
3624 *
3625 * Integrate the content of an include node in the current grammar
3626 *
3627 * Returns 0 in case of success or -1 in case of error
3628 */
3629static int
3630xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3631 xmlRelaxNGIncludePtr incl;
3632 xmlNodePtr root;
3633 int ret = 0, tmp;
3634
3635 incl = node->_private;
3636 if (incl == NULL) {
3637 if (ctxt->error != NULL)
3638 ctxt->error(ctxt->userData,
3639 "Include node has no data\n");
3640 ctxt->nbErrors++;
3641 return(-1);
3642 }
3643 root = xmlDocGetRootElement(incl->doc);
3644 if (root == NULL) {
3645 if (ctxt->error != NULL)
3646 ctxt->error(ctxt->userData,
3647 "Include document is empty\n");
3648 ctxt->nbErrors++;
3649 return(-1);
3650 }
3651 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
3652 if (ctxt->error != NULL)
3653 ctxt->error(ctxt->userData,
3654 "Include document root is not a grammar\n");
3655 ctxt->nbErrors++;
3656 return(-1);
3657 }
3658
3659 /*
3660 * Merge the definition from both the include and the internal list
3661 */
3662 if (root->children != NULL) {
3663 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
3664 if (tmp != 0)
3665 ret = -1;
3666 }
3667 if (node->children != NULL) {
3668 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
3669 if (tmp != 0)
3670 ret = -1;
3671 }
3672 return(ret);
3673}
3674
3675/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00003676 * xmlRelaxNGParseDefine:
3677 * @ctxt: a Relax-NG parser context
3678 * @node: the define node
3679 *
3680 * parse the content of a RelaxNG define element node.
3681 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003682 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00003683 */
3684static int
3685xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3686 xmlChar *name;
3687 int ret = 0, tmp;
3688 xmlRelaxNGDefinePtr def;
3689 const xmlChar *olddefine;
3690
3691 name = xmlGetProp(node, BAD_CAST "name");
3692 if (name == NULL) {
3693 if (ctxt->error != NULL)
3694 ctxt->error(ctxt->userData,
3695 "define has no name\n");
3696 ctxt->nbErrors++;
3697 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003698 xmlRelaxNGNormExtSpace(name);
3699 if (xmlValidateNCName(name, 0)) {
3700 if (ctxt->error != NULL)
3701 ctxt->error(ctxt->userData,
3702 "define name '%s' is not an NCName\n",
3703 name);
3704 ctxt->nbErrors++;
3705 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003706 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard276be4a2003-01-24 01:03:34 +00003707 if (def == NULL) {
3708 xmlFree(name);
3709 return(-1);
3710 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00003711 def->type = XML_RELAXNG_DEF;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003712 def->name = name;
3713 if (node->children == NULL) {
3714 if (ctxt->error != NULL)
3715 ctxt->error(ctxt->userData,
3716 "define has no children\n");
3717 ctxt->nbErrors++;
3718 } else {
3719 olddefine = ctxt->define;
3720 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00003721 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00003722 ctxt->define = olddefine;
3723 }
3724 if (ctxt->grammar->defs == NULL)
3725 ctxt->grammar->defs = xmlHashCreate(10);
3726 if (ctxt->grammar->defs == NULL) {
3727 if (ctxt->error != NULL)
3728 ctxt->error(ctxt->userData,
3729 "Could not create definition hash\n");
3730 ctxt->nbErrors++;
3731 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003732 } else {
3733 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
3734 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00003735 xmlRelaxNGDefinePtr prev;
3736
3737 prev = xmlHashLookup(ctxt->grammar->defs, name);
3738 if (prev == NULL) {
3739 if (ctxt->error != NULL)
3740 ctxt->error(ctxt->userData,
3741 "Internal error on define aggregation of %s\n",
3742 name);
3743 ctxt->nbErrors++;
3744 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00003745 } else {
3746 while (prev->nextHash != NULL)
3747 prev = prev->nextHash;
3748 prev->nextHash = def;
3749 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003750 }
3751 }
3752 }
3753 return(ret);
3754}
3755
3756/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00003757 * xmlRelaxNGProcessExternalRef:
3758 * @ctxt: the parser context
3759 * @node: the externlRef node
3760 *
3761 * Process and compile an externlRef node
3762 *
3763 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
3764 */
3765static xmlRelaxNGDefinePtr
3766xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3767 xmlRelaxNGDocumentPtr docu;
3768 xmlNodePtr root, tmp;
3769 xmlChar *ns;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003770 int newNs = 0, oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003771 xmlRelaxNGDefinePtr def;
3772
3773 docu = node->_private;
3774 if (docu != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003775 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillardfebcca42003-02-16 15:44:18 +00003776 if (def == NULL)
3777 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003778 def->type = XML_RELAXNG_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003779
3780 if (docu->content == NULL) {
3781 /*
3782 * Then do the parsing for good
3783 */
3784 root = xmlDocGetRootElement(docu->doc);
3785 if (root == NULL) {
3786 if (ctxt->error != NULL)
3787 ctxt->error(ctxt->userData,
3788 "xmlRelaxNGParse: %s is empty\n",
3789 ctxt->URL);
3790 ctxt->nbErrors++;
3791 return (NULL);
3792 }
3793 /*
3794 * ns transmission rules
3795 */
3796 ns = xmlGetProp(root, BAD_CAST "ns");
3797 if (ns == NULL) {
3798 tmp = node;
3799 while ((tmp != NULL) &&
3800 (tmp->type == XML_ELEMENT_NODE)) {
3801 ns = xmlGetProp(tmp, BAD_CAST "ns");
3802 if (ns != NULL) {
3803 break;
3804 }
3805 tmp = tmp->parent;
3806 }
3807 if (ns != NULL) {
3808 xmlSetProp(root, BAD_CAST "ns", ns);
3809 newNs = 1;
3810 xmlFree(ns);
3811 }
3812 } else {
3813 xmlFree(ns);
3814 }
3815
3816 /*
3817 * Parsing to get a precompiled schemas.
3818 */
Daniel Veillard77648bb2003-02-20 15:03:22 +00003819 oldflags = ctxt->flags;
3820 ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003821 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard77648bb2003-02-20 15:03:22 +00003822 ctxt->flags = oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00003823 if ((docu->schema != NULL) &&
3824 (docu->schema->topgrammar != NULL)) {
3825 docu->content = docu->schema->topgrammar->start;
3826 }
3827
3828 /*
3829 * the externalRef may be reused in a different ns context
3830 */
3831 if (newNs == 1) {
3832 xmlUnsetProp(root, BAD_CAST "ns");
3833 }
3834 }
3835 def->content = docu->content;
3836 } else {
3837 def = NULL;
3838 }
3839 return(def);
3840}
3841
3842/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003843 * xmlRelaxNGParsePattern:
3844 * @ctxt: a Relax-NG parser context
3845 * @node: the pattern node.
3846 *
3847 * parse the content of a RelaxNG pattern node.
3848 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00003849 * Returns the definition pointer or NULL in case of error or if no
3850 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00003851 */
3852static xmlRelaxNGDefinePtr
3853xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3854 xmlRelaxNGDefinePtr def = NULL;
3855
Daniel Veillardd2298792003-02-14 16:54:11 +00003856 if (node == NULL) {
3857 return(NULL);
3858 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003859 if (IS_RELAXNG(node, "element")) {
3860 def = xmlRelaxNGParseElement(ctxt, node);
3861 } else if (IS_RELAXNG(node, "attribute")) {
3862 def = xmlRelaxNGParseAttribute(ctxt, node);
3863 } else if (IS_RELAXNG(node, "empty")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003864 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003865 if (def == NULL)
3866 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003867 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00003868 if (node->children != NULL) {
3869 if (ctxt->error != NULL)
3870 ctxt->error(ctxt->userData, "empty: had a child node\n");
3871 ctxt->nbErrors++;
3872 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003873 } else if (IS_RELAXNG(node, "text")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003874 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003875 if (def == NULL)
3876 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003877 def->type = XML_RELAXNG_TEXT;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003878 if (node->children != NULL) {
3879 if (ctxt->error != NULL)
3880 ctxt->error(ctxt->userData, "text: had a child node\n");
3881 ctxt->nbErrors++;
3882 }
3883 } else if (IS_RELAXNG(node, "zeroOrMore")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003884 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003885 if (def == NULL)
3886 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003887 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00003888 if (node->children == NULL) {
3889 if (ctxt->error != NULL)
3890 ctxt->error(ctxt->userData,
3891 "Element %s is empty\n", node->name);
3892 ctxt->nbErrors++;
3893 } else {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003894 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
Daniel Veillardd2298792003-02-14 16:54:11 +00003895 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003896 } else if (IS_RELAXNG(node, "oneOrMore")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003897 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003898 if (def == NULL)
3899 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003900 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00003901 if (node->children == NULL) {
3902 if (ctxt->error != NULL)
3903 ctxt->error(ctxt->userData,
3904 "Element %s is empty\n", node->name);
3905 ctxt->nbErrors++;
3906 } else {
3907 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
3908 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003909 } else if (IS_RELAXNG(node, "optional")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003910 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003911 if (def == NULL)
3912 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003913 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00003914 if (node->children == NULL) {
3915 if (ctxt->error != NULL)
3916 ctxt->error(ctxt->userData,
3917 "Element %s is empty\n", node->name);
3918 ctxt->nbErrors++;
3919 } else {
3920 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
3921 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003922 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00003923 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003924 if (def == NULL)
3925 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00003926 def->type = XML_RELAXNG_CHOICE;
3927 if (node->children == NULL) {
3928 if (ctxt->error != NULL)
3929 ctxt->error(ctxt->userData,
3930 "Element %s is empty\n", node->name);
3931 ctxt->nbErrors++;
3932 } else {
3933 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3934 }
3935 } else if (IS_RELAXNG(node, "group")) {
3936 def = xmlRelaxNGNewDefine(ctxt, node);
3937 if (def == NULL)
3938 return(NULL);
3939 def->type = XML_RELAXNG_GROUP;
3940 if (node->children == NULL) {
3941 if (ctxt->error != NULL)
3942 ctxt->error(ctxt->userData,
3943 "Element %s is empty\n", node->name);
3944 ctxt->nbErrors++;
3945 } else {
3946 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3947 }
3948 } else if (IS_RELAXNG(node, "ref")) {
3949 def = xmlRelaxNGNewDefine(ctxt, node);
3950 if (def == NULL)
3951 return(NULL);
3952 def->type = XML_RELAXNG_REF;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003953 def->name = xmlGetProp(node, BAD_CAST "name");
3954 if (def->name == NULL) {
3955 if (ctxt->error != NULL)
3956 ctxt->error(ctxt->userData,
3957 "ref has no name\n");
3958 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003959 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003960 xmlRelaxNGNormExtSpace(def->name);
3961 if (xmlValidateNCName(def->name, 0)) {
3962 if (ctxt->error != NULL)
3963 ctxt->error(ctxt->userData,
3964 "ref name '%s' is not an NCName\n",
3965 def->name);
3966 ctxt->nbErrors++;
3967 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003968 }
3969 if (node->children != NULL) {
3970 if (ctxt->error != NULL)
3971 ctxt->error(ctxt->userData,
3972 "ref is not empty\n");
3973 ctxt->nbErrors++;
3974 }
3975 if (ctxt->grammar->refs == NULL)
3976 ctxt->grammar->refs = xmlHashCreate(10);
3977 if (ctxt->grammar->refs == NULL) {
3978 if (ctxt->error != NULL)
3979 ctxt->error(ctxt->userData,
3980 "Could not create references hash\n");
3981 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003982 def = NULL;
3983 } else {
3984 int tmp;
3985
3986 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
3987 if (tmp < 0) {
3988 xmlRelaxNGDefinePtr prev;
3989
3990 prev = (xmlRelaxNGDefinePtr)
3991 xmlHashLookup(ctxt->grammar->refs, def->name);
3992 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00003993 if (def->name != NULL) {
3994 if (ctxt->error != NULL)
3995 ctxt->error(ctxt->userData,
3996 "Error refs definitions '%s'\n",
3997 def->name);
3998 } else {
3999 if (ctxt->error != NULL)
4000 ctxt->error(ctxt->userData,
4001 "Error refs definitions\n");
4002 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004003 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004004 def = NULL;
4005 } else {
4006 def->nextHash = prev->nextHash;
4007 prev->nextHash = def;
4008 }
4009 }
4010 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00004011 } else if (IS_RELAXNG(node, "data")) {
4012 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardedc91922003-01-26 00:52:04 +00004013 } else if (IS_RELAXNG(node, "value")) {
4014 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004015 } else if (IS_RELAXNG(node, "list")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004016 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00004017 if (def == NULL)
4018 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00004019 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00004020 if (node->children == NULL) {
4021 if (ctxt->error != NULL)
4022 ctxt->error(ctxt->userData,
4023 "Element %s is empty\n", node->name);
4024 ctxt->nbErrors++;
4025 } else {
4026 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
4027 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00004028 } else if (IS_RELAXNG(node, "interleave")) {
4029 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004030 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00004031 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004032 } else if (IS_RELAXNG(node, "notAllowed")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004033 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004034 if (def == NULL)
4035 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00004036 def->type = XML_RELAXNG_NOT_ALLOWED;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004037 if (node->children != NULL) {
4038 if (ctxt->error != NULL)
4039 ctxt->error(ctxt->userData,
4040 "xmlRelaxNGParse: notAllowed element is not empty\n");
4041 ctxt->nbErrors++;
4042 }
Daniel Veillard419a7682003-02-03 23:22:49 +00004043 } else if (IS_RELAXNG(node, "grammar")) {
4044 xmlRelaxNGGrammarPtr grammar, old;
4045 xmlRelaxNGGrammarPtr oldparent;
4046
Daniel Veillardc482e262003-02-26 14:48:48 +00004047#ifdef DEBUG_GRAMMAR
4048 xmlGenericError(xmlGenericErrorContext, "Found <grammar> pattern\n");
4049#endif
4050
Daniel Veillard419a7682003-02-03 23:22:49 +00004051 oldparent = ctxt->parentgrammar;
4052 old = ctxt->grammar;
4053 ctxt->parentgrammar = old;
4054 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
4055 if (old != NULL) {
4056 ctxt->grammar = old;
4057 ctxt->parentgrammar = oldparent;
Daniel Veillardc482e262003-02-26 14:48:48 +00004058#if 0
Daniel Veillard419a7682003-02-03 23:22:49 +00004059 if (grammar != NULL) {
4060 grammar->next = old->next;
4061 old->next = grammar;
4062 }
Daniel Veillardc482e262003-02-26 14:48:48 +00004063#endif
Daniel Veillard419a7682003-02-03 23:22:49 +00004064 }
4065 if (grammar != NULL)
4066 def = grammar->start;
4067 else
4068 def = NULL;
4069 } else if (IS_RELAXNG(node, "parentRef")) {
4070 if (ctxt->parentgrammar == NULL) {
4071 if (ctxt->error != NULL)
4072 ctxt->error(ctxt->userData,
4073 "Use of parentRef without a parent grammar\n");
4074 ctxt->nbErrors++;
4075 return(NULL);
4076 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00004077 def = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard419a7682003-02-03 23:22:49 +00004078 if (def == NULL)
4079 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00004080 def->type = XML_RELAXNG_PARENTREF;
Daniel Veillard419a7682003-02-03 23:22:49 +00004081 def->name = xmlGetProp(node, BAD_CAST "name");
4082 if (def->name == NULL) {
4083 if (ctxt->error != NULL)
4084 ctxt->error(ctxt->userData,
4085 "parentRef has no name\n");
4086 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00004087 } else {
4088 xmlRelaxNGNormExtSpace(def->name);
4089 if (xmlValidateNCName(def->name, 0)) {
4090 if (ctxt->error != NULL)
4091 ctxt->error(ctxt->userData,
4092 "parentRef name '%s' is not an NCName\n",
4093 def->name);
4094 ctxt->nbErrors++;
4095 }
Daniel Veillard419a7682003-02-03 23:22:49 +00004096 }
4097 if (node->children != NULL) {
4098 if (ctxt->error != NULL)
4099 ctxt->error(ctxt->userData,
4100 "parentRef is not empty\n");
4101 ctxt->nbErrors++;
4102 }
4103 if (ctxt->parentgrammar->refs == NULL)
4104 ctxt->parentgrammar->refs = xmlHashCreate(10);
4105 if (ctxt->parentgrammar->refs == NULL) {
4106 if (ctxt->error != NULL)
4107 ctxt->error(ctxt->userData,
4108 "Could not create references hash\n");
4109 ctxt->nbErrors++;
4110 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00004111 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00004112 int tmp;
4113
4114 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
4115 if (tmp < 0) {
4116 xmlRelaxNGDefinePtr prev;
4117
4118 prev = (xmlRelaxNGDefinePtr)
4119 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
4120 if (prev == NULL) {
4121 if (ctxt->error != NULL)
4122 ctxt->error(ctxt->userData,
4123 "Internal error parentRef definitions '%s'\n",
4124 def->name);
4125 ctxt->nbErrors++;
4126 def = NULL;
4127 } else {
4128 def->nextHash = prev->nextHash;
4129 prev->nextHash = def;
4130 }
4131 }
4132 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004133 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004134 if (node->children == NULL) {
4135 if (ctxt->error != NULL)
4136 ctxt->error(ctxt->userData,
4137 "Mixed is empty\n");
4138 ctxt->nbErrors++;
4139 def = NULL;
4140 } else {
4141 def = xmlRelaxNGParseInterleave(ctxt, node);
4142 if (def != NULL) {
4143 xmlRelaxNGDefinePtr tmp;
4144
4145 if ((def->content != NULL) && (def->content->next != NULL)) {
4146 tmp = xmlRelaxNGNewDefine(ctxt, node);
4147 if (tmp != NULL) {
4148 tmp->type = XML_RELAXNG_GROUP;
4149 tmp->content = def->content;
4150 def->content = tmp;
4151 }
4152 }
4153
4154 tmp = xmlRelaxNGNewDefine(ctxt, node);
4155 if (tmp == NULL)
4156 return(def);
4157 tmp->type = XML_RELAXNG_TEXT;
4158 tmp->next = def->content;
4159 def->content = tmp;
4160 }
4161 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004162 } else {
4163 if (ctxt->error != NULL)
4164 ctxt->error(ctxt->userData,
4165 "Unexpected node %s is not a pattern\n",
4166 node->name);
4167 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004168 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004169 }
4170 return(def);
4171}
4172
4173/**
4174 * xmlRelaxNGParseAttribute:
4175 * @ctxt: a Relax-NG parser context
4176 * @node: the element node
4177 *
4178 * parse the content of a RelaxNG attribute node.
4179 *
4180 * Returns the definition pointer or NULL in case of error.
4181 */
4182static xmlRelaxNGDefinePtr
4183xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004184 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004185 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004186 int old_flags;
4187
Daniel Veillardfd573f12003-03-16 17:52:32 +00004188 ret = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004189 if (ret == NULL)
4190 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00004191 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004192 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004193 child = node->children;
4194 if (child == NULL) {
4195 if (ctxt->error != NULL)
4196 ctxt->error(ctxt->userData,
4197 "xmlRelaxNGParseattribute: attribute has no children\n");
4198 ctxt->nbErrors++;
4199 return(ret);
4200 }
4201 old_flags = ctxt->flags;
4202 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004203 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
4204 if (cur != NULL)
4205 child = child->next;
4206
Daniel Veillardd2298792003-02-14 16:54:11 +00004207 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004208 cur = xmlRelaxNGParsePattern(ctxt, child);
4209 if (cur != NULL) {
4210 switch (cur->type) {
4211 case XML_RELAXNG_EMPTY:
4212 case XML_RELAXNG_NOT_ALLOWED:
4213 case XML_RELAXNG_TEXT:
4214 case XML_RELAXNG_ELEMENT:
4215 case XML_RELAXNG_DATATYPE:
4216 case XML_RELAXNG_VALUE:
4217 case XML_RELAXNG_LIST:
4218 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00004219 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004220 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004221 case XML_RELAXNG_DEF:
4222 case XML_RELAXNG_ONEORMORE:
Daniel Veillardfd573f12003-03-16 17:52:32 +00004223 case XML_RELAXNG_ZEROORMORE:
4224 case XML_RELAXNG_OPTIONAL:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004225 case XML_RELAXNG_CHOICE:
4226 case XML_RELAXNG_GROUP:
4227 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard77648bb2003-02-20 15:03:22 +00004228 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00004229 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004230 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004231 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004232 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00004233 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00004234 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00004235 if (ctxt->error != NULL)
4236 ctxt->error(ctxt->userData,
4237 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004238 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004239 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004240 case XML_RELAXNG_NOOP:
4241 TODO
4242 if (ctxt->error != NULL)
4243 ctxt->error(ctxt->userData,
4244 "Internal error, noop found\n");
4245 ctxt->nbErrors++;
4246 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004247 }
4248 }
4249 child = child->next;
4250 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004251 if (child != NULL) {
4252 if (ctxt->error != NULL)
4253 ctxt->error(ctxt->userData, "attribute has multiple children\n");
4254 ctxt->nbErrors++;
4255 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004256 ctxt->flags = old_flags;
4257 return(ret);
4258}
4259
4260/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004261 * xmlRelaxNGParseExceptNameClass:
4262 * @ctxt: a Relax-NG parser context
4263 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00004264 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004265 *
4266 * parse the content of a RelaxNG nameClass node.
4267 *
4268 * Returns the definition pointer or NULL in case of error.
4269 */
4270static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00004271xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
4272 xmlNodePtr node, int attr) {
4273 xmlRelaxNGDefinePtr ret, cur, last = NULL;
4274 xmlNodePtr child;
4275
Daniel Veillardd2298792003-02-14 16:54:11 +00004276 if (!IS_RELAXNG(node, "except")) {
4277 if (ctxt->error != NULL)
4278 ctxt->error(ctxt->userData,
4279 "Expecting an except node\n");
4280 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00004281 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00004282 }
4283 if (node->next != NULL) {
4284 if (ctxt->error != NULL)
4285 ctxt->error(ctxt->userData,
4286 "exceptNameClass allows only a single except node\n");
4287 ctxt->nbErrors++;
4288 }
Daniel Veillard144fae12003-02-03 13:17:57 +00004289 if (node->children == NULL) {
4290 if (ctxt->error != NULL)
4291 ctxt->error(ctxt->userData,
4292 "except has no content\n");
4293 ctxt->nbErrors++;
4294 return(NULL);
4295 }
4296
Daniel Veillardfd573f12003-03-16 17:52:32 +00004297 ret = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard144fae12003-02-03 13:17:57 +00004298 if (ret == NULL)
4299 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00004300 ret->type = XML_RELAXNG_EXCEPT;
Daniel Veillard144fae12003-02-03 13:17:57 +00004301 child = node->children;
4302 while (child != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004303 cur = xmlRelaxNGNewDefine(ctxt, child);
Daniel Veillard144fae12003-02-03 13:17:57 +00004304 if (cur == NULL)
4305 break;
4306 if (attr)
4307 cur->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004308 else
4309 cur->type = XML_RELAXNG_ELEMENT;
Daniel Veillard144fae12003-02-03 13:17:57 +00004310
Daniel Veillard419a7682003-02-03 23:22:49 +00004311 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00004312 if (last == NULL) {
4313 ret->content = cur;
4314 } else {
4315 last->next = cur;
4316 }
4317 last = cur;
4318 }
4319 child = child->next;
4320 }
4321
4322 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004323}
4324
4325/**
4326 * xmlRelaxNGParseNameClass:
4327 * @ctxt: a Relax-NG parser context
4328 * @node: the nameClass node
4329 * @def: the current definition
4330 *
4331 * parse the content of a RelaxNG nameClass node.
4332 *
4333 * Returns the definition pointer or NULL in case of error.
4334 */
4335static xmlRelaxNGDefinePtr
4336xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
4337 xmlRelaxNGDefinePtr def) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004338 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004339 xmlChar *val;
4340
Daniel Veillard2e9b1652003-02-19 13:29:45 +00004341 ret = def;
4342 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
4343 (IS_RELAXNG(node, "nsName"))) {
4344 if ((def->type != XML_RELAXNG_ELEMENT) &&
4345 (def->type != XML_RELAXNG_ATTRIBUTE)) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004346 ret = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard2e9b1652003-02-19 13:29:45 +00004347 if (ret == NULL)
4348 return(NULL);
4349 ret->parent = def;
4350 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
4351 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004352 else
4353 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard2e9b1652003-02-19 13:29:45 +00004354 }
4355 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004356 if (IS_RELAXNG(node, "name")) {
4357 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00004358 xmlRelaxNGNormExtSpace(val);
4359 if (xmlValidateNCName(val, 0)) {
4360 if (ctxt->error != NULL) {
4361 if (node->parent != NULL)
4362 ctxt->error(ctxt->userData,
4363 "Element %s name '%s' is not an NCName\n",
4364 node->parent->name, val);
4365 else
4366 ctxt->error(ctxt->userData,
4367 "name '%s' is not an NCName\n",
4368 val);
4369 }
4370 ctxt->nbErrors++;
4371 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004372 ret->name = val;
4373 val = xmlGetProp(node, BAD_CAST "ns");
4374 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00004375 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
4376 (val != NULL) &&
4377 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
4378 ctxt->error(ctxt->userData,
4379 "Attribute with namespace '%s' is not allowed\n",
4380 val);
4381 ctxt->nbErrors++;
4382 }
4383 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
4384 (val != NULL) &&
4385 (val[0] == 0) &&
4386 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
4387 ctxt->error(ctxt->userData,
4388 "Attribute with QName 'xmlns' is not allowed\n",
4389 val);
4390 ctxt->nbErrors++;
4391 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004392 } else if (IS_RELAXNG(node, "anyName")) {
4393 ret->name = NULL;
4394 ret->ns = NULL;
4395 if (node->children != NULL) {
4396 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00004397 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
4398 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004399 }
4400 } else if (IS_RELAXNG(node, "nsName")) {
4401 ret->name = NULL;
4402 ret->ns = xmlGetProp(node, BAD_CAST "ns");
4403 if (ret->ns == NULL) {
4404 if (ctxt->error != NULL)
4405 ctxt->error(ctxt->userData,
4406 "nsName has no ns attribute\n");
4407 ctxt->nbErrors++;
4408 }
Daniel Veillard416589a2003-02-17 17:25:42 +00004409 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
4410 (ret->ns != NULL) &&
4411 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
4412 ctxt->error(ctxt->userData,
4413 "Attribute with namespace '%s' is not allowed\n",
4414 ret->ns);
4415 ctxt->nbErrors++;
4416 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004417 if (node->children != NULL) {
4418 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00004419 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
4420 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004421 }
4422 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004423 xmlNodePtr child;
4424 xmlRelaxNGDefinePtr last = NULL;
4425
4426 ret = xmlRelaxNGNewDefine(ctxt, node);
4427 if (ret == NULL)
4428 return(NULL);
4429 ret->parent = def;
4430 ret->type = XML_RELAXNG_CHOICE;
4431
Daniel Veillardd2298792003-02-14 16:54:11 +00004432 if (node->children == NULL) {
4433 if (ctxt->error != NULL)
4434 ctxt->error(ctxt->userData,
4435 "Element choice is empty\n");
4436 ctxt->nbErrors++;
4437 } else {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004438
4439 child = node->children;
4440 while (child != NULL) {
4441 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
4442 if (tmp != NULL) {
4443 if (last == NULL) {
4444 last = ret->nameClass = tmp;
4445 } else {
4446 last->next = tmp;
4447 last = tmp;
4448 }
4449 }
4450 child = child->next;
4451 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004452 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004453 } else {
4454 if (ctxt->error != NULL)
4455 ctxt->error(ctxt->userData,
Daniel Veillardfd573f12003-03-16 17:52:32 +00004456 "expecting name, anyName, nsName or choice : got %s\n",
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004457 node->name);
4458 ctxt->nbErrors++;
4459 return(NULL);
4460 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00004461 if (ret != def) {
4462 if (def->nameClass == NULL) {
4463 def->nameClass = ret;
4464 } else {
4465 tmp = def->nameClass;
4466 while (tmp->next != NULL) {
4467 tmp = tmp->next;
4468 }
4469 tmp->next = ret;
4470 }
4471 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004472 return(ret);
4473}
4474
4475/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004476 * xmlRelaxNGParseElement:
4477 * @ctxt: a Relax-NG parser context
4478 * @node: the element node
4479 *
4480 * parse the content of a RelaxNG element node.
4481 *
4482 * Returns the definition pointer or NULL in case of error.
4483 */
4484static xmlRelaxNGDefinePtr
4485xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4486 xmlRelaxNGDefinePtr ret, cur, last;
4487 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004488 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004489
Daniel Veillardfd573f12003-03-16 17:52:32 +00004490 ret = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004491 if (ret == NULL)
4492 return(NULL);
Daniel Veillardfd573f12003-03-16 17:52:32 +00004493 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004494 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004495 child = node->children;
4496 if (child == NULL) {
4497 if (ctxt->error != NULL)
4498 ctxt->error(ctxt->userData,
4499 "xmlRelaxNGParseElement: element has no children\n");
4500 ctxt->nbErrors++;
4501 return(ret);
4502 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00004503 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
4504 if (cur != NULL)
4505 child = child->next;
4506
Daniel Veillard6eadf632003-01-23 18:29:16 +00004507 if (child == NULL) {
4508 if (ctxt->error != NULL)
4509 ctxt->error(ctxt->userData,
4510 "xmlRelaxNGParseElement: element has no content\n");
4511 ctxt->nbErrors++;
4512 return(ret);
4513 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004514 olddefine = ctxt->define;
4515 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004516 last = NULL;
4517 while (child != NULL) {
4518 cur = xmlRelaxNGParsePattern(ctxt, child);
4519 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004520 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004521 switch (cur->type) {
4522 case XML_RELAXNG_EMPTY:
4523 case XML_RELAXNG_NOT_ALLOWED:
4524 case XML_RELAXNG_TEXT:
4525 case XML_RELAXNG_ELEMENT:
4526 case XML_RELAXNG_DATATYPE:
4527 case XML_RELAXNG_VALUE:
4528 case XML_RELAXNG_LIST:
4529 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00004530 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004531 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004532 case XML_RELAXNG_DEF:
Daniel Veillardfd573f12003-03-16 17:52:32 +00004533 case XML_RELAXNG_ZEROORMORE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004534 case XML_RELAXNG_ONEORMORE:
Daniel Veillardfd573f12003-03-16 17:52:32 +00004535 case XML_RELAXNG_OPTIONAL:
Daniel Veillard6eadf632003-01-23 18:29:16 +00004536 case XML_RELAXNG_CHOICE:
4537 case XML_RELAXNG_GROUP:
4538 case XML_RELAXNG_INTERLEAVE:
4539 if (last == NULL) {
4540 ret->content = last = cur;
4541 } else {
4542 if ((last->type == XML_RELAXNG_ELEMENT) &&
4543 (ret->content == last)) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004544 ret->content = xmlRelaxNGNewDefine(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004545 if (ret->content != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004546 ret->content->type = XML_RELAXNG_GROUP;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004547 ret->content->content = last;
4548 } else {
4549 ret->content = last;
4550 }
4551 }
4552 last->next = cur;
4553 last = cur;
4554 }
4555 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004556 case XML_RELAXNG_ATTRIBUTE:
4557 cur->next = ret->attrs;
4558 ret->attrs = cur;
4559 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004560 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00004561 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00004562 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004563 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004564 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004565 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004566 case XML_RELAXNG_NOOP:
4567 TODO
4568 if (ctxt->error != NULL)
4569 ctxt->error(ctxt->userData,
4570 "Internal error, noop found\n");
4571 ctxt->nbErrors++;
4572 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004573 }
4574 }
4575 child = child->next;
4576 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004577 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004578 return(ret);
4579}
4580
4581/**
4582 * xmlRelaxNGParsePatterns:
4583 * @ctxt: a Relax-NG parser context
4584 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00004585 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00004586 *
4587 * parse the content of a RelaxNG start node.
4588 *
4589 * Returns the definition pointer or NULL in case of error.
4590 */
4591static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00004592xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
4593 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004594 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004595
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004596 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004597 while (nodes != NULL) {
4598 if (IS_RELAXNG(nodes, "element")) {
4599 cur = xmlRelaxNGParseElement(ctxt, nodes);
4600 if (def == NULL) {
4601 def = last = cur;
4602 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00004603 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
4604 (def == last)) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004605 def = xmlRelaxNGNewDefine(ctxt, nodes);
4606 def->type = XML_RELAXNG_GROUP;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004607 def->content = last;
4608 }
4609 last->next = cur;
4610 last = cur;
4611 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004612 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004613 } else {
4614 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00004615 if (cur != NULL) {
4616 if (def == NULL) {
4617 def = last = cur;
4618 } else {
4619 last->next = cur;
4620 last = cur;
4621 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004622 }
4623 }
4624 nodes = nodes->next;
4625 }
4626 return(def);
4627}
4628
4629/**
4630 * xmlRelaxNGParseStart:
4631 * @ctxt: a Relax-NG parser context
4632 * @nodes: start children nodes
4633 *
4634 * parse the content of a RelaxNG start node.
4635 *
4636 * Returns 0 in case of success, -1 in case of error
4637 */
4638static int
4639xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
4640 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004641 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004642
Daniel Veillardd2298792003-02-14 16:54:11 +00004643 if (nodes == NULL) {
4644 if (ctxt->error != NULL)
4645 ctxt->error(ctxt->userData,
4646 "start has no children\n");
4647 ctxt->nbErrors++;
4648 return(-1);
4649 }
4650 if (IS_RELAXNG(nodes, "empty")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004651 def = xmlRelaxNGNewDefine(ctxt, nodes);
Daniel Veillardd2298792003-02-14 16:54:11 +00004652 if (def == NULL)
4653 return(-1);
Daniel Veillardfd573f12003-03-16 17:52:32 +00004654 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00004655 if (nodes->children != NULL) {
4656 if (ctxt->error != NULL)
4657 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004658 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004659 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004660 } else if (IS_RELAXNG(nodes, "notAllowed")) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00004661 def = xmlRelaxNGNewDefine(ctxt, nodes);
Daniel Veillardd2298792003-02-14 16:54:11 +00004662 if (def == NULL)
4663 return(-1);
Daniel Veillardfd573f12003-03-16 17:52:32 +00004664 def->type = XML_RELAXNG_NOT_ALLOWED;
Daniel Veillardd2298792003-02-14 16:54:11 +00004665 if (nodes->children != NULL) {
4666 if (ctxt->error != NULL)
4667 ctxt->error(ctxt->userData,
4668 "element notAllowed is not empty\n");
4669 ctxt->nbErrors++;
4670 }
Daniel Veillardd2298792003-02-14 16:54:11 +00004671 } else {
4672 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00004673 }
4674 if (ctxt->grammar->start != NULL) {
4675 last = ctxt->grammar->start;
4676 while (last->next != NULL)
4677 last = last->next;
4678 last->next = def;
4679 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004680 ctxt->grammar->start = def;
4681 }
4682 nodes = nodes->next;
4683 if (nodes != NULL) {
4684 if (ctxt->error != NULL)
4685 ctxt->error(ctxt->userData,
4686 "start more than one children\n");
4687 ctxt->nbErrors++;
4688 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004689 }
4690 return(ret);
4691}
4692
4693/**
4694 * xmlRelaxNGParseGrammarContent:
4695 * @ctxt: a Relax-NG parser context
4696 * @nodes: grammar children nodes
4697 *
4698 * parse the content of a RelaxNG grammar node.
4699 *
4700 * Returns 0 in case of success, -1 in case of error
4701 */
4702static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004703xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00004704{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004705 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004706
4707 if (nodes == NULL) {
4708 if (ctxt->error != NULL)
4709 ctxt->error(ctxt->userData,
4710 "grammar has no children\n");
4711 ctxt->nbErrors++;
4712 return(-1);
4713 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004714 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004715 if (IS_RELAXNG(nodes, "start")) {
4716 if (nodes->children == NULL) {
4717 if (ctxt->error != NULL)
4718 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00004719 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004720 ctxt->nbErrors++;
4721 } else {
4722 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
4723 if (tmp != 0)
4724 ret = -1;
4725 }
4726 } else if (IS_RELAXNG(nodes, "define")) {
4727 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
4728 if (tmp != 0)
4729 ret = -1;
4730 } else if (IS_RELAXNG(nodes, "include")) {
4731 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
4732 if (tmp != 0)
4733 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004734 } else {
4735 if (ctxt->error != NULL)
4736 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004737 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004738 ctxt->nbErrors++;
4739 ret = -1;
4740 }
4741 nodes = nodes->next;
4742 }
4743 return (ret);
4744}
4745
4746/**
4747 * xmlRelaxNGCheckReference:
4748 * @ref: the ref
4749 * @ctxt: a Relax-NG parser context
4750 * @name: the name associated to the defines
4751 *
4752 * Applies the 4.17. combine attribute rule for all the define
4753 * element of a given grammar using the same name.
4754 */
4755static void
4756xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
4757 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
4758 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004759 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004760
4761 grammar = ctxt->grammar;
4762 if (grammar == NULL) {
4763 if (ctxt->error != NULL)
4764 ctxt->error(ctxt->userData,
4765 "Internal error: no grammar in CheckReference %s\n",
4766 name);
4767 ctxt->nbErrors++;
4768 return;
4769 }
4770 if (ref->content != NULL) {
4771 if (ctxt->error != NULL)
4772 ctxt->error(ctxt->userData,
4773 "Internal error: reference has content in CheckReference %s\n",
4774 name);
4775 ctxt->nbErrors++;
4776 return;
4777 }
4778 if (grammar->defs != NULL) {
4779 def = xmlHashLookup(grammar->defs, name);
4780 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00004781 cur = ref;
4782 while (cur != NULL) {
4783 cur->content = def;
4784 cur = cur->nextHash;
4785 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004786 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00004787 if (ctxt->error != NULL)
4788 ctxt->error(ctxt->userData,
4789 "Reference %s has no matching definition\n",
4790 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004791 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004792 }
Daniel Veillardd4310742003-02-18 21:12:46 +00004793 } else {
4794 if (ctxt->error != NULL)
4795 ctxt->error(ctxt->userData,
4796 "Reference %s has no matching definition\n",
4797 name);
4798 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004799 }
4800 /*
4801 * TODO: make a closure and verify there is no loop !
4802 */
4803}
4804
4805/**
4806 * xmlRelaxNGCheckCombine:
4807 * @define: the define(s) list
4808 * @ctxt: a Relax-NG parser context
4809 * @name: the name associated to the defines
4810 *
4811 * Applies the 4.17. combine attribute rule for all the define
4812 * element of a given grammar using the same name.
4813 */
4814static void
4815xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
4816 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
4817 xmlChar *combine;
4818 int choiceOrInterleave = -1;
4819 int missing = 0;
4820 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
4821
4822 if (define->nextHash == NULL)
4823 return;
4824 cur = define;
4825 while (cur != NULL) {
4826 combine = xmlGetProp(cur->node, BAD_CAST "combine");
4827 if (combine != NULL) {
4828 if (xmlStrEqual(combine, BAD_CAST "choice")) {
4829 if (choiceOrInterleave == -1)
4830 choiceOrInterleave = 1;
4831 else if (choiceOrInterleave == 0) {
4832 if (ctxt->error != NULL)
4833 ctxt->error(ctxt->userData,
4834 "Defines for %s use both 'choice' and 'interleave'\n",
4835 name);
4836 ctxt->nbErrors++;
4837 }
Daniel Veillard154877e2003-01-30 12:17:05 +00004838 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004839 if (choiceOrInterleave == -1)
4840 choiceOrInterleave = 0;
4841 else if (choiceOrInterleave == 1) {
4842 if (ctxt->error != NULL)
4843 ctxt->error(ctxt->userData,
4844 "Defines for %s use both 'choice' and 'interleave'\n",
4845 name);
4846 ctxt->nbErrors++;
4847 }
4848 } else {
4849 if (ctxt->error != NULL)
4850 ctxt->error(ctxt->userData,
4851 "Defines for %s use unknown combine value '%s''\n",
4852 name, combine);
4853 ctxt->nbErrors++;
4854 }
4855 xmlFree(combine);
4856 } else {
4857 if (missing == 0)
4858 missing = 1;
4859 else {
4860 if (ctxt->error != NULL)
4861 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00004862 "Some defines for %s needs the combine attribute\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00004863 name);
4864 ctxt->nbErrors++;
4865 }
4866 }
4867
4868 cur = cur->nextHash;
4869 }
4870#ifdef DEBUG
4871 xmlGenericError(xmlGenericErrorContext,
4872 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
4873 name, choiceOrInterleave);
4874#endif
4875 if (choiceOrInterleave == -1)
4876 choiceOrInterleave = 0;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004877 cur = xmlRelaxNGNewDefine(ctxt, define->node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004878 if (cur == NULL)
4879 return;
4880 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00004881 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004882 else
4883 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004884 tmp = define;
4885 last = NULL;
4886 while (tmp != NULL) {
4887 if (tmp->content != NULL) {
4888 if (tmp->content->next != NULL) {
4889 /*
4890 * we need first to create a wrapper.
4891 */
Daniel Veillardfd573f12003-03-16 17:52:32 +00004892 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004893 if (tmp2 == NULL)
4894 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004895 tmp2->type = XML_RELAXNG_GROUP;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004896 tmp2->content = tmp->content;
4897 } else {
4898 tmp2 = tmp->content;
4899 }
4900 if (last == NULL) {
4901 cur->content = tmp2;
4902 } else {
4903 last->next = tmp2;
4904 }
4905 last = tmp2;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004906 }
Daniel Veillard952379b2003-03-17 15:37:12 +00004907 tmp->content = cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004908 tmp = tmp->nextHash;
4909 }
4910 define->content = cur;
Daniel Veillardfd573f12003-03-16 17:52:32 +00004911 if (choiceOrInterleave == 0) {
4912 if (ctxt->interleaves == NULL)
4913 ctxt->interleaves = xmlHashCreate(10);
4914 if (ctxt->interleaves == NULL) {
4915 if (ctxt->error != NULL)
4916 ctxt->error(ctxt->userData,
4917 "Failed to create interleaves hash table\n");
4918 ctxt->nbErrors++;
4919 } else {
4920 char tmpname[32];
4921
4922 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
4923 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
4924 if (ctxt->error != NULL)
4925 ctxt->error(ctxt->userData,
4926 "Failed to add %s to hash table\n", tmpname);
4927 ctxt->nbErrors++;
4928 }
4929 }
4930 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004931}
4932
4933/**
4934 * xmlRelaxNGCombineStart:
4935 * @ctxt: a Relax-NG parser context
4936 * @grammar: the grammar
4937 *
4938 * Applies the 4.17. combine rule for all the start
4939 * element of a given grammar.
4940 */
4941static void
4942xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
4943 xmlRelaxNGGrammarPtr grammar) {
4944 xmlRelaxNGDefinePtr starts;
4945 xmlChar *combine;
4946 int choiceOrInterleave = -1;
4947 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004948 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004949
Daniel Veillard2df2de22003-02-17 23:34:33 +00004950 starts = grammar->start;
4951 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00004952 return;
4953 cur = starts;
4954 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00004955 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
4956 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
4957 combine = NULL;
4958 if (ctxt->error != NULL)
4959 ctxt->error(ctxt->userData,
4960 "Internal error: start element not found\n");
4961 ctxt->nbErrors++;
4962 } else {
4963 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
4964 }
4965
Daniel Veillard6eadf632003-01-23 18:29:16 +00004966 if (combine != NULL) {
4967 if (xmlStrEqual(combine, BAD_CAST "choice")) {
4968 if (choiceOrInterleave == -1)
4969 choiceOrInterleave = 1;
4970 else if (choiceOrInterleave == 0) {
4971 if (ctxt->error != NULL)
4972 ctxt->error(ctxt->userData,
4973 "<start> use both 'choice' and 'interleave'\n");
4974 ctxt->nbErrors++;
4975 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00004976 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004977 if (choiceOrInterleave == -1)
4978 choiceOrInterleave = 0;
4979 else if (choiceOrInterleave == 1) {
4980 if (ctxt->error != NULL)
4981 ctxt->error(ctxt->userData,
4982 "<start> use both 'choice' and 'interleave'\n");
4983 ctxt->nbErrors++;
4984 }
4985 } else {
4986 if (ctxt->error != NULL)
4987 ctxt->error(ctxt->userData,
4988 "<start> uses unknown combine value '%s''\n", combine);
4989 ctxt->nbErrors++;
4990 }
4991 xmlFree(combine);
4992 } else {
4993 if (missing == 0)
4994 missing = 1;
4995 else {
4996 if (ctxt->error != NULL)
4997 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00004998 "Some <start> element miss the combine attribute\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +00004999 ctxt->nbErrors++;
5000 }
5001 }
5002
Daniel Veillard2df2de22003-02-17 23:34:33 +00005003 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005004 }
5005#ifdef DEBUG
5006 xmlGenericError(xmlGenericErrorContext,
5007 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
5008 choiceOrInterleave);
5009#endif
5010 if (choiceOrInterleave == -1)
5011 choiceOrInterleave = 0;
Daniel Veillardfd573f12003-03-16 17:52:32 +00005012 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005013 if (cur == NULL)
5014 return;
5015 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00005016 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillardfd573f12003-03-16 17:52:32 +00005017 else
5018 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00005019 cur->content = grammar->start;
5020 grammar->start = cur;
Daniel Veillardfd573f12003-03-16 17:52:32 +00005021 if (choiceOrInterleave == 0) {
5022 if (ctxt->interleaves == NULL)
5023 ctxt->interleaves = xmlHashCreate(10);
5024 if (ctxt->interleaves == NULL) {
5025 if (ctxt->error != NULL)
5026 ctxt->error(ctxt->userData,
5027 "Failed to create interleaves hash table\n");
5028 ctxt->nbErrors++;
5029 } else {
5030 char tmpname[32];
5031
5032 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
5033 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
5034 if (ctxt->error != NULL)
5035 ctxt->error(ctxt->userData,
5036 "Failed to add %s to hash table\n", tmpname);
5037 ctxt->nbErrors++;
5038 }
5039 }
5040 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005041}
5042
5043/**
Daniel Veillardd4310742003-02-18 21:12:46 +00005044 * xmlRelaxNGCheckCycles:
5045 * @ctxt: a Relax-NG parser context
5046 * @nodes: grammar children nodes
5047 * @depth: the counter
5048 *
5049 * Check for cycles.
5050 *
5051 * Returns 0 if check passed, and -1 in case of error
5052 */
5053static int
5054xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
5055 xmlRelaxNGDefinePtr cur, int depth) {
5056 int ret = 0;
5057
5058 while ((ret == 0) && (cur != NULL)) {
5059 if ((cur->type == XML_RELAXNG_REF) ||
5060 (cur->type == XML_RELAXNG_PARENTREF)) {
5061 if (cur->depth == -1) {
5062 cur->depth = depth;
5063 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
5064 cur->depth = -2;
5065 } else if (depth == cur->depth) {
5066 if (ctxt->error != NULL)
5067 ctxt->error(ctxt->userData,
5068 "Detected a cycle in %s references\n", cur->name);
5069 ctxt->nbErrors++;
5070 return(-1);
5071 }
5072 } else if (cur->type == XML_RELAXNG_ELEMENT) {
5073 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
5074 } else {
Daniel Veillardfd573f12003-03-16 17:52:32 +00005075 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
Daniel Veillardd4310742003-02-18 21:12:46 +00005076 }
5077 cur = cur->next;
5078 }
5079 return(ret);
5080}
5081
5082/**
Daniel Veillard77648bb2003-02-20 15:03:22 +00005083 * xmlRelaxNGTryUnlink:
5084 * @ctxt: a Relax-NG parser context
5085 * @cur: the definition to unlink
5086 * @parent: the parent definition
5087 * @prev: the previous sibling definition
5088 *
5089 * Try to unlink a definition. If not possble make it a NOOP
5090 *
5091 * Returns the new prev definition
5092 */
5093static xmlRelaxNGDefinePtr
5094xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
5095 xmlRelaxNGDefinePtr cur,
5096 xmlRelaxNGDefinePtr parent,
5097 xmlRelaxNGDefinePtr prev) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00005098 if (prev != NULL) {
5099 prev->next = cur->next;
5100 } else {
5101 if (parent != NULL) {
5102 if (parent->content == cur)
5103 parent->content = cur->next;
5104 else if (parent->attrs == cur)
5105 parent->attrs = cur->next;
5106 else if (parent->nameClass == cur)
5107 parent->nameClass = cur->next;
5108 } else {
5109 cur->type = XML_RELAXNG_NOOP;
5110 prev = cur;
5111 }
5112 }
5113 return(prev);
Daniel Veillard77648bb2003-02-20 15:03:22 +00005114}
5115
5116/**
Daniel Veillard4c5cf702003-02-21 15:40:34 +00005117 * xmlRelaxNGSimplify:
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005118 * @ctxt: a Relax-NG parser context
5119 * @nodes: grammar children nodes
5120 *
5121 * Check for simplification of empty and notAllowed
5122 */
5123static void
5124xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
5125 xmlRelaxNGDefinePtr cur,
5126 xmlRelaxNGDefinePtr parent) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00005127 xmlRelaxNGDefinePtr prev = NULL;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005128
Daniel Veillardfd573f12003-03-16 17:52:32 +00005129 while (cur != NULL) {
5130 if ((cur->type == XML_RELAXNG_REF) ||
5131 (cur->type == XML_RELAXNG_PARENTREF)) {
5132 if (cur->depth != -3) {
5133 cur->depth = -3;
5134 xmlRelaxNGSimplify(ctxt, cur->content, cur);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005135 }
5136 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00005137 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005138 if ((parent != NULL) &&
5139 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
5140 (parent->type == XML_RELAXNG_LIST) ||
5141 (parent->type == XML_RELAXNG_GROUP) ||
5142 (parent->type == XML_RELAXNG_INTERLEAVE) ||
Daniel Veillardfd573f12003-03-16 17:52:32 +00005143 (parent->type == XML_RELAXNG_ONEORMORE) ||
5144 (parent->type == XML_RELAXNG_ZEROORMORE))) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005145 parent->type = XML_RELAXNG_NOT_ALLOWED;
Daniel Veillardfd573f12003-03-16 17:52:32 +00005146 break;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005147 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005148 if ((parent != NULL) &&
Daniel Veillardfd573f12003-03-16 17:52:32 +00005149 (parent->type == XML_RELAXNG_CHOICE)) {
5150 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
5151 } else
5152 prev = cur;
5153 } else if (cur->type == XML_RELAXNG_EMPTY){
5154 cur->parent = parent;
5155 if ((parent != NULL) &&
5156 ((parent->type == XML_RELAXNG_ONEORMORE) ||
5157 (parent->type == XML_RELAXNG_ZEROORMORE))) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005158 parent->type = XML_RELAXNG_EMPTY;
Daniel Veillardfd573f12003-03-16 17:52:32 +00005159 break;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005160 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00005161 if ((parent != NULL) &&
5162 ((parent->type == XML_RELAXNG_GROUP) ||
5163 (parent->type == XML_RELAXNG_INTERLEAVE))) {
5164 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
5165 } else
5166 prev = cur;
5167 } else {
5168 cur->parent = parent;
5169 if (cur->content != NULL)
5170 xmlRelaxNGSimplify(ctxt, cur->content, cur);
5171 if (cur->attrs != NULL)
5172 xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
5173 if (cur->nameClass != NULL)
5174 xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
5175 /*
5176 * This may result in a simplification
5177 */
5178 if ((cur->type == XML_RELAXNG_GROUP) ||
5179 (cur->type == XML_RELAXNG_INTERLEAVE)) {
5180 if (cur->content == NULL)
5181 cur->type = XML_RELAXNG_EMPTY;
5182 else if (cur->content->next == NULL) {
5183 if ((parent == NULL) && (prev == NULL)) {
5184 cur->type = XML_RELAXNG_NOOP;
5185 } else if (prev == NULL) {
5186 parent->content = cur->content;
5187 cur->content->next = cur->next;
5188 cur = cur->content;
5189 } else {
5190 cur->content->next = cur->next;
5191 prev->next = cur->content;
5192 cur = cur->content;
5193 }
Daniel Veillard1564e6e2003-03-15 21:30:25 +00005194 }
5195 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00005196 /*
5197 * the current node may have been transformed back
5198 */
5199 if ((cur->type == XML_RELAXNG_EXCEPT) &&
5200 (cur->content != NULL) &&
5201 (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
5202 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
5203 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
5204 if ((parent != NULL) &&
5205 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
5206 (parent->type == XML_RELAXNG_LIST) ||
5207 (parent->type == XML_RELAXNG_GROUP) ||
5208 (parent->type == XML_RELAXNG_INTERLEAVE) ||
5209 (parent->type == XML_RELAXNG_ONEORMORE) ||
5210 (parent->type == XML_RELAXNG_ZEROORMORE))) {
5211 parent->type = XML_RELAXNG_NOT_ALLOWED;
5212 break;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00005213 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00005214 if ((parent != NULL) &&
5215 (parent->type == XML_RELAXNG_CHOICE)) {
5216 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
5217 } else
5218 prev = cur;
5219 } else if (cur->type == XML_RELAXNG_EMPTY){
5220 if ((parent != NULL) &&
5221 ((parent->type == XML_RELAXNG_ONEORMORE) ||
5222 (parent->type == XML_RELAXNG_ZEROORMORE))) {
5223 parent->type = XML_RELAXNG_EMPTY;
5224 break;
5225 }
5226 if ((parent != NULL) &&
5227 ((parent->type == XML_RELAXNG_GROUP) ||
5228 (parent->type == XML_RELAXNG_INTERLEAVE) ||
5229 (parent->type == XML_RELAXNG_CHOICE))) {
5230 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
5231 } else
5232 prev = cur;
5233 } else {
5234 prev = cur;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00005235 }
Daniel Veillard1564e6e2003-03-15 21:30:25 +00005236 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00005237 cur = cur->next;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005238 }
5239}
5240
Daniel Veillard4c5cf702003-02-21 15:40:34 +00005241/**
5242 * xmlRelaxNGGroupContentType:
5243 * @ct1: the first content type
5244 * @ct2: the second content type
5245 *
5246 * Try to group 2 content types
5247 *
5248 * Returns the content type
5249 */
5250static xmlRelaxNGContentType
5251xmlRelaxNGGroupContentType(xmlRelaxNGContentType ct1,
5252 xmlRelaxNGContentType ct2) {
5253 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
5254 (ct2 == XML_RELAXNG_CONTENT_ERROR))
5255 return(XML_RELAXNG_CONTENT_ERROR);
5256 if (ct1 == XML_RELAXNG_CONTENT_EMPTY)
5257 return(ct2);
5258 if (ct2 == XML_RELAXNG_CONTENT_EMPTY)
5259 return(ct1);
5260 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) &&
5261 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
5262 return(XML_RELAXNG_CONTENT_COMPLEX);
5263 return(XML_RELAXNG_CONTENT_ERROR);
5264}
5265
5266/**
5267 * xmlRelaxNGMaxContentType:
5268 * @ct1: the first content type
5269 * @ct2: the second content type
5270 *
5271 * Compute the max content-type
5272 *
5273 * Returns the content type
5274 */
5275static xmlRelaxNGContentType
5276xmlRelaxNGMaxContentType(xmlRelaxNGContentType ct1,
5277 xmlRelaxNGContentType ct2) {
5278 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
5279 (ct2 == XML_RELAXNG_CONTENT_ERROR))
5280 return(XML_RELAXNG_CONTENT_ERROR);
5281 if ((ct1 == XML_RELAXNG_CONTENT_SIMPLE) ||
5282 (ct2 == XML_RELAXNG_CONTENT_SIMPLE))
5283 return(XML_RELAXNG_CONTENT_SIMPLE);
5284 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) ||
5285 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
5286 return(XML_RELAXNG_CONTENT_COMPLEX);
5287 return(XML_RELAXNG_CONTENT_EMPTY);
5288}
Daniel Veillard77648bb2003-02-20 15:03:22 +00005289
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005290/**
5291 * xmlRelaxNGCheckRules:
5292 * @ctxt: a Relax-NG parser context
Daniel Veillard4c5cf702003-02-21 15:40:34 +00005293 * @cur: the current definition
Daniel Veillard77648bb2003-02-20 15:03:22 +00005294 * @flags: some accumulated flags
Daniel Veillardfd573f12003-03-16 17:52:32 +00005295 * @ptype: the parent type
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005296 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00005297 * Check for rules in section 7.1 and 7.2
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005298 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00005299 * Returns the content type of @cur
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005300 */
Daniel Veillard4c5cf702003-02-21 15:40:34 +00005301static xmlRelaxNGContentType
Daniel Veillardfd573f12003-03-16 17:52:32 +00005302xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
5303 xmlRelaxNGDefinePtr cur, int flags,
5304 xmlRelaxNGType ptype) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00005305 int nflags = flags;
Daniel Veillardfd573f12003-03-16 17:52:32 +00005306 xmlRelaxNGContentType ret, tmp, val = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005307
Daniel Veillardfd573f12003-03-16 17:52:32 +00005308 while (cur != NULL) {
5309 ret = XML_RELAXNG_CONTENT_EMPTY;
5310 if ((cur->type == XML_RELAXNG_REF) ||
5311 (cur->type == XML_RELAXNG_PARENTREF)) {
5312 if (flags & XML_RELAXNG_IN_LIST) {
5313 if (ctxt->error != NULL)
5314 ctxt->error(ctxt->userData,
5315 "Found forbidden pattern list//ref\n");
5316 ctxt->nbErrors++;
5317 }
5318 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5319 if (ctxt->error != NULL)
5320 ctxt->error(ctxt->userData,
5321 "Found forbidden pattern data/except//ref\n");
5322 ctxt->nbErrors++;
5323 }
5324 if (cur->depth > -4) {
5325 cur->depth = -4;
5326 ret = xmlRelaxNGCheckRules(ctxt, cur->content,
5327 flags, cur->type);
5328 cur->depth = ret - 15 ;
5329 } else if (cur->depth == -4) {
5330 ret = XML_RELAXNG_CONTENT_COMPLEX;
5331 } else {
5332 ret = (xmlRelaxNGContentType) cur->depth + 15;
5333 }
5334 } else if (cur->type == XML_RELAXNG_ELEMENT) {
5335 /*
5336 * The 7.3 Attribute derivation rule for groups is plugged there
5337 */
5338 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
5339 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5340 if (ctxt->error != NULL)
5341 ctxt->error(ctxt->userData,
5342 "Found forbidden pattern data/except//element(ref)\n");
5343 ctxt->nbErrors++;
5344 }
5345 if (flags & XML_RELAXNG_IN_LIST) {
5346 if (ctxt->error != NULL)
5347 ctxt->error(ctxt->userData,
5348 "Found forbidden pattern list//element(ref)\n");
5349 ctxt->nbErrors++;
5350 }
5351 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
5352 if (ctxt->error != NULL)
5353 ctxt->error(ctxt->userData,
5354 "Found forbidden pattern attribute//element(ref)\n");
5355 ctxt->nbErrors++;
5356 }
5357 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
5358 if (ctxt->error != NULL)
5359 ctxt->error(ctxt->userData,
Daniel Veillard463a5472003-02-27 21:30:32 +00005360 "Found forbidden pattern attribute//element(ref)\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00005361 ctxt->nbErrors++;
5362 }
5363 /*
5364 * reset since in the simple form elements are only child
5365 * of grammar/define
5366 */
5367 nflags = 0;
5368 ret = xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags, cur->type);
5369 if (ret != XML_RELAXNG_CONTENT_EMPTY) {
5370 if (ctxt->error != NULL)
5371 ctxt->error(ctxt->userData,
5372 "Element %s attributes have a content type error\n",
5373 cur->name);
5374 ctxt->nbErrors++;
5375 }
5376 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5377 if (ret == XML_RELAXNG_CONTENT_ERROR) {
5378 if (ctxt->error != NULL)
5379 ctxt->error(ctxt->userData,
5380 "Element %s has a content type error\n",
5381 cur->name);
5382 ctxt->nbErrors++;
5383 } else {
5384 ret = XML_RELAXNG_CONTENT_COMPLEX;
5385 }
5386 } else if (cur->type == XML_RELAXNG_ATTRIBUTE) {
5387 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
5388 if (ctxt->error != NULL)
5389 ctxt->error(ctxt->userData,
5390 "Found forbidden pattern attribute//attribute\n");
5391 ctxt->nbErrors++;
5392 }
5393 if (flags & XML_RELAXNG_IN_LIST) {
5394 if (ctxt->error != NULL)
5395 ctxt->error(ctxt->userData,
5396 "Found forbidden pattern list//attribute\n");
5397 ctxt->nbErrors++;
5398 }
5399 if (flags & XML_RELAXNG_IN_OOMGROUP) {
5400 if (ctxt->error != NULL)
5401 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00005402 "Found forbidden pattern oneOrMore//group//attribute\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00005403 ctxt->nbErrors++;
5404 }
5405 if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) {
5406 if (ctxt->error != NULL)
5407 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00005408 "Found forbidden pattern oneOrMore//interleave//attribute\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00005409 ctxt->nbErrors++;
5410 }
5411 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5412 if (ctxt->error != NULL)
5413 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00005414 "Found forbidden pattern data/except//attribute\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00005415 ctxt->nbErrors++;
5416 }
5417 if (flags & XML_RELAXNG_IN_START) {
5418 if (ctxt->error != NULL)
5419 ctxt->error(ctxt->userData,
5420 "Found forbidden pattern start//attribute\n");
5421 ctxt->nbErrors++;
5422 }
Daniel Veillard1564e6e2003-03-15 21:30:25 +00005423 if ((!(flags & XML_RELAXNG_IN_ONEORMORE)) && (cur->name == NULL)) {
5424 if (cur->ns == NULL) {
5425 if (ctxt->error != NULL)
5426 ctxt->error(ctxt->userData,
5427 "Found anyName attribute without oneOrMore ancestor\n");
5428 ctxt->nbErrors++;
5429 } else {
5430 if (ctxt->error != NULL)
5431 ctxt->error(ctxt->userData,
5432 "Found nsName attribute without oneOrMore ancestor\n");
5433 ctxt->nbErrors++;
5434 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00005435 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00005436 nflags = flags | XML_RELAXNG_IN_ATTRIBUTE;
5437 xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5438 ret = XML_RELAXNG_CONTENT_EMPTY;
5439 } else if ((cur->type == XML_RELAXNG_ONEORMORE) ||
5440 (cur->type == XML_RELAXNG_ZEROORMORE)) {
5441 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5442 if (ctxt->error != NULL)
5443 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00005444 "Found forbidden pattern data/except//oneOrMore\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00005445 ctxt->nbErrors++;
5446 }
5447 if (flags & XML_RELAXNG_IN_START) {
5448 if (ctxt->error != NULL)
5449 ctxt->error(ctxt->userData,
5450 "Found forbidden pattern start//oneOrMore\n");
5451 ctxt->nbErrors++;
5452 }
5453 nflags = flags | XML_RELAXNG_IN_ONEORMORE;
5454 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5455 ret = xmlRelaxNGGroupContentType(ret, ret);
5456 } else if (cur->type == XML_RELAXNG_LIST) {
5457 if (flags & XML_RELAXNG_IN_LIST) {
5458 if (ctxt->error != NULL)
5459 ctxt->error(ctxt->userData,
5460 "Found forbidden pattern list//list\n");
5461 ctxt->nbErrors++;
5462 }
5463 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5464 if (ctxt->error != NULL)
5465 ctxt->error(ctxt->userData,
5466 "Found forbidden pattern data/except//list\n");
5467 ctxt->nbErrors++;
5468 }
5469 if (flags & XML_RELAXNG_IN_START) {
5470 if (ctxt->error != NULL)
5471 ctxt->error(ctxt->userData,
5472 "Found forbidden pattern start//list\n");
5473 ctxt->nbErrors++;
5474 }
5475 nflags = flags | XML_RELAXNG_IN_LIST;
5476 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5477 } else if (cur->type == XML_RELAXNG_GROUP) {
5478 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5479 if (ctxt->error != NULL)
5480 ctxt->error(ctxt->userData,
5481 "Found forbidden pattern data/except//group\n");
5482 ctxt->nbErrors++;
5483 }
5484 if (flags & XML_RELAXNG_IN_START) {
5485 if (ctxt->error != NULL)
5486 ctxt->error(ctxt->userData,
5487 "Found forbidden pattern start//group\n");
5488 ctxt->nbErrors++;
5489 }
5490 if (flags & XML_RELAXNG_IN_ONEORMORE)
5491 nflags = flags | XML_RELAXNG_IN_OOMGROUP;
5492 else
5493 nflags = flags;
5494 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5495 /*
5496 * The 7.3 Attribute derivation rule for groups is plugged there
5497 */
5498 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
5499 } else if (cur->type == XML_RELAXNG_INTERLEAVE) {
5500 if (flags & XML_RELAXNG_IN_LIST) {
5501 if (ctxt->error != NULL)
5502 ctxt->error(ctxt->userData,
5503 "Found forbidden pattern list//interleave\n");
5504 ctxt->nbErrors++;
5505 }
5506 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5507 if (ctxt->error != NULL)
5508 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00005509 "Found forbidden pattern data/except//interleave\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00005510 ctxt->nbErrors++;
5511 }
5512 if (flags & XML_RELAXNG_IN_START) {
5513 if (ctxt->error != NULL)
5514 ctxt->error(ctxt->userData,
5515 "Found forbidden pattern start//interleave\n");
5516 ctxt->nbErrors++;
5517 }
5518 if (flags & XML_RELAXNG_IN_ONEORMORE)
5519 nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE;
5520 else
5521 nflags = flags;
5522 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5523 } else if (cur->type == XML_RELAXNG_EXCEPT) {
5524 if ((cur->parent != NULL) &&
5525 (cur->parent->type == XML_RELAXNG_DATATYPE))
5526 nflags = flags | XML_RELAXNG_IN_DATAEXCEPT;
5527 else
5528 nflags = flags;
5529 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
5530 } else if (cur->type == XML_RELAXNG_DATATYPE) {
5531 if (flags & XML_RELAXNG_IN_START) {
5532 if (ctxt->error != NULL)
5533 ctxt->error(ctxt->userData,
5534 "Found forbidden pattern start//data\n");
5535 ctxt->nbErrors++;
5536 }
5537 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
5538 ret = XML_RELAXNG_CONTENT_SIMPLE;
5539 } else if (cur->type == XML_RELAXNG_VALUE) {
5540 if (flags & XML_RELAXNG_IN_START) {
5541 if (ctxt->error != NULL)
5542 ctxt->error(ctxt->userData,
5543 "Found forbidden pattern start//value\n");
5544 ctxt->nbErrors++;
5545 }
5546 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
5547 ret = XML_RELAXNG_CONTENT_SIMPLE;
5548 } else if (cur->type == XML_RELAXNG_TEXT) {
5549 if (flags & XML_RELAXNG_IN_LIST) {
5550 if (ctxt->error != NULL)
5551 ctxt->error(ctxt->userData,
5552 "Found forbidden pattern list//text\n");
5553 ctxt->nbErrors++;
5554 }
5555 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5556 if (ctxt->error != NULL)
5557 ctxt->error(ctxt->userData,
5558 "Found forbidden pattern data/except//text\n");
5559 ctxt->nbErrors++;
5560 }
5561 if (flags & XML_RELAXNG_IN_START) {
5562 if (ctxt->error != NULL)
5563 ctxt->error(ctxt->userData,
5564 "Found forbidden pattern start//text\n");
5565 ctxt->nbErrors++;
5566 }
5567 ret = XML_RELAXNG_CONTENT_COMPLEX;
5568 } else if (cur->type == XML_RELAXNG_EMPTY) {
5569 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
5570 if (ctxt->error != NULL)
5571 ctxt->error(ctxt->userData,
5572 "Found forbidden pattern data/except//empty\n");
5573 ctxt->nbErrors++;
5574 }
5575 if (flags & XML_RELAXNG_IN_START) {
5576 if (ctxt->error != NULL)
5577 ctxt->error(ctxt->userData,
5578 "Found forbidden pattern start//empty\n");
5579 ctxt->nbErrors++;
5580 }
5581 ret = XML_RELAXNG_CONTENT_EMPTY;
5582 } else if (cur->type == XML_RELAXNG_CHOICE) {
5583 xmlRelaxNGCheckChoiceDeterminism(ctxt, cur);
5584 ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
5585 } else {
5586 ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
5587 }
5588 cur = cur->next;
5589 if (ptype == XML_RELAXNG_GROUP) {
5590 val = xmlRelaxNGGroupContentType(val, ret);
5591 } else if (ptype == XML_RELAXNG_INTERLEAVE) {
5592 tmp = xmlRelaxNGGroupContentType(val, ret);
5593 if (tmp != XML_RELAXNG_CONTENT_ERROR)
5594 tmp = xmlRelaxNGMaxContentType(val, ret);
5595 } else if (ptype == XML_RELAXNG_CHOICE) {
5596 val = xmlRelaxNGMaxContentType(val, ret);
5597 } else if (ptype == XML_RELAXNG_LIST) {
5598 val = XML_RELAXNG_CONTENT_SIMPLE;
5599 } else if (ptype == XML_RELAXNG_EXCEPT) {
5600 if (ret == XML_RELAXNG_CONTENT_ERROR)
5601 val = XML_RELAXNG_CONTENT_ERROR;
5602 else
5603 val = XML_RELAXNG_CONTENT_SIMPLE;
5604 } else {
5605 val = xmlRelaxNGGroupContentType(val, ret);
5606 }
5607
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005608 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00005609 return(val);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005610}
Daniel Veillard1c745ad2003-02-20 00:11:02 +00005611
5612/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005613 * xmlRelaxNGParseGrammar:
5614 * @ctxt: a Relax-NG parser context
5615 * @nodes: grammar children nodes
5616 *
5617 * parse a Relax-NG <grammar> node
5618 *
5619 * Returns the internal xmlRelaxNGGrammarPtr built or
5620 * NULL in case of error
5621 */
5622static xmlRelaxNGGrammarPtr
5623xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
5624 xmlRelaxNGGrammarPtr ret, tmp, old;
5625
Daniel Veillardc482e262003-02-26 14:48:48 +00005626#ifdef DEBUG_GRAMMAR
5627 xmlGenericError(xmlGenericErrorContext, "Parsing a new grammar\n");
5628#endif
5629
Daniel Veillard6eadf632003-01-23 18:29:16 +00005630 ret = xmlRelaxNGNewGrammar(ctxt);
5631 if (ret == NULL)
5632 return(NULL);
5633
5634 /*
5635 * Link the new grammar in the tree
5636 */
5637 ret->parent = ctxt->grammar;
5638 if (ctxt->grammar != NULL) {
5639 tmp = ctxt->grammar->children;
5640 if (tmp == NULL) {
5641 ctxt->grammar->children = ret;
5642 } else {
5643 while (tmp->next != NULL)
5644 tmp = tmp->next;
5645 tmp->next = ret;
5646 }
5647 }
5648
5649 old = ctxt->grammar;
5650 ctxt->grammar = ret;
5651 xmlRelaxNGParseGrammarContent(ctxt, nodes);
5652 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00005653 if (ctxt->grammar == NULL) {
5654 if (ctxt->error != NULL)
5655 ctxt->error(ctxt->userData,
5656 "Failed to parse <grammar> content\n");
5657 ctxt->nbErrors++;
5658 } else if (ctxt->grammar->start == NULL) {
5659 if (ctxt->error != NULL)
5660 ctxt->error(ctxt->userData,
5661 "Element <grammar> has no <start>\n");
5662 ctxt->nbErrors++;
5663 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005664
5665 /*
5666 * Apply 4.17 mergingd rules to defines and starts
5667 */
5668 xmlRelaxNGCombineStart(ctxt, ret);
5669 if (ret->defs != NULL) {
5670 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
5671 ctxt);
5672 }
5673
5674 /*
5675 * link together defines and refs in this grammar
5676 */
5677 if (ret->refs != NULL) {
5678 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
5679 ctxt);
5680 }
Daniel Veillard952379b2003-03-17 15:37:12 +00005681
Daniel Veillard6eadf632003-01-23 18:29:16 +00005682 ctxt->grammar = old;
5683 return(ret);
5684}
5685
5686/**
5687 * xmlRelaxNGParseDocument:
5688 * @ctxt: a Relax-NG parser context
5689 * @node: the root node of the RelaxNG schema
5690 *
5691 * parse a Relax-NG definition resource and build an internal
5692 * xmlRelaxNG struture which can be used to validate instances.
5693 *
5694 * Returns the internal XML RelaxNG structure built or
5695 * NULL in case of error
5696 */
5697static xmlRelaxNGPtr
5698xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
5699 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00005700 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00005701 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005702
5703 if ((ctxt == NULL) || (node == NULL))
5704 return (NULL);
5705
5706 schema = xmlRelaxNGNewRelaxNG(ctxt);
5707 if (schema == NULL)
5708 return(NULL);
5709
Daniel Veillard276be4a2003-01-24 01:03:34 +00005710 olddefine = ctxt->define;
5711 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005712 if (IS_RELAXNG(node, "grammar")) {
5713 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
5714 } else {
Daniel Veillardc482e262003-02-26 14:48:48 +00005715 xmlRelaxNGGrammarPtr tmp, ret;
5716
5717 schema->topgrammar = ret = xmlRelaxNGNewGrammar(ctxt);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005718 if (schema->topgrammar == NULL) {
5719 return(schema);
5720 }
Daniel Veillardc482e262003-02-26 14:48:48 +00005721 /*
5722 * Link the new grammar in the tree
5723 */
5724 ret->parent = ctxt->grammar;
5725 if (ctxt->grammar != NULL) {
5726 tmp = ctxt->grammar->children;
5727 if (tmp == NULL) {
5728 ctxt->grammar->children = ret;
5729 } else {
5730 while (tmp->next != NULL)
5731 tmp = tmp->next;
5732 tmp->next = ret;
5733 }
5734 }
Daniel Veillarde431a272003-01-29 23:02:33 +00005735 old = ctxt->grammar;
Daniel Veillardc482e262003-02-26 14:48:48 +00005736 ctxt->grammar = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005737 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00005738 if (old != NULL)
5739 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005740 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00005741 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00005742 if (schema->topgrammar->start != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00005743 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
Daniel Veillard77648bb2003-02-20 15:03:22 +00005744 if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00005745 xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
5746 while ((schema->topgrammar->start != NULL) &&
5747 (schema->topgrammar->start->type == XML_RELAXNG_NOOP) &&
5748 (schema->topgrammar->start->next != NULL))
5749 schema->topgrammar->start = schema->topgrammar->start->content;
5750 xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start,
5751 XML_RELAXNG_IN_START, XML_RELAXNG_NOOP);
Daniel Veillard77648bb2003-02-20 15:03:22 +00005752 }
Daniel Veillardd4310742003-02-18 21:12:46 +00005753 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005754
5755#ifdef DEBUG
5756 if (schema == NULL)
5757 xmlGenericError(xmlGenericErrorContext,
5758 "xmlRelaxNGParseDocument() failed\n");
5759#endif
5760
5761 return (schema);
5762}
5763
5764/************************************************************************
5765 * *
5766 * Reading RelaxNGs *
5767 * *
5768 ************************************************************************/
5769
5770/**
5771 * xmlRelaxNGNewParserCtxt:
5772 * @URL: the location of the schema
5773 *
5774 * Create an XML RelaxNGs parse context for that file/resource expected
5775 * to contain an XML RelaxNGs file.
5776 *
5777 * Returns the parser context or NULL in case of error
5778 */
5779xmlRelaxNGParserCtxtPtr
5780xmlRelaxNGNewParserCtxt(const char *URL) {
5781 xmlRelaxNGParserCtxtPtr ret;
5782
5783 if (URL == NULL)
5784 return(NULL);
5785
5786 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
5787 if (ret == NULL) {
5788 xmlGenericError(xmlGenericErrorContext,
5789 "Failed to allocate new schama parser context for %s\n", URL);
5790 return (NULL);
5791 }
5792 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
5793 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005794 ret->error = xmlGenericError;
5795 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005796 return (ret);
5797}
5798
5799/**
5800 * xmlRelaxNGNewMemParserCtxt:
5801 * @buffer: a pointer to a char array containing the schemas
5802 * @size: the size of the array
5803 *
5804 * Create an XML RelaxNGs parse context for that memory buffer expected
5805 * to contain an XML RelaxNGs file.
5806 *
5807 * Returns the parser context or NULL in case of error
5808 */
5809xmlRelaxNGParserCtxtPtr
5810xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
5811 xmlRelaxNGParserCtxtPtr ret;
5812
5813 if ((buffer == NULL) || (size <= 0))
5814 return(NULL);
5815
5816 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
5817 if (ret == NULL) {
5818 xmlGenericError(xmlGenericErrorContext,
5819 "Failed to allocate new schama parser context\n");
5820 return (NULL);
5821 }
5822 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
5823 ret->buffer = buffer;
5824 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005825 ret->error = xmlGenericError;
5826 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005827 return (ret);
5828}
5829
5830/**
5831 * xmlRelaxNGFreeParserCtxt:
5832 * @ctxt: the schema parser context
5833 *
5834 * Free the resources associated to the schema parser context
5835 */
5836void
5837xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
5838 if (ctxt == NULL)
5839 return;
5840 if (ctxt->URL != NULL)
5841 xmlFree(ctxt->URL);
5842 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005843 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005844 if (ctxt->interleaves != NULL)
5845 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005846 if (ctxt->documents != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +00005847 xmlRelaxNGFreeDocumentList(ctxt->documents);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005848 if (ctxt->includes != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +00005849 xmlRelaxNGFreeIncludeList(ctxt->includes);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005850 if (ctxt->docTab != NULL)
5851 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005852 if (ctxt->incTab != NULL)
5853 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00005854 if (ctxt->defTab != NULL) {
5855 int i;
5856
5857 for (i = 0;i < ctxt->defNr;i++)
5858 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
5859 xmlFree(ctxt->defTab);
5860 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005861 xmlFree(ctxt);
5862}
5863
Daniel Veillard6eadf632003-01-23 18:29:16 +00005864/**
Daniel Veillardd2298792003-02-14 16:54:11 +00005865 * xmlRelaxNGNormExtSpace:
5866 * @value: a value
5867 *
5868 * Removes the leading and ending spaces of the value
5869 * The string is modified "in situ"
5870 */
5871static void
5872xmlRelaxNGNormExtSpace(xmlChar *value) {
5873 xmlChar *start = value;
5874 xmlChar *cur = value;
5875 if (value == NULL)
5876 return;
5877
5878 while (IS_BLANK(*cur)) cur++;
5879 if (cur == start) {
5880 do {
5881 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5882 if (*cur == 0)
5883 return;
5884 start = cur;
5885 while (IS_BLANK(*cur)) cur++;
5886 if (*cur == 0) {
5887 *start = 0;
5888 return;
5889 }
5890 } while (1);
5891 } else {
5892 do {
5893 while ((*cur != 0) && (!IS_BLANK(*cur)))
5894 *start++ = *cur++;
5895 if (*cur == 0) {
5896 *start = 0;
5897 return;
5898 }
5899 /* don't try to normalize the inner spaces */
5900 while (IS_BLANK(*cur)) cur++;
5901 *start++ = *cur++;
5902 if (*cur == 0) {
5903 *start = 0;
5904 return;
5905 }
5906 } while (1);
5907 }
5908}
5909
5910/**
5911 * xmlRelaxNGCheckAttributes:
5912 * @ctxt: a Relax-NG parser context
5913 * @node: a Relax-NG node
5914 *
5915 * Check all the attributes on the given node
5916 */
5917static void
5918xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
5919 xmlAttrPtr cur, next;
5920
5921 cur = node->properties;
5922 while (cur != NULL) {
5923 next = cur->next;
5924 if ((cur->ns == NULL) ||
5925 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
5926 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
5927 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
5928 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
5929 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
5930 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00005931 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00005932 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
5933 if (ctxt->error != NULL)
5934 ctxt->error(ctxt->userData,
5935 "Attribute %s is not allowed on %s\n",
5936 cur->name, node->name);
5937 ctxt->nbErrors++;
5938 }
5939 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
5940 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
5941 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
5942 if (ctxt->error != NULL)
5943 ctxt->error(ctxt->userData,
5944 "Attribute %s is not allowed on %s\n",
5945 cur->name, node->name);
5946 ctxt->nbErrors++;
5947 }
5948 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
5949 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
5950 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
5951 if (ctxt->error != NULL)
5952 ctxt->error(ctxt->userData,
5953 "Attribute %s is not allowed on %s\n",
5954 cur->name, node->name);
5955 ctxt->nbErrors++;
5956 }
5957 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
5958 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
5959 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
5960 if (ctxt->error != NULL)
5961 ctxt->error(ctxt->userData,
5962 "Attribute %s is not allowed on %s\n",
5963 cur->name, node->name);
5964 ctxt->nbErrors++;
5965 }
5966 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
5967 xmlChar *val;
5968 xmlURIPtr uri;
5969
5970 val = xmlNodeListGetString(node->doc, cur->children, 1);
5971 if (val != NULL) {
5972 if (val[0] != 0) {
5973 uri = xmlParseURI((const char *) val);
5974 if (uri == NULL) {
5975 if (ctxt->error != NULL)
5976 ctxt->error(ctxt->userData,
5977 "Attribute %s contains invalid URI %s\n",
5978 cur->name, val);
5979 ctxt->nbErrors++;
5980 } else {
5981 if (uri->scheme == NULL) {
5982 if (ctxt->error != NULL)
5983 ctxt->error(ctxt->userData,
5984 "Attribute %s URI %s is not absolute\n",
5985 cur->name, val);
5986 ctxt->nbErrors++;
5987 }
5988 if (uri->fragment != NULL) {
5989 if (ctxt->error != NULL)
5990 ctxt->error(ctxt->userData,
5991 "Attribute %s URI %s has a fragment ID\n",
5992 cur->name, val);
5993 ctxt->nbErrors++;
5994 }
5995 xmlFreeURI(uri);
5996 }
5997 }
5998 xmlFree(val);
5999 }
6000 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
6001 if (ctxt->error != NULL)
6002 ctxt->error(ctxt->userData,
6003 "Unknown attribute %s on %s\n",
6004 cur->name, node->name);
6005 ctxt->nbErrors++;
6006 }
6007 }
6008 cur = next;
6009 }
6010}
6011
6012/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00006013 * xmlRelaxNGCleanupTree:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006014 * @ctxt: a Relax-NG parser context
Daniel Veillardc5312d72003-02-21 17:14:10 +00006015 * @root: an xmlNodePtr subtree
Daniel Veillard6eadf632003-01-23 18:29:16 +00006016 *
Daniel Veillardfd573f12003-03-16 17:52:32 +00006017 * Cleanup the subtree from unwanted nodes for parsing, resolve
6018 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00006019 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00006020static void
Daniel Veillardfd573f12003-03-16 17:52:32 +00006021xmlRelaxNGCleanupTree(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr root) {
Daniel Veillardc5312d72003-02-21 17:14:10 +00006022 xmlNodePtr cur, delete;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006023
Daniel Veillard6eadf632003-01-23 18:29:16 +00006024 delete = NULL;
6025 cur = root;
6026 while (cur != NULL) {
6027 if (delete != NULL) {
6028 xmlUnlinkNode(delete);
6029 xmlFreeNode(delete);
6030 delete = NULL;
6031 }
6032 if (cur->type == XML_ELEMENT_NODE) {
6033 /*
6034 * Simplification 4.1. Annotations
6035 */
6036 if ((cur->ns == NULL) ||
6037 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00006038 if ((cur->parent != NULL) &&
6039 (cur->parent->type == XML_ELEMENT_NODE) &&
6040 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
6041 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
6042 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
6043 if (ctxt->error != NULL)
6044 ctxt->error(ctxt->userData,
6045 "element %s doesn't allow foreign elements\n",
6046 cur->parent->name);
6047 ctxt->nbErrors++;
6048 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006049 delete = cur;
6050 goto skip_children;
Daniel Veillardfd573f12003-03-16 17:52:32 +00006051 } else {
6052 xmlRelaxNGCleanupAttributes(ctxt, cur);
6053 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
6054 xmlChar *href, *ns, *base, *URL;
6055 xmlRelaxNGDocumentPtr docu;
6056 xmlNodePtr tmp;
6057
6058 ns = xmlGetProp(cur, BAD_CAST "ns");
6059 if (ns == NULL) {
6060 tmp = cur->parent;
6061 while ((tmp != NULL) &&
6062 (tmp->type == XML_ELEMENT_NODE)) {
6063 ns = xmlGetProp(tmp, BAD_CAST "ns");
6064 if (ns != NULL)
6065 break;
6066 tmp = tmp->parent;
6067 }
6068 }
6069 href = xmlGetProp(cur, BAD_CAST "href");
6070 if (href == NULL) {
6071 if (ctxt->error != NULL)
6072 ctxt->error(ctxt->userData,
6073 "xmlRelaxNGParse: externalRef has no href attribute\n");
6074 ctxt->nbErrors++;
6075 delete = cur;
6076 goto skip_children;
6077 }
6078 base = xmlNodeGetBase(cur->doc, cur);
6079 URL = xmlBuildURI(href, base);
6080 if (URL == NULL) {
6081 if (ctxt->error != NULL)
6082 ctxt->error(ctxt->userData,
6083 "Failed to compute URL for externalRef %s\n", href);
6084 ctxt->nbErrors++;
6085 if (href != NULL)
6086 xmlFree(href);
6087 if (base != NULL)
6088 xmlFree(base);
6089 delete = cur;
6090 goto skip_children;
6091 }
6092 if (href != NULL)
6093 xmlFree(href);
6094 if (base != NULL)
6095 xmlFree(base);
6096 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
6097 if (docu == NULL) {
6098 if (ctxt->error != NULL)
6099 ctxt->error(ctxt->userData,
6100 "Failed to load externalRef %s\n", URL);
6101 ctxt->nbErrors++;
6102 xmlFree(URL);
6103 delete = cur;
6104 goto skip_children;
6105 }
6106 if (ns != NULL)
6107 xmlFree(ns);
6108 xmlFree(URL);
6109 cur->_private = docu;
6110 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
6111 xmlChar *href, *ns, *base, *URL;
6112 xmlRelaxNGIncludePtr incl;
6113 xmlNodePtr tmp;
6114
6115 href = xmlGetProp(cur, BAD_CAST "href");
6116 if (href == NULL) {
6117 if (ctxt->error != NULL)
6118 ctxt->error(ctxt->userData,
6119 "xmlRelaxNGParse: include has no href attribute\n");
6120 ctxt->nbErrors++;
6121 delete = cur;
6122 goto skip_children;
6123 }
6124 base = xmlNodeGetBase(cur->doc, cur);
6125 URL = xmlBuildURI(href, base);
6126 if (URL == NULL) {
6127 if (ctxt->error != NULL)
6128 ctxt->error(ctxt->userData,
6129 "Failed to compute URL for include %s\n", href);
6130 ctxt->nbErrors++;
6131 if (href != NULL)
6132 xmlFree(href);
6133 if (base != NULL)
6134 xmlFree(base);
6135 delete = cur;
6136 goto skip_children;
6137 }
6138 if (href != NULL)
6139 xmlFree(href);
6140 if (base != NULL)
6141 xmlFree(base);
6142 ns = xmlGetProp(cur, BAD_CAST "ns");
6143 if (ns == NULL) {
6144 tmp = cur->parent;
6145 while ((tmp != NULL) &&
6146 (tmp->type == XML_ELEMENT_NODE)) {
6147 ns = xmlGetProp(tmp, BAD_CAST "ns");
6148 if (ns != NULL)
6149 break;
6150 tmp = tmp->parent;
6151 }
6152 }
6153 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
6154 if (ns != NULL)
6155 xmlFree(ns);
6156 if (incl == NULL) {
6157 if (ctxt->error != NULL)
6158 ctxt->error(ctxt->userData,
6159 "Failed to load include %s\n", URL);
6160 ctxt->nbErrors++;
6161 xmlFree(URL);
6162 delete = cur;
6163 goto skip_children;
6164 }
6165 xmlFree(URL);
6166 cur->_private = incl;
6167 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
6168 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
6169 xmlChar *name, *ns;
6170 xmlNodePtr text = NULL;
6171
6172 /*
6173 * Simplification 4.8. name attribute of element
6174 * and attribute elements
6175 */
6176 name = xmlGetProp(cur, BAD_CAST "name");
6177 if (name != NULL) {
6178 if (cur->children == NULL) {
6179 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
6180 name);
6181 } else {
6182 xmlNodePtr node;
6183 node = xmlNewNode(cur->ns, BAD_CAST "name");
6184 if (node != NULL) {
6185 xmlAddPrevSibling(cur->children, node);
6186 text = xmlNewText(name);
6187 xmlAddChild(node, text);
6188 text = node;
6189 }
6190 }
6191 if (text == NULL) {
6192 if (ctxt->error != NULL)
6193 ctxt->error(ctxt->userData,
6194 "Failed to create a name %s element\n", name);
6195 ctxt->nbErrors++;
6196 }
6197 xmlUnsetProp(cur, BAD_CAST "name");
6198 xmlFree(name);
6199 ns = xmlGetProp(cur, BAD_CAST "ns");
6200 if (ns != NULL) {
6201 if (text != NULL) {
6202 xmlSetProp(text, BAD_CAST "ns", ns);
6203 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
6204 }
6205 xmlFree(ns);
6206 } else if (xmlStrEqual(cur->name,
6207 BAD_CAST "attribute")) {
6208 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
6209 }
6210 }
6211 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
6212 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
6213 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
6214 /*
6215 * Simplification 4.8. name attribute of element
6216 * and attribute elements
6217 */
6218 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
6219 xmlNodePtr node;
6220 xmlChar *ns = NULL;
6221
6222 node = cur->parent;
6223 while ((node != NULL) &&
6224 (node->type == XML_ELEMENT_NODE)) {
6225 ns = xmlGetProp(node, BAD_CAST "ns");
6226 if (ns != NULL) {
6227 break;
6228 }
6229 node = node->parent;
6230 }
6231 if (ns == NULL) {
6232 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
6233 } else {
6234 xmlSetProp(cur, BAD_CAST "ns", ns);
6235 xmlFree(ns);
6236 }
6237 }
6238 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
6239 xmlChar *name, *local, *prefix;
6240
6241 /*
6242 * Simplification: 4.10. QNames
6243 */
6244 name = xmlNodeGetContent(cur);
6245 if (name != NULL) {
6246 local = xmlSplitQName2(name, &prefix);
6247 if (local != NULL) {
6248 xmlNsPtr ns;
6249
6250 ns = xmlSearchNs(cur->doc, cur, prefix);
6251 if (ns == NULL) {
6252 if (ctxt->error != NULL)
6253 ctxt->error(ctxt->userData,
6254 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
6255 ctxt->nbErrors++;
6256 } else {
6257 xmlSetProp(cur, BAD_CAST "ns", ns->href);
6258 xmlNodeSetContent(cur, local);
6259 }
6260 xmlFree(local);
6261 xmlFree(prefix);
6262 }
6263 xmlFree(name);
6264 }
6265 }
6266 /*
6267 * 4.16
6268 */
6269 if (xmlStrEqual(cur->name, BAD_CAST "nsName")) {
6270 if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
6271 if (ctxt->error != NULL)
6272 ctxt->error(ctxt->userData,
6273 "Found nsName/except//nsName forbidden construct\n");
6274 ctxt->nbErrors++;
6275 }
6276 }
6277 } else if ((xmlStrEqual(cur->name, BAD_CAST "except")) &&
6278 (cur != root)) {
6279 int oldflags = ctxt->flags;
6280
6281 /*
6282 * 4.16
6283 */
6284 if ((cur->parent != NULL) &&
6285 (xmlStrEqual(cur->parent->name, BAD_CAST "anyName"))) {
6286 ctxt->flags |= XML_RELAXNG_IN_ANYEXCEPT;
6287 xmlRelaxNGCleanupTree(ctxt, cur);
6288 ctxt->flags = oldflags;
6289 goto skip_children;
6290 } else if ((cur->parent != NULL) &&
6291 (xmlStrEqual(cur->parent->name, BAD_CAST "nsName"))) {
6292 ctxt->flags |= XML_RELAXNG_IN_NSEXCEPT;
6293 xmlRelaxNGCleanupTree(ctxt, cur);
6294 ctxt->flags = oldflags;
6295 goto skip_children;
6296 }
6297 } else if (xmlStrEqual(cur->name, BAD_CAST "anyName")) {
6298 /*
6299 * 4.16
6300 */
6301 if (ctxt->flags & XML_RELAXNG_IN_ANYEXCEPT) {
6302 if (ctxt->error != NULL)
6303 ctxt->error(ctxt->userData,
6304 "Found anyName/except//anyName forbidden construct\n");
6305 ctxt->nbErrors++;
6306 } else if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
6307 if (ctxt->error != NULL)
6308 ctxt->error(ctxt->userData,
6309 "Found nsName/except//anyName forbidden construct\n");
6310 ctxt->nbErrors++;
6311 }
6312 }
6313 /*
6314 * Thisd is not an else since "include" is transformed
6315 * into a div
6316 */
6317 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
6318 xmlChar *ns;
6319 xmlNodePtr child, ins, tmp;
6320
6321 /*
6322 * implements rule 4.11
6323 */
6324
6325 ns = xmlGetProp(cur, BAD_CAST "ns");
6326
6327 child = cur->children;
6328 ins = cur;
6329 while (child != NULL) {
6330 if (ns != NULL) {
6331 if (!xmlHasProp(child, BAD_CAST "ns")) {
6332 xmlSetProp(child, BAD_CAST "ns", ns);
6333 }
6334 }
6335 tmp = child->next;
6336 xmlUnlinkNode(child);
6337 ins = xmlAddNextSibling(ins, child);
6338 child = tmp;
6339 }
6340 if (ns != NULL)
6341 xmlFree(ns);
6342 delete = cur;
6343 goto skip_children;
6344 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006345 }
6346 }
6347 /*
6348 * Simplification 4.2 whitespaces
6349 */
Daniel Veillard39eb88b2003-03-11 11:21:28 +00006350 else if ((cur->type == XML_TEXT_NODE) ||
6351 (cur->type == XML_CDATA_SECTION_NODE)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006352 if (IS_BLANK_NODE(cur)) {
6353 if (cur->parent->type == XML_ELEMENT_NODE) {
6354 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
6355 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
6356 delete = cur;
6357 } else {
6358 delete = cur;
6359 goto skip_children;
6360 }
6361 }
Daniel Veillard39eb88b2003-03-11 11:21:28 +00006362 } else {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006363 delete = cur;
6364 goto skip_children;
6365 }
6366
6367 /*
6368 * Skip to next node
6369 */
6370 if (cur->children != NULL) {
6371 if ((cur->children->type != XML_ENTITY_DECL) &&
6372 (cur->children->type != XML_ENTITY_REF_NODE) &&
6373 (cur->children->type != XML_ENTITY_NODE)) {
6374 cur = cur->children;
6375 continue;
6376 }
6377 }
6378skip_children:
6379 if (cur->next != NULL) {
6380 cur = cur->next;
6381 continue;
6382 }
6383
6384 do {
6385 cur = cur->parent;
6386 if (cur == NULL)
6387 break;
6388 if (cur == root) {
6389 cur = NULL;
6390 break;
6391 }
6392 if (cur->next != NULL) {
6393 cur = cur->next;
6394 break;
6395 }
6396 } while (cur != NULL);
6397 }
6398 if (delete != NULL) {
6399 xmlUnlinkNode(delete);
6400 xmlFreeNode(delete);
6401 delete = NULL;
6402 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00006403}
Daniel Veillard6eadf632003-01-23 18:29:16 +00006404
Daniel Veillardc5312d72003-02-21 17:14:10 +00006405/**
6406 * xmlRelaxNGCleanupDoc:
6407 * @ctxt: a Relax-NG parser context
6408 * @doc: an xmldocPtr document pointer
6409 *
6410 * Cleanup the document from unwanted nodes for parsing, resolve
6411 * Include and externalRef lookups.
6412 *
6413 * Returns the cleaned up document or NULL in case of error
6414 */
6415static xmlDocPtr
6416xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
6417 xmlNodePtr root;
6418
6419 /*
6420 * Extract the root
6421 */
6422 root = xmlDocGetRootElement(doc);
6423 if (root == NULL) {
6424 if (ctxt->error != NULL)
6425 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
6426 ctxt->URL);
6427 ctxt->nbErrors++;
6428 return (NULL);
6429 }
6430 xmlRelaxNGCleanupTree(ctxt, root);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006431 return(doc);
6432}
6433
6434/**
6435 * xmlRelaxNGParse:
6436 * @ctxt: a Relax-NG parser context
6437 *
6438 * parse a schema definition resource and build an internal
6439 * XML Shema struture which can be used to validate instances.
6440 * *WARNING* this interface is highly subject to change
6441 *
6442 * Returns the internal XML RelaxNG structure built from the resource or
6443 * NULL in case of error
6444 */
6445xmlRelaxNGPtr
6446xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
6447{
6448 xmlRelaxNGPtr ret = NULL;
6449 xmlDocPtr doc;
6450 xmlNodePtr root;
6451
6452 xmlRelaxNGInitTypes();
6453
6454 if (ctxt == NULL)
6455 return (NULL);
6456
6457 /*
6458 * First step is to parse the input document into an DOM/Infoset
6459 */
6460 if (ctxt->URL != NULL) {
6461 doc = xmlParseFile((const char *) ctxt->URL);
6462 if (doc == NULL) {
6463 if (ctxt->error != NULL)
6464 ctxt->error(ctxt->userData,
6465 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
6466 ctxt->nbErrors++;
6467 return (NULL);
6468 }
6469 } else if (ctxt->buffer != NULL) {
6470 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
6471 if (doc == NULL) {
6472 if (ctxt->error != NULL)
6473 ctxt->error(ctxt->userData,
6474 "xmlRelaxNGParse: could not parse schemas\n");
6475 ctxt->nbErrors++;
6476 return (NULL);
6477 }
6478 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
6479 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
6480 } else {
6481 if (ctxt->error != NULL)
6482 ctxt->error(ctxt->userData,
6483 "xmlRelaxNGParse: nothing to parse\n");
6484 ctxt->nbErrors++;
6485 return (NULL);
6486 }
6487 ctxt->document = doc;
6488
6489 /*
6490 * Some preprocessing of the document content
6491 */
6492 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
6493 if (doc == NULL) {
6494 xmlFreeDoc(ctxt->document);
6495 ctxt->document = NULL;
6496 return(NULL);
6497 }
6498
Daniel Veillard6eadf632003-01-23 18:29:16 +00006499 /*
6500 * Then do the parsing for good
6501 */
6502 root = xmlDocGetRootElement(doc);
6503 if (root == NULL) {
6504 if (ctxt->error != NULL)
6505 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
6506 ctxt->URL);
6507 ctxt->nbErrors++;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006508 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006509 return (NULL);
6510 }
6511 ret = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006512 if (ret == NULL) {
6513 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006514 return(NULL);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006515 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006516
6517 /*
Daniel Veillardfd573f12003-03-16 17:52:32 +00006518 * Check the ref/defines links
6519 */
6520 /*
6521 * try to preprocess interleaves
6522 */
6523 if (ctxt->interleaves != NULL) {
6524 xmlHashScan(ctxt->interleaves,
6525 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
6526 }
6527
6528 /*
Daniel Veillard6eadf632003-01-23 18:29:16 +00006529 * if there was a parsing error return NULL
6530 */
6531 if (ctxt->nbErrors > 0) {
6532 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006533 ctxt->document = NULL;
6534 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006535 return(NULL);
6536 }
6537
6538 /*
6539 * Transfer the pointer for cleanup at the schema level.
6540 */
6541 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006542 ctxt->document = NULL;
6543 ret->documents = ctxt->documents;
6544 ctxt->documents = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006545
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006546 ret->includes = ctxt->includes;
6547 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00006548 ret->defNr = ctxt->defNr;
6549 ret->defTab = ctxt->defTab;
6550 ctxt->defTab = NULL;
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006551 if (ctxt->idref == 1)
6552 ret->idref = 1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006553
6554 return (ret);
6555}
6556
6557/**
6558 * xmlRelaxNGSetParserErrors:
6559 * @ctxt: a Relax-NG validation context
6560 * @err: the error callback
6561 * @warn: the warning callback
6562 * @ctx: contextual data for the callbacks
6563 *
6564 * Set the callback functions used to handle errors for a validation context
6565 */
6566void
6567xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
6568 xmlRelaxNGValidityErrorFunc err,
6569 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
6570 if (ctxt == NULL)
6571 return;
6572 ctxt->error = err;
6573 ctxt->warning = warn;
6574 ctxt->userData = ctx;
6575}
6576/************************************************************************
6577 * *
6578 * Dump back a compiled form *
6579 * *
6580 ************************************************************************/
6581static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
6582
6583/**
6584 * xmlRelaxNGDumpDefines:
6585 * @output: the file output
6586 * @defines: a list of define structures
6587 *
6588 * Dump a RelaxNG structure back
6589 */
6590static void
6591xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
6592 while (defines != NULL) {
6593 xmlRelaxNGDumpDefine(output, defines);
6594 defines = defines->next;
6595 }
6596}
6597
6598/**
6599 * xmlRelaxNGDumpDefine:
6600 * @output: the file output
6601 * @define: a define structure
6602 *
6603 * Dump a RelaxNG structure back
6604 */
6605static void
6606xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
6607 if (define == NULL)
6608 return;
6609 switch(define->type) {
6610 case XML_RELAXNG_EMPTY:
6611 fprintf(output, "<empty/>\n");
6612 break;
6613 case XML_RELAXNG_NOT_ALLOWED:
6614 fprintf(output, "<notAllowed/>\n");
6615 break;
6616 case XML_RELAXNG_TEXT:
6617 fprintf(output, "<text/>\n");
6618 break;
6619 case XML_RELAXNG_ELEMENT:
6620 fprintf(output, "<element>\n");
6621 if (define->name != NULL) {
6622 fprintf(output, "<name");
6623 if (define->ns != NULL)
6624 fprintf(output, " ns=\"%s\"", define->ns);
6625 fprintf(output, ">%s</name>\n", define->name);
6626 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00006627 xmlRelaxNGDumpDefines(output, define->attrs);
6628 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006629 fprintf(output, "</element>\n");
6630 break;
6631 case XML_RELAXNG_LIST:
6632 fprintf(output, "<list>\n");
6633 xmlRelaxNGDumpDefines(output, define->content);
6634 fprintf(output, "</list>\n");
6635 break;
6636 case XML_RELAXNG_ONEORMORE:
6637 fprintf(output, "<oneOrMore>\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006638 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006639 fprintf(output, "</oneOrMore>\n");
6640 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00006641 case XML_RELAXNG_ZEROORMORE:
6642 fprintf(output, "<zeroOrMore>\n");
6643 xmlRelaxNGDumpDefines(output, define->content);
6644 fprintf(output, "</zeroOrMore>\n");
6645 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006646 case XML_RELAXNG_CHOICE:
6647 fprintf(output, "<choice>\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006648 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006649 fprintf(output, "</choice>\n");
6650 break;
6651 case XML_RELAXNG_GROUP:
6652 fprintf(output, "<group>\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006653 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006654 fprintf(output, "</group>\n");
6655 break;
6656 case XML_RELAXNG_INTERLEAVE:
6657 fprintf(output, "<interleave>\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006658 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006659 fprintf(output, "</interleave>\n");
6660 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00006661 case XML_RELAXNG_OPTIONAL:
6662 fprintf(output, "<optional>\n");
6663 xmlRelaxNGDumpDefines(output, define->content);
6664 fprintf(output, "</optional>\n");
6665 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006666 case XML_RELAXNG_ATTRIBUTE:
6667 fprintf(output, "<attribute>\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006668 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006669 fprintf(output, "</attribute>\n");
6670 break;
6671 case XML_RELAXNG_DEF:
6672 fprintf(output, "<define");
6673 if (define->name != NULL)
6674 fprintf(output, " name=\"%s\"", define->name);
6675 fprintf(output, ">\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006676 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006677 fprintf(output, "</define>\n");
6678 break;
6679 case XML_RELAXNG_REF:
6680 fprintf(output, "<ref");
6681 if (define->name != NULL)
6682 fprintf(output, " name=\"%s\"", define->name);
6683 fprintf(output, ">\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006684 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006685 fprintf(output, "</ref>\n");
6686 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00006687 case XML_RELAXNG_PARENTREF:
6688 fprintf(output, "<parentRef");
6689 if (define->name != NULL)
6690 fprintf(output, " name=\"%s\"", define->name);
6691 fprintf(output, ">\n");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006692 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard419a7682003-02-03 23:22:49 +00006693 fprintf(output, "</parentRef>\n");
6694 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006695 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00006696 fprintf(output, "<externalRef>");
Daniel Veillardfd573f12003-03-16 17:52:32 +00006697 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillarde431a272003-01-29 23:02:33 +00006698 fprintf(output, "</externalRef>\n");
6699 break;
6700 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006701 case XML_RELAXNG_VALUE:
Daniel Veillardfd573f12003-03-16 17:52:32 +00006702 TODO
Daniel Veillard6eadf632003-01-23 18:29:16 +00006703 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006704 case XML_RELAXNG_START:
Daniel Veillardfd573f12003-03-16 17:52:32 +00006705 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00006706 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00006707 TODO
6708 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00006709 case XML_RELAXNG_NOOP:
Daniel Veillardfd573f12003-03-16 17:52:32 +00006710 xmlRelaxNGDumpDefines(output, define->content);
Daniel Veillard77648bb2003-02-20 15:03:22 +00006711 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006712 }
6713}
6714
6715/**
6716 * xmlRelaxNGDumpGrammar:
6717 * @output: the file output
6718 * @grammar: a grammar structure
6719 * @top: is this a top grammar
6720 *
6721 * Dump a RelaxNG structure back
6722 */
6723static void
6724xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
6725{
6726 if (grammar == NULL)
6727 return;
6728
6729 fprintf(output, "<grammar");
6730 if (top)
6731 fprintf(output,
6732 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
6733 switch(grammar->combine) {
6734 case XML_RELAXNG_COMBINE_UNDEFINED:
6735 break;
6736 case XML_RELAXNG_COMBINE_CHOICE:
6737 fprintf(output, " combine=\"choice\"");
6738 break;
6739 case XML_RELAXNG_COMBINE_INTERLEAVE:
6740 fprintf(output, " combine=\"interleave\"");
6741 break;
6742 default:
6743 fprintf(output, " <!-- invalid combine value -->");
6744 }
6745 fprintf(output, ">\n");
6746 if (grammar->start == NULL) {
6747 fprintf(output, " <!-- grammar had no start -->");
6748 } else {
6749 fprintf(output, "<start>\n");
6750 xmlRelaxNGDumpDefine(output, grammar->start);
6751 fprintf(output, "</start>\n");
6752 }
6753 /* TODO ? Dump the defines ? */
6754 fprintf(output, "</grammar>\n");
6755}
6756
6757/**
6758 * xmlRelaxNGDump:
6759 * @output: the file output
6760 * @schema: a schema structure
6761 *
6762 * Dump a RelaxNG structure back
6763 */
6764void
6765xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
6766{
6767 if (schema == NULL) {
6768 fprintf(output, "RelaxNG empty or failed to compile\n");
6769 return;
6770 }
6771 fprintf(output, "RelaxNG: ");
6772 if (schema->doc == NULL) {
6773 fprintf(output, "no document\n");
6774 } else if (schema->doc->URL != NULL) {
6775 fprintf(output, "%s\n", schema->doc->URL);
6776 } else {
6777 fprintf(output, "\n");
6778 }
6779 if (schema->topgrammar == NULL) {
6780 fprintf(output, "RelaxNG has no top grammar\n");
6781 return;
6782 }
6783 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
6784}
6785
Daniel Veillardfebcca42003-02-16 15:44:18 +00006786/**
6787 * xmlRelaxNGDumpTree:
6788 * @output: the file output
6789 * @schema: a schema structure
6790 *
6791 * Dump the transformed RelaxNG tree.
6792 */
6793void
6794xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
6795{
6796 if (schema == NULL) {
6797 fprintf(output, "RelaxNG empty or failed to compile\n");
6798 return;
6799 }
6800 if (schema->doc == NULL) {
6801 fprintf(output, "no document\n");
6802 } else {
6803 xmlDocDump(output, schema->doc);
6804 }
6805}
6806
Daniel Veillard6eadf632003-01-23 18:29:16 +00006807/************************************************************************
6808 * *
6809 * Validation implementation *
6810 * *
6811 ************************************************************************/
Daniel Veillardfd573f12003-03-16 17:52:32 +00006812static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
6813 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006814static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
6815 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006816
6817/**
6818 * xmlRelaxNGSkipIgnored:
6819 * @ctxt: a schema validation context
6820 * @node: the top node.
6821 *
6822 * Skip ignorable nodes in that context
6823 *
6824 * Returns the new sibling or NULL in case of error.
6825 */
6826static xmlNodePtr
6827xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
6828 xmlNodePtr node) {
6829 /*
6830 * TODO complete and handle entities
6831 */
6832 while ((node != NULL) &&
6833 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006834 (node->type == XML_PI_NODE) ||
Daniel Veillard39eb88b2003-03-11 11:21:28 +00006835 (((node->type == XML_TEXT_NODE) ||
6836 (node->type == XML_CDATA_SECTION_NODE)) &&
Daniel Veillard249d7bb2003-03-19 21:02:29 +00006837 ((ctxt->flags & FLAGS_MIXED_CONTENT) ||
6838 (IS_BLANK_NODE(node)))))) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006839 node = node->next;
6840 }
6841 return(node);
6842}
6843
6844/**
Daniel Veillardedc91922003-01-26 00:52:04 +00006845 * xmlRelaxNGNormalize:
6846 * @ctxt: a schema validation context
6847 * @str: the string to normalize
6848 *
6849 * Implements the normalizeWhiteSpace( s ) function from
6850 * section 6.2.9 of the spec
6851 *
6852 * Returns the new string or NULL in case of error.
6853 */
6854static xmlChar *
6855xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
6856 xmlChar *ret, *p;
6857 const xmlChar *tmp;
6858 int len;
6859
6860 if (str == NULL)
6861 return(NULL);
6862 tmp = str;
6863 while (*tmp != 0) tmp++;
6864 len = tmp - str;
6865
6866 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
6867 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00006868 if (ctxt != NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006869 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
Daniel Veillardea3f3982003-01-26 19:45:18 +00006870 } else {
6871 xmlGenericError(xmlGenericErrorContext,
6872 "xmlRelaxNGNormalize: out of memory\n");
6873 }
Daniel Veillardedc91922003-01-26 00:52:04 +00006874 return(NULL);
6875 }
6876 p = ret;
6877 while (IS_BLANK(*str)) str++;
6878 while (*str != 0) {
6879 if (IS_BLANK(*str)) {
6880 while (IS_BLANK(*str)) str++;
6881 if (*str == 0)
6882 break;
6883 *p++ = ' ';
6884 } else
6885 *p++ = *str++;
6886 }
6887 *p = 0;
6888 return(ret);
6889}
6890
6891/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006892 * xmlRelaxNGValidateDatatype:
6893 * @ctxt: a Relax-NG validation context
6894 * @value: the string value
6895 * @type: the datatype definition
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006896 * @node: the node
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006897 *
6898 * Validate the given value against the dataype
6899 *
6900 * Returns 0 if the validation succeeded or an error code.
6901 */
6902static int
6903xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006904 xmlRelaxNGDefinePtr define, xmlNodePtr node) {
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006905 int ret, tmp;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006906 xmlRelaxNGTypeLibraryPtr lib;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006907 void *result = NULL;
6908 xmlRelaxNGDefinePtr cur;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006909
6910 if ((define == NULL) || (define->data == NULL)) {
6911 return(-1);
6912 }
6913 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006914 if (lib->check != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00006915 if ((define->attrs != NULL) &&
6916 (define->attrs->type == XML_RELAXNG_PARAM)) {
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006917 ret = lib->check(lib->data, define->name, value, &result, node);
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006918 } else {
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006919 ret = lib->check(lib->data, define->name, value, NULL, node);
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006920 }
6921 } else
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006922 ret = -1;
6923 if (ret < 0) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00006924 VALID_ERR2(XML_RELAXNG_ERR_TYPE, define->name);
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006925 if ((result != NULL) && (lib != NULL) && (lib->freef != NULL))
6926 lib->freef(lib->data, result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006927 return(-1);
6928 } else if (ret == 1) {
6929 ret = 0;
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006930 } else if (ret == 2) {
6931 VALID_ERR2P(XML_RELAXNG_ERR_DUPID, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006932 } else {
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006933 VALID_ERR3P(XML_RELAXNG_ERR_TYPEVAL, define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006934 ret = -1;
6935 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00006936 cur = define->attrs;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006937 while ((ret == 0) && (cur != NULL) && (cur->type == XML_RELAXNG_PARAM)) {
6938 if (lib->facet != NULL) {
6939 tmp = lib->facet(lib->data, define->name, cur->name,
6940 cur->value, value, result);
6941 if (tmp != 0)
6942 ret = -1;
6943 }
6944 cur = cur->next;
6945 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006946 if ((ret == 0) && (define->content != NULL)) {
6947 const xmlChar *oldvalue, *oldendvalue;
6948
6949 oldvalue = ctxt->state->value;
6950 oldendvalue = ctxt->state->endvalue;
6951 ctxt->state->value = (xmlChar *) value;
6952 ctxt->state->endvalue = NULL;
6953 ret = xmlRelaxNGValidateValue(ctxt, define->content);
6954 ctxt->state->value = (xmlChar *) oldvalue;
6955 ctxt->state->endvalue = (xmlChar *) oldendvalue;
6956 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00006957 if ((result != NULL) && (lib != NULL) && (lib->freef != NULL))
6958 lib->freef(lib->data, result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006959 return(ret);
6960}
6961
6962/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006963 * xmlRelaxNGNextValue:
6964 * @ctxt: a Relax-NG validation context
6965 *
6966 * Skip to the next value when validating within a list
6967 *
6968 * Returns 0 if the operation succeeded or an error code.
6969 */
6970static int
6971xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
6972 xmlChar *cur;
6973
6974 cur = ctxt->state->value;
6975 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
6976 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006977 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006978 return(0);
6979 }
6980 while (*cur != 0) cur++;
6981 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
6982 if (cur == ctxt->state->endvalue)
6983 ctxt->state->value = NULL;
6984 else
6985 ctxt->state->value = cur;
6986 return(0);
6987}
6988
6989/**
6990 * xmlRelaxNGValidateValueList:
6991 * @ctxt: a Relax-NG validation context
6992 * @defines: the list of definitions to verify
6993 *
6994 * Validate the given set of definitions for the current value
6995 *
6996 * Returns 0 if the validation succeeded or an error code.
6997 */
6998static int
6999xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
7000 xmlRelaxNGDefinePtr defines) {
7001 int ret = 0;
7002
7003 while (defines != NULL) {
7004 ret = xmlRelaxNGValidateValue(ctxt, defines);
7005 if (ret != 0)
7006 break;
7007 defines = defines->next;
7008 }
7009 return(ret);
7010}
7011
7012/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00007013 * xmlRelaxNGValidateValue:
7014 * @ctxt: a Relax-NG validation context
7015 * @define: the definition to verify
7016 *
7017 * Validate the given definition for the current value
7018 *
7019 * Returns 0 if the validation succeeded or an error code.
7020 */
7021static int
7022xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
7023 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00007024 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007025 xmlChar *value;
7026
7027 value = ctxt->state->value;
7028 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007029 case XML_RELAXNG_EMPTY: {
7030 if ((value != NULL) && (value[0] != 0)) {
7031 int idx = 0;
7032
7033 while (IS_BLANK(value[idx]))
7034 idx++;
7035 if (value[idx] != 0)
7036 ret = -1;
7037 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007038 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00007039 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007040 case XML_RELAXNG_TEXT:
7041 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00007042 case XML_RELAXNG_VALUE: {
7043 if (!xmlStrEqual(value, define->value)) {
7044 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00007045 xmlRelaxNGTypeLibraryPtr lib;
7046
7047 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
7048 if ((lib != NULL) && (lib->comp != NULL))
7049 ret = lib->comp(lib->data, define->name, value,
7050 define->value);
7051 else
7052 ret = -1;
7053 if (ret < 0) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007054 VALID_ERR2(XML_RELAXNG_ERR_TYPECMP, define->name);
Daniel Veillardea3f3982003-01-26 19:45:18 +00007055 return(-1);
7056 } else if (ret == 1) {
7057 ret = 0;
7058 } else {
7059 ret = -1;
7060 }
Daniel Veillardedc91922003-01-26 00:52:04 +00007061 } else {
7062 xmlChar *nval, *nvalue;
7063
7064 /*
7065 * TODO: trivial optimizations are possible by
7066 * computing at compile-time
7067 */
7068 nval = xmlRelaxNGNormalize(ctxt, define->value);
7069 nvalue = xmlRelaxNGNormalize(ctxt, value);
7070
Daniel Veillardea3f3982003-01-26 19:45:18 +00007071 if ((nval == NULL) || (nvalue == NULL) ||
7072 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00007073 ret = -1;
7074 if (nval != NULL)
7075 xmlFree(nval);
7076 if (nvalue != NULL)
7077 xmlFree(nvalue);
7078 }
7079 }
Daniel Veillard416589a2003-02-17 17:25:42 +00007080 if (ret == 0)
7081 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00007082 break;
7083 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007084 case XML_RELAXNG_DATATYPE: {
Daniel Veillardc3da18a2003-03-18 00:31:04 +00007085 ret = xmlRelaxNGValidateDatatype(ctxt, value, define,
7086 ctxt->state->seq);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007087 if (ret == 0)
7088 xmlRelaxNGNextValue(ctxt);
7089
7090 break;
7091 }
7092 case XML_RELAXNG_CHOICE: {
Daniel Veillardfd573f12003-03-16 17:52:32 +00007093 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007094 xmlChar *oldvalue;
7095
7096 oldflags = ctxt->flags;
7097 ctxt->flags |= FLAGS_IGNORABLE;
7098
7099 oldvalue = ctxt->state->value;
Daniel Veillardfd573f12003-03-16 17:52:32 +00007100 while (list != NULL) {
7101 ret = xmlRelaxNGValidateValue(ctxt, list);
7102 if (ret == 0) {
7103 break;
7104 }
7105 ctxt->state->value = oldvalue;
7106 list = list->next;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007107 }
7108 ctxt->flags = oldflags;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007109 if (ret != 0) {
7110 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7111 xmlRelaxNGDumpValidError(ctxt);
7112 } else {
Daniel Veillard28c52ab2003-03-18 11:39:17 +00007113 if (ctxt->errNr > 0) xmlRelaxNGPopErrors(ctxt, 0);
Daniel Veillard42f12e92003-03-07 18:32:59 +00007114 }
Daniel Veillard416589a2003-02-17 17:25:42 +00007115 if (ret == 0)
7116 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007117 break;
7118 }
7119 case XML_RELAXNG_LIST: {
Daniel Veillardfd573f12003-03-16 17:52:32 +00007120 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007121 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00007122#ifdef DEBUG_LIST
7123 int nb_values = 0;
7124#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007125
7126 oldvalue = ctxt->state->value;
7127 oldend = ctxt->state->endvalue;
7128
7129 val = xmlStrdup(oldvalue);
7130 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007131 val = xmlStrdup(BAD_CAST "");
7132 }
7133 if (val == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00007134 VALID_ERR(XML_RELAXNG_ERR_NOSTATE);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007135 return(-1);
7136 }
7137 cur = val;
7138 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00007139 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007140 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00007141 cur++;
7142#ifdef DEBUG_LIST
7143 nb_values++;
7144#endif
7145 while (IS_BLANK(*cur))
7146 *cur++ = 0;
7147 } else
7148 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007149 }
Daniel Veillard416589a2003-02-17 17:25:42 +00007150#ifdef DEBUG_LIST
7151 xmlGenericError(xmlGenericErrorContext,
7152 "list value: '%s' found %d items\n", oldvalue, nb_values);
7153 nb_values = 0;
7154#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007155 ctxt->state->endvalue = cur;
7156 cur = val;
7157 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007158
Daniel Veillardfd573f12003-03-16 17:52:32 +00007159 ctxt->state->value = cur;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007160
Daniel Veillardfd573f12003-03-16 17:52:32 +00007161 while (list != NULL) {
7162 if (ctxt->state->value == ctxt->state->endvalue)
7163 ctxt->state->value = NULL;
7164 ret = xmlRelaxNGValidateValue(ctxt, list);
7165 if (ret != 0) {
7166#ifdef DEBUG_LIST
7167 xmlGenericError(xmlGenericErrorContext,
7168 "Failed to validate value: '%s' with %d rule\n",
7169 ctxt->state->value, nb_values);
7170#endif
7171 break;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007172 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00007173#ifdef DEBUG_LIST
7174 nb_values++;
7175#endif
7176 list = list->next;
7177 }
7178
7179 if ((ret == 0) && (ctxt->state->value != NULL) &&
7180 (ctxt->state->value != ctxt->state->endvalue)) {
7181 VALID_ERR2(XML_RELAXNG_ERR_LISTEXTRA, ctxt->state->value);
7182 ret = -1;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007183 }
7184 xmlFree(val);
7185 ctxt->state->value = oldvalue;
7186 ctxt->state->endvalue = oldend;
7187 break;
7188 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00007189 case XML_RELAXNG_ONEORMORE:
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007190 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
7191 if (ret != 0) {
7192 break;
7193 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00007194 /* no break on purpose */
7195 case XML_RELAXNG_ZEROORMORE: {
7196 xmlChar *cur, *temp;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007197
7198 oldflags = ctxt->flags;
7199 ctxt->flags |= FLAGS_IGNORABLE;
7200 cur = ctxt->state->value;
7201 temp = NULL;
7202 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
7203 (temp != cur)) {
7204 temp = cur;
7205 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
7206 if (ret != 0) {
7207 ctxt->state->value = temp;
7208 ret = 0;
7209 break;
7210 }
7211 cur = ctxt->state->value;
7212 }
7213 ctxt->flags = oldflags;
Daniel Veillard42f12e92003-03-07 18:32:59 +00007214 if (ret != 0) {
7215 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7216 xmlRelaxNGDumpValidError(ctxt);
7217 } else {
Daniel Veillard28c52ab2003-03-18 11:39:17 +00007218 if (ctxt->errNr > 0) xmlRelaxNGPopErrors(ctxt, 0);
Daniel Veillard42f12e92003-03-07 18:32:59 +00007219 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007220 break;
7221 }
Daniel Veillard416589a2003-02-17 17:25:42 +00007222 case XML_RELAXNG_EXCEPT: {
7223 xmlRelaxNGDefinePtr list;
7224
7225 list = define->content;
7226 while (list != NULL) {
7227 ret = xmlRelaxNGValidateValue(ctxt, list);
7228 if (ret == 0) {
7229 ret = -1;
7230 break;
7231 } else
7232 ret = 0;
7233 list = list->next;
7234 }
7235 break;
7236 }
Daniel Veillard463a5472003-02-27 21:30:32 +00007237 case XML_RELAXNG_DEF:
Daniel Veillardfd573f12003-03-16 17:52:32 +00007238 case XML_RELAXNG_GROUP: {
7239 xmlRelaxNGDefinePtr list;
7240
7241 list = define->content;
7242 while (list != NULL) {
7243 ret = xmlRelaxNGValidateValue(ctxt, list);
7244 if (ret != 0) {
7245 ret = -1;
7246 break;
7247 } else
7248 ret = 0;
7249 list = list->next;
7250 }
Daniel Veillardd4310742003-02-18 21:12:46 +00007251 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00007252 }
Daniel Veillard463a5472003-02-27 21:30:32 +00007253 case XML_RELAXNG_REF:
7254 case XML_RELAXNG_PARENTREF:
7255 ret = xmlRelaxNGValidateValue(ctxt, define->content);
7256 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007257 default:
7258 TODO
7259 ret = -1;
7260 }
7261 return(ret);
7262}
7263
7264/**
7265 * xmlRelaxNGValidateValueContent:
7266 * @ctxt: a Relax-NG validation context
7267 * @defines: the list of definitions to verify
7268 *
7269 * Validate the given definitions for the current value
7270 *
7271 * Returns 0 if the validation succeeded or an error code.
7272 */
7273static int
7274xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
7275 xmlRelaxNGDefinePtr defines) {
7276 int ret = 0;
7277
7278 while (defines != NULL) {
7279 ret = xmlRelaxNGValidateValue(ctxt, defines);
7280 if (ret != 0)
7281 break;
7282 defines = defines->next;
7283 }
7284 return(ret);
7285}
7286
7287/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00007288 * xmlRelaxNGAttributeMatch:
7289 * @ctxt: a Relax-NG validation context
7290 * @define: the definition to check
7291 * @prop: the attribute
7292 *
7293 * Check if the attribute matches the definition nameClass
7294 *
7295 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
7296 */
7297static int
7298xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
7299 xmlRelaxNGDefinePtr define,
7300 xmlAttrPtr prop) {
7301 int ret;
7302
7303 if (define->name != NULL) {
7304 if (!xmlStrEqual(define->name, prop->name))
7305 return(0);
7306 }
7307 if (define->ns != NULL) {
7308 if (define->ns[0] == 0) {
7309 if (prop->ns != NULL)
7310 return(0);
7311 } else {
7312 if ((prop->ns == NULL) ||
7313 (!xmlStrEqual(define->ns, prop->ns->href)))
7314 return(0);
7315 }
7316 }
7317 if (define->nameClass == NULL)
7318 return(1);
7319 define = define->nameClass;
7320 if (define->type == XML_RELAXNG_EXCEPT) {
7321 xmlRelaxNGDefinePtr list;
7322
7323 list = define->content;
7324 while (list != NULL) {
7325 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
7326 if (ret == 1)
7327 return(0);
7328 if (ret < 0)
7329 return(ret);
7330 list = list->next;
7331 }
7332 } else {
7333 TODO
7334 }
7335 return(1);
7336}
7337
7338/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00007339 * xmlRelaxNGValidateAttribute:
7340 * @ctxt: a Relax-NG validation context
7341 * @define: the definition to verify
7342 *
7343 * Validate the given attribute definition for that node
7344 *
7345 * Returns 0 if the validation succeeded or an error code.
7346 */
7347static int
7348xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
7349 xmlRelaxNGDefinePtr define) {
7350 int ret = 0, i;
7351 xmlChar *value, *oldvalue;
7352 xmlAttrPtr prop = NULL, tmp;
Daniel Veillardc3da18a2003-03-18 00:31:04 +00007353 xmlNodePtr oldseq;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007354
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007355 if (ctxt->state->nbAttrLeft <= 0)
7356 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007357 if (define->name != NULL) {
7358 for (i = 0;i < ctxt->state->nbAttrs;i++) {
7359 tmp = ctxt->state->attrs[i];
7360 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
7361 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
7362 (tmp->ns == NULL)) ||
7363 ((tmp->ns != NULL) &&
7364 (xmlStrEqual(define->ns, tmp->ns->href)))) {
7365 prop = tmp;
7366 break;
7367 }
7368 }
7369 }
7370 if (prop != NULL) {
7371 value = xmlNodeListGetString(prop->doc, prop->children, 1);
7372 oldvalue = ctxt->state->value;
Daniel Veillardc3da18a2003-03-18 00:31:04 +00007373 oldseq = ctxt->state->seq;
7374 ctxt->state->seq = (xmlNodePtr) prop;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007375 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00007376 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007377 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00007378 if (ctxt->state->value != NULL)
7379 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007380 if (value != NULL)
7381 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00007382 ctxt->state->value = oldvalue;
Daniel Veillardc3da18a2003-03-18 00:31:04 +00007383 ctxt->state->seq = oldseq;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007384 if (ret == 0) {
7385 /*
7386 * flag the attribute as processed
7387 */
7388 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007389 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007390 }
7391 } else {
7392 ret = -1;
7393 }
7394#ifdef DEBUG
7395 xmlGenericError(xmlGenericErrorContext,
7396 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
7397#endif
7398 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007399 for (i = 0;i < ctxt->state->nbAttrs;i++) {
7400 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00007401 if ((tmp != NULL) &&
Daniel Veillardfd573f12003-03-16 17:52:32 +00007402 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007403 prop = tmp;
7404 break;
7405 }
7406 }
7407 if (prop != NULL) {
7408 value = xmlNodeListGetString(prop->doc, prop->children, 1);
7409 oldvalue = ctxt->state->value;
Daniel Veillardc3da18a2003-03-18 00:31:04 +00007410 oldseq = ctxt->state->seq;
7411 ctxt->state->seq = (xmlNodePtr) prop;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007412 ctxt->state->value = value;
7413 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00007414 if (ctxt->state->value != NULL)
7415 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007416 if (value != NULL)
7417 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00007418 ctxt->state->value = oldvalue;
Daniel Veillardc3da18a2003-03-18 00:31:04 +00007419 ctxt->state->seq = oldseq;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007420 if (ret == 0) {
7421 /*
7422 * flag the attribute as processed
7423 */
7424 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007425 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007426 }
7427 } else {
7428 ret = -1;
7429 }
7430#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00007431 if (define->ns != NULL) {
7432 xmlGenericError(xmlGenericErrorContext,
7433 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
7434 define->ns, ret);
7435 } else {
7436 xmlGenericError(xmlGenericErrorContext,
7437 "xmlRelaxNGValidateAttribute(anyName): %d\n",
7438 ret);
7439 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00007440#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00007441 }
7442
7443 return(ret);
7444}
7445
7446/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00007447 * xmlRelaxNGValidateAttributeList:
7448 * @ctxt: a Relax-NG validation context
7449 * @define: the list of definition to verify
7450 *
7451 * Validate the given node against the list of attribute definitions
7452 *
7453 * Returns 0 if the validation succeeded or an error code.
7454 */
7455static int
7456xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
7457 xmlRelaxNGDefinePtr defines) {
7458 int ret = 0;
7459 while (defines != NULL) {
7460 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
7461 ret = -1;
7462 defines = defines->next;
7463 }
7464 return(ret);
7465}
7466
7467/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00007468 * xmlRelaxNGNodeMatchesList:
7469 * @node: the node
7470 * @list: a NULL terminated array of definitions
7471 *
7472 * Check if a node can be matched by one of the definitions
7473 *
7474 * Returns 1 if matches 0 otherwise
7475 */
7476static int
7477xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
7478 xmlRelaxNGDefinePtr cur;
7479 int i = 0;
7480
7481 if ((node == NULL) || (list == NULL))
7482 return(0);
7483
7484 cur = list[i++];
7485 while (cur != NULL) {
7486 if ((node->type == XML_ELEMENT_NODE) &&
7487 (cur->type == XML_RELAXNG_ELEMENT)) {
7488 if (cur->name == NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00007489 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
7490 if (node->ns == NULL)
7491 return(1);
7492 } else {
7493 if ((node->ns != NULL) &&
7494 (xmlStrEqual(node->ns->href, cur->ns)))
7495 return(1);
7496 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00007497 } else if (xmlStrEqual(cur->name, node->name)) {
7498 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
7499 if (node->ns == NULL)
7500 return(1);
7501 } else {
7502 if ((node->ns != NULL) &&
7503 (xmlStrEqual(node->ns->href, cur->ns)))
7504 return(1);
7505 }
7506 }
Daniel Veillard39eb88b2003-03-11 11:21:28 +00007507 } else if (((node->type == XML_TEXT_NODE) ||
7508 (node->type == XML_CDATA_SECTION_NODE)) &&
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00007509 (cur->type == XML_RELAXNG_TEXT)) {
7510 return(1);
7511 }
7512 cur = list[i++];
7513 }
7514 return(0);
7515}
7516
7517/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00007518 * xmlRelaxNGValidateInterleave:
7519 * @ctxt: a Relax-NG validation context
7520 * @define: the definition to verify
7521 *
7522 * Validate an interleave definition for a node.
7523 *
7524 * Returns 0 if the validation succeeded or an error code.
7525 */
7526static int
7527xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
7528 xmlRelaxNGDefinePtr define) {
7529 int ret = 0, i, nbgroups, left;
7530 int errNr = ctxt->errNr;
Daniel Veillard249d7bb2003-03-19 21:02:29 +00007531 int oldflags;
Daniel Veillardfd573f12003-03-16 17:52:32 +00007532
7533 xmlRelaxNGValidStatePtr oldstate;
7534 xmlRelaxNGPartitionPtr partitions;
7535 xmlRelaxNGInterleaveGroupPtr group = NULL;
7536 xmlNodePtr cur, start, last = NULL, lastchg = NULL, lastelem;
7537 xmlNodePtr *list = NULL, *lasts = NULL;
7538
7539 if (define->data != NULL) {
7540 partitions = (xmlRelaxNGPartitionPtr) define->data;
7541 nbgroups = partitions->nbgroups;
7542 left = nbgroups;
7543 } else {
7544 VALID_ERR(XML_RELAXNG_ERR_INTERNODATA);
7545 return(-1);
7546 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00007547 /*
7548 * Optimizations for MIXED
7549 */
7550 oldflags = ctxt->flags;
7551 if (define->flags & IS_MIXED) {
7552 ctxt->flags |= FLAGS_MIXED_CONTENT;
7553 if (nbgroups == 2) {
7554 /*
7555 * this is a pure <mixed> case
7556 */
7557 if (ctxt->state != NULL)
7558 ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt,
7559 ctxt->state->seq);
7560 if (partitions->groups[0]->rule->type == XML_RELAXNG_TEXT)
7561 ret = xmlRelaxNGValidateDefinition(ctxt,
7562 partitions->groups[1]->rule);
7563 else
7564 ret = xmlRelaxNGValidateDefinition(ctxt,
7565 partitions->groups[0]->rule);
7566 if (ret == 0) {
7567 if (ctxt->state != NULL)
7568 ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt,
7569 ctxt->state->seq);
7570 }
7571 ctxt->flags = oldflags;
7572 return(ret);
7573 }
7574 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00007575
7576 /*
7577 * Build arrays to store the first and last node of the chain
7578 * pertaining to each group
7579 */
7580 list = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
7581 if (list == NULL) {
7582 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
7583 return(-1);
7584 }
7585 memset(list, 0, nbgroups * sizeof(xmlNodePtr));
7586 lasts = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
7587 if (lasts == NULL) {
7588 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
7589 return(-1);
7590 }
7591 memset(lasts, 0, nbgroups * sizeof(xmlNodePtr));
7592
7593 /*
7594 * Walk the sequence of children finding the right group and
7595 * sorting them in sequences.
7596 */
7597 cur = ctxt->state->seq;
7598 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
7599 start = cur;
7600 while (cur != NULL) {
7601 ctxt->state->seq = cur;
7602 for (i = 0;i < nbgroups;i++) {
7603 group = partitions->groups[i];
7604 if (group == NULL)
7605 continue;
7606 if (xmlRelaxNGNodeMatchesList(cur, group->defs))
7607 break;
7608 }
7609 /*
7610 * We break as soon as an element not matched is found
7611 */
7612 if (i >= nbgroups) {
7613 break;
7614 }
7615 if (lasts[i] != NULL) {
7616 lasts[i]->next = cur;
7617 lasts[i] = cur;
7618 } else {
7619 list[i] = cur;
7620 lasts[i] = cur;
7621 }
7622 if (cur->next != NULL)
7623 lastchg = cur->next;
7624 else
7625 lastchg = cur;
7626 cur = xmlRelaxNGSkipIgnored(ctxt, cur->next);
7627 }
7628 if (ret != 0) {
7629 VALID_ERR(XML_RELAXNG_ERR_INTERSEQ);
7630 ret = -1;
7631 goto done;
7632 }
7633 lastelem = cur;
7634 oldstate = ctxt->state;
7635 for (i = 0;i < nbgroups;i++) {
7636 ctxt->state = xmlRelaxNGCopyValidState(ctxt, oldstate);
7637 group = partitions->groups[i];
7638 if (lasts[i] != NULL) {
7639 last = lasts[i]->next;
7640 lasts[i]->next = NULL;
7641 }
7642 ctxt->state->seq = list[i];
7643 ret = xmlRelaxNGValidateDefinition(ctxt, group->rule);
7644 if (ret != 0)
7645 break;
7646 if (ctxt->state != NULL) {
7647 cur = ctxt->state->seq;
7648 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
Daniel Veillard798024a2003-03-19 10:36:09 +00007649 xmlRelaxNGFreeValidState(ctxt,oldstate);
Daniel Veillardfd573f12003-03-16 17:52:32 +00007650 oldstate = ctxt->state;
7651 ctxt->state = NULL;
7652 if (cur != NULL) {
7653 VALID_ERR2(XML_RELAXNG_ERR_INTEREXTRA, cur->name);
7654 ret = -1;
7655 ctxt->state = oldstate;
7656 goto done;
7657 }
7658 } else if (ctxt->states != NULL) {
7659 int j;
7660 int found = 0;
7661
7662 for (j = 0;j < ctxt->states->nbState;j++) {
7663 cur = ctxt->states->tabState[j]->seq;
7664 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
7665 if (cur == NULL) {
7666 found = 1;
7667 break;
7668 }
7669 }
7670 if (ctxt->states->nbState > 0) {
Daniel Veillard798024a2003-03-19 10:36:09 +00007671 xmlRelaxNGFreeValidState(ctxt,oldstate);
Daniel Veillardfd573f12003-03-16 17:52:32 +00007672 oldstate = ctxt->states->tabState[ctxt->states->nbState - 1];
7673 }
7674 for (j = 0;j < ctxt->states->nbState - 1;j++) {
Daniel Veillard798024a2003-03-19 10:36:09 +00007675 xmlRelaxNGFreeValidState(ctxt,ctxt->states->tabState[j]);
Daniel Veillardfd573f12003-03-16 17:52:32 +00007676 }
7677 xmlRelaxNGFreeStates(ctxt, ctxt->states);
7678 ctxt->states = NULL;
7679 if (found == 0) {
7680 VALID_ERR2(XML_RELAXNG_ERR_INTEREXTRA, cur->name);
7681 ret = -1;
7682 ctxt->state = oldstate;
7683 goto done;
7684 }
7685 } else {
7686 ret = -1;
7687 break;
7688 }
7689 if (lasts[i] != NULL) {
7690 lasts[i]->next = last;
7691 }
7692 }
7693 if (ctxt->state != NULL)
Daniel Veillard798024a2003-03-19 10:36:09 +00007694 xmlRelaxNGFreeValidState(ctxt,ctxt->state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00007695 ctxt->state = oldstate;
7696 ctxt->state->seq = lastelem;
7697 if (ret != 0) {
7698 VALID_ERR(XML_RELAXNG_ERR_INTERSEQ);
7699 ret = -1;
7700 goto done;
7701 }
7702
7703done:
Daniel Veillard249d7bb2003-03-19 21:02:29 +00007704 ctxt->flags = oldflags;
Daniel Veillardfd573f12003-03-16 17:52:32 +00007705 /*
7706 * builds the next links chain from the prev one
7707 */
7708 cur = lastchg;
7709 while (cur != NULL) {
7710 if ((cur == start) || (cur->prev == NULL))
7711 break;
7712 cur->prev->next = cur;
7713 cur = cur->prev;
7714 }
7715 if (ret == 0) {
Daniel Veillard28c52ab2003-03-18 11:39:17 +00007716 if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr);
Daniel Veillardfd573f12003-03-16 17:52:32 +00007717 }
7718
7719 xmlFree(list);
7720 xmlFree(lasts);
7721 return(ret);
7722}
7723
7724/**
7725 * xmlRelaxNGValidateDefinitionList:
7726 * @ctxt: a Relax-NG validation context
7727 * @define: the list of definition to verify
7728 *
7729 * Validate the given node content against the (list) of definitions
7730 *
7731 * Returns 0 if the validation succeeded or an error code.
7732 */
7733static int
7734xmlRelaxNGValidateDefinitionList(xmlRelaxNGValidCtxtPtr ctxt,
7735 xmlRelaxNGDefinePtr defines) {
7736 int ret = 0, res;
7737
7738
Daniel Veillard952379b2003-03-17 15:37:12 +00007739 if (defines == NULL) {
7740 VALID_ERR2(XML_RELAXNG_ERR_INTERNAL, BAD_CAST "NULL definition list");
7741 return(-1);
7742 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00007743 while (defines != NULL) {
7744 if ((ctxt->state != NULL) || (ctxt->states != NULL)) {
7745 res = xmlRelaxNGValidateDefinition(ctxt, defines);
7746 if (res < 0)
7747 ret = -1;
7748 } else {
7749 VALID_ERR(XML_RELAXNG_ERR_NOSTATE);
7750 return(-1);
7751 }
7752 if (ret < 0)
7753 break;
7754 defines = defines->next;
7755 }
7756
7757 return(ret);
7758}
7759
7760/**
7761 * xmlRelaxNGElementMatch:
Daniel Veillard416589a2003-02-17 17:25:42 +00007762 * @ctxt: a Relax-NG validation context
7763 * @define: the definition to check
Daniel Veillardfd573f12003-03-16 17:52:32 +00007764 * @elem: the element
Daniel Veillard416589a2003-02-17 17:25:42 +00007765 *
Daniel Veillardfd573f12003-03-16 17:52:32 +00007766 * Check if the element matches the definition nameClass
Daniel Veillard416589a2003-02-17 17:25:42 +00007767 *
Daniel Veillardfd573f12003-03-16 17:52:32 +00007768 * Returns 1 if the element matches, 0 if no, or -1 in case of error
Daniel Veillard416589a2003-02-17 17:25:42 +00007769 */
7770static int
Daniel Veillardfd573f12003-03-16 17:52:32 +00007771xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
7772 xmlRelaxNGDefinePtr define,
7773 xmlNodePtr elem) {
Daniel Veillard416589a2003-02-17 17:25:42 +00007774 int ret, oldflags;
7775
Daniel Veillardfd573f12003-03-16 17:52:32 +00007776 if (define->name != NULL) {
7777 if (!xmlStrEqual(elem->name, define->name)) {
7778 VALID_ERR3(XML_RELAXNG_ERR_ELEMNAME, define->name, elem->name);
7779 return(0);
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007780 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007781 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00007782 if ((define->ns != NULL) && (define->ns[0] != 0)) {
7783 if (elem->ns == NULL) {
7784 VALID_ERR2(XML_RELAXNG_ERR_ELEMNONS,
7785 elem->name);
7786 return(0);
7787 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
7788 VALID_ERR3(XML_RELAXNG_ERR_ELEMWRONGNS,
7789 elem->name, define->ns);
7790 return(0);
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007791 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00007792 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
7793 (define->name == NULL)) {
7794 VALID_ERR2(XML_RELAXNG_ERR_ELEMEXTRANS,
7795 elem->name);
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007796 return(0);
Daniel Veillardfd573f12003-03-16 17:52:32 +00007797 } else if ((elem->ns != NULL) && (define->name != NULL)) {
7798 VALID_ERR2(XML_RELAXNG_ERR_ELEMEXTRANS,
7799 define->name);
7800 return(0);
7801 }
7802
7803 if (define->nameClass == NULL)
7804 return(1);
7805
7806 define = define->nameClass;
7807 if (define->type == XML_RELAXNG_EXCEPT) {
7808 xmlRelaxNGDefinePtr list;
7809 oldflags = ctxt->flags;
7810 ctxt->flags |= FLAGS_IGNORABLE;
7811
7812 list = define->content;
7813 while (list != NULL) {
7814 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
7815 if (ret == 1) {
7816 ctxt->flags = oldflags;
7817 return(0);
7818 }
7819 if (ret < 0) {
7820 ctxt->flags = oldflags;
7821 return(ret);
7822 }
7823 list = list->next;
7824 }
7825 ret = 1;
7826 ctxt->flags = oldflags;
7827 } else if (define->type == XML_RELAXNG_CHOICE) {
7828 xmlRelaxNGDefinePtr list;
7829
7830 oldflags = ctxt->flags;
7831 ctxt->flags |= FLAGS_IGNORABLE;
7832
7833 list = define->nameClass;
7834 while (list != NULL) {
7835 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
7836 if (ret == 1) {
7837 ctxt->flags = oldflags;
7838 return(1);
7839 }
7840 if (ret < 0) {
7841 ctxt->flags = oldflags;
7842 return(ret);
7843 }
7844 list = list->next;
7845 }
7846 if (ret != 0) {
7847 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7848 xmlRelaxNGDumpValidError(ctxt);
7849 } else {
Daniel Veillard28c52ab2003-03-18 11:39:17 +00007850 if (ctxt->errNr > 0) xmlRelaxNGPopErrors(ctxt, 0);
Daniel Veillardfd573f12003-03-16 17:52:32 +00007851 }
7852 ret = 0;
7853 ctxt->flags = oldflags;
7854 } else {
7855 TODO
7856 ret = -1;
7857 }
7858 return(ret);
7859}
7860
7861/**
7862 * xmlRelaxNGValidateElementEnd:
7863 * @ctxt: a Relax-NG validation context
7864 *
7865 * Validate the end of the element, implements check that
7866 * there is nothing left not consumed in the element content
7867 * or in the attribute list.
7868 *
7869 * Returns 0 if the validation succeeded or an error code.
7870 */
7871static int
7872xmlRelaxNGValidateElementEnd(xmlRelaxNGValidCtxtPtr ctxt) {
7873 int ret = 0, i;
7874 xmlRelaxNGValidStatePtr state;
7875
7876 state = ctxt->state;
7877 if (state->seq != NULL) {
7878 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
7879 if (state->seq != NULL) {
7880 VALID_ERR3(XML_RELAXNG_ERR_EXTRACONTENT,
7881 state->node->name, state->seq->name);
7882 ret = -1;
7883 }
7884 }
7885 for (i = 0;i < state->nbAttrs;i++) {
7886 if (state->attrs[i] != NULL) {
7887 VALID_ERR3(XML_RELAXNG_ERR_INVALIDATTR,
7888 state->attrs[i]->name, state->node->name);
7889 ret = -1;
7890 }
7891 }
7892 return(ret);
7893}
7894
7895/**
7896 * xmlRelaxNGValidateState:
7897 * @ctxt: a Relax-NG validation context
7898 * @define: the definition to verify
7899 *
7900 * Validate the current state against the definition
7901 *
7902 * Returns 0 if the validation succeeded or an error code.
7903 */
7904static int
7905xmlRelaxNGValidateState(xmlRelaxNGValidCtxtPtr ctxt,
7906 xmlRelaxNGDefinePtr define) {
7907 xmlNodePtr node;
7908 int ret = 0, i, tmp, oldflags, errNr;
7909 xmlRelaxNGValidStatePtr oldstate, state;
7910
7911 if (define == NULL) {
7912 VALID_ERR(XML_RELAXNG_ERR_NODEFINE);
7913 return(-1);
7914 }
7915
7916 if (ctxt->state != NULL) {
7917 node = ctxt->state->seq;
7918 } else {
7919 node = NULL;
7920 }
7921#ifdef DEBUG
7922 for (i = 0;i < ctxt->depth;i++)
7923 xmlGenericError(xmlGenericErrorContext, " ");
7924 xmlGenericError(xmlGenericErrorContext,
7925 "Start validating %s ", xmlRelaxNGDefName(define));
7926 if (define->name != NULL)
7927 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
7928 if ((node != NULL) && (node->name != NULL))
7929 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
7930 else
7931 xmlGenericError(xmlGenericErrorContext, "\n");
7932#endif
7933 ctxt->depth++;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007934 switch (define->type) {
7935 case XML_RELAXNG_EMPTY:
Daniel Veillardfd573f12003-03-16 17:52:32 +00007936 node = xmlRelaxNGSkipIgnored(ctxt, node);
7937 ret = 0;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007938 break;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007939 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillardfd573f12003-03-16 17:52:32 +00007940 ret = -1;
7941 break;
7942 case XML_RELAXNG_TEXT:
7943 while ((node != NULL) &&
7944 ((node->type == XML_TEXT_NODE) ||
7945 (node->type == XML_COMMENT_NODE) ||
7946 (node->type == XML_PI_NODE) ||
7947 (node->type == XML_CDATA_SECTION_NODE)))
7948 node = node->next;
7949 ctxt->state->seq = node;
7950 break;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00007951 case XML_RELAXNG_ELEMENT:
Daniel Veillardfd573f12003-03-16 17:52:32 +00007952 errNr = ctxt->errNr;
7953 node = xmlRelaxNGSkipIgnored(ctxt, node);
7954 if (node == NULL) {
7955 VALID_ERR2(XML_RELAXNG_ERR_NOELEM, define->name);
7956 ret = -1;
7957 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7958 xmlRelaxNGDumpValidError(ctxt);
7959 break;
7960 }
7961 if (node->type != XML_ELEMENT_NODE) {
7962 VALID_ERR(XML_RELAXNG_ERR_NOTELEM);
7963 ret = -1;
7964 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7965 xmlRelaxNGDumpValidError(ctxt);
7966 break;
7967 }
7968 /*
7969 * This node was already validated successfully against
7970 * this definition.
7971 */
7972 if (node->_private == define) {
7973 ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
Daniel Veillard249d7bb2003-03-19 21:02:29 +00007974 if (ctxt->errNr != 0) {
7975 while ((ctxt->err != NULL) &&
7976 (((ctxt->err->err == XML_RELAXNG_ERR_ELEMNAME) &&
7977 (xmlStrEqual(ctxt->err->arg2, node->name))) ||
7978 (ctxt->err->err == XML_RELAXNG_ERR_NOELEM) ||
7979 (ctxt->err->err == XML_RELAXNG_ERR_NOTELEM)))
7980 xmlRelaxNGValidErrorPop(ctxt);
7981 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00007982 break;
7983 }
7984
7985 ret = xmlRelaxNGElementMatch(ctxt, define, node);
7986 if (ret <= 0) {
7987 ret = -1;
7988 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
7989 xmlRelaxNGDumpValidError(ctxt);
7990 break;
7991 }
7992 ret = 0;
7993 if (ctxt->errNr != 0) {
7994 while ((ctxt->err != NULL) &&
7995 (((ctxt->err->err == XML_RELAXNG_ERR_ELEMNAME) &&
7996 (xmlStrEqual(ctxt->err->arg2, node->name))) ||
7997 (ctxt->err->err == XML_RELAXNG_ERR_NOELEM) ||
7998 (ctxt->err->err == XML_RELAXNG_ERR_NOTELEM)))
7999 xmlRelaxNGValidErrorPop(ctxt);
8000 }
8001 errNr = ctxt->errNr;
8002
Daniel Veillard249d7bb2003-03-19 21:02:29 +00008003 oldflags = ctxt->flags;
8004 if (ctxt->flags & FLAGS_MIXED_CONTENT) {
8005 ctxt->flags -= FLAGS_MIXED_CONTENT;
8006 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00008007 state = xmlRelaxNGNewValidState(ctxt, node);
8008 if (state == NULL) {
8009 ret = -1;
8010 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
8011 xmlRelaxNGDumpValidError(ctxt);
8012 break;
8013 }
8014
8015 oldstate = ctxt->state;
8016 ctxt->state = state;
8017 if (define->attrs != NULL) {
8018 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
8019 if (tmp != 0) {
8020 ret = -1;
8021 VALID_ERR2(XML_RELAXNG_ERR_ATTRVALID, node->name);
8022 }
8023 }
8024 if (define->content != NULL) {
8025 tmp = xmlRelaxNGValidateDefinitionList(ctxt, define->content);
8026 if (tmp != 0) {
8027 ret = -1;
8028 VALID_ERR2(XML_RELAXNG_ERR_CONTENTVALID, node->name);
8029 }
8030 }
8031 if (ctxt->states != NULL) {
8032 tmp = -1;
8033
Daniel Veillardfd573f12003-03-16 17:52:32 +00008034 ctxt->flags |= FLAGS_IGNORABLE;
8035
8036 for (i = 0;i < ctxt->states->nbState;i++) {
8037 state = ctxt->states->tabState[i];
8038 ctxt->state = state;
8039
8040 if (xmlRelaxNGValidateElementEnd(ctxt) == 0)
8041 tmp = 0;
Daniel Veillard798024a2003-03-19 10:36:09 +00008042 xmlRelaxNGFreeValidState(ctxt,state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008043 }
8044 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8045 ctxt->flags = oldflags;
8046 ctxt->states = NULL;
8047 if ((ret == 0) && (tmp == -1))
8048 ret = -1;
8049 } else {
8050 state = ctxt->state;
8051 if (ret == 0)
8052 ret = xmlRelaxNGValidateElementEnd(ctxt);
Daniel Veillard798024a2003-03-19 10:36:09 +00008053 xmlRelaxNGFreeValidState(ctxt,state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008054 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00008055 ctxt->flags = oldflags;
Daniel Veillardfd573f12003-03-16 17:52:32 +00008056 ctxt->state = oldstate;
8057 if (oldstate != NULL)
8058 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
8059 if (ret == 0) {
8060 node->_private = define;
8061 }
8062 if (ret != 0) {
8063 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
8064 xmlRelaxNGDumpValidError(ctxt);
8065 } else {
Daniel Veillard28c52ab2003-03-18 11:39:17 +00008066 if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008067 }
8068
8069#ifdef DEBUG
8070 xmlGenericError(xmlGenericErrorContext,
8071 "xmlRelaxNGValidateDefinition(): validated %s : %d",
8072 node->name, ret);
8073 if (oldstate == NULL)
8074 xmlGenericError(xmlGenericErrorContext, ": no state\n");
8075 else if (oldstate->seq == NULL)
8076 xmlGenericError(xmlGenericErrorContext, ": done\n");
8077 else if (oldstate->seq->type == XML_ELEMENT_NODE)
8078 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
8079 oldstate->seq->name);
8080 else
8081 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
8082 oldstate->seq->name, oldstate->seq->type);
8083#endif
8084 break;
8085 case XML_RELAXNG_OPTIONAL: {
8086 oldflags = ctxt->flags;
8087 ctxt->flags |= FLAGS_IGNORABLE;
8088 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
8089 ret = xmlRelaxNGValidateDefinitionList(ctxt, define->content);
8090 if (ret != 0) {
8091 if (ctxt->state != NULL)
Daniel Veillard798024a2003-03-19 10:36:09 +00008092 xmlRelaxNGFreeValidState(ctxt,ctxt->state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008093 ctxt->state = oldstate;
8094 ctxt->flags = oldflags;
8095 ret = 0;
8096 break;
8097 }
8098 if (ctxt->states != NULL) {
8099 xmlRelaxNGAddStates(ctxt, ctxt->states, oldstate);
8100 } else {
8101 ctxt->states = xmlRelaxNGNewStates(ctxt, 1);
8102 if (ctxt->states == NULL) {
Daniel Veillard798024a2003-03-19 10:36:09 +00008103 xmlRelaxNGFreeValidState(ctxt,oldstate);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008104 ctxt->flags = oldflags;
8105 ret = -1;
8106 break;
8107 }
8108 xmlRelaxNGAddStates(ctxt, ctxt->states, oldstate);
8109 xmlRelaxNGAddStates(ctxt, ctxt->states, ctxt->state);
8110 ctxt->state = NULL;
8111 }
8112 ctxt->flags = oldflags;
8113 ret = 0;
8114 break;
8115 }
8116 case XML_RELAXNG_ONEORMORE:
8117 ret = xmlRelaxNGValidateDefinitionList(ctxt, define->content);
8118 if (ret != 0) {
8119 break;
8120 }
8121 /* no break on purpose */
8122 case XML_RELAXNG_ZEROORMORE: {
8123 int progress;
8124 xmlRelaxNGStatesPtr states = NULL, res = NULL;
8125 int base, j;
8126
8127 res = xmlRelaxNGNewStates(ctxt, 1);
8128 if (res == NULL) {
8129 ret = -1;
8130 break;
8131 }
8132 /*
8133 * All the input states are also exit states
8134 */
8135 if (ctxt->state != NULL) {
8136 xmlRelaxNGAddStates(ctxt, res,
8137 xmlRelaxNGCopyValidState(ctxt, ctxt->state));
8138 } else {
8139 for (j = 0;j < ctxt->states->nbState;j++) {
8140 xmlRelaxNGAddStates(ctxt, res,
8141 xmlRelaxNGCopyValidState(ctxt,
8142 ctxt->states->tabState[j]));
8143 }
8144 }
8145 oldflags = ctxt->flags;
8146 ctxt->flags |= FLAGS_IGNORABLE;
8147 do {
8148 progress = 0;
8149 base = res->nbState;
8150
8151 if (ctxt->states != NULL) {
8152 states = ctxt->states;
8153 for (i = 0;i < states->nbState;i++) {
8154 ctxt->state = states->tabState[i];
8155 ctxt->states = NULL;
8156 ret = xmlRelaxNGValidateDefinitionList(ctxt,
8157 define->content);
8158 if (ret == 0) {
8159 if (ctxt->state != NULL) {
8160 tmp = xmlRelaxNGAddStates(ctxt, res,
8161 ctxt->state);
8162 ctxt->state = NULL;
8163 if (tmp == 1)
8164 progress = 1;
8165 } else if (ctxt->states != NULL) {
8166 for (j = 0;j < ctxt->states->nbState;j++) {
8167 tmp = xmlRelaxNGAddStates(ctxt, res,
8168 ctxt->states->tabState[j]);
8169 if (tmp == 1)
8170 progress = 1;
8171 }
8172 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8173 ctxt->states = NULL;
8174 }
8175 } else {
8176 if (ctxt->state != NULL) {
Daniel Veillard798024a2003-03-19 10:36:09 +00008177 xmlRelaxNGFreeValidState(ctxt,ctxt->state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008178 ctxt->state = NULL;
8179 }
8180 }
8181 }
8182 } else {
8183 ret = xmlRelaxNGValidateDefinitionList(ctxt,
8184 define->content);
8185 if (ret != 0) {
Daniel Veillard798024a2003-03-19 10:36:09 +00008186 xmlRelaxNGFreeValidState(ctxt,ctxt->state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008187 ctxt->state = NULL;
8188 } else {
8189 base = res->nbState;
8190 if (ctxt->state != NULL) {
8191 tmp = xmlRelaxNGAddStates(ctxt, res,
8192 ctxt->state);
8193 ctxt->state = NULL;
8194 if (tmp == 1)
8195 progress = 1;
8196 } else if (ctxt->states != NULL) {
8197 for (j = 0;j < ctxt->states->nbState;j++) {
8198 tmp = xmlRelaxNGAddStates(ctxt, res,
8199 ctxt->states->tabState[j]);
8200 if (tmp == 1)
8201 progress = 1;
8202 }
8203 if (states == NULL) {
8204 states = ctxt->states;
8205 } else {
8206 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8207 }
8208 ctxt->states = NULL;
8209 }
8210 }
8211 }
8212 if (progress) {
8213 /*
8214 * Collect all the new nodes added at that step
8215 * and make them the new node set
8216 */
8217 if (res->nbState - base == 1) {
8218 ctxt->state = xmlRelaxNGCopyValidState(ctxt,
8219 res->tabState[base]);
8220 } else {
8221 if (states == NULL) {
8222 xmlRelaxNGNewStates(ctxt, res->nbState - base);
8223 }
8224 states->nbState = 0;
8225 for (i = base;i < res->nbState;i++)
8226 xmlRelaxNGAddStates(ctxt, states,
8227 xmlRelaxNGCopyValidState(ctxt,
8228 res->tabState[i]));
8229 ctxt->states = states;
8230 }
8231 }
8232 } while (progress == 1);
8233 if (states != NULL) {
8234 xmlRelaxNGFreeStates(ctxt, states);
8235 }
8236 ctxt->states = res;
8237 ctxt->flags = oldflags;
8238 ret = 0;
8239 break;
8240 }
8241 case XML_RELAXNG_CHOICE: {
8242 xmlRelaxNGDefinePtr list = define->content;
8243 xmlRelaxNGStatesPtr states = NULL;
8244
8245
8246 oldflags = ctxt->flags;
8247 errNr = ctxt->errNr;
8248 ctxt->flags |= FLAGS_IGNORABLE;
8249 node = xmlRelaxNGSkipIgnored(ctxt, node);
8250
8251 while (list != NULL) {
8252 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
8253 ret = xmlRelaxNGValidateDefinition(ctxt, list);
8254 if (ret == 0) {
8255 if (states == NULL) {
8256 states = xmlRelaxNGNewStates(ctxt, 1);
8257 }
8258 if (ctxt->state != NULL) {
8259 xmlRelaxNGAddStates(ctxt, states, ctxt->state);
8260 } else if (ctxt->states != NULL) {
8261 for (i = 0;i < ctxt->states->nbState;i++) {
8262 xmlRelaxNGAddStates(ctxt, states,
8263 ctxt->states->tabState[i]);
8264 }
8265 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8266 ctxt->states = NULL;
8267 }
8268 } else {
Daniel Veillard798024a2003-03-19 10:36:09 +00008269 xmlRelaxNGFreeValidState(ctxt,ctxt->state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008270 }
8271 ctxt->state = oldstate;
8272 list = list->next;
8273 }
8274 if (states != NULL) {
Daniel Veillard798024a2003-03-19 10:36:09 +00008275 xmlRelaxNGFreeValidState(ctxt,oldstate);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008276 ctxt->states = states;
8277 ctxt->state = NULL;
8278 ret = 0;
8279 } else {
8280 ctxt->states = NULL;
8281 }
8282 ctxt->flags = oldflags;
8283 if (ret != 0) {
8284 if ((ctxt->flags & FLAGS_IGNORABLE) == 0)
8285 xmlRelaxNGDumpValidError(ctxt);
8286 } else if ((ctxt->flags & FLAGS_IGNORABLE) == 0) {
Daniel Veillard28c52ab2003-03-18 11:39:17 +00008287 if (ctxt->errNr > errNr) xmlRelaxNGPopErrors(ctxt, errNr);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008288 }
8289 break;
8290 }
8291 case XML_RELAXNG_DEF:
8292 case XML_RELAXNG_GROUP:
8293 ret = xmlRelaxNGValidateDefinitionList(ctxt, define->content);
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008294 break;
8295 case XML_RELAXNG_INTERLEAVE:
Daniel Veillardfd573f12003-03-16 17:52:32 +00008296 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008297 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00008298 case XML_RELAXNG_ATTRIBUTE:
8299 ret = xmlRelaxNGValidateAttribute(ctxt, define);
8300 break;
8301 case XML_RELAXNG_NOOP:
8302 case XML_RELAXNG_REF:
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008303 case XML_RELAXNG_EXTERNALREF:
Daniel Veillardfd573f12003-03-16 17:52:32 +00008304 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
8305 break;
Daniel Veillard952379b2003-03-17 15:37:12 +00008306 case XML_RELAXNG_PARENTREF:
8307 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
8308 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00008309 case XML_RELAXNG_DATATYPE: {
8310 xmlNodePtr child;
8311 xmlChar *content = NULL;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008312
Daniel Veillardfd573f12003-03-16 17:52:32 +00008313 child = node;
8314 while (child != NULL) {
8315 if (child->type == XML_ELEMENT_NODE) {
8316 VALID_ERR2(XML_RELAXNG_ERR_DATAELEM,
8317 node->parent->name);
8318 ret = -1;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008319 break;
Daniel Veillardfd573f12003-03-16 17:52:32 +00008320 } else if ((child->type == XML_TEXT_NODE) ||
8321 (child->type == XML_CDATA_SECTION_NODE)) {
8322 content = xmlStrcat(content, child->content);
8323 }
8324 /* TODO: handle entities ... */
8325 child = child->next;
8326 }
8327 if (ret == -1) {
8328 if (content != NULL)
8329 xmlFree(content);
8330 break;
8331 }
8332 if (content == NULL) {
8333 content = xmlStrdup(BAD_CAST "");
8334 if (content == NULL) {
8335 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
8336 ret = -1;
8337 break;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008338 }
8339 }
Daniel Veillardc3da18a2003-03-18 00:31:04 +00008340 ret = xmlRelaxNGValidateDatatype(ctxt, content, define,
8341 ctxt->state->seq);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008342 if (ret == -1) {
8343 VALID_ERR2(XML_RELAXNG_ERR_DATATYPE, define->name);
8344 } else if (ret == 0) {
8345 ctxt->state->seq = NULL;
8346 }
8347 if (content != NULL)
8348 xmlFree(content);
8349 break;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008350 }
Daniel Veillardfd573f12003-03-16 17:52:32 +00008351 case XML_RELAXNG_VALUE: {
8352 xmlChar *content = NULL;
8353 xmlChar *oldvalue;
8354 xmlNodePtr child;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008355
Daniel Veillardfd573f12003-03-16 17:52:32 +00008356 child = node;
8357 while (child != NULL) {
8358 if (child->type == XML_ELEMENT_NODE) {
8359 VALID_ERR2(XML_RELAXNG_ERR_VALELEM,
8360 node->parent->name);
8361 ret = -1;
8362 break;
8363 } else if ((child->type == XML_TEXT_NODE) ||
8364 (child->type == XML_CDATA_SECTION_NODE)) {
8365 content = xmlStrcat(content, child->content);
8366 }
8367 /* TODO: handle entities ... */
8368 child = child->next;
8369 }
8370 if (ret == -1) {
8371 if (content != NULL)
8372 xmlFree(content);
8373 break;
8374 }
8375 if (content == NULL) {
8376 content = xmlStrdup(BAD_CAST "");
8377 if (content == NULL) {
8378 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
8379 ret = -1;
8380 break;
8381 }
8382 }
8383 oldvalue = ctxt->state->value;
8384 ctxt->state->value = content;
8385 ret = xmlRelaxNGValidateValue(ctxt, define);
8386 ctxt->state->value = oldvalue;
8387 if (ret == -1) {
8388 VALID_ERR2(XML_RELAXNG_ERR_VALUE, define->name);
8389 } else if (ret == 0) {
8390 ctxt->state->seq = NULL;
8391 }
8392 if (content != NULL)
8393 xmlFree(content);
8394 break;
8395 }
8396 case XML_RELAXNG_LIST: {
8397 xmlChar *content;
8398 xmlNodePtr child;
8399 xmlChar *oldvalue, *oldendvalue;
8400 int len;
8401
8402 /*
8403 * Make sure it's only text nodes
8404 */
8405
8406 content = NULL;
8407 child = node;
8408 while (child != NULL) {
8409 if (child->type == XML_ELEMENT_NODE) {
8410 VALID_ERR2(XML_RELAXNG_ERR_LISTELEM,
8411 node->parent->name);
8412 ret = -1;
8413 break;
8414 } else if ((child->type == XML_TEXT_NODE) ||
8415 (child->type == XML_CDATA_SECTION_NODE)) {
8416 content = xmlStrcat(content, child->content);
8417 }
8418 /* TODO: handle entities ... */
8419 child = child->next;
8420 }
8421 if (ret == -1) {
8422 if (content != NULL)
8423 xmlFree(content);
8424 break;
8425 }
8426 if (content == NULL) {
8427 content = xmlStrdup(BAD_CAST "");
8428 if (content == NULL) {
8429 VALID_ERR(XML_RELAXNG_ERR_MEMORY);
8430 ret = -1;
8431 break;
8432 }
8433 }
8434 len = xmlStrlen(content);
8435 oldvalue = ctxt->state->value;
8436 oldendvalue = ctxt->state->endvalue;
8437 ctxt->state->value = content;
8438 ctxt->state->endvalue = content + len;
8439 ret = xmlRelaxNGValidateValue(ctxt, define);
8440 ctxt->state->value = oldvalue;
8441 ctxt->state->endvalue = oldendvalue;
8442 if (ret == -1) {
8443 VALID_ERR(XML_RELAXNG_ERR_LIST);
8444 } else if ((ret == 0) && (node != NULL)) {
8445 ctxt->state->seq = node->next;
8446 }
8447 if (content != NULL)
8448 xmlFree(content);
8449 break;
8450 }
8451 case XML_RELAXNG_START:
8452 case XML_RELAXNG_EXCEPT:
8453 case XML_RELAXNG_PARAM:
8454 TODO
8455 ret = -1;
8456 break;
8457 }
8458 ctxt->depth--;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008459#ifdef DEBUG
Daniel Veillardfd573f12003-03-16 17:52:32 +00008460 for (i = 0;i < ctxt->depth;i++)
8461 xmlGenericError(xmlGenericErrorContext, " ");
8462 xmlGenericError(xmlGenericErrorContext,
8463 "Validating %s ", xmlRelaxNGDefName(define));
8464 if (define->name != NULL)
8465 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
8466 if (ret == 0)
8467 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
8468 else
8469 xmlGenericError(xmlGenericErrorContext, "failed\n");
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008470#endif
Daniel Veillardfd573f12003-03-16 17:52:32 +00008471 return(ret);
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008472}
8473
8474/**
Daniel Veillardfd573f12003-03-16 17:52:32 +00008475 * xmlRelaxNGValidateDefinition:
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008476 * @ctxt: a Relax-NG validation context
8477 * @define: the definition to verify
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008478 *
Daniel Veillardfd573f12003-03-16 17:52:32 +00008479 * Validate the current node lists against the definition
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008480 *
Daniel Veillardfd573f12003-03-16 17:52:32 +00008481 * Returns 0 if the validation succeeded or an error code.
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008482 */
8483static int
Daniel Veillardfd573f12003-03-16 17:52:32 +00008484xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
8485 xmlRelaxNGDefinePtr define) {
8486 xmlRelaxNGStatesPtr states, res;
8487 int i, j, k, ret, oldflags;
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008488
Daniel Veillardfd573f12003-03-16 17:52:32 +00008489 /*
8490 * We should NOT have both ctxt->state and ctxt->states
8491 */
8492 if ((ctxt->state != NULL) && (ctxt->states != NULL)) {
8493 TODO
Daniel Veillard798024a2003-03-19 10:36:09 +00008494 xmlRelaxNGFreeValidState(ctxt,ctxt->state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008495 ctxt->state = NULL;
8496 }
8497
8498 if ((ctxt->states == NULL) || (ctxt->states->nbState == 1)) {
8499 if (ctxt->states != NULL) {
8500 ctxt->state = ctxt->states->tabState[0];
8501 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8502 ctxt->states = NULL;
8503 }
8504 ret = xmlRelaxNGValidateState(ctxt, define);
8505 if ((ctxt->state != NULL) && (ctxt->states != NULL)) {
8506 TODO
Daniel Veillard798024a2003-03-19 10:36:09 +00008507 xmlRelaxNGFreeValidState(ctxt,ctxt->state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008508 ctxt->state = NULL;
8509 }
8510 if ((ctxt->states != NULL) && (ctxt->states->nbState == 1)) {
8511 ctxt->state = ctxt->states->tabState[0];
8512 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8513 ctxt->states = NULL;
8514 }
8515 return(ret);
8516 }
8517
8518 states = ctxt->states;
8519 ctxt->states = NULL;
8520 res = NULL;
8521 j = 0;
8522 oldflags = ctxt->flags;
8523 ctxt->flags |= FLAGS_IGNORABLE;
8524 for (i = 0;i < states->nbState;i++) {
8525 ctxt->state = states->tabState[i];
8526 ctxt->states = NULL;
8527 ret = xmlRelaxNGValidateState(ctxt, define);
8528 /*
8529 * We should NOT have both ctxt->state and ctxt->states
8530 */
8531 if ((ctxt->state != NULL) && (ctxt->states != NULL)) {
8532 TODO
Daniel Veillard798024a2003-03-19 10:36:09 +00008533 xmlRelaxNGFreeValidState(ctxt,ctxt->state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008534 ctxt->state = NULL;
8535 }
8536 if (ret == 0) {
8537 if (ctxt->states == NULL) {
8538 if (res != NULL) {
8539 /* add the state to the container */
8540 xmlRelaxNGAddStates(ctxt, res, ctxt->state);
8541 ctxt->state = NULL;
8542 } else {
8543 /* add the state directly in states */
8544 states->tabState[j++] = ctxt->state;
8545 ctxt->state = NULL;
8546 }
8547 } else {
8548 if (res == NULL) {
8549 /* make it the new container and copy other results */
8550 res = ctxt->states;
8551 ctxt->states = NULL;
8552 for (k = 0;k < j;k++)
8553 xmlRelaxNGAddStates(ctxt, res, states->tabState[k]);
8554 } else {
8555 /* add all the new results to res and reff the container */
8556 for (k = 0;k < ctxt->states->nbState;k++)
8557 xmlRelaxNGAddStates(ctxt, res,
8558 ctxt->states->tabState[k]);
8559 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8560 ctxt->states = NULL;
8561 }
8562 }
8563 } else {
8564 if (ctxt->state != NULL) {
Daniel Veillard798024a2003-03-19 10:36:09 +00008565 xmlRelaxNGFreeValidState(ctxt,ctxt->state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008566 ctxt->state = NULL;
8567 } else if (ctxt->states != NULL) {
8568 for (k = 0;k < ctxt->states->nbState;k++)
Daniel Veillard798024a2003-03-19 10:36:09 +00008569 xmlRelaxNGFreeValidState(ctxt,ctxt->states->tabState[k]);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008570 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8571 ctxt->states = NULL;
8572 }
8573 }
8574 }
8575 ctxt->flags = oldflags;
8576 if (res != NULL) {
8577 xmlRelaxNGFreeStates(ctxt, states);
8578 ctxt->states = res;
8579 ret = 0;
8580 } else if (j > 1) {
8581 states->nbState = j;
8582 ctxt->states = states;
8583 ret =0;
8584 } else if (j == 1) {
8585 ctxt->state = states->tabState[0];
8586 xmlRelaxNGFreeStates(ctxt, states);
8587 ret = 0;
8588 } else {
8589 ret = -1;
8590 xmlRelaxNGFreeStates(ctxt, states);
8591 if (ctxt->states != NULL) {
8592 xmlRelaxNGFreeStates(ctxt, ctxt->states);
8593 ctxt->states = NULL;
8594 }
8595 }
8596 if ((ctxt->state != NULL) && (ctxt->states != NULL)) {
8597 TODO
Daniel Veillard798024a2003-03-19 10:36:09 +00008598 xmlRelaxNGFreeValidState(ctxt,ctxt->state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008599 ctxt->state = NULL;
8600 }
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008601 return(ret);
8602}
8603
Daniel Veillard1564e6e2003-03-15 21:30:25 +00008604/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00008605 * xmlRelaxNGValidateDocument:
8606 * @ctxt: a Relax-NG validation context
8607 * @doc: the document
8608 *
8609 * Validate the given document
8610 *
8611 * Returns 0 if the validation succeeded or an error code.
8612 */
8613static int
8614xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
8615 int ret;
8616 xmlRelaxNGPtr schema;
8617 xmlRelaxNGGrammarPtr grammar;
8618 xmlRelaxNGValidStatePtr state;
Daniel Veillardfd573f12003-03-16 17:52:32 +00008619 xmlNodePtr node;
Daniel Veillard6eadf632003-01-23 18:29:16 +00008620
8621 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
8622 return(-1);
8623
8624 schema = ctxt->schema;
8625 grammar = schema->topgrammar;
8626 if (grammar == NULL) {
Daniel Veillard42f12e92003-03-07 18:32:59 +00008627 VALID_ERR(XML_RELAXNG_ERR_NOGRAMMAR);
Daniel Veillard6eadf632003-01-23 18:29:16 +00008628 return(-1);
8629 }
8630 state = xmlRelaxNGNewValidState(ctxt, NULL);
8631 ctxt->state = state;
8632 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008633 if ((ctxt->state != NULL) && (state->seq != NULL)) {
8634 state = ctxt->state;
Daniel Veillard6eadf632003-01-23 18:29:16 +00008635 node = state->seq;
8636 node = xmlRelaxNGSkipIgnored(ctxt, node);
8637 if (node != NULL) {
Daniel Veillardfd573f12003-03-16 17:52:32 +00008638 if (ret != -1) {
8639 VALID_ERR(XML_RELAXNG_ERR_EXTRADATA);
8640 ret = -1;
8641 }
8642 }
8643 } else if (ctxt->states != NULL) {
8644 int i;
8645 int tmp = -1;
8646
8647 for (i = 0;i < ctxt->states->nbState;i++) {
8648 state = ctxt->states->tabState[i];
8649 node = state->seq;
8650 node = xmlRelaxNGSkipIgnored(ctxt, node);
8651 if (node == NULL)
8652 tmp = 0;
Daniel Veillard798024a2003-03-19 10:36:09 +00008653 xmlRelaxNGFreeValidState(ctxt,state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008654 }
8655 if (tmp == -1) {
8656 if (ret != -1) {
8657 VALID_ERR(XML_RELAXNG_ERR_EXTRADATA);
8658 ret = -1;
8659 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00008660 }
8661 }
Daniel Veillard798024a2003-03-19 10:36:09 +00008662 xmlRelaxNGFreeValidState(ctxt,state);
Daniel Veillardfd573f12003-03-16 17:52:32 +00008663 if (ret != 0)
8664 xmlRelaxNGDumpValidError(ctxt);
Daniel Veillardc3da18a2003-03-18 00:31:04 +00008665 if (ctxt->idref == 1) {
8666 xmlValidCtxt vctxt;
8667
8668 memset(&vctxt, 0, sizeof(xmlValidCtxt));
8669 vctxt.valid = 1;
8670 vctxt.error = ctxt->error;
8671 vctxt.warning = ctxt->warning;
8672 vctxt.userData = ctxt->userData;
8673
8674 if (xmlValidateDocumentFinal(&vctxt, doc) != 1)
8675 ret = -1;
8676 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00008677
8678 return(ret);
8679}
8680
Daniel Veillardfd573f12003-03-16 17:52:32 +00008681/************************************************************************
8682 * *
8683 * Validation interfaces *
8684 * *
8685 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +00008686/**
8687 * xmlRelaxNGNewValidCtxt:
8688 * @schema: a precompiled XML RelaxNGs
8689 *
8690 * Create an XML RelaxNGs validation context based on the given schema
8691 *
8692 * Returns the validation context or NULL in case of error
8693 */
8694xmlRelaxNGValidCtxtPtr
8695xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
8696 xmlRelaxNGValidCtxtPtr ret;
8697
8698 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
8699 if (ret == NULL) {
8700 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardfd573f12003-03-16 17:52:32 +00008701 "Failed to allocate new schema validation context\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +00008702 return (NULL);
8703 }
8704 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
8705 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00008706 ret->error = xmlGenericError;
8707 ret->userData = xmlGenericErrorContext;
Daniel Veillard42f12e92003-03-07 18:32:59 +00008708 ret->errNr = 0;
8709 ret->errMax = 0;
8710 ret->err = NULL;
8711 ret->errTab = NULL;
Daniel Veillardc3da18a2003-03-18 00:31:04 +00008712 ret->idref = schema->idref;
Daniel Veillard798024a2003-03-19 10:36:09 +00008713 ret->states = NULL;
8714 ret->freeState = NULL;
8715 ret->freeStates = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00008716 return (ret);
8717}
8718
8719/**
8720 * xmlRelaxNGFreeValidCtxt:
8721 * @ctxt: the schema validation context
8722 *
8723 * Free the resources associated to the schema validation context
8724 */
8725void
8726xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
Daniel Veillard798024a2003-03-19 10:36:09 +00008727 int k;
8728
Daniel Veillard6eadf632003-01-23 18:29:16 +00008729 if (ctxt == NULL)
8730 return;
Daniel Veillardfd573f12003-03-16 17:52:32 +00008731 if (ctxt->states != NULL)
Daniel Veillard798024a2003-03-19 10:36:09 +00008732 xmlRelaxNGFreeStates(NULL, ctxt->states);
8733 if (ctxt->freeState != NULL) {
8734 for (k = 0;k < ctxt->freeState->nbState;k++) {
8735 xmlRelaxNGFreeValidState(NULL, ctxt->freeState->tabState[k]);
8736 }
8737 xmlRelaxNGFreeStates(NULL, ctxt->freeState);
8738 }
Daniel Veillard798024a2003-03-19 10:36:09 +00008739 if (ctxt->freeStates != NULL) {
8740 for (k = 0;k < ctxt->freeStatesNr;k++) {
8741 xmlRelaxNGFreeStates(NULL, ctxt->freeStates[k]);
8742 }
8743 xmlFree(ctxt->freeStates);
8744 }
Daniel Veillard42f12e92003-03-07 18:32:59 +00008745 if (ctxt->errTab != NULL)
8746 xmlFree(ctxt->errTab);
Daniel Veillard6eadf632003-01-23 18:29:16 +00008747 xmlFree(ctxt);
8748}
8749
8750/**
8751 * xmlRelaxNGSetValidErrors:
8752 * @ctxt: a Relax-NG validation context
8753 * @err: the error function
8754 * @warn: the warning function
8755 * @ctx: the functions context
8756 *
8757 * Set the error and warning callback informations
8758 */
8759void
8760xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
8761 xmlRelaxNGValidityErrorFunc err,
8762 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
8763 if (ctxt == NULL)
8764 return;
8765 ctxt->error = err;
8766 ctxt->warning = warn;
8767 ctxt->userData = ctx;
8768}
8769
8770/**
8771 * xmlRelaxNGValidateDoc:
8772 * @ctxt: a Relax-NG validation context
8773 * @doc: a parsed document tree
8774 *
8775 * Validate a document tree in memory.
8776 *
8777 * Returns 0 if the document is valid, a positive error code
8778 * number otherwise and -1 in case of internal or API error.
8779 */
8780int
8781xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
8782 int ret;
8783
8784 if ((ctxt == NULL) || (doc == NULL))
8785 return(-1);
8786
8787 ctxt->doc = doc;
8788
8789 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00008790 /*
8791 * TODO: build error codes
8792 */
8793 if (ret == -1)
8794 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00008795 return(ret);
8796}
8797
8798#endif /* LIBXML_SCHEMAS_ENABLED */
8799