blob: ff11f3ec1a8cd49ba8768b9c1d8fa549b3acaecd [file] [log] [blame]
Daniel Veillard6eadf632003-01-23 18:29:16 +00001/*
2 * relaxng.c : implementation of the Relax-NG handling and validity checking
3 *
4 * See Copyright for the status of this software.
5 *
6 * Daniel Veillard <veillard@redhat.com>
7 */
8
Daniel Veillardd41f4f42003-01-29 21:07:52 +00009/**
10 * TODO:
Daniel Veillardd41f4f42003-01-29 21:07:52 +000011 * - error reporting
Daniel Veillard1ed7f362003-02-03 10:57:45 +000012 * - handle namespace declarations as attributes.
Daniel Veillardf4b4f982003-02-13 11:02:08 +000013 * - add support for DTD compatibility spec
14 * http://www.oasis-open.org/committees/relax-ng/compatibility-20011203.html
Daniel Veillardd41f4f42003-01-29 21:07:52 +000015 */
16
Daniel Veillard6eadf632003-01-23 18:29:16 +000017#define IN_LIBXML
18#include "libxml.h"
19
20#ifdef LIBXML_SCHEMAS_ENABLED
21
22#include <string.h>
23#include <stdio.h>
24#include <libxml/xmlmemory.h>
25#include <libxml/parser.h>
26#include <libxml/parserInternals.h>
27#include <libxml/hash.h>
28#include <libxml/uri.h>
29
30#include <libxml/relaxng.h>
31
32#include <libxml/xmlschemastypes.h>
33#include <libxml/xmlautomata.h>
34#include <libxml/xmlregexp.h>
Daniel Veillardc6e997c2003-01-27 12:35:42 +000035#include <libxml/xmlschemastypes.h>
Daniel Veillard6eadf632003-01-23 18:29:16 +000036
37/*
38 * The Relax-NG namespace
39 */
40static const xmlChar *xmlRelaxNGNs = (const xmlChar *)
41 "http://relaxng.org/ns/structure/1.0";
42
43#define IS_RELAXNG(node, type) \
44 ((node != NULL) && (node->ns != NULL) && \
45 (xmlStrEqual(node->name, (const xmlChar *) type)) && \
46 (xmlStrEqual(node->ns->href, xmlRelaxNGNs)))
47
48
Daniel Veillard71531f32003-02-05 13:19:53 +000049/* #define DEBUG 1 */ /* very verbose output */
Daniel Veillardc482e262003-02-26 14:48:48 +000050/* #define DEBUG_GRAMMAR 1 */
Daniel Veillard71531f32003-02-05 13:19:53 +000051/* #define DEBUG_CONTENT 1 */
52/* #define DEBUG_TYPE 1 */
53/* #define DEBUG_VALID 1 */
Daniel Veillarde5b110b2003-02-04 14:43:39 +000054/* #define DEBUG_INTERLEAVE 1 */
Daniel Veillard416589a2003-02-17 17:25:42 +000055/* #define DEBUG_LIST 1 */
Daniel Veillard6eadf632003-01-23 18:29:16 +000056
57#define UNBOUNDED (1 << 30)
58#define TODO \
59 xmlGenericError(xmlGenericErrorContext, \
60 "Unimplemented block at %s:%d\n", \
61 __FILE__, __LINE__);
62
63typedef struct _xmlRelaxNGSchema xmlRelaxNGSchema;
64typedef xmlRelaxNGSchema *xmlRelaxNGSchemaPtr;
65
66typedef struct _xmlRelaxNGDefine xmlRelaxNGDefine;
67typedef xmlRelaxNGDefine *xmlRelaxNGDefinePtr;
68
Daniel Veillardd41f4f42003-01-29 21:07:52 +000069typedef struct _xmlRelaxNGDocument xmlRelaxNGDocument;
70typedef xmlRelaxNGDocument *xmlRelaxNGDocumentPtr;
71
Daniel Veillarda9d912d2003-02-01 17:43:10 +000072typedef struct _xmlRelaxNGInclude xmlRelaxNGInclude;
73typedef xmlRelaxNGInclude *xmlRelaxNGIncludePtr;
74
Daniel Veillard6eadf632003-01-23 18:29:16 +000075typedef enum {
76 XML_RELAXNG_COMBINE_UNDEFINED = 0, /* undefined */
77 XML_RELAXNG_COMBINE_CHOICE, /* choice */
78 XML_RELAXNG_COMBINE_INTERLEAVE /* interleave */
79} xmlRelaxNGCombine;
80
Daniel Veillard4c5cf702003-02-21 15:40:34 +000081typedef enum {
82 XML_RELAXNG_CONTENT_ERROR = -1,
83 XML_RELAXNG_CONTENT_EMPTY = 0,
84 XML_RELAXNG_CONTENT_SIMPLE,
85 XML_RELAXNG_CONTENT_COMPLEX
86} xmlRelaxNGContentType;
87
Daniel Veillard6eadf632003-01-23 18:29:16 +000088typedef struct _xmlRelaxNGGrammar xmlRelaxNGGrammar;
89typedef xmlRelaxNGGrammar *xmlRelaxNGGrammarPtr;
90
91struct _xmlRelaxNGGrammar {
92 xmlRelaxNGGrammarPtr parent;/* the parent grammar if any */
93 xmlRelaxNGGrammarPtr children;/* the children grammar if any */
94 xmlRelaxNGGrammarPtr next; /* the next grammar if any */
95 xmlRelaxNGDefinePtr start; /* <start> content */
96 xmlRelaxNGCombine combine; /* the default combine value */
Daniel Veillardd41f4f42003-01-29 21:07:52 +000097 xmlRelaxNGDefinePtr startList;/* list of <start> definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +000098 xmlHashTablePtr defs; /* define* */
99 xmlHashTablePtr refs; /* references */
100};
101
102
Daniel Veillard6eadf632003-01-23 18:29:16 +0000103typedef enum {
Daniel Veillard77648bb2003-02-20 15:03:22 +0000104 XML_RELAXNG_NOOP = -1, /* a no operation from simplification */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000105 XML_RELAXNG_EMPTY = 0, /* an empty pattern */
106 XML_RELAXNG_NOT_ALLOWED, /* not allowed top */
Daniel Veillard144fae12003-02-03 13:17:57 +0000107 XML_RELAXNG_EXCEPT, /* except present in nameclass defs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000108 XML_RELAXNG_TEXT, /* textual content */
109 XML_RELAXNG_ELEMENT, /* an element */
110 XML_RELAXNG_DATATYPE, /* extenal data type definition */
Daniel Veillard8fe98712003-02-19 00:19:14 +0000111 XML_RELAXNG_PARAM, /* extenal data type parameter */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000112 XML_RELAXNG_VALUE, /* value from an extenal data type definition */
113 XML_RELAXNG_LIST, /* a list of patterns */
114 XML_RELAXNG_ATTRIBUTE, /* an attrbute following a pattern */
115 XML_RELAXNG_DEF, /* a definition */
116 XML_RELAXNG_REF, /* reference to a definition */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000117 XML_RELAXNG_EXTERNALREF, /* reference to an external def */
Daniel Veillard419a7682003-02-03 23:22:49 +0000118 XML_RELAXNG_PARENTREF, /* reference to a def in the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000119 XML_RELAXNG_OPTIONAL, /* optional patterns */
120 XML_RELAXNG_ZEROORMORE, /* zero or more non empty patterns */
121 XML_RELAXNG_ONEORMORE, /* one or more non empty patterns */
122 XML_RELAXNG_CHOICE, /* a choice between non empty patterns */
123 XML_RELAXNG_GROUP, /* a pair/group of non empty patterns */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000124 XML_RELAXNG_INTERLEAVE, /* interleaving choice of non-empty patterns */
125 XML_RELAXNG_START /* Used to keep track of starts on grammars */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000126} xmlRelaxNGType;
127
128struct _xmlRelaxNGDefine {
129 xmlRelaxNGType type; /* the type of definition */
130 xmlNodePtr node; /* the node in the source */
131 xmlChar *name; /* the element local name if present */
132 xmlChar *ns; /* the namespace local name if present */
Daniel Veillardedc91922003-01-26 00:52:04 +0000133 xmlChar *value; /* value when available */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000134 void *data; /* data lib or specific pointer */
Daniel Veillardd4310742003-02-18 21:12:46 +0000135 int depth; /* used for the cycle detection */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000136 xmlRelaxNGDefinePtr content;/* the expected content */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000137 xmlRelaxNGDefinePtr parent; /* the parent definition, if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000138 xmlRelaxNGDefinePtr next; /* list within grouping sequences */
139 xmlRelaxNGDefinePtr attrs; /* list of attributes for elements */
Daniel Veillard3b2e4e12003-02-03 08:52:58 +0000140 xmlRelaxNGDefinePtr nameClass;/* the nameClass definition if any */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000141 xmlRelaxNGDefinePtr nextHash;/* next define in defs/refs hash tables */
142};
143
144/**
145 * _xmlRelaxNG:
146 *
147 * A RelaxNGs definition
148 */
149struct _xmlRelaxNG {
150 xmlRelaxNGGrammarPtr topgrammar;
151 xmlDocPtr doc;
152
153 xmlHashTablePtr defs; /* define */
154 xmlHashTablePtr refs; /* references */
Daniel Veillardc482e262003-02-26 14:48:48 +0000155 xmlRelaxNGDocumentPtr documents; /* all the documents loaded */
156 xmlRelaxNGIncludePtr includes; /* all the includes loaded */
Daniel Veillard419a7682003-02-03 23:22:49 +0000157 int defNr; /* number of defines used */
158 xmlRelaxNGDefinePtr *defTab;/* pointer to the allocated definitions */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000159 void *_private; /* unused by the library for users or bindings */
160};
161
162typedef enum {
163 XML_RELAXNG_ERR_OK = 0,
164 XML_RELAXNG_ERR_NOROOT = 1,
165 XML_RELAXNG_ERR_
166} xmlRelaxNGValidError;
167
Daniel Veillard77648bb2003-02-20 15:03:22 +0000168#define XML_RELAXNG_IN_ATTRIBUTE (1 << 0)
169#define XML_RELAXNG_IN_ONEORMORE (1 << 1)
170#define XML_RELAXNG_IN_LIST (1 << 2)
171#define XML_RELAXNG_IN_DATAEXCEPT (1 << 3)
172#define XML_RELAXNG_IN_START (1 << 4)
173#define XML_RELAXNG_IN_OOMGROUP (1 << 5)
174#define XML_RELAXNG_IN_OOMINTERLEAVE (1 << 6)
175#define XML_RELAXNG_IN_EXTERNALREF (1 << 7)
Daniel Veillardc5312d72003-02-21 17:14:10 +0000176#define XML_RELAXNG_IN_ANYEXCEPT (1 << 8)
177#define XML_RELAXNG_IN_NSEXCEPT (1 << 9)
Daniel Veillard6eadf632003-01-23 18:29:16 +0000178
179struct _xmlRelaxNGParserCtxt {
180 void *userData; /* user specific data block */
181 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
182 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
183 xmlRelaxNGValidError err;
184
185 xmlRelaxNGPtr schema; /* The schema in use */
186 xmlRelaxNGGrammarPtr grammar; /* the current grammar */
Daniel Veillard419a7682003-02-03 23:22:49 +0000187 xmlRelaxNGGrammarPtr parentgrammar;/* the parent grammar */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000188 int flags; /* parser flags */
189 int nbErrors; /* number of errors at parse time */
190 int nbWarnings; /* number of warnings at parse time */
Daniel Veillard276be4a2003-01-24 01:03:34 +0000191 const xmlChar *define; /* the current define scope */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000192 xmlRelaxNGDefinePtr def; /* the current define */
193
194 int nbInterleaves;
195 xmlHashTablePtr interleaves; /* keep track of all the interleaves */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000196
Daniel Veillardc482e262003-02-26 14:48:48 +0000197 xmlRelaxNGDocumentPtr documents; /* all the documents loaded */
198 xmlRelaxNGIncludePtr includes; /* all the includes loaded */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000199 xmlChar *URL;
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000200 xmlDocPtr document;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000201
Daniel Veillard419a7682003-02-03 23:22:49 +0000202 int defNr; /* number of defines used */
203 int defMax; /* number of defines aloocated */
204 xmlRelaxNGDefinePtr *defTab; /* pointer to the allocated definitions */
205
Daniel Veillard6eadf632003-01-23 18:29:16 +0000206 const char *buffer;
207 int size;
208
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000209 /* the document stack */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000210 xmlRelaxNGDocumentPtr doc; /* Current parsed external ref */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000211 int docNr; /* Depth of the parsing stack */
212 int docMax; /* Max depth of the parsing stack */
213 xmlRelaxNGDocumentPtr *docTab; /* array of docs */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000214
215 /* the include stack */
Daniel Veillardd4310742003-02-18 21:12:46 +0000216 xmlRelaxNGIncludePtr inc; /* Current parsed include */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000217 int incNr; /* Depth of the include parsing stack */
218 int incMax; /* Max depth of the parsing stack */
219 xmlRelaxNGIncludePtr *incTab; /* array of incs */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000220};
221
222#define FLAGS_IGNORABLE 1
223#define FLAGS_NEGATIVE 2
224
225/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000226 * xmlRelaxNGInterleaveGroup:
227 *
228 * A RelaxNGs partition set associated to lists of definitions
229 */
230typedef struct _xmlRelaxNGInterleaveGroup xmlRelaxNGInterleaveGroup;
231typedef xmlRelaxNGInterleaveGroup *xmlRelaxNGInterleaveGroupPtr;
232struct _xmlRelaxNGInterleaveGroup {
233 xmlRelaxNGDefinePtr rule; /* the rule to satisfy */
234 xmlRelaxNGDefinePtr *defs; /* the array of element definitions */
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000235 xmlRelaxNGDefinePtr *attrs; /* the array of attributes definitions */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000236};
237
238/**
239 * xmlRelaxNGPartitions:
240 *
241 * A RelaxNGs partition associated to an interleave group
242 */
243typedef struct _xmlRelaxNGPartition xmlRelaxNGPartition;
244typedef xmlRelaxNGPartition *xmlRelaxNGPartitionPtr;
245struct _xmlRelaxNGPartition {
246 int nbgroups; /* number of groups in the partitions */
247 xmlRelaxNGInterleaveGroupPtr *groups;
248};
249
250/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000251 * xmlRelaxNGValidState:
252 *
253 * A RelaxNGs validation state
254 */
255#define MAX_ATTR 20
256typedef struct _xmlRelaxNGValidState xmlRelaxNGValidState;
257typedef xmlRelaxNGValidState *xmlRelaxNGValidStatePtr;
258struct _xmlRelaxNGValidState {
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000259 xmlNodePtr node; /* the current node */
260 xmlNodePtr seq; /* the sequence of children left to validate */
261 int nbAttrs; /* the number of attributes */
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000262 int nbAttrLeft; /* the number of attributes left to validate */
Daniel Veillardc6e997c2003-01-27 12:35:42 +0000263 xmlChar *value; /* the value when operating on string */
264 xmlChar *endvalue; /* the end value when operating on string */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000265 xmlAttrPtr attrs[1]; /* the array of attributes */
266};
267
268/**
269 * xmlRelaxNGValidCtxt:
270 *
271 * A RelaxNGs validation context
272 */
273
274struct _xmlRelaxNGValidCtxt {
275 void *userData; /* user specific data block */
276 xmlRelaxNGValidityErrorFunc error; /* the callback in case of errors */
277 xmlRelaxNGValidityWarningFunc warning;/* the callback in case of warning */
278
279 xmlRelaxNGPtr schema; /* The schema in use */
280 xmlDocPtr doc; /* the document being validated */
281 xmlRelaxNGValidStatePtr state; /* the current validation state */
282 int flags; /* validation flags */
Daniel Veillard231d7912003-02-09 14:22:17 +0000283 int depth; /* validation depth */
Daniel Veillard6eadf632003-01-23 18:29:16 +0000284};
285
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000286/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000287 * xmlRelaxNGInclude:
288 *
289 * Structure associated to a RelaxNGs document element
290 */
291struct _xmlRelaxNGInclude {
Daniel Veillardc482e262003-02-26 14:48:48 +0000292 xmlRelaxNGIncludePtr next; /* keep a chain of includes */
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000293 xmlChar *href; /* the normalized href value */
294 xmlDocPtr doc; /* the associated XML document */
295 xmlRelaxNGDefinePtr content;/* the definitions */
296 xmlRelaxNGPtr schema; /* the schema */
297};
298
299/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000300 * xmlRelaxNGDocument:
301 *
302 * Structure associated to a RelaxNGs document element
303 */
304struct _xmlRelaxNGDocument {
Daniel Veillardc482e262003-02-26 14:48:48 +0000305 xmlRelaxNGDocumentPtr next; /* keep a chain of documents */
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000306 xmlChar *href; /* the normalized href value */
307 xmlDocPtr doc; /* the associated XML document */
308 xmlRelaxNGDefinePtr content;/* the definitions */
309 xmlRelaxNGPtr schema; /* the schema */
310};
311
Daniel Veillard3ebc7d42003-02-24 17:17:58 +0000312
Daniel Veillard6eadf632003-01-23 18:29:16 +0000313/************************************************************************
314 * *
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000315 * Preliminary type checking interfaces *
316 * *
317 ************************************************************************/
318/**
319 * xmlRelaxNGTypeHave:
320 * @data: data needed for the library
321 * @type: the type name
322 * @value: the value to check
323 *
324 * Function provided by a type library to check if a type is exported
325 *
326 * Returns 1 if yes, 0 if no and -1 in case of error.
327 */
328typedef int (*xmlRelaxNGTypeHave) (void *data, const xmlChar *type);
329
330/**
331 * xmlRelaxNGTypeCheck:
332 * @data: data needed for the library
333 * @type: the type name
334 * @value: the value to check
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000335 * @result: place to store the result if needed
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000336 *
337 * Function provided by a type library to check if a value match a type
338 *
339 * Returns 1 if yes, 0 if no and -1 in case of error.
340 */
341typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000342 const xmlChar *value, void **result);
343
344/**
345 * xmlRelaxNGFacetCheck:
346 * @data: data needed for the library
347 * @type: the type name
348 * @facet: the facet name
349 * @val: the facet value
350 * @strval: the string value
351 * @value: the value to check
352 *
353 * Function provided by a type library to check a value facet
354 *
355 * Returns 1 if yes, 0 if no and -1 in case of error.
356 */
357typedef int (*xmlRelaxNGFacetCheck) (void *data, const xmlChar *type,
358 const xmlChar *facet, const xmlChar *val,
359 const xmlChar *strval, void *value);
360
361/**
362 * xmlRelaxNGTypeFree:
363 * @data: data needed for the library
364 * @result: the value to free
365 *
366 * Function provided by a type library to free a returned result
367 */
368typedef void (*xmlRelaxNGTypeFree) (void *data, void *result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000369
370/**
371 * xmlRelaxNGTypeCompare:
372 * @data: data needed for the library
373 * @type: the type name
374 * @value1: the first value
375 * @value2: the second value
376 *
377 * Function provided by a type library to compare two values accordingly
378 * to a type.
379 *
380 * Returns 1 if yes, 0 if no and -1 in case of error.
381 */
382typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
383 const xmlChar *value1,
384 const xmlChar *value2);
385typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
386typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
387struct _xmlRelaxNGTypeLibrary {
388 const xmlChar *namespace; /* the datatypeLibrary value */
389 void *data; /* data needed for the library */
390 xmlRelaxNGTypeHave have; /* the export function */
391 xmlRelaxNGTypeCheck check; /* the checking function */
392 xmlRelaxNGTypeCompare comp; /* the compare function */
Daniel Veillard8bc6cf92003-02-27 17:42:22 +0000393 xmlRelaxNGFacetCheck facet; /* the facet check function */
394 xmlRelaxNGTypeFree freef; /* the freeing function */
Daniel Veillarddd1655c2003-01-25 18:01:32 +0000395};
396
397/************************************************************************
398 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000399 * Allocation functions *
400 * *
401 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000402static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
403static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000404static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillardc482e262003-02-26 14:48:48 +0000405static void xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000406
407/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000408 * xmlRelaxNGFreeDocument:
409 * @docu: a document structure
410 *
411 * Deallocate a RelaxNG document structure.
412 */
413static void
414xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
415{
416 if (docu == NULL)
417 return;
418
419 if (docu->href != NULL)
420 xmlFree(docu->href);
421 if (docu->doc != NULL)
422 xmlFreeDoc(docu->doc);
423 if (docu->schema != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000424 xmlRelaxNGFreeInnerSchema(docu->schema);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000425 xmlFree(docu);
426}
427
428/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000429 * xmlRelaxNGFreeDocumentList:
430 * @docu: a list of document structure
431 *
432 * Deallocate a RelaxNG document structures.
433 */
434static void
435xmlRelaxNGFreeDocumentList(xmlRelaxNGDocumentPtr docu)
436{
437 xmlRelaxNGDocumentPtr next;
438 while (docu != NULL) {
439 next = docu->next;
440 xmlRelaxNGFreeDocument(docu);
441 docu = next;
442 }
443}
444
445/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000446 * xmlRelaxNGFreeInclude:
447 * @incl: a include structure
448 *
449 * Deallocate a RelaxNG include structure.
450 */
451static void
452xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
453{
454 if (incl == NULL)
455 return;
456
457 if (incl->href != NULL)
458 xmlFree(incl->href);
459 if (incl->doc != NULL)
460 xmlFreeDoc(incl->doc);
461 if (incl->schema != NULL)
462 xmlRelaxNGFree(incl->schema);
463 xmlFree(incl);
464}
465
466/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000467 * xmlRelaxNGFreeIncludeList:
468 * @incl: a include structure list
469 *
470 * Deallocate a RelaxNG include structure.
471 */
472static void
473xmlRelaxNGFreeIncludeList(xmlRelaxNGIncludePtr incl)
474{
475 xmlRelaxNGIncludePtr next;
476 while (incl != NULL) {
477 next = incl->next;
478 xmlRelaxNGFreeInclude(incl);
479 incl = next;
480 }
481}
482
483/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000484 * xmlRelaxNGNewRelaxNG:
485 * @ctxt: a Relax-NG validation context (optional)
486 *
487 * Allocate a new RelaxNG structure.
488 *
489 * Returns the newly allocated structure or NULL in case or error
490 */
491static xmlRelaxNGPtr
492xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
493{
494 xmlRelaxNGPtr ret;
495
496 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
497 if (ret == NULL) {
498 if ((ctxt != NULL) && (ctxt->error != NULL))
499 ctxt->error(ctxt->userData, "Out of memory\n");
500 ctxt->nbErrors++;
501 return (NULL);
502 }
503 memset(ret, 0, sizeof(xmlRelaxNG));
504
505 return (ret);
506}
507
508/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000509 * xmlRelaxNGFreeInnerSchema:
510 * @schema: a schema structure
511 *
512 * Deallocate a RelaxNG schema structure.
513 */
514static void
515xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema)
516{
517 if (schema == NULL)
518 return;
519
520 if (schema->doc != NULL)
521 xmlFreeDoc(schema->doc);
522 if (schema->defTab != NULL) {
523 int i;
524
525 for (i = 0;i < schema->defNr;i++)
526 xmlRelaxNGFreeDefine(schema->defTab[i]);
527 xmlFree(schema->defTab);
528 }
529
530 xmlFree(schema);
531}
532
533/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000534 * xmlRelaxNGFree:
535 * @schema: a schema structure
536 *
537 * Deallocate a RelaxNG structure.
538 */
539void
540xmlRelaxNGFree(xmlRelaxNGPtr schema)
541{
542 if (schema == NULL)
543 return;
544
Daniel Veillard6eadf632003-01-23 18:29:16 +0000545 if (schema->topgrammar != NULL)
546 xmlRelaxNGFreeGrammar(schema->topgrammar);
547 if (schema->doc != NULL)
548 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000549 if (schema->documents != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000550 xmlRelaxNGFreeDocumentList(schema->documents);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000551 if (schema->includes != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000552 xmlRelaxNGFreeIncludeList(schema->includes);
Daniel Veillard419a7682003-02-03 23:22:49 +0000553 if (schema->defTab != NULL) {
554 int i;
555
556 for (i = 0;i < schema->defNr;i++)
557 xmlRelaxNGFreeDefine(schema->defTab[i]);
558 xmlFree(schema->defTab);
559 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000560
561 xmlFree(schema);
562}
563
564/**
565 * xmlRelaxNGNewGrammar:
566 * @ctxt: a Relax-NG validation context (optional)
567 *
568 * Allocate a new RelaxNG grammar.
569 *
570 * Returns the newly allocated structure or NULL in case or error
571 */
572static xmlRelaxNGGrammarPtr
573xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
574{
575 xmlRelaxNGGrammarPtr ret;
576
577 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
578 if (ret == NULL) {
579 if ((ctxt != NULL) && (ctxt->error != NULL))
580 ctxt->error(ctxt->userData, "Out of memory\n");
581 ctxt->nbErrors++;
582 return (NULL);
583 }
584 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
585
586 return (ret);
587}
588
589/**
590 * xmlRelaxNGFreeGrammar:
591 * @grammar: a grammar structure
592 *
593 * Deallocate a RelaxNG grammar structure.
594 */
595static void
596xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
597{
598 if (grammar == NULL)
599 return;
600
Daniel Veillardc482e262003-02-26 14:48:48 +0000601 if (grammar->children != NULL) {
602 xmlRelaxNGFreeGrammar(grammar->children);
603 }
Daniel Veillard419a7682003-02-03 23:22:49 +0000604 if (grammar->next != NULL) {
605 xmlRelaxNGFreeGrammar(grammar->next);
606 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000607 if (grammar->refs != NULL) {
608 xmlHashFree(grammar->refs, NULL);
609 }
610 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000611 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000612 }
613
614 xmlFree(grammar);
615}
616
617/**
618 * xmlRelaxNGNewDefine:
619 * @ctxt: a Relax-NG validation context
620 * @node: the node in the input document.
621 *
622 * Allocate a new RelaxNG define.
623 *
624 * Returns the newly allocated structure or NULL in case or error
625 */
626static xmlRelaxNGDefinePtr
627xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
628{
629 xmlRelaxNGDefinePtr ret;
630
Daniel Veillard419a7682003-02-03 23:22:49 +0000631 if (ctxt->defMax == 0) {
632 ctxt->defMax = 16;
633 ctxt->defNr = 0;
634 ctxt->defTab = (xmlRelaxNGDefinePtr *)
635 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
636 if (ctxt->defTab == NULL) {
637 if ((ctxt != NULL) && (ctxt->error != NULL))
638 ctxt->error(ctxt->userData, "Out of memory\n");
639 ctxt->nbErrors++;
640 return (NULL);
641 }
642 } else if (ctxt->defMax <= ctxt->defNr) {
643 xmlRelaxNGDefinePtr *tmp;
644 ctxt->defMax *= 2;
645 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
646 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
647 if (tmp == NULL) {
648 if ((ctxt != NULL) && (ctxt->error != NULL))
649 ctxt->error(ctxt->userData, "Out of memory\n");
650 ctxt->nbErrors++;
651 return (NULL);
652 }
653 ctxt->defTab = tmp;
654 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000655 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
656 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000657 if ((ctxt != NULL) && (ctxt->error != NULL))
658 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000659 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000660 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000661 }
662 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000663 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000664 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000665 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000666 return (ret);
667}
668
669/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000670 * xmlRelaxNGFreePartition:
671 * @partitions: a partition set structure
672 *
673 * Deallocate RelaxNG partition set structures.
674 */
675static void
676xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
677 xmlRelaxNGInterleaveGroupPtr group;
678 int j;
679
680 if (partitions != NULL) {
681 if (partitions->groups != NULL) {
682 for (j = 0;j < partitions->nbgroups;j++) {
683 group = partitions->groups[j];
684 if (group != NULL) {
685 if (group->defs != NULL)
686 xmlFree(group->defs);
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000687 if (group->attrs != NULL)
688 xmlFree(group->attrs);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000689 xmlFree(group);
690 }
691 }
692 xmlFree(partitions->groups);
693 }
694 xmlFree(partitions);
695 }
696}
697/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000698 * xmlRelaxNGFreeDefine:
699 * @define: a define structure
700 *
701 * Deallocate a RelaxNG define structure.
702 */
703static void
704xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
705{
706 if (define == NULL)
707 return;
708
Daniel Veillard419a7682003-02-03 23:22:49 +0000709 if ((define->data != NULL) &&
710 (define->type == XML_RELAXNG_INTERLEAVE))
711 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000712 if (define->name != NULL)
713 xmlFree(define->name);
714 if (define->ns != NULL)
715 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000716 if (define->value != NULL)
717 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000718 xmlFree(define);
719}
720
721/**
722 * xmlRelaxNGNewValidState:
723 * @ctxt: a Relax-NG validation context
724 * @node: the current node or NULL for the document
725 *
726 * Allocate a new RelaxNG validation state
727 *
728 * Returns the newly allocated structure or NULL in case or error
729 */
730static xmlRelaxNGValidStatePtr
731xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
732{
733 xmlRelaxNGValidStatePtr ret;
734 xmlAttrPtr attr;
735 xmlAttrPtr attrs[MAX_ATTR];
736 int nbAttrs = 0;
737 xmlNodePtr root = NULL;
738
739 if (node == NULL) {
740 root = xmlDocGetRootElement(ctxt->doc);
741 if (root == NULL)
742 return(NULL);
743 } else {
744 attr = node->properties;
745 while (attr != NULL) {
746 if (nbAttrs < MAX_ATTR)
747 attrs[nbAttrs++] = attr;
748 else
749 nbAttrs++;
750 attr = attr->next;
751 }
752 }
753
754 if (nbAttrs < MAX_ATTR)
755 attrs[nbAttrs] = NULL;
756 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
757 nbAttrs * sizeof(xmlAttrPtr));
758 if (ret == NULL) {
759 if ((ctxt != NULL) && (ctxt->error != NULL))
760 ctxt->error(ctxt->userData, "Out of memory\n");
761 return (NULL);
762 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000763 ret->value = NULL;
764 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000765 if (node == NULL) {
766 ret->node = (xmlNodePtr) ctxt->doc;
767 ret->seq = root;
768 ret->nbAttrs = 0;
769 } else {
770 ret->node = node;
771 ret->seq = node->children;
772 ret->nbAttrs = nbAttrs;
773 if (nbAttrs > 0) {
774 if (nbAttrs < MAX_ATTR) {
775 memcpy(&(ret->attrs[0]), attrs,
776 sizeof(xmlAttrPtr) * (nbAttrs + 1));
777 } else {
778 attr = node->properties;
779 nbAttrs = 0;
780 while (attr != NULL) {
781 ret->attrs[nbAttrs++] = attr;
782 attr = attr->next;
783 }
784 ret->attrs[nbAttrs] = NULL;
785 }
786 }
787 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000788 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000789 return (ret);
790}
791
792/**
793 * xmlRelaxNGCopyValidState:
794 * @ctxt: a Relax-NG validation context
795 * @state: a validation state
796 *
797 * Copy the validation state
798 *
799 * Returns the newly allocated structure or NULL in case or error
800 */
801static xmlRelaxNGValidStatePtr
802xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
803 xmlRelaxNGValidStatePtr state)
804{
805 xmlRelaxNGValidStatePtr ret;
806 unsigned int size;
807
808 if (state == NULL)
809 return(NULL);
810
811 size = sizeof(xmlRelaxNGValidState) +
812 state->nbAttrs * sizeof(xmlAttrPtr);
813 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
814 if (ret == NULL) {
815 if ((ctxt != NULL) && (ctxt->error != NULL))
816 ctxt->error(ctxt->userData, "Out of memory\n");
817 return (NULL);
818 }
819 memcpy(ret, state, size);
820 return(ret);
821}
822
823/**
Daniel Veillardce14fa52003-02-19 17:32:48 +0000824 * xmlRelaxNGEqualValidState:
825 * @ctxt: a Relax-NG validation context
826 * @state1: a validation state
827 * @state2: a validation state
828 *
829 * Compare the validation states for equality
830 *
831 * Returns 1 if equald, 0 otherwise
832 */
833static int
834xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
835 xmlRelaxNGValidStatePtr state1,
836 xmlRelaxNGValidStatePtr state2)
837{
838 int i;
839
840 if ((state1 == NULL) || (state2 == NULL))
841 return(0);
842 if (state1 == state2)
843 return(1);
844 if (state1->node != state2->node)
845 return(0);
846 if (state1->seq != state2->seq)
847 return(0);
848 if (state1->nbAttrLeft != state2->nbAttrLeft)
849 return(0);
850 if (state1->nbAttrs != state2->nbAttrs)
851 return(0);
852 if (state1->endvalue != state2->endvalue)
853 return(0);
854 if ((state1->value != state2->value) &&
855 (!xmlStrEqual(state1->value, state2->value)))
856 return(0);
857 for (i = 0;i < state1->nbAttrs;i++) {
858 if (state1->attrs[i] != state2->attrs[i])
859 return(0);
860 }
861 return(1);
862}
863
864/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000865 * xmlRelaxNGFreeValidState:
866 * @state: a validation state structure
867 *
868 * Deallocate a RelaxNG validation state structure.
869 */
870static void
871xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
872{
873 if (state == NULL)
874 return;
875
876 xmlFree(state);
877}
878
879/************************************************************************
880 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000881 * Document functions *
882 * *
883 ************************************************************************/
884static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
885 xmlDocPtr doc);
886
887/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000888 * xmlRelaxNGIncludePush:
889 * @ctxt: the parser context
890 * @value: the element doc
891 *
892 * Pushes a new include on top of the include stack
893 *
894 * Returns 0 in case of error, the index in the stack otherwise
895 */
896static int
897xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
898 xmlRelaxNGIncludePtr value)
899{
900 if (ctxt->incTab == NULL) {
901 ctxt->incMax = 4;
902 ctxt->incNr = 0;
903 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
904 ctxt->incMax * sizeof(ctxt->incTab[0]));
905 if (ctxt->incTab == NULL) {
906 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
907 return (0);
908 }
909 }
910 if (ctxt->incNr >= ctxt->incMax) {
911 ctxt->incMax *= 2;
912 ctxt->incTab =
913 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
914 ctxt->incMax *
915 sizeof(ctxt->incTab[0]));
916 if (ctxt->incTab == NULL) {
917 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
918 return (0);
919 }
920 }
921 ctxt->incTab[ctxt->incNr] = value;
922 ctxt->inc = value;
923 return (ctxt->incNr++);
924}
925
926/**
927 * xmlRelaxNGIncludePop:
928 * @ctxt: the parser context
929 *
930 * Pops the top include from the include stack
931 *
932 * Returns the include just removed
933 */
934static xmlRelaxNGIncludePtr
935xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
936{
937 xmlRelaxNGIncludePtr ret;
938
939 if (ctxt->incNr <= 0)
940 return (0);
941 ctxt->incNr--;
942 if (ctxt->incNr > 0)
943 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
944 else
945 ctxt->inc = NULL;
946 ret = ctxt->incTab[ctxt->incNr];
947 ctxt->incTab[ctxt->incNr] = 0;
948 return (ret);
949}
950
951/**
952 * xmlRelaxNGLoadInclude:
953 * @ctxt: the parser context
954 * @URL: the normalized URL
955 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +0000956 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000957 *
958 * First lookup if the document is already loaded into the parser context,
959 * check against recursion. If not found the resource is loaded and
960 * the content is preprocessed before being returned back to the caller.
961 *
962 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
963 */
964static xmlRelaxNGIncludePtr
965xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +0000966 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000967 xmlRelaxNGIncludePtr ret = NULL;
968 xmlDocPtr doc;
969 int i;
970 xmlNodePtr root, tmp, tmp2, cur;
971
972 /*
973 * check against recursion in the stack
974 */
975 for (i = 0;i < ctxt->incNr;i++) {
976 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
977 if (ctxt->error != NULL)
978 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +0000979 "Detected an Include recursion for %s\n",
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000980 URL);
981 ctxt->nbErrors++;
982 return(NULL);
983 }
984 }
985
986 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000987 * load the document
988 */
989 doc = xmlParseFile((const char *) URL);
990 if (doc == NULL) {
991 if (ctxt->error != NULL)
992 ctxt->error(ctxt->userData,
993 "xmlRelaxNG: could not load %s\n", URL);
994 ctxt->nbErrors++;
995 return (NULL);
996 }
997
998 /*
999 * Allocate the document structures and register it first.
1000 */
1001 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
1002 if (ret == NULL) {
1003 if (ctxt->error != NULL)
1004 ctxt->error(ctxt->userData,
1005 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1006 ctxt->nbErrors++;
1007 xmlFreeDoc(doc);
1008 return (NULL);
1009 }
1010 memset(ret, 0, sizeof(xmlRelaxNGInclude));
1011 ret->doc = doc;
1012 ret->href = xmlStrdup(URL);
Daniel Veillardc482e262003-02-26 14:48:48 +00001013 ret->next = ctxt->includes;
1014 ctxt->includes = ret;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001015
1016 /*
Daniel Veillard416589a2003-02-17 17:25:42 +00001017 * transmit the ns if needed
1018 */
1019 if (ns != NULL) {
1020 root = xmlDocGetRootElement(doc);
1021 if (root != NULL) {
1022 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1023 xmlSetProp(root, BAD_CAST"ns", ns);
1024 }
1025 }
1026 }
1027
1028 /*
Daniel Veillardc482e262003-02-26 14:48:48 +00001029 * push it on the stack
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001030 */
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001031 xmlRelaxNGIncludePush(ctxt, ret);
1032
1033 /*
1034 * Some preprocessing of the document content, this include recursing
1035 * in the include stack.
1036 */
1037 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1038 if (doc == NULL) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001039 ctxt->inc = NULL;
1040 return(NULL);
1041 }
1042
1043 /*
1044 * Pop up the include from the stack
1045 */
1046 xmlRelaxNGIncludePop(ctxt);
1047
1048 /*
1049 * Check that the top element is a grammar
1050 */
1051 root = xmlDocGetRootElement(doc);
1052 if (root == NULL) {
1053 if (ctxt->error != NULL)
1054 ctxt->error(ctxt->userData,
1055 "xmlRelaxNG: included document is empty %s\n", URL);
1056 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001057 return (NULL);
1058 }
1059 if (!IS_RELAXNG(root, "grammar")) {
1060 if (ctxt->error != NULL)
1061 ctxt->error(ctxt->userData,
1062 "xmlRelaxNG: included document %s root is not a grammar\n",
1063 URL);
1064 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001065 return (NULL);
1066 }
1067
1068 /*
1069 * Elimination of redefined rules in the include.
1070 */
1071 cur = node->children;
1072 while (cur != NULL) {
1073 if (IS_RELAXNG(cur, "start")) {
1074 int found = 0;
1075
1076 tmp = root->children;
1077 while (tmp != NULL) {
1078 tmp2 = tmp->next;
1079 if (IS_RELAXNG(tmp, "start")) {
1080 found = 1;
1081 xmlUnlinkNode(tmp);
1082 xmlFreeNode(tmp);
1083 }
1084 tmp = tmp2;
1085 }
1086 if (!found) {
1087 if (ctxt->error != NULL)
1088 ctxt->error(ctxt->userData,
1089 "xmlRelaxNG: include %s has a start but not the included grammar\n",
1090 URL);
1091 ctxt->nbErrors++;
1092 }
1093 } else if (IS_RELAXNG(cur, "define")) {
1094 xmlChar *name, *name2;
1095
1096 name = xmlGetProp(cur, BAD_CAST "name");
1097 if (name == NULL) {
1098 if (ctxt->error != NULL)
1099 ctxt->error(ctxt->userData,
1100 "xmlRelaxNG: include %s has define without name\n",
1101 URL);
1102 ctxt->nbErrors++;
1103 } else {
1104 int found = 0;
1105
Daniel Veillardd2298792003-02-14 16:54:11 +00001106 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001107 tmp = root->children;
1108 while (tmp != NULL) {
1109 tmp2 = tmp->next;
1110 if (IS_RELAXNG(tmp, "define")) {
1111 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +00001112 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001113 if (name2 != NULL) {
1114 if (xmlStrEqual(name, name2)) {
1115 found = 1;
1116 xmlUnlinkNode(tmp);
1117 xmlFreeNode(tmp);
1118 }
1119 xmlFree(name2);
1120 }
1121 }
1122 tmp = tmp2;
1123 }
1124 if (!found) {
1125 if (ctxt->error != NULL)
1126 ctxt->error(ctxt->userData,
1127 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1128 URL, name);
1129 ctxt->nbErrors++;
1130 }
1131 xmlFree(name);
1132 }
1133 }
1134 cur = cur->next;
1135 }
1136
1137
1138 return(ret);
1139}
1140
1141/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001142 * xmlRelaxNGDocumentPush:
1143 * @ctxt: the parser context
1144 * @value: the element doc
1145 *
1146 * Pushes a new doc on top of the doc stack
1147 *
1148 * Returns 0 in case of error, the index in the stack otherwise
1149 */
1150static int
1151xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1152 xmlRelaxNGDocumentPtr value)
1153{
1154 if (ctxt->docTab == NULL) {
1155 ctxt->docMax = 4;
1156 ctxt->docNr = 0;
1157 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1158 ctxt->docMax * sizeof(ctxt->docTab[0]));
1159 if (ctxt->docTab == NULL) {
1160 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1161 return (0);
1162 }
1163 }
1164 if (ctxt->docNr >= ctxt->docMax) {
1165 ctxt->docMax *= 2;
1166 ctxt->docTab =
1167 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1168 ctxt->docMax *
1169 sizeof(ctxt->docTab[0]));
1170 if (ctxt->docTab == NULL) {
1171 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1172 return (0);
1173 }
1174 }
1175 ctxt->docTab[ctxt->docNr] = value;
1176 ctxt->doc = value;
1177 return (ctxt->docNr++);
1178}
1179
1180/**
1181 * xmlRelaxNGDocumentPop:
1182 * @ctxt: the parser context
1183 *
1184 * Pops the top doc from the doc stack
1185 *
1186 * Returns the doc just removed
1187 */
1188static xmlRelaxNGDocumentPtr
1189xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1190{
1191 xmlRelaxNGDocumentPtr ret;
1192
1193 if (ctxt->docNr <= 0)
1194 return (0);
1195 ctxt->docNr--;
1196 if (ctxt->docNr > 0)
1197 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1198 else
1199 ctxt->doc = NULL;
1200 ret = ctxt->docTab[ctxt->docNr];
1201 ctxt->docTab[ctxt->docNr] = 0;
1202 return (ret);
1203}
1204
1205/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001206 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001207 * @ctxt: the parser context
1208 * @URL: the normalized URL
1209 * @ns: the inherited ns if any
1210 *
1211 * First lookup if the document is already loaded into the parser context,
1212 * check against recursion. If not found the resource is loaded and
1213 * the content is preprocessed before being returned back to the caller.
1214 *
1215 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1216 */
1217static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001218xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001219 const xmlChar *ns) {
1220 xmlRelaxNGDocumentPtr ret = NULL;
1221 xmlDocPtr doc;
1222 xmlNodePtr root;
1223 int i;
1224
1225 /*
1226 * check against recursion in the stack
1227 */
1228 for (i = 0;i < ctxt->docNr;i++) {
1229 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1230 if (ctxt->error != NULL)
1231 ctxt->error(ctxt->userData,
1232 "Detected an externalRef recursion for %s\n",
1233 URL);
1234 ctxt->nbErrors++;
1235 return(NULL);
1236 }
1237 }
1238
1239 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001240 * load the document
1241 */
1242 doc = xmlParseFile((const char *) URL);
1243 if (doc == NULL) {
1244 if (ctxt->error != NULL)
1245 ctxt->error(ctxt->userData,
1246 "xmlRelaxNG: could not load %s\n", URL);
1247 ctxt->nbErrors++;
1248 return (NULL);
1249 }
1250
1251 /*
1252 * Allocate the document structures and register it first.
1253 */
1254 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1255 if (ret == NULL) {
1256 if (ctxt->error != NULL)
1257 ctxt->error(ctxt->userData,
1258 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1259 ctxt->nbErrors++;
1260 xmlFreeDoc(doc);
1261 return (NULL);
1262 }
1263 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1264 ret->doc = doc;
1265 ret->href = xmlStrdup(URL);
Daniel Veillardc482e262003-02-26 14:48:48 +00001266 ret->next = ctxt->documents;
1267 ctxt->documents = ret;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001268
1269 /*
1270 * transmit the ns if needed
1271 */
1272 if (ns != NULL) {
1273 root = xmlDocGetRootElement(doc);
1274 if (root != NULL) {
1275 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1276 xmlSetProp(root, BAD_CAST"ns", ns);
1277 }
1278 }
1279 }
1280
1281 /*
1282 * push it on the stack and register it in the hash table
1283 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001284 xmlRelaxNGDocumentPush(ctxt, ret);
1285
1286 /*
1287 * Some preprocessing of the document content
1288 */
1289 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1290 if (doc == NULL) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001291 ctxt->doc = NULL;
1292 return(NULL);
1293 }
1294
1295 xmlRelaxNGDocumentPop(ctxt);
1296
1297 return(ret);
1298}
1299
1300/************************************************************************
1301 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001302 * Error functions *
1303 * *
1304 ************************************************************************/
1305
1306#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001307 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1308 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001309 "error detected at %s:%d\n", \
1310 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001311
1312#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001313 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001314 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1315#define VALID_ERROR2(a, b) \
1316 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1317 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1318#define VALID_ERROR3(a, b, c) \
1319 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1320 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001321
Daniel Veillardd2298792003-02-14 16:54:11 +00001322#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001323static const char *
1324xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1325 if (def == NULL)
1326 return("none");
1327 switch(def->type) {
1328 case XML_RELAXNG_EMPTY: return("empty");
1329 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1330 case XML_RELAXNG_EXCEPT: return("except");
1331 case XML_RELAXNG_TEXT: return("text");
1332 case XML_RELAXNG_ELEMENT: return("element");
1333 case XML_RELAXNG_DATATYPE: return("datatype");
1334 case XML_RELAXNG_VALUE: return("value");
1335 case XML_RELAXNG_LIST: return("list");
1336 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1337 case XML_RELAXNG_DEF: return("def");
1338 case XML_RELAXNG_REF: return("ref");
1339 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1340 case XML_RELAXNG_PARENTREF: return("parentRef");
1341 case XML_RELAXNG_OPTIONAL: return("optional");
1342 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1343 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1344 case XML_RELAXNG_CHOICE: return("choice");
1345 case XML_RELAXNG_GROUP: return("group");
1346 case XML_RELAXNG_INTERLEAVE: return("interleave");
1347 case XML_RELAXNG_START: return("start");
1348 }
1349 return("unknown");
1350}
Daniel Veillardd2298792003-02-14 16:54:11 +00001351#endif
1352
Daniel Veillard6eadf632003-01-23 18:29:16 +00001353#if 0
1354/**
1355 * xmlRelaxNGErrorContext:
1356 * @ctxt: the parsing context
1357 * @schema: the schema being built
1358 * @node: the node being processed
1359 * @child: the child being processed
1360 *
1361 * Dump a RelaxNGType structure
1362 */
1363static void
1364xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1365 xmlNodePtr node, xmlNodePtr child)
1366{
1367 int line = 0;
1368 const xmlChar *file = NULL;
1369 const xmlChar *name = NULL;
1370 const char *type = "error";
1371
1372 if ((ctxt == NULL) || (ctxt->error == NULL))
1373 return;
1374
1375 if (child != NULL)
1376 node = child;
1377
1378 if (node != NULL) {
1379 if ((node->type == XML_DOCUMENT_NODE) ||
1380 (node->type == XML_HTML_DOCUMENT_NODE)) {
1381 xmlDocPtr doc = (xmlDocPtr) node;
1382
1383 file = doc->URL;
1384 } else {
1385 /*
1386 * Try to find contextual informations to report
1387 */
1388 if (node->type == XML_ELEMENT_NODE) {
1389 line = (int) node->content;
1390 } else if ((node->prev != NULL) &&
1391 (node->prev->type == XML_ELEMENT_NODE)) {
1392 line = (int) node->prev->content;
1393 } else if ((node->parent != NULL) &&
1394 (node->parent->type == XML_ELEMENT_NODE)) {
1395 line = (int) node->parent->content;
1396 }
1397 if ((node->doc != NULL) && (node->doc->URL != NULL))
1398 file = node->doc->URL;
1399 if (node->name != NULL)
1400 name = node->name;
1401 }
1402 }
1403
1404 if (ctxt != NULL)
1405 type = "compilation error";
1406 else if (schema != NULL)
1407 type = "runtime error";
1408
1409 if ((file != NULL) && (line != 0) && (name != NULL))
1410 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1411 type, file, line, name);
1412 else if ((file != NULL) && (name != NULL))
1413 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1414 type, file, name);
1415 else if ((file != NULL) && (line != 0))
1416 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1417 else if (file != NULL)
1418 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1419 else if (name != NULL)
1420 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1421 else
1422 ctxt->error(ctxt->userData, "%s\n", type);
1423}
1424#endif
1425
1426/************************************************************************
1427 * *
1428 * Type library hooks *
1429 * *
1430 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001431static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1432 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001433
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001434/**
1435 * xmlRelaxNGSchemaTypeHave:
1436 * @data: data needed for the library
1437 * @type: the type name
1438 *
1439 * Check if the given type is provided by
1440 * the W3C XMLSchema Datatype library.
1441 *
1442 * Returns 1 if yes, 0 if no and -1 in case of error.
1443 */
1444static int
1445xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001446 const xmlChar *type) {
1447 xmlSchemaTypePtr typ;
1448
1449 if (type == NULL)
1450 return(-1);
1451 typ = xmlSchemaGetPredefinedType(type,
1452 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1453 if (typ == NULL)
1454 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001455 return(1);
1456}
1457
1458/**
1459 * xmlRelaxNGSchemaTypeCheck:
1460 * @data: data needed for the library
1461 * @type: the type name
1462 * @value: the value to check
1463 *
1464 * Check if the given type and value are validated by
1465 * the W3C XMLSchema Datatype library.
1466 *
1467 * Returns 1 if yes, 0 if no and -1 in case of error.
1468 */
1469static int
1470xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001471 const xmlChar *type,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001472 const xmlChar *value,
1473 void **result) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001474 xmlSchemaTypePtr typ;
1475 int ret;
1476
1477 /*
1478 * TODO: the type should be cached ab provided back, interface subject
1479 * to changes.
1480 * TODO: handle facets, may require an additional interface and keep
1481 * the value returned from the validation.
1482 */
1483 if ((type == NULL) || (value == NULL))
1484 return(-1);
1485 typ = xmlSchemaGetPredefinedType(type,
1486 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1487 if (typ == NULL)
1488 return(-1);
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001489 ret = xmlSchemaValidatePredefinedType(typ, value,
1490 (xmlSchemaValPtr *) result);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001491 if (ret == 0)
1492 return(1);
1493 if (ret > 0)
1494 return(0);
1495 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001496}
1497
1498/**
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001499 * xmlRelaxNGSchemaFacetCheck:
1500 * @data: data needed for the library
1501 * @type: the type name
1502 * @facet: the facet name
1503 * @val: the facet value
1504 * @strval: the string value
1505 * @value: the value to check
1506 *
1507 * Function provided by a type library to check a value facet
1508 *
1509 * Returns 1 if yes, 0 if no and -1 in case of error.
1510 */
1511static int
1512xmlRelaxNGSchemaFacetCheck (void *data, const xmlChar *type,
1513 const xmlChar *facetname, const xmlChar *val,
1514 const xmlChar *strval, void *value) {
1515 xmlSchemaFacetPtr facet;
1516 xmlSchemaTypePtr typ;
1517 int ret;
1518
1519 if ((type == NULL) || (strval == NULL))
1520 return(-1);
1521 typ = xmlSchemaGetPredefinedType(type,
1522 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1523 if (typ == NULL)
1524 return(-1);
1525
1526 facet = xmlSchemaNewFacet();
1527 if (facet == NULL)
1528 return(-1);
1529
1530 if (xmlStrEqual(facetname, BAD_CAST "minInclusive")) {
1531 facet->type = XML_SCHEMA_FACET_MININCLUSIVE;
1532 } else if (xmlStrEqual(facetname, BAD_CAST "minExclusive")) {
1533 facet->type = XML_SCHEMA_FACET_MINEXCLUSIVE;
1534 } else if (xmlStrEqual(facetname, BAD_CAST "maxInclusive")) {
1535 facet->type = XML_SCHEMA_FACET_MAXINCLUSIVE;
1536 } else if (xmlStrEqual(facetname, BAD_CAST "maxExclusive")) {
1537 facet->type = XML_SCHEMA_FACET_MAXEXCLUSIVE;
1538 } else if (xmlStrEqual(facetname, BAD_CAST "totalDigits")) {
1539 facet->type = XML_SCHEMA_FACET_TOTALDIGITS;
1540 } else if (xmlStrEqual(facetname, BAD_CAST "fractionDigits")) {
1541 facet->type = XML_SCHEMA_FACET_FRACTIONDIGITS;
1542 } else if (xmlStrEqual(facetname, BAD_CAST "pattern")) {
1543 facet->type = XML_SCHEMA_FACET_PATTERN;
1544 } else if (xmlStrEqual(facetname, BAD_CAST "enumeration")) {
1545 facet->type = XML_SCHEMA_FACET_ENUMERATION;
1546 } else if (xmlStrEqual(facetname, BAD_CAST "whiteSpace")) {
1547 facet->type = XML_SCHEMA_FACET_WHITESPACE;
1548 } else if (xmlStrEqual(facetname, BAD_CAST "length")) {
1549 facet->type = XML_SCHEMA_FACET_LENGTH;
1550 } else if (xmlStrEqual(facetname, BAD_CAST "maxLength")) {
1551 facet->type = XML_SCHEMA_FACET_MAXLENGTH;
1552 } else if (xmlStrEqual(facetname, BAD_CAST "minLength")) {
1553 facet->type = XML_SCHEMA_FACET_MINLENGTH;
1554 } else {
1555 xmlSchemaFreeFacet(facet);
1556 return(-1);
1557 }
1558 facet->value = xmlStrdup(val);
1559 ret = xmlSchemaCheckFacet(facet, typ, NULL, type);
1560 if (ret != 0) {
1561 xmlSchemaFreeFacet(facet);
1562 return(-1);
1563 }
1564 ret = xmlSchemaValidateFacet(typ, facet, strval, value);
1565 xmlSchemaFreeFacet(facet);
1566 if (ret != 0)
1567 return(-1);
1568 return(0);
1569}
1570
1571/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001572 * xmlRelaxNGSchemaTypeCompare:
1573 * @data: data needed for the library
1574 * @type: the type name
1575 * @value1: the first value
1576 * @value2: the second value
1577 *
1578 * Compare two values accordingly a type from the W3C XMLSchema
1579 * Datatype library.
1580 *
1581 * Returns 1 if yes, 0 if no and -1 in case of error.
1582 */
1583static int
1584xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1585 const xmlChar *type ATTRIBUTE_UNUSED,
1586 const xmlChar *value1 ATTRIBUTE_UNUSED,
1587 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1588 TODO
1589 return(1);
1590}
1591
1592/**
1593 * xmlRelaxNGDefaultTypeHave:
1594 * @data: data needed for the library
1595 * @type: the type name
1596 *
1597 * Check if the given type is provided by
1598 * the default datatype library.
1599 *
1600 * Returns 1 if yes, 0 if no and -1 in case of error.
1601 */
1602static int
1603xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1604 if (type == NULL)
1605 return(-1);
1606 if (xmlStrEqual(type, BAD_CAST "string"))
1607 return(1);
1608 if (xmlStrEqual(type, BAD_CAST "token"))
1609 return(1);
1610 return(0);
1611}
1612
1613/**
1614 * xmlRelaxNGDefaultTypeCheck:
1615 * @data: data needed for the library
1616 * @type: the type name
1617 * @value: the value to check
1618 *
1619 * Check if the given type and value are validated by
1620 * the default datatype library.
1621 *
1622 * Returns 1 if yes, 0 if no and -1 in case of error.
1623 */
1624static int
1625xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1626 const xmlChar *type ATTRIBUTE_UNUSED,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001627 const xmlChar *value ATTRIBUTE_UNUSED,
1628 void **result ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001629 if (value == NULL)
1630 return(-1);
1631 if (xmlStrEqual(type, BAD_CAST "string"))
1632 return(1);
1633 if (xmlStrEqual(type, BAD_CAST "token")) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001634 return(1);
1635 }
1636
1637 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001638}
1639
1640/**
1641 * xmlRelaxNGDefaultTypeCompare:
1642 * @data: data needed for the library
1643 * @type: the type name
1644 * @value1: the first value
1645 * @value2: the second value
1646 *
1647 * Compare two values accordingly a type from the default
1648 * datatype library.
1649 *
1650 * Returns 1 if yes, 0 if no and -1 in case of error.
1651 */
1652static int
1653xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1654 const xmlChar *type ATTRIBUTE_UNUSED,
1655 const xmlChar *value1 ATTRIBUTE_UNUSED,
1656 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001657 int ret = -1;
1658
1659 if (xmlStrEqual(type, BAD_CAST "string")) {
1660 ret = xmlStrEqual(value1, value2);
1661 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1662 if (!xmlStrEqual(value1, value2)) {
1663 xmlChar *nval, *nvalue;
1664
1665 /*
1666 * TODO: trivial optimizations are possible by
1667 * computing at compile-time
1668 */
1669 nval = xmlRelaxNGNormalize(NULL, value1);
1670 nvalue = xmlRelaxNGNormalize(NULL, value2);
1671
Daniel Veillardd4310742003-02-18 21:12:46 +00001672 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00001673 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00001674 else if (xmlStrEqual(nval, nvalue))
1675 ret = 1;
1676 else
1677 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001678 if (nval != NULL)
1679 xmlFree(nval);
1680 if (nvalue != NULL)
1681 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00001682 } else
1683 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001684 }
1685 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001686}
1687
1688static int xmlRelaxNGTypeInitialized = 0;
1689static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1690
1691/**
1692 * xmlRelaxNGFreeTypeLibrary:
1693 * @lib: the type library structure
1694 * @namespace: the URI bound to the library
1695 *
1696 * Free the structure associated to the type library
1697 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001698static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001699xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1700 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1701 if (lib == NULL)
1702 return;
1703 if (lib->namespace != NULL)
1704 xmlFree((xmlChar *)lib->namespace);
1705 xmlFree(lib);
1706}
1707
1708/**
1709 * xmlRelaxNGRegisterTypeLibrary:
1710 * @namespace: the URI bound to the library
1711 * @data: data associated to the library
1712 * @have: the provide function
1713 * @check: the checking function
1714 * @comp: the comparison function
1715 *
1716 * Register a new type library
1717 *
1718 * Returns 0 in case of success and -1 in case of error.
1719 */
1720static int
1721xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1722 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001723 xmlRelaxNGTypeCompare comp, xmlRelaxNGFacetCheck facet,
1724 xmlRelaxNGTypeFree freef) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001725 xmlRelaxNGTypeLibraryPtr lib;
1726 int ret;
1727
1728 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1729 (check == NULL) || (comp == NULL))
1730 return(-1);
1731 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1732 xmlGenericError(xmlGenericErrorContext,
1733 "Relax-NG types library '%s' already registered\n",
1734 namespace);
1735 return(-1);
1736 }
1737 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1738 if (lib == NULL) {
1739 xmlGenericError(xmlGenericErrorContext,
1740 "Relax-NG types library '%s' malloc() failed\n",
1741 namespace);
1742 return (-1);
1743 }
1744 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1745 lib->namespace = xmlStrdup(namespace);
1746 lib->data = data;
1747 lib->have = have;
1748 lib->comp = comp;
1749 lib->check = check;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001750 lib->facet = facet;
1751 lib->freef = freef;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001752 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1753 if (ret < 0) {
1754 xmlGenericError(xmlGenericErrorContext,
1755 "Relax-NG types library failed to register '%s'\n",
1756 namespace);
1757 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1758 return(-1);
1759 }
1760 return(0);
1761}
1762
1763/**
1764 * xmlRelaxNGInitTypes:
1765 *
1766 * Initilize the default type libraries.
1767 *
1768 * Returns 0 in case of success and -1 in case of error.
1769 */
1770static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001771xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001772 if (xmlRelaxNGTypeInitialized != 0)
1773 return(0);
1774 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1775 if (xmlRelaxNGRegisteredTypes == NULL) {
1776 xmlGenericError(xmlGenericErrorContext,
1777 "Failed to allocate sh table for Relax-NG types\n");
1778 return(-1);
1779 }
1780 xmlRelaxNGRegisterTypeLibrary(
1781 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1782 NULL,
1783 xmlRelaxNGSchemaTypeHave,
1784 xmlRelaxNGSchemaTypeCheck,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001785 xmlRelaxNGSchemaTypeCompare,
1786 xmlRelaxNGSchemaFacetCheck,
1787 (xmlRelaxNGTypeFree) xmlSchemaFreeValue);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001788 xmlRelaxNGRegisterTypeLibrary(
1789 xmlRelaxNGNs,
1790 NULL,
1791 xmlRelaxNGDefaultTypeHave,
1792 xmlRelaxNGDefaultTypeCheck,
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00001793 xmlRelaxNGDefaultTypeCompare,
1794 NULL,
1795 NULL);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001796 xmlRelaxNGTypeInitialized = 1;
1797 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001798}
1799
1800/**
1801 * xmlRelaxNGCleanupTypes:
1802 *
1803 * Cleanup the default Schemas type library associated to RelaxNG
1804 */
1805void
1806xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001807 if (xmlRelaxNGTypeInitialized == 0)
1808 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001809 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001810 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1811 xmlRelaxNGFreeTypeLibrary);
1812 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001813}
1814
1815/************************************************************************
1816 * *
1817 * Parsing functions *
1818 * *
1819 ************************************************************************/
1820
1821static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1822 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1823static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1824 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1825static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001826 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001827static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1828 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001829static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1830 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001831static int xmlRelaxNGParseGrammarContent(
1832 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001833static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1834 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1835 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001836static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1837 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard17bed982003-02-24 20:11:43 +00001838static int xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
1839 xmlRelaxNGDefinePtr define, xmlNodePtr elem);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001840
1841
1842#define IS_BLANK_NODE(n) \
1843 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1844
1845/**
1846 * xmlRelaxNGIsBlank:
1847 * @str: a string
1848 *
1849 * Check if a string is ignorable c.f. 4.2. Whitespace
1850 *
1851 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1852 */
1853static int
1854xmlRelaxNGIsBlank(xmlChar *str) {
1855 if (str == NULL)
1856 return(1);
1857 while (*str != 0) {
1858 if (!(IS_BLANK(*str))) return(0);
1859 str++;
1860 }
1861 return(1);
1862}
1863
Daniel Veillard6eadf632003-01-23 18:29:16 +00001864/**
1865 * xmlRelaxNGGetDataTypeLibrary:
1866 * @ctxt: a Relax-NG parser context
1867 * @node: the current data or value element
1868 *
1869 * Applies algorithm from 4.3. datatypeLibrary attribute
1870 *
1871 * Returns the datatypeLibary value or NULL if not found
1872 */
1873static xmlChar *
1874xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1875 xmlNodePtr node) {
1876 xmlChar *ret, *escape;
1877
Daniel Veillard6eadf632003-01-23 18:29:16 +00001878 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1879 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1880 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001881 if (ret[0] == 0) {
1882 xmlFree(ret);
1883 return(NULL);
1884 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001885 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001886 if (escape == NULL) {
1887 return(ret);
1888 }
1889 xmlFree(ret);
1890 return(escape);
1891 }
1892 }
1893 node = node->parent;
1894 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001895 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1896 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001897 if (ret[0] == 0) {
1898 xmlFree(ret);
1899 return(NULL);
1900 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001901 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1902 if (escape == NULL) {
1903 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001904 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001905 xmlFree(ret);
1906 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001907 }
1908 node = node->parent;
1909 }
1910 return(NULL);
1911}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001912
1913/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001914 * xmlRelaxNGParseValue:
1915 * @ctxt: a Relax-NG parser context
1916 * @node: the data node.
1917 *
1918 * parse the content of a RelaxNG value node.
1919 *
1920 * Returns the definition pointer or NULL in case of error
1921 */
1922static xmlRelaxNGDefinePtr
1923xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1924 xmlRelaxNGDefinePtr def = NULL;
1925 xmlRelaxNGTypeLibraryPtr lib;
1926 xmlChar *type;
1927 xmlChar *library;
1928 int tmp;
1929
1930 def = xmlRelaxNGNewDefine(ctxt, node);
1931 if (def == NULL)
1932 return(NULL);
1933 def->type = XML_RELAXNG_VALUE;
1934
1935 type = xmlGetProp(node, BAD_CAST "type");
1936 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001937 xmlRelaxNGNormExtSpace(type);
1938 if (xmlValidateNCName(type, 0)) {
1939 if (ctxt->error != NULL)
1940 ctxt->error(ctxt->userData,
1941 "value type '%s' is not an NCName\n",
1942 type);
1943 ctxt->nbErrors++;
1944 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001945 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1946 if (library == NULL)
1947 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1948
1949 def->name = type;
1950 def->ns = library;
1951
1952 lib = (xmlRelaxNGTypeLibraryPtr)
1953 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1954 if (lib == NULL) {
1955 if (ctxt->error != NULL)
1956 ctxt->error(ctxt->userData,
1957 "Use of unregistered type library '%s'\n",
1958 library);
1959 ctxt->nbErrors++;
1960 def->data = NULL;
1961 } else {
1962 def->data = lib;
1963 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001964 if (ctxt->error != NULL)
1965 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001966 "Internal error with type library '%s': no 'have'\n",
1967 library);
1968 ctxt->nbErrors++;
1969 } else {
1970 tmp = lib->have(lib->data, def->name);
1971 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001972 if (ctxt->error != NULL)
1973 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001974 "Error type '%s' is not exported by type library '%s'\n",
1975 def->name, library);
1976 ctxt->nbErrors++;
1977 }
1978 }
1979 }
1980 }
1981 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001982 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00001983 } else if ((node->children->type != XML_TEXT_NODE) ||
1984 (node->children->next != NULL)) {
1985 if (ctxt->error != NULL)
1986 ctxt->error(ctxt->userData,
1987 "Expecting a single text value for <value>content\n");
1988 ctxt->nbErrors++;
1989 } else {
1990 def->value = xmlNodeGetContent(node);
1991 if (def->value == NULL) {
1992 if (ctxt->error != NULL)
1993 ctxt->error(ctxt->userData,
1994 "Element <value> has no content\n");
1995 ctxt->nbErrors++;
1996 }
1997 }
1998 /* TODO check ahead of time that the value is okay per the type */
1999 return(def);
2000}
2001
2002/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002003 * xmlRelaxNGParseData:
2004 * @ctxt: a Relax-NG parser context
2005 * @node: the data node.
2006 *
2007 * parse the content of a RelaxNG data node.
2008 *
2009 * Returns the definition pointer or NULL in case of error
2010 */
2011static xmlRelaxNGDefinePtr
2012xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002013 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002014 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002015 xmlRelaxNGTypeLibraryPtr lib;
2016 xmlChar *type;
2017 xmlChar *library;
2018 xmlNodePtr content;
2019 int tmp;
2020
2021 type = xmlGetProp(node, BAD_CAST "type");
2022 if (type == NULL) {
2023 if (ctxt->error != NULL)
2024 ctxt->error(ctxt->userData,
2025 "data has no type\n");
2026 ctxt->nbErrors++;
2027 return(NULL);
2028 }
Daniel Veillardd2298792003-02-14 16:54:11 +00002029 xmlRelaxNGNormExtSpace(type);
2030 if (xmlValidateNCName(type, 0)) {
2031 if (ctxt->error != NULL)
2032 ctxt->error(ctxt->userData,
2033 "data type '%s' is not an NCName\n",
2034 type);
2035 ctxt->nbErrors++;
2036 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002037 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
2038 if (library == NULL)
2039 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
2040
2041 def = xmlRelaxNGNewDefine(ctxt, node);
2042 if (def == NULL) {
2043 xmlFree(type);
2044 return(NULL);
2045 }
2046 def->type = XML_RELAXNG_DATATYPE;
2047 def->name = type;
2048 def->ns = library;
2049
2050 lib = (xmlRelaxNGTypeLibraryPtr)
2051 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
2052 if (lib == NULL) {
2053 if (ctxt->error != NULL)
2054 ctxt->error(ctxt->userData,
2055 "Use of unregistered type library '%s'\n",
2056 library);
2057 ctxt->nbErrors++;
2058 def->data = NULL;
2059 } else {
2060 def->data = lib;
2061 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002062 if (ctxt->error != NULL)
2063 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002064 "Internal error with type library '%s': no 'have'\n",
2065 library);
2066 ctxt->nbErrors++;
2067 } else {
2068 tmp = lib->have(lib->data, def->name);
2069 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00002070 if (ctxt->error != NULL)
2071 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002072 "Error type '%s' is not exported by type library '%s'\n",
2073 def->name, library);
2074 ctxt->nbErrors++;
2075 }
2076 }
2077 }
2078 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00002079
2080 /*
2081 * Handle optional params
2082 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002083 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00002084 if (!xmlStrEqual(content->name, BAD_CAST "param"))
2085 break;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002086 if (xmlStrEqual(library,
2087 BAD_CAST"http://relaxng.org/ns/structure/1.0")) {
2088 if (ctxt->error != NULL)
2089 ctxt->error(ctxt->userData,
2090 "Type library '%s' does not allow type parameters\n",
2091 library);
2092 ctxt->nbErrors++;
2093 content = content->next;
2094 while ((content != NULL) &&
2095 (xmlStrEqual(content->name, BAD_CAST "param")))
2096 content = content->next;
2097 } else {
2098 param = xmlRelaxNGNewDefine(ctxt, node);
2099 if (param != NULL) {
2100 param->type = XML_RELAXNG_PARAM;
2101 param->name = xmlGetProp(content, BAD_CAST "name");
2102 if (param->name == NULL) {
2103 if (ctxt->error != NULL)
2104 ctxt->error(ctxt->userData,
2105 "param has no name\n");
2106 ctxt->nbErrors++;
2107 }
2108 param->value = xmlNodeGetContent(content);
2109 if (lastparam == NULL) {
2110 def->attrs = lastparam = param;
2111 } else {
2112 lastparam->next = param;
2113 lastparam = param;
2114 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00002115 if (lib != NULL) {
2116 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00002117 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002118 content = content->next;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002119 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002120 }
Daniel Veillard416589a2003-02-17 17:25:42 +00002121 /*
2122 * Handle optional except
2123 */
2124 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
2125 xmlNodePtr child;
2126 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
2127
2128 except = xmlRelaxNGNewDefine(ctxt, node);
2129 if (except == NULL) {
2130 return(def);
2131 }
2132 except->type = XML_RELAXNG_EXCEPT;
2133 child = content->children;
2134 if (last == NULL) {
2135 def->content = except;
2136 } else {
2137 last->next = except;
2138 }
2139 if (child == NULL) {
2140 if (ctxt->error != NULL)
2141 ctxt->error(ctxt->userData,
2142 "except has no content\n");
2143 ctxt->nbErrors++;
2144 }
2145 while (child != NULL) {
2146 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
2147 if (tmp2 != NULL) {
2148 if (last2 == NULL) {
2149 except->content = last2 = tmp2;
2150 } else {
2151 last2->next = tmp2;
2152 last2 = tmp2;
2153 }
2154 }
2155 child = child->next;
2156 }
2157 content = content->next;
2158 }
2159 /*
2160 * Check there is no unhandled data
2161 */
2162 if (content != NULL) {
2163 if (ctxt->error != NULL)
2164 ctxt->error(ctxt->userData,
2165 "Element data has unexpected content %s\n", content->name);
2166 ctxt->nbErrors++;
2167 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002168
2169 return(def);
2170}
2171
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002172static const xmlChar *invalidName = BAD_CAST "\1";
2173
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002174/**
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002175 * xmlRelaxNGCompareNameClasses:
2176 * @defs1: the first element/attribute defs
2177 * @defs2: the second element/attribute defs
2178 * @name: the restriction on the name
2179 * @ns: the restriction on the namespace
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002180 *
2181 * Compare the 2 lists of element definitions. The comparison is
2182 * that if both lists do not accept the same QNames, it returns 1
2183 * If the 2 lists can accept the same QName the comparison returns 0
2184 *
2185 * Returns 1 disttinct, 0 if equal
2186 */
2187static int
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002188xmlRelaxNGCompareNameClasses(xmlRelaxNGDefinePtr def1,
2189 xmlRelaxNGDefinePtr def2) {
2190 int ret = 1;
2191 xmlNode node;
2192 xmlNs ns;
2193 xmlRelaxNGValidCtxt ctxt;
2194 ctxt.flags = FLAGS_IGNORABLE;
2195
2196 if ((def1->type == XML_RELAXNG_ELEMENT) ||
2197 (def1->type == XML_RELAXNG_ATTRIBUTE)) {
2198 if (def2->type == XML_RELAXNG_TEXT)
2199 return(1);
2200 if (def1->name != NULL) {
2201 node.name = def1->name;
2202 } else {
2203 node.name = invalidName;
2204 }
2205 node.ns = &ns;
2206 if (def1->ns != NULL) {
2207 if (def1->ns[0] == 0) {
2208 node.ns = NULL;
2209 } else {
2210 ns.href = def1->ns;
2211 }
2212 } else {
2213 ns.href = invalidName;
2214 }
2215 if (xmlRelaxNGElementMatch(&ctxt, def2, &node)) {
2216 if (def1->nameClass != NULL) {
2217 ret = xmlRelaxNGCompareNameClasses(def1->nameClass, def2);
2218 } else {
2219 ret = 0;
2220 }
2221 } else {
2222 ret = 1;
2223 }
2224 } else if (def1->type == XML_RELAXNG_TEXT) {
2225 if (def2->type == XML_RELAXNG_TEXT)
2226 return(0);
2227 return(1);
2228 } else if (def1->type == XML_RELAXNG_EXCEPT) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002229 TODO
2230 ret = 0;
2231 } else {
2232 TODO
2233 ret = 0;
2234 }
2235 if (ret == 0)
2236 return(ret);
2237 if ((def2->type == XML_RELAXNG_ELEMENT) ||
2238 (def2->type == XML_RELAXNG_ATTRIBUTE)) {
2239 if (def2->name != NULL) {
2240 node.name = def2->name;
2241 } else {
2242 node.name = invalidName;
2243 }
2244 node.ns = &ns;
2245 if (def2->ns != NULL) {
2246 if (def2->ns[0] == 0) {
2247 node.ns = NULL;
2248 } else {
2249 ns.href = def2->ns;
2250 }
2251 } else {
2252 ns.href = invalidName;
2253 }
2254 if (xmlRelaxNGElementMatch(&ctxt, def1, &node)) {
2255 if (def2->nameClass != NULL) {
2256 ret = xmlRelaxNGCompareNameClasses(def2->nameClass, def1);
2257 } else {
2258 ret = 0;
2259 }
2260 } else {
2261 ret = 1;
2262 }
2263 } else {
2264 TODO
2265 ret = 0;
2266 }
2267
2268 return(ret);
2269}
2270
2271/**
2272 * xmlRelaxNGCompareElemDefLists:
2273 * @ctxt: a Relax-NG parser context
2274 * @defs1: the first list of element/attribute defs
2275 * @defs2: the second list of element/attribute defs
2276 *
2277 * Compare the 2 lists of element or attribute definitions. The comparison
2278 * is that if both lists do not accept the same QNames, it returns 1
2279 * If the 2 lists can accept the same QName the comparison returns 0
2280 *
2281 * Returns 1 disttinct, 0 if equal
2282 */
2283static int
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002284xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2285 xmlRelaxNGDefinePtr *def1,
2286 xmlRelaxNGDefinePtr *def2) {
2287 xmlRelaxNGDefinePtr *basedef2 = def2;
2288
Daniel Veillard154877e2003-01-30 12:17:05 +00002289 if ((def1 == NULL) || (def2 == NULL))
2290 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002291 if ((*def1 == NULL) || (*def2 == NULL))
2292 return(1);
2293 while (*def1 != NULL) {
2294 while ((*def2) != NULL) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002295 if (xmlRelaxNGCompareNameClasses(*def1, *def2) == 0)
2296 return(0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002297 def2++;
2298 }
2299 def2 = basedef2;
2300 def1++;
2301 }
2302 return(1);
2303}
2304
2305/**
2306 * xmlRelaxNGGetElements:
2307 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002308 * @def: the definition definition
2309 * @eora: gather elements (0) or attributes (1)
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002310 *
2311 * Compute the list of top elements a definition can generate
2312 *
2313 * Returns a list of elements or NULL if none was found.
2314 */
2315static xmlRelaxNGDefinePtr *
2316xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002317 xmlRelaxNGDefinePtr def,
2318 int eora) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002319 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2320 int len = 0;
2321 int max = 0;
2322
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002323 /*
2324 * Don't run that check in case of error. Infinite recursion
2325 * becomes possible.
2326 */
2327 if (ctxt->nbErrors != 0)
2328 return(NULL);
2329
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002330 parent = NULL;
2331 cur = def;
2332 while (cur != NULL) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002333 if (((eora == 0) && ((cur->type == XML_RELAXNG_ELEMENT) ||
2334 (cur->type == XML_RELAXNG_TEXT))) ||
2335 ((eora == 1) && (cur->type == XML_RELAXNG_ATTRIBUTE))) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002336 if (ret == NULL) {
2337 max = 10;
2338 ret = (xmlRelaxNGDefinePtr *)
2339 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2340 if (ret == NULL) {
2341 if (ctxt->error != NULL)
2342 ctxt->error(ctxt->userData,
2343 "Out of memory in element search\n");
2344 ctxt->nbErrors++;
2345 return(NULL);
2346 }
2347 } else if (max <= len) {
2348 max *= 2;
2349 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2350 if (ret == NULL) {
2351 if (ctxt->error != NULL)
2352 ctxt->error(ctxt->userData,
2353 "Out of memory in element search\n");
2354 ctxt->nbErrors++;
2355 return(NULL);
2356 }
2357 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002358 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002359 ret[len] = NULL;
2360 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2361 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2362 (cur->type == XML_RELAXNG_GROUP) ||
2363 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002364 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2365 (cur->type == XML_RELAXNG_OPTIONAL) ||
2366 (cur->type == XML_RELAXNG_REF) ||
2367 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002368 /*
2369 * Don't go within elements or attributes or string values.
2370 * Just gather the element top list
2371 */
2372 if (cur->content != NULL) {
2373 parent = cur;
2374 cur = cur->content;
2375 tmp = cur;
2376 while (tmp != NULL) {
2377 tmp->parent = parent;
2378 tmp = tmp->next;
2379 }
2380 continue;
2381 }
2382 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002383 if (cur == def)
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002384 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002385 if (cur->next != NULL) {
2386 cur = cur->next;
2387 continue;
2388 }
2389 do {
2390 cur = cur->parent;
2391 if (cur == NULL) break;
2392 if (cur == def) return(ret);
2393 if (cur->next != NULL) {
2394 cur = cur->next;
2395 break;
2396 }
2397 } while (cur != NULL);
2398 }
2399 return(ret);
2400}
2401
2402/**
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002403 * xmlRelaxNGCheckGroupAttrs:
2404 * @ctxt: a Relax-NG parser context
2405 * @def: the group definition
2406 *
2407 * Detects violations of rule 7.3
2408 */
2409static void
2410xmlRelaxNGCheckGroupAttrs(xmlRelaxNGParserCtxtPtr ctxt,
2411 xmlRelaxNGDefinePtr def) {
2412 xmlRelaxNGDefinePtr **list;
2413 xmlRelaxNGDefinePtr cur;
2414 int nbchild = 0, i, j, ret;
2415
2416 if ((def == NULL) ||
2417 ((def->type != XML_RELAXNG_GROUP) &&
2418 (def->type != XML_RELAXNG_ELEMENT)))
2419 return;
2420
2421 /*
2422 * Don't run that check in case of error. Infinite recursion
2423 * becomes possible.
2424 */
2425 if (ctxt->nbErrors != 0)
2426 return;
2427
2428 cur = def->attrs;
2429 while (cur != NULL) {
2430 nbchild++;
2431 cur = cur->next;
2432 }
2433 cur = def->content;
2434 while (cur != NULL) {
2435 nbchild++;
2436 cur = cur->next;
2437 }
2438
2439 list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
2440 sizeof(xmlRelaxNGDefinePtr *));
2441 if (list == NULL) {
2442 if (ctxt->error != NULL)
2443 ctxt->error(ctxt->userData,
2444 "Out of memory in group computation\n");
2445 ctxt->nbErrors++;
2446 return;
2447 }
2448 i = 0;
2449 cur = def->attrs;
2450 while (cur != NULL) {
2451 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2452 i++;
2453 cur = cur->next;
2454 }
2455 cur = def->content;
2456 while (cur != NULL) {
2457 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2458 i++;
2459 cur = cur->next;
2460 }
2461
2462 for (i = 0;i < nbchild;i++) {
2463 if (list[i] == NULL)
2464 continue;
2465 for (j = 0;j < i;j++) {
2466 if (list[j] == NULL)
2467 continue;
2468 ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
2469 if (ret == 0) {
2470 if (ctxt->error != NULL)
2471 ctxt->error(ctxt->userData,
2472 "Attributes conflicts in group\n");
2473 ctxt->nbErrors++;
2474 }
2475 }
2476 }
2477 for (i = 0;i < nbchild;i++) {
2478 if (list[i] != NULL)
2479 xmlFree(list[i]);
2480 }
2481 xmlFree(list);
2482}
2483
2484/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002485 * xmlRelaxNGComputeInterleaves:
2486 * @def: the interleave definition
2487 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002488 * @name: the definition name
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002489 *
2490 * A lot of work for preprocessing interleave definitions
2491 * is potentially needed to get a decent execution speed at runtime
2492 * - trying to get a total order on the element nodes generated
2493 * by the interleaves, order the list of interleave definitions
2494 * following that order.
2495 * - if <text/> is used to handle mixed content, it is better to
2496 * flag this in the define and simplify the runtime checking
2497 * algorithm
2498 */
2499static void
2500xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2501 xmlRelaxNGParserCtxtPtr ctxt,
2502 xmlChar *name ATTRIBUTE_UNUSED) {
2503 xmlRelaxNGDefinePtr cur;
2504
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002505 xmlRelaxNGPartitionPtr partitions = NULL;
2506 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2507 xmlRelaxNGInterleaveGroupPtr group;
2508 int i,j,ret;
2509 int nbgroups = 0;
2510 int nbchild = 0;
2511
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002512 /*
2513 * Don't run that check in case of error. Infinite recursion
2514 * becomes possible.
2515 */
2516 if (ctxt->nbErrors != 0)
2517 return;
2518
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002519#ifdef DEBUG_INTERLEAVE
2520 xmlGenericError(xmlGenericErrorContext,
2521 "xmlRelaxNGComputeInterleaves(%s)\n",
2522 name);
2523#endif
2524 cur = def->content;
2525 while (cur != NULL) {
2526 nbchild++;
2527 cur = cur->next;
2528 }
2529
2530#ifdef DEBUG_INTERLEAVE
2531 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2532#endif
2533 groups = (xmlRelaxNGInterleaveGroupPtr *)
2534 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2535 if (groups == NULL)
2536 goto error;
2537 cur = def->content;
2538 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002539 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2540 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2541 if (groups[nbgroups] == NULL)
2542 goto error;
2543 groups[nbgroups]->rule = cur;
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002544 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur, 0);
2545 groups[nbgroups]->attrs = xmlRelaxNGGetElements(ctxt, cur, 1);
Daniel Veillard154877e2003-01-30 12:17:05 +00002546 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002547 cur = cur->next;
2548 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002549#ifdef DEBUG_INTERLEAVE
2550 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2551#endif
2552
2553 /*
2554 * Let's check that all rules makes a partitions according to 7.4
2555 */
2556 partitions = (xmlRelaxNGPartitionPtr)
2557 xmlMalloc(sizeof(xmlRelaxNGPartition));
2558 if (partitions == NULL)
2559 goto error;
2560 partitions->nbgroups = nbgroups;
2561 for (i = 0;i < nbgroups;i++) {
2562 group = groups[i];
2563 for (j = i+1;j < nbgroups;j++) {
2564 if (groups[j] == NULL)
2565 continue;
2566 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2567 groups[j]->defs);
2568 if (ret == 0) {
2569 if (ctxt->error != NULL)
2570 ctxt->error(ctxt->userData,
2571 "Element or text conflicts in interleave\n");
2572 ctxt->nbErrors++;
2573 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002574 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->attrs,
2575 groups[j]->attrs);
2576 if (ret == 0) {
2577 if (ctxt->error != NULL)
2578 ctxt->error(ctxt->userData,
2579 "Attributes conflicts in interleave\n");
2580 ctxt->nbErrors++;
2581 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002582 }
2583 }
2584 partitions->groups = groups;
2585
2586 /*
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002587 * and save the partition list back in the def
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002588 */
2589 def->data = partitions;
2590 return;
2591
2592error:
2593 if (ctxt->error != NULL)
2594 ctxt->error(ctxt->userData,
2595 "Out of memory in interleave computation\n");
2596 ctxt->nbErrors++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002597 if (groups != NULL) {
2598 for (i = 0;i < nbgroups;i++)
2599 if (groups[i] != NULL) {
2600 if (groups[i]->defs != NULL)
2601 xmlFree(groups[i]->defs);
2602 xmlFree(groups[i]);
2603 }
2604 xmlFree(groups);
2605 }
2606 xmlRelaxNGFreePartition(partitions);
2607}
2608
2609/**
2610 * xmlRelaxNGParseInterleave:
2611 * @ctxt: a Relax-NG parser context
2612 * @node: the data node.
2613 *
2614 * parse the content of a RelaxNG interleave node.
2615 *
2616 * Returns the definition pointer or NULL in case of error
2617 */
2618static xmlRelaxNGDefinePtr
2619xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2620 xmlRelaxNGDefinePtr def = NULL;
2621 xmlRelaxNGDefinePtr last = NULL, cur;
2622 xmlNodePtr child;
2623
2624 def = xmlRelaxNGNewDefine(ctxt, node);
2625 if (def == NULL) {
2626 return(NULL);
2627 }
2628 def->type = XML_RELAXNG_INTERLEAVE;
2629
2630 if (ctxt->interleaves == NULL)
2631 ctxt->interleaves = xmlHashCreate(10);
2632 if (ctxt->interleaves == NULL) {
2633 if (ctxt->error != NULL)
2634 ctxt->error(ctxt->userData,
2635 "Failed to create interleaves hash table\n");
2636 ctxt->nbErrors++;
2637 } else {
2638 char name[32];
2639
2640 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2641 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2642 if (ctxt->error != NULL)
2643 ctxt->error(ctxt->userData,
2644 "Failed to add %s to hash table\n", name);
2645 ctxt->nbErrors++;
2646 }
2647 }
2648 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002649 if (child == NULL) {
2650 if (ctxt->error != NULL)
2651 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2652 ctxt->nbErrors++;
2653 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002654 while (child != NULL) {
2655 if (IS_RELAXNG(child, "element")) {
2656 cur = xmlRelaxNGParseElement(ctxt, child);
2657 } else {
2658 cur = xmlRelaxNGParsePattern(ctxt, child);
2659 }
2660 if (cur != NULL) {
2661 cur->parent = def;
2662 if (last == NULL) {
2663 def->content = last = cur;
2664 } else {
2665 last->next = cur;
2666 last = cur;
2667 }
2668 }
2669 child = child->next;
2670 }
2671
2672 return(def);
2673}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002674
2675/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002676 * xmlRelaxNGParseInclude:
2677 * @ctxt: a Relax-NG parser context
2678 * @node: the include node
2679 *
2680 * Integrate the content of an include node in the current grammar
2681 *
2682 * Returns 0 in case of success or -1 in case of error
2683 */
2684static int
2685xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2686 xmlRelaxNGIncludePtr incl;
2687 xmlNodePtr root;
2688 int ret = 0, tmp;
2689
2690 incl = node->_private;
2691 if (incl == NULL) {
2692 if (ctxt->error != NULL)
2693 ctxt->error(ctxt->userData,
2694 "Include node has no data\n");
2695 ctxt->nbErrors++;
2696 return(-1);
2697 }
2698 root = xmlDocGetRootElement(incl->doc);
2699 if (root == NULL) {
2700 if (ctxt->error != NULL)
2701 ctxt->error(ctxt->userData,
2702 "Include document is empty\n");
2703 ctxt->nbErrors++;
2704 return(-1);
2705 }
2706 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2707 if (ctxt->error != NULL)
2708 ctxt->error(ctxt->userData,
2709 "Include document root is not a grammar\n");
2710 ctxt->nbErrors++;
2711 return(-1);
2712 }
2713
2714 /*
2715 * Merge the definition from both the include and the internal list
2716 */
2717 if (root->children != NULL) {
2718 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2719 if (tmp != 0)
2720 ret = -1;
2721 }
2722 if (node->children != NULL) {
2723 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2724 if (tmp != 0)
2725 ret = -1;
2726 }
2727 return(ret);
2728}
2729
2730/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002731 * xmlRelaxNGParseDefine:
2732 * @ctxt: a Relax-NG parser context
2733 * @node: the define node
2734 *
2735 * parse the content of a RelaxNG define element node.
2736 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002737 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002738 */
2739static int
2740xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2741 xmlChar *name;
2742 int ret = 0, tmp;
2743 xmlRelaxNGDefinePtr def;
2744 const xmlChar *olddefine;
2745
2746 name = xmlGetProp(node, BAD_CAST "name");
2747 if (name == NULL) {
2748 if (ctxt->error != NULL)
2749 ctxt->error(ctxt->userData,
2750 "define has no name\n");
2751 ctxt->nbErrors++;
2752 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002753 xmlRelaxNGNormExtSpace(name);
2754 if (xmlValidateNCName(name, 0)) {
2755 if (ctxt->error != NULL)
2756 ctxt->error(ctxt->userData,
2757 "define name '%s' is not an NCName\n",
2758 name);
2759 ctxt->nbErrors++;
2760 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002761 def = xmlRelaxNGNewDefine(ctxt, node);
2762 if (def == NULL) {
2763 xmlFree(name);
2764 return(-1);
2765 }
2766 def->type = XML_RELAXNG_DEF;
2767 def->name = name;
2768 if (node->children == NULL) {
2769 if (ctxt->error != NULL)
2770 ctxt->error(ctxt->userData,
2771 "define has no children\n");
2772 ctxt->nbErrors++;
2773 } else {
2774 olddefine = ctxt->define;
2775 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002776 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002777 ctxt->define = olddefine;
2778 }
2779 if (ctxt->grammar->defs == NULL)
2780 ctxt->grammar->defs = xmlHashCreate(10);
2781 if (ctxt->grammar->defs == NULL) {
2782 if (ctxt->error != NULL)
2783 ctxt->error(ctxt->userData,
2784 "Could not create definition hash\n");
2785 ctxt->nbErrors++;
2786 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002787 } else {
2788 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2789 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002790 xmlRelaxNGDefinePtr prev;
2791
2792 prev = xmlHashLookup(ctxt->grammar->defs, name);
2793 if (prev == NULL) {
2794 if (ctxt->error != NULL)
2795 ctxt->error(ctxt->userData,
2796 "Internal error on define aggregation of %s\n",
2797 name);
2798 ctxt->nbErrors++;
2799 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002800 } else {
2801 while (prev->nextHash != NULL)
2802 prev = prev->nextHash;
2803 prev->nextHash = def;
2804 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002805 }
2806 }
2807 }
2808 return(ret);
2809}
2810
2811/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002812 * xmlRelaxNGProcessExternalRef:
2813 * @ctxt: the parser context
2814 * @node: the externlRef node
2815 *
2816 * Process and compile an externlRef node
2817 *
2818 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2819 */
2820static xmlRelaxNGDefinePtr
2821xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2822 xmlRelaxNGDocumentPtr docu;
2823 xmlNodePtr root, tmp;
2824 xmlChar *ns;
Daniel Veillard77648bb2003-02-20 15:03:22 +00002825 int newNs = 0, oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002826 xmlRelaxNGDefinePtr def;
2827
2828 docu = node->_private;
2829 if (docu != NULL) {
2830 def = xmlRelaxNGNewDefine(ctxt, node);
2831 if (def == NULL)
2832 return(NULL);
2833 def->type = XML_RELAXNG_EXTERNALREF;
2834
2835 if (docu->content == NULL) {
2836 /*
2837 * Then do the parsing for good
2838 */
2839 root = xmlDocGetRootElement(docu->doc);
2840 if (root == NULL) {
2841 if (ctxt->error != NULL)
2842 ctxt->error(ctxt->userData,
2843 "xmlRelaxNGParse: %s is empty\n",
2844 ctxt->URL);
2845 ctxt->nbErrors++;
2846 return (NULL);
2847 }
2848 /*
2849 * ns transmission rules
2850 */
2851 ns = xmlGetProp(root, BAD_CAST "ns");
2852 if (ns == NULL) {
2853 tmp = node;
2854 while ((tmp != NULL) &&
2855 (tmp->type == XML_ELEMENT_NODE)) {
2856 ns = xmlGetProp(tmp, BAD_CAST "ns");
2857 if (ns != NULL) {
2858 break;
2859 }
2860 tmp = tmp->parent;
2861 }
2862 if (ns != NULL) {
2863 xmlSetProp(root, BAD_CAST "ns", ns);
2864 newNs = 1;
2865 xmlFree(ns);
2866 }
2867 } else {
2868 xmlFree(ns);
2869 }
2870
2871 /*
2872 * Parsing to get a precompiled schemas.
2873 */
Daniel Veillard77648bb2003-02-20 15:03:22 +00002874 oldflags = ctxt->flags;
2875 ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002876 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard77648bb2003-02-20 15:03:22 +00002877 ctxt->flags = oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002878 if ((docu->schema != NULL) &&
2879 (docu->schema->topgrammar != NULL)) {
2880 docu->content = docu->schema->topgrammar->start;
2881 }
2882
2883 /*
2884 * the externalRef may be reused in a different ns context
2885 */
2886 if (newNs == 1) {
2887 xmlUnsetProp(root, BAD_CAST "ns");
2888 }
2889 }
2890 def->content = docu->content;
2891 } else {
2892 def = NULL;
2893 }
2894 return(def);
2895}
2896
2897/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002898 * xmlRelaxNGParsePattern:
2899 * @ctxt: a Relax-NG parser context
2900 * @node: the pattern node.
2901 *
2902 * parse the content of a RelaxNG pattern node.
2903 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002904 * Returns the definition pointer or NULL in case of error or if no
2905 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002906 */
2907static xmlRelaxNGDefinePtr
2908xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2909 xmlRelaxNGDefinePtr def = NULL;
2910
Daniel Veillardd2298792003-02-14 16:54:11 +00002911 if (node == NULL) {
2912 return(NULL);
2913 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002914 if (IS_RELAXNG(node, "element")) {
2915 def = xmlRelaxNGParseElement(ctxt, node);
2916 } else if (IS_RELAXNG(node, "attribute")) {
2917 def = xmlRelaxNGParseAttribute(ctxt, node);
2918 } else if (IS_RELAXNG(node, "empty")) {
2919 def = xmlRelaxNGNewDefine(ctxt, node);
2920 if (def == NULL)
2921 return(NULL);
2922 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002923 if (node->children != NULL) {
2924 if (ctxt->error != NULL)
2925 ctxt->error(ctxt->userData, "empty: had a child node\n");
2926 ctxt->nbErrors++;
2927 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002928 } else if (IS_RELAXNG(node, "text")) {
2929 def = xmlRelaxNGNewDefine(ctxt, node);
2930 if (def == NULL)
2931 return(NULL);
2932 def->type = XML_RELAXNG_TEXT;
2933 if (node->children != NULL) {
2934 if (ctxt->error != NULL)
2935 ctxt->error(ctxt->userData, "text: had a child node\n");
2936 ctxt->nbErrors++;
2937 }
2938 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2939 def = xmlRelaxNGNewDefine(ctxt, node);
2940 if (def == NULL)
2941 return(NULL);
2942 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002943 if (node->children == NULL) {
2944 if (ctxt->error != NULL)
2945 ctxt->error(ctxt->userData,
2946 "Element %s is empty\n", node->name);
2947 ctxt->nbErrors++;
2948 } else {
2949 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2950 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002951 } else if (IS_RELAXNG(node, "oneOrMore")) {
2952 def = xmlRelaxNGNewDefine(ctxt, node);
2953 if (def == NULL)
2954 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002955 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002956 if (node->children == NULL) {
2957 if (ctxt->error != NULL)
2958 ctxt->error(ctxt->userData,
2959 "Element %s is empty\n", node->name);
2960 ctxt->nbErrors++;
2961 } else {
2962 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2963 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002964 } else if (IS_RELAXNG(node, "optional")) {
2965 def = xmlRelaxNGNewDefine(ctxt, node);
2966 if (def == NULL)
2967 return(NULL);
2968 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002969 if (node->children == NULL) {
2970 if (ctxt->error != NULL)
2971 ctxt->error(ctxt->userData,
2972 "Element %s is empty\n", node->name);
2973 ctxt->nbErrors++;
2974 } else {
2975 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2976 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002977 } else if (IS_RELAXNG(node, "choice")) {
2978 def = xmlRelaxNGNewDefine(ctxt, node);
2979 if (def == NULL)
2980 return(NULL);
2981 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002982 if (node->children == NULL) {
2983 if (ctxt->error != NULL)
2984 ctxt->error(ctxt->userData,
2985 "Element %s is empty\n", node->name);
2986 ctxt->nbErrors++;
2987 } else {
2988 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2989 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002990 } else if (IS_RELAXNG(node, "group")) {
2991 def = xmlRelaxNGNewDefine(ctxt, node);
2992 if (def == NULL)
2993 return(NULL);
2994 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002995 if (node->children == NULL) {
2996 if (ctxt->error != NULL)
2997 ctxt->error(ctxt->userData,
2998 "Element %s is empty\n", node->name);
2999 ctxt->nbErrors++;
3000 } else {
3001 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3002 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003003 } else if (IS_RELAXNG(node, "ref")) {
3004 def = xmlRelaxNGNewDefine(ctxt, node);
3005 if (def == NULL)
3006 return(NULL);
3007 def->type = XML_RELAXNG_REF;
3008 def->name = xmlGetProp(node, BAD_CAST "name");
3009 if (def->name == NULL) {
3010 if (ctxt->error != NULL)
3011 ctxt->error(ctxt->userData,
3012 "ref has no name\n");
3013 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003014 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003015 xmlRelaxNGNormExtSpace(def->name);
3016 if (xmlValidateNCName(def->name, 0)) {
3017 if (ctxt->error != NULL)
3018 ctxt->error(ctxt->userData,
3019 "ref name '%s' is not an NCName\n",
3020 def->name);
3021 ctxt->nbErrors++;
3022 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003023 }
3024 if (node->children != NULL) {
3025 if (ctxt->error != NULL)
3026 ctxt->error(ctxt->userData,
3027 "ref is not empty\n");
3028 ctxt->nbErrors++;
3029 }
3030 if (ctxt->grammar->refs == NULL)
3031 ctxt->grammar->refs = xmlHashCreate(10);
3032 if (ctxt->grammar->refs == NULL) {
3033 if (ctxt->error != NULL)
3034 ctxt->error(ctxt->userData,
3035 "Could not create references hash\n");
3036 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003037 def = NULL;
3038 } else {
3039 int tmp;
3040
3041 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
3042 if (tmp < 0) {
3043 xmlRelaxNGDefinePtr prev;
3044
3045 prev = (xmlRelaxNGDefinePtr)
3046 xmlHashLookup(ctxt->grammar->refs, def->name);
3047 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00003048 if (def->name != NULL) {
3049 if (ctxt->error != NULL)
3050 ctxt->error(ctxt->userData,
3051 "Error refs definitions '%s'\n",
3052 def->name);
3053 } else {
3054 if (ctxt->error != NULL)
3055 ctxt->error(ctxt->userData,
3056 "Error refs definitions\n");
3057 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003058 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003059 def = NULL;
3060 } else {
3061 def->nextHash = prev->nextHash;
3062 prev->nextHash = def;
3063 }
3064 }
3065 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00003066 } else if (IS_RELAXNG(node, "data")) {
3067 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardedc91922003-01-26 00:52:04 +00003068 } else if (IS_RELAXNG(node, "value")) {
3069 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00003070 } else if (IS_RELAXNG(node, "list")) {
3071 def = xmlRelaxNGNewDefine(ctxt, node);
3072 if (def == NULL)
3073 return(NULL);
3074 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00003075 if (node->children == NULL) {
3076 if (ctxt->error != NULL)
3077 ctxt->error(ctxt->userData,
3078 "Element %s is empty\n", node->name);
3079 ctxt->nbErrors++;
3080 } else {
3081 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
3082 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003083 } else if (IS_RELAXNG(node, "interleave")) {
3084 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003085 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00003086 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003087 } else if (IS_RELAXNG(node, "notAllowed")) {
3088 def = xmlRelaxNGNewDefine(ctxt, node);
3089 if (def == NULL)
3090 return(NULL);
3091 def->type = XML_RELAXNG_NOT_ALLOWED;
3092 if (node->children != NULL) {
3093 if (ctxt->error != NULL)
3094 ctxt->error(ctxt->userData,
3095 "xmlRelaxNGParse: notAllowed element is not empty\n");
3096 ctxt->nbErrors++;
3097 }
Daniel Veillard419a7682003-02-03 23:22:49 +00003098 } else if (IS_RELAXNG(node, "grammar")) {
3099 xmlRelaxNGGrammarPtr grammar, old;
3100 xmlRelaxNGGrammarPtr oldparent;
3101
Daniel Veillardc482e262003-02-26 14:48:48 +00003102#ifdef DEBUG_GRAMMAR
3103 xmlGenericError(xmlGenericErrorContext, "Found <grammar> pattern\n");
3104#endif
3105
Daniel Veillard419a7682003-02-03 23:22:49 +00003106 oldparent = ctxt->parentgrammar;
3107 old = ctxt->grammar;
3108 ctxt->parentgrammar = old;
3109 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3110 if (old != NULL) {
3111 ctxt->grammar = old;
3112 ctxt->parentgrammar = oldparent;
Daniel Veillardc482e262003-02-26 14:48:48 +00003113#if 0
Daniel Veillard419a7682003-02-03 23:22:49 +00003114 if (grammar != NULL) {
3115 grammar->next = old->next;
3116 old->next = grammar;
3117 }
Daniel Veillardc482e262003-02-26 14:48:48 +00003118#endif
Daniel Veillard419a7682003-02-03 23:22:49 +00003119 }
3120 if (grammar != NULL)
3121 def = grammar->start;
3122 else
3123 def = NULL;
3124 } else if (IS_RELAXNG(node, "parentRef")) {
3125 if (ctxt->parentgrammar == NULL) {
3126 if (ctxt->error != NULL)
3127 ctxt->error(ctxt->userData,
3128 "Use of parentRef without a parent grammar\n");
3129 ctxt->nbErrors++;
3130 return(NULL);
3131 }
3132 def = xmlRelaxNGNewDefine(ctxt, node);
3133 if (def == NULL)
3134 return(NULL);
3135 def->type = XML_RELAXNG_PARENTREF;
3136 def->name = xmlGetProp(node, BAD_CAST "name");
3137 if (def->name == NULL) {
3138 if (ctxt->error != NULL)
3139 ctxt->error(ctxt->userData,
3140 "parentRef has no name\n");
3141 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00003142 } else {
3143 xmlRelaxNGNormExtSpace(def->name);
3144 if (xmlValidateNCName(def->name, 0)) {
3145 if (ctxt->error != NULL)
3146 ctxt->error(ctxt->userData,
3147 "parentRef name '%s' is not an NCName\n",
3148 def->name);
3149 ctxt->nbErrors++;
3150 }
Daniel Veillard419a7682003-02-03 23:22:49 +00003151 }
3152 if (node->children != NULL) {
3153 if (ctxt->error != NULL)
3154 ctxt->error(ctxt->userData,
3155 "parentRef is not empty\n");
3156 ctxt->nbErrors++;
3157 }
3158 if (ctxt->parentgrammar->refs == NULL)
3159 ctxt->parentgrammar->refs = xmlHashCreate(10);
3160 if (ctxt->parentgrammar->refs == NULL) {
3161 if (ctxt->error != NULL)
3162 ctxt->error(ctxt->userData,
3163 "Could not create references hash\n");
3164 ctxt->nbErrors++;
3165 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00003166 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00003167 int tmp;
3168
3169 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
3170 if (tmp < 0) {
3171 xmlRelaxNGDefinePtr prev;
3172
3173 prev = (xmlRelaxNGDefinePtr)
3174 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
3175 if (prev == NULL) {
3176 if (ctxt->error != NULL)
3177 ctxt->error(ctxt->userData,
3178 "Internal error parentRef definitions '%s'\n",
3179 def->name);
3180 ctxt->nbErrors++;
3181 def = NULL;
3182 } else {
3183 def->nextHash = prev->nextHash;
3184 prev->nextHash = def;
3185 }
3186 }
3187 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003188 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00003189 if (node->children == NULL) {
3190 if (ctxt->error != NULL)
3191 ctxt->error(ctxt->userData,
3192 "Mixed is empty\n");
3193 ctxt->nbErrors++;
3194 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00003195 } else {
3196 def = xmlRelaxNGParseInterleave(ctxt, node);
3197 if (def != NULL) {
3198 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003199
3200 if ((def->content != NULL) && (def->content->next != NULL)) {
3201 tmp = xmlRelaxNGNewDefine(ctxt, node);
3202 if (tmp != NULL) {
3203 tmp->type = XML_RELAXNG_GROUP;
3204 tmp->content = def->content;
3205 def->content = tmp;
3206 }
3207 }
3208
Daniel Veillard416589a2003-02-17 17:25:42 +00003209 tmp = xmlRelaxNGNewDefine(ctxt, node);
3210 if (tmp == NULL)
3211 return(def);
3212 tmp->type = XML_RELAXNG_TEXT;
3213 tmp->next = def->content;
3214 def->content = tmp;
3215 }
3216 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003217 } else {
3218 if (ctxt->error != NULL)
3219 ctxt->error(ctxt->userData,
3220 "Unexpected node %s is not a pattern\n",
3221 node->name);
3222 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003223 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003224 }
3225 return(def);
3226}
3227
3228/**
3229 * xmlRelaxNGParseAttribute:
3230 * @ctxt: a Relax-NG parser context
3231 * @node: the element node
3232 *
3233 * parse the content of a RelaxNG attribute node.
3234 *
3235 * Returns the definition pointer or NULL in case of error.
3236 */
3237static xmlRelaxNGDefinePtr
3238xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00003239 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003240 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003241 int old_flags;
3242
3243 ret = xmlRelaxNGNewDefine(ctxt, node);
3244 if (ret == NULL)
3245 return(NULL);
3246 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003247 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003248 child = node->children;
3249 if (child == NULL) {
3250 if (ctxt->error != NULL)
3251 ctxt->error(ctxt->userData,
3252 "xmlRelaxNGParseattribute: attribute has no children\n");
3253 ctxt->nbErrors++;
3254 return(ret);
3255 }
3256 old_flags = ctxt->flags;
3257 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003258 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3259 if (cur != NULL)
3260 child = child->next;
3261
Daniel Veillardd2298792003-02-14 16:54:11 +00003262 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003263 cur = xmlRelaxNGParsePattern(ctxt, child);
3264 if (cur != NULL) {
3265 switch (cur->type) {
3266 case XML_RELAXNG_EMPTY:
3267 case XML_RELAXNG_NOT_ALLOWED:
3268 case XML_RELAXNG_TEXT:
3269 case XML_RELAXNG_ELEMENT:
3270 case XML_RELAXNG_DATATYPE:
3271 case XML_RELAXNG_VALUE:
3272 case XML_RELAXNG_LIST:
3273 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003274 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003275 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003276 case XML_RELAXNG_DEF:
3277 case XML_RELAXNG_ONEORMORE:
3278 case XML_RELAXNG_ZEROORMORE:
3279 case XML_RELAXNG_OPTIONAL:
3280 case XML_RELAXNG_CHOICE:
3281 case XML_RELAXNG_GROUP:
3282 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard77648bb2003-02-20 15:03:22 +00003283 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00003284 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003285 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003286 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003287 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003288 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003289 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00003290 if (ctxt->error != NULL)
3291 ctxt->error(ctxt->userData,
3292 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003293 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003294 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003295 case XML_RELAXNG_NOOP:
3296 TODO
3297 if (ctxt->error != NULL)
3298 ctxt->error(ctxt->userData,
3299 "Internal error, noop found\n");
3300 ctxt->nbErrors++;
3301 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003302 }
3303 }
3304 child = child->next;
3305 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003306 if (child != NULL) {
3307 if (ctxt->error != NULL)
3308 ctxt->error(ctxt->userData, "attribute has multiple children\n");
3309 ctxt->nbErrors++;
3310 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003311 ctxt->flags = old_flags;
3312 return(ret);
3313}
3314
3315/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003316 * xmlRelaxNGParseExceptNameClass:
3317 * @ctxt: a Relax-NG parser context
3318 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00003319 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003320 *
3321 * parse the content of a RelaxNG nameClass node.
3322 *
3323 * Returns the definition pointer or NULL in case of error.
3324 */
3325static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00003326xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
3327 xmlNodePtr node, int attr) {
3328 xmlRelaxNGDefinePtr ret, cur, last = NULL;
3329 xmlNodePtr child;
3330
Daniel Veillardd2298792003-02-14 16:54:11 +00003331 if (!IS_RELAXNG(node, "except")) {
3332 if (ctxt->error != NULL)
3333 ctxt->error(ctxt->userData,
3334 "Expecting an except node\n");
3335 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00003336 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00003337 }
3338 if (node->next != NULL) {
3339 if (ctxt->error != NULL)
3340 ctxt->error(ctxt->userData,
3341 "exceptNameClass allows only a single except node\n");
3342 ctxt->nbErrors++;
3343 }
Daniel Veillard144fae12003-02-03 13:17:57 +00003344 if (node->children == NULL) {
3345 if (ctxt->error != NULL)
3346 ctxt->error(ctxt->userData,
3347 "except has no content\n");
3348 ctxt->nbErrors++;
3349 return(NULL);
3350 }
3351
3352 ret = xmlRelaxNGNewDefine(ctxt, node);
3353 if (ret == NULL)
3354 return(NULL);
3355 ret->type = XML_RELAXNG_EXCEPT;
3356 child = node->children;
3357 while (child != NULL) {
3358 cur = xmlRelaxNGNewDefine(ctxt, child);
3359 if (cur == NULL)
3360 break;
3361 if (attr)
3362 cur->type = XML_RELAXNG_ATTRIBUTE;
3363 else
3364 cur->type = XML_RELAXNG_ELEMENT;
3365
Daniel Veillard419a7682003-02-03 23:22:49 +00003366 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00003367 if (last == NULL) {
3368 ret->content = cur;
3369 } else {
3370 last->next = cur;
3371 }
3372 last = cur;
3373 }
3374 child = child->next;
3375 }
3376
3377 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003378}
3379
3380/**
3381 * xmlRelaxNGParseNameClass:
3382 * @ctxt: a Relax-NG parser context
3383 * @node: the nameClass node
3384 * @def: the current definition
3385 *
3386 * parse the content of a RelaxNG nameClass node.
3387 *
3388 * Returns the definition pointer or NULL in case of error.
3389 */
3390static xmlRelaxNGDefinePtr
3391xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
3392 xmlRelaxNGDefinePtr def) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003393 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003394 xmlChar *val;
3395
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003396 ret = def;
3397 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
3398 (IS_RELAXNG(node, "nsName"))) {
3399 if ((def->type != XML_RELAXNG_ELEMENT) &&
3400 (def->type != XML_RELAXNG_ATTRIBUTE)) {
3401 ret = xmlRelaxNGNewDefine(ctxt, node);
3402 if (ret == NULL)
3403 return(NULL);
3404 ret->parent = def;
3405 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
3406 ret->type = XML_RELAXNG_ATTRIBUTE;
3407 else
3408 ret->type = XML_RELAXNG_ELEMENT;
3409 }
3410 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003411 if (IS_RELAXNG(node, "name")) {
3412 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003413 xmlRelaxNGNormExtSpace(val);
3414 if (xmlValidateNCName(val, 0)) {
3415 if (ctxt->error != NULL) {
3416 if (node->parent != NULL)
3417 ctxt->error(ctxt->userData,
3418 "Element %s name '%s' is not an NCName\n",
3419 node->parent->name, val);
3420 else
3421 ctxt->error(ctxt->userData,
3422 "name '%s' is not an NCName\n",
3423 val);
3424 }
3425 ctxt->nbErrors++;
3426 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003427 ret->name = val;
3428 val = xmlGetProp(node, BAD_CAST "ns");
3429 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003430 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3431 (val != NULL) &&
3432 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3433 ctxt->error(ctxt->userData,
3434 "Attribute with namespace '%s' is not allowed\n",
3435 val);
3436 ctxt->nbErrors++;
3437 }
3438 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3439 (val != NULL) &&
3440 (val[0] == 0) &&
3441 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3442 ctxt->error(ctxt->userData,
3443 "Attribute with QName 'xmlns' is not allowed\n",
3444 val);
3445 ctxt->nbErrors++;
3446 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003447 } else if (IS_RELAXNG(node, "anyName")) {
3448 ret->name = NULL;
3449 ret->ns = NULL;
3450 if (node->children != NULL) {
3451 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003452 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3453 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003454 }
3455 } else if (IS_RELAXNG(node, "nsName")) {
3456 ret->name = NULL;
3457 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3458 if (ret->ns == NULL) {
3459 if (ctxt->error != NULL)
3460 ctxt->error(ctxt->userData,
3461 "nsName has no ns attribute\n");
3462 ctxt->nbErrors++;
3463 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003464 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3465 (ret->ns != NULL) &&
3466 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3467 ctxt->error(ctxt->userData,
3468 "Attribute with namespace '%s' is not allowed\n",
3469 ret->ns);
3470 ctxt->nbErrors++;
3471 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003472 if (node->children != NULL) {
3473 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003474 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3475 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003476 }
3477 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003478 xmlNodePtr child;
3479 xmlRelaxNGDefinePtr last = NULL;
3480
Daniel Veillardd4310742003-02-18 21:12:46 +00003481 ret = xmlRelaxNGNewDefine(ctxt, node);
3482 if (ret == NULL)
3483 return(NULL);
3484 ret->parent = def;
3485 ret->type = XML_RELAXNG_CHOICE;
3486
Daniel Veillardd2298792003-02-14 16:54:11 +00003487 if (node->children == NULL) {
3488 if (ctxt->error != NULL)
3489 ctxt->error(ctxt->userData,
3490 "Element choice is empty\n");
3491 ctxt->nbErrors++;
3492 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003493
3494 child = node->children;
3495 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003496 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003497 if (tmp != NULL) {
3498 if (last == NULL) {
3499 last = ret->nameClass = tmp;
3500 } else {
3501 last->next = tmp;
3502 last = tmp;
3503 }
3504 }
3505 child = child->next;
3506 }
3507 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003508 } else {
3509 if (ctxt->error != NULL)
3510 ctxt->error(ctxt->userData,
3511 "expecting name, anyName, nsName or choice : got %s\n",
3512 node->name);
3513 ctxt->nbErrors++;
3514 return(NULL);
3515 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003516 if (ret != def) {
3517 if (def->nameClass == NULL) {
3518 def->nameClass = ret;
3519 } else {
3520 tmp = def->nameClass;
3521 while (tmp->next != NULL) {
3522 tmp = tmp->next;
3523 }
3524 tmp->next = ret;
3525 }
3526 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003527 return(ret);
3528}
3529
3530/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003531 * xmlRelaxNGParseElement:
3532 * @ctxt: a Relax-NG parser context
3533 * @node: the element node
3534 *
3535 * parse the content of a RelaxNG element node.
3536 *
3537 * Returns the definition pointer or NULL in case of error.
3538 */
3539static xmlRelaxNGDefinePtr
3540xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3541 xmlRelaxNGDefinePtr ret, cur, last;
3542 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003543 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003544
3545 ret = xmlRelaxNGNewDefine(ctxt, node);
3546 if (ret == NULL)
3547 return(NULL);
3548 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003549 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003550 child = node->children;
3551 if (child == NULL) {
3552 if (ctxt->error != NULL)
3553 ctxt->error(ctxt->userData,
3554 "xmlRelaxNGParseElement: element has no children\n");
3555 ctxt->nbErrors++;
3556 return(ret);
3557 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003558 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3559 if (cur != NULL)
3560 child = child->next;
3561
Daniel Veillard6eadf632003-01-23 18:29:16 +00003562 if (child == NULL) {
3563 if (ctxt->error != NULL)
3564 ctxt->error(ctxt->userData,
3565 "xmlRelaxNGParseElement: element has no content\n");
3566 ctxt->nbErrors++;
3567 return(ret);
3568 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003569 olddefine = ctxt->define;
3570 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003571 last = NULL;
3572 while (child != NULL) {
3573 cur = xmlRelaxNGParsePattern(ctxt, child);
3574 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003575 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003576 switch (cur->type) {
3577 case XML_RELAXNG_EMPTY:
3578 case XML_RELAXNG_NOT_ALLOWED:
3579 case XML_RELAXNG_TEXT:
3580 case XML_RELAXNG_ELEMENT:
3581 case XML_RELAXNG_DATATYPE:
3582 case XML_RELAXNG_VALUE:
3583 case XML_RELAXNG_LIST:
3584 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003585 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003586 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003587 case XML_RELAXNG_DEF:
3588 case XML_RELAXNG_ZEROORMORE:
3589 case XML_RELAXNG_ONEORMORE:
3590 case XML_RELAXNG_OPTIONAL:
3591 case XML_RELAXNG_CHOICE:
3592 case XML_RELAXNG_GROUP:
3593 case XML_RELAXNG_INTERLEAVE:
3594 if (last == NULL) {
3595 ret->content = last = cur;
3596 } else {
3597 if ((last->type == XML_RELAXNG_ELEMENT) &&
3598 (ret->content == last)) {
3599 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3600 if (ret->content != NULL) {
3601 ret->content->type = XML_RELAXNG_GROUP;
3602 ret->content->content = last;
3603 } else {
3604 ret->content = last;
3605 }
3606 }
3607 last->next = cur;
3608 last = cur;
3609 }
3610 break;
3611 case XML_RELAXNG_ATTRIBUTE:
3612 cur->next = ret->attrs;
3613 ret->attrs = cur;
3614 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003615 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003616 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003617 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003618 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003619 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003620 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003621 case XML_RELAXNG_NOOP:
3622 TODO
3623 if (ctxt->error != NULL)
3624 ctxt->error(ctxt->userData,
3625 "Internal error, noop found\n");
3626 ctxt->nbErrors++;
3627 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003628 }
3629 }
3630 child = child->next;
3631 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003632 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003633 return(ret);
3634}
3635
3636/**
3637 * xmlRelaxNGParsePatterns:
3638 * @ctxt: a Relax-NG parser context
3639 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003640 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003641 *
3642 * parse the content of a RelaxNG start node.
3643 *
3644 * Returns the definition pointer or NULL in case of error.
3645 */
3646static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003647xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3648 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003649 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003650
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003651 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003652 while (nodes != NULL) {
3653 if (IS_RELAXNG(nodes, "element")) {
3654 cur = xmlRelaxNGParseElement(ctxt, nodes);
3655 if (def == NULL) {
3656 def = last = cur;
3657 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003658 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3659 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003660 def = xmlRelaxNGNewDefine(ctxt, nodes);
3661 def->type = XML_RELAXNG_GROUP;
3662 def->content = last;
3663 }
3664 last->next = cur;
3665 last = cur;
3666 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003667 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003668 } else {
3669 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003670 if (cur != NULL) {
3671 if (def == NULL) {
3672 def = last = cur;
3673 } else {
3674 last->next = cur;
3675 last = cur;
3676 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003677 }
3678 }
3679 nodes = nodes->next;
3680 }
3681 return(def);
3682}
3683
3684/**
3685 * xmlRelaxNGParseStart:
3686 * @ctxt: a Relax-NG parser context
3687 * @nodes: start children nodes
3688 *
3689 * parse the content of a RelaxNG start node.
3690 *
3691 * Returns 0 in case of success, -1 in case of error
3692 */
3693static int
3694xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3695 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003696 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003697
Daniel Veillardd2298792003-02-14 16:54:11 +00003698 if (nodes == NULL) {
3699 if (ctxt->error != NULL)
3700 ctxt->error(ctxt->userData,
3701 "start has no children\n");
3702 ctxt->nbErrors++;
3703 return(-1);
3704 }
3705 if (IS_RELAXNG(nodes, "empty")) {
3706 def = xmlRelaxNGNewDefine(ctxt, nodes);
3707 if (def == NULL)
3708 return(-1);
3709 def->type = XML_RELAXNG_EMPTY;
3710 if (nodes->children != NULL) {
3711 if (ctxt->error != NULL)
3712 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003713 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003714 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003715 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3716 def = xmlRelaxNGNewDefine(ctxt, nodes);
3717 if (def == NULL)
3718 return(-1);
3719 def->type = XML_RELAXNG_NOT_ALLOWED;
3720 if (nodes->children != NULL) {
3721 if (ctxt->error != NULL)
3722 ctxt->error(ctxt->userData,
3723 "element notAllowed is not empty\n");
3724 ctxt->nbErrors++;
3725 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003726 } else {
3727 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003728 }
3729 if (ctxt->grammar->start != NULL) {
3730 last = ctxt->grammar->start;
3731 while (last->next != NULL)
3732 last = last->next;
3733 last->next = def;
3734 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003735 ctxt->grammar->start = def;
3736 }
3737 nodes = nodes->next;
3738 if (nodes != NULL) {
3739 if (ctxt->error != NULL)
3740 ctxt->error(ctxt->userData,
3741 "start more than one children\n");
3742 ctxt->nbErrors++;
3743 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003744 }
3745 return(ret);
3746}
3747
3748/**
3749 * xmlRelaxNGParseGrammarContent:
3750 * @ctxt: a Relax-NG parser context
3751 * @nodes: grammar children nodes
3752 *
3753 * parse the content of a RelaxNG grammar node.
3754 *
3755 * Returns 0 in case of success, -1 in case of error
3756 */
3757static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003758xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003759{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003760 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003761
3762 if (nodes == NULL) {
3763 if (ctxt->error != NULL)
3764 ctxt->error(ctxt->userData,
3765 "grammar has no children\n");
3766 ctxt->nbErrors++;
3767 return(-1);
3768 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003769 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003770 if (IS_RELAXNG(nodes, "start")) {
3771 if (nodes->children == NULL) {
3772 if (ctxt->error != NULL)
3773 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003774 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003775 ctxt->nbErrors++;
3776 } else {
3777 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3778 if (tmp != 0)
3779 ret = -1;
3780 }
3781 } else if (IS_RELAXNG(nodes, "define")) {
3782 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3783 if (tmp != 0)
3784 ret = -1;
3785 } else if (IS_RELAXNG(nodes, "include")) {
3786 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3787 if (tmp != 0)
3788 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003789 } else {
3790 if (ctxt->error != NULL)
3791 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003792 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003793 ctxt->nbErrors++;
3794 ret = -1;
3795 }
3796 nodes = nodes->next;
3797 }
3798 return (ret);
3799}
3800
3801/**
3802 * xmlRelaxNGCheckReference:
3803 * @ref: the ref
3804 * @ctxt: a Relax-NG parser context
3805 * @name: the name associated to the defines
3806 *
3807 * Applies the 4.17. combine attribute rule for all the define
3808 * element of a given grammar using the same name.
3809 */
3810static void
3811xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3812 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3813 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003814 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003815
3816 grammar = ctxt->grammar;
3817 if (grammar == NULL) {
3818 if (ctxt->error != NULL)
3819 ctxt->error(ctxt->userData,
3820 "Internal error: no grammar in CheckReference %s\n",
3821 name);
3822 ctxt->nbErrors++;
3823 return;
3824 }
3825 if (ref->content != NULL) {
3826 if (ctxt->error != NULL)
3827 ctxt->error(ctxt->userData,
3828 "Internal error: reference has content in CheckReference %s\n",
3829 name);
3830 ctxt->nbErrors++;
3831 return;
3832 }
3833 if (grammar->defs != NULL) {
3834 def = xmlHashLookup(grammar->defs, name);
3835 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003836 cur = ref;
3837 while (cur != NULL) {
3838 cur->content = def;
3839 cur = cur->nextHash;
3840 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003841 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00003842 if (ctxt->error != NULL)
3843 ctxt->error(ctxt->userData,
3844 "Reference %s has no matching definition\n",
3845 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003846 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003847 }
Daniel Veillardd4310742003-02-18 21:12:46 +00003848 } else {
3849 if (ctxt->error != NULL)
3850 ctxt->error(ctxt->userData,
3851 "Reference %s has no matching definition\n",
3852 name);
3853 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003854 }
3855 /*
3856 * TODO: make a closure and verify there is no loop !
3857 */
3858}
3859
3860/**
3861 * xmlRelaxNGCheckCombine:
3862 * @define: the define(s) list
3863 * @ctxt: a Relax-NG parser context
3864 * @name: the name associated to the defines
3865 *
3866 * Applies the 4.17. combine attribute rule for all the define
3867 * element of a given grammar using the same name.
3868 */
3869static void
3870xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3871 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3872 xmlChar *combine;
3873 int choiceOrInterleave = -1;
3874 int missing = 0;
3875 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3876
3877 if (define->nextHash == NULL)
3878 return;
3879 cur = define;
3880 while (cur != NULL) {
3881 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3882 if (combine != NULL) {
3883 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3884 if (choiceOrInterleave == -1)
3885 choiceOrInterleave = 1;
3886 else if (choiceOrInterleave == 0) {
3887 if (ctxt->error != NULL)
3888 ctxt->error(ctxt->userData,
3889 "Defines for %s use both 'choice' and 'interleave'\n",
3890 name);
3891 ctxt->nbErrors++;
3892 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003893 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003894 if (choiceOrInterleave == -1)
3895 choiceOrInterleave = 0;
3896 else if (choiceOrInterleave == 1) {
3897 if (ctxt->error != NULL)
3898 ctxt->error(ctxt->userData,
3899 "Defines for %s use both 'choice' and 'interleave'\n",
3900 name);
3901 ctxt->nbErrors++;
3902 }
3903 } else {
3904 if (ctxt->error != NULL)
3905 ctxt->error(ctxt->userData,
3906 "Defines for %s use unknown combine value '%s''\n",
3907 name, combine);
3908 ctxt->nbErrors++;
3909 }
3910 xmlFree(combine);
3911 } else {
3912 if (missing == 0)
3913 missing = 1;
3914 else {
3915 if (ctxt->error != NULL)
3916 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00003917 "Some defines for %s needs the combine attribute\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00003918 name);
3919 ctxt->nbErrors++;
3920 }
3921 }
3922
3923 cur = cur->nextHash;
3924 }
3925#ifdef DEBUG
3926 xmlGenericError(xmlGenericErrorContext,
3927 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3928 name, choiceOrInterleave);
3929#endif
3930 if (choiceOrInterleave == -1)
3931 choiceOrInterleave = 0;
3932 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3933 if (cur == NULL)
3934 return;
3935 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003936 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003937 else
3938 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003939 tmp = define;
3940 last = NULL;
3941 while (tmp != NULL) {
3942 if (tmp->content != NULL) {
3943 if (tmp->content->next != NULL) {
3944 /*
3945 * we need first to create a wrapper.
3946 */
3947 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3948 if (tmp2 == NULL)
3949 break;
3950 tmp2->type = XML_RELAXNG_GROUP;
3951 tmp2->content = tmp->content;
3952 } else {
3953 tmp2 = tmp->content;
3954 }
3955 if (last == NULL) {
3956 cur->content = tmp2;
3957 } else {
3958 last->next = tmp2;
3959 }
3960 last = tmp2;
3961 tmp->content = NULL;
3962 }
3963 tmp = tmp->nextHash;
3964 }
3965 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003966 if (choiceOrInterleave == 0) {
3967 if (ctxt->interleaves == NULL)
3968 ctxt->interleaves = xmlHashCreate(10);
3969 if (ctxt->interleaves == NULL) {
3970 if (ctxt->error != NULL)
3971 ctxt->error(ctxt->userData,
3972 "Failed to create interleaves hash table\n");
3973 ctxt->nbErrors++;
3974 } else {
3975 char tmpname[32];
3976
3977 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3978 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3979 if (ctxt->error != NULL)
3980 ctxt->error(ctxt->userData,
3981 "Failed to add %s to hash table\n", tmpname);
3982 ctxt->nbErrors++;
3983 }
3984 }
3985 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003986}
3987
3988/**
3989 * xmlRelaxNGCombineStart:
3990 * @ctxt: a Relax-NG parser context
3991 * @grammar: the grammar
3992 *
3993 * Applies the 4.17. combine rule for all the start
3994 * element of a given grammar.
3995 */
3996static void
3997xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3998 xmlRelaxNGGrammarPtr grammar) {
3999 xmlRelaxNGDefinePtr starts;
4000 xmlChar *combine;
4001 int choiceOrInterleave = -1;
4002 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004003 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004004
Daniel Veillard2df2de22003-02-17 23:34:33 +00004005 starts = grammar->start;
4006 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00004007 return;
4008 cur = starts;
4009 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00004010 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
4011 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
4012 combine = NULL;
4013 if (ctxt->error != NULL)
4014 ctxt->error(ctxt->userData,
4015 "Internal error: start element not found\n");
4016 ctxt->nbErrors++;
4017 } else {
4018 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
4019 }
4020
Daniel Veillard6eadf632003-01-23 18:29:16 +00004021 if (combine != NULL) {
4022 if (xmlStrEqual(combine, BAD_CAST "choice")) {
4023 if (choiceOrInterleave == -1)
4024 choiceOrInterleave = 1;
4025 else if (choiceOrInterleave == 0) {
4026 if (ctxt->error != NULL)
4027 ctxt->error(ctxt->userData,
4028 "<start> use both 'choice' and 'interleave'\n");
4029 ctxt->nbErrors++;
4030 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00004031 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00004032 if (choiceOrInterleave == -1)
4033 choiceOrInterleave = 0;
4034 else if (choiceOrInterleave == 1) {
4035 if (ctxt->error != NULL)
4036 ctxt->error(ctxt->userData,
4037 "<start> use both 'choice' and 'interleave'\n");
4038 ctxt->nbErrors++;
4039 }
4040 } else {
4041 if (ctxt->error != NULL)
4042 ctxt->error(ctxt->userData,
4043 "<start> uses unknown combine value '%s''\n", combine);
4044 ctxt->nbErrors++;
4045 }
4046 xmlFree(combine);
4047 } else {
4048 if (missing == 0)
4049 missing = 1;
4050 else {
4051 if (ctxt->error != NULL)
4052 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00004053 "Some <start> element miss the combine attribute\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +00004054 ctxt->nbErrors++;
4055 }
4056 }
4057
Daniel Veillard2df2de22003-02-17 23:34:33 +00004058 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004059 }
4060#ifdef DEBUG
4061 xmlGenericError(xmlGenericErrorContext,
4062 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
4063 choiceOrInterleave);
4064#endif
4065 if (choiceOrInterleave == -1)
4066 choiceOrInterleave = 0;
4067 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
4068 if (cur == NULL)
4069 return;
4070 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00004071 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004072 else
4073 cur->type = XML_RELAXNG_CHOICE;
4074 cur->content = grammar->start;
4075 grammar->start = cur;
4076 if (choiceOrInterleave == 0) {
4077 if (ctxt->interleaves == NULL)
4078 ctxt->interleaves = xmlHashCreate(10);
4079 if (ctxt->interleaves == NULL) {
4080 if (ctxt->error != NULL)
4081 ctxt->error(ctxt->userData,
4082 "Failed to create interleaves hash table\n");
4083 ctxt->nbErrors++;
4084 } else {
4085 char tmpname[32];
4086
4087 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
4088 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
4089 if (ctxt->error != NULL)
4090 ctxt->error(ctxt->userData,
4091 "Failed to add %s to hash table\n", tmpname);
4092 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004093 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004094 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004095 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004096}
4097
4098/**
Daniel Veillardd4310742003-02-18 21:12:46 +00004099 * xmlRelaxNGCheckCycles:
4100 * @ctxt: a Relax-NG parser context
4101 * @nodes: grammar children nodes
4102 * @depth: the counter
4103 *
4104 * Check for cycles.
4105 *
4106 * Returns 0 if check passed, and -1 in case of error
4107 */
4108static int
4109xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
4110 xmlRelaxNGDefinePtr cur, int depth) {
4111 int ret = 0;
4112
4113 while ((ret == 0) && (cur != NULL)) {
4114 if ((cur->type == XML_RELAXNG_REF) ||
4115 (cur->type == XML_RELAXNG_PARENTREF)) {
4116 if (cur->depth == -1) {
4117 cur->depth = depth;
4118 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
4119 cur->depth = -2;
4120 } else if (depth == cur->depth) {
4121 if (ctxt->error != NULL)
4122 ctxt->error(ctxt->userData,
4123 "Detected a cycle in %s references\n", cur->name);
4124 ctxt->nbErrors++;
4125 return(-1);
4126 }
4127 } else if (cur->type == XML_RELAXNG_ELEMENT) {
4128 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
4129 } else {
4130 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
4131 }
4132 cur = cur->next;
4133 }
4134 return(ret);
4135}
4136
4137/**
Daniel Veillard77648bb2003-02-20 15:03:22 +00004138 * xmlRelaxNGTryUnlink:
4139 * @ctxt: a Relax-NG parser context
4140 * @cur: the definition to unlink
4141 * @parent: the parent definition
4142 * @prev: the previous sibling definition
4143 *
4144 * Try to unlink a definition. If not possble make it a NOOP
4145 *
4146 * Returns the new prev definition
4147 */
4148static xmlRelaxNGDefinePtr
4149xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
4150 xmlRelaxNGDefinePtr cur,
4151 xmlRelaxNGDefinePtr parent,
4152 xmlRelaxNGDefinePtr prev) {
4153 if (prev != NULL) {
4154 prev->next = cur->next;
4155 } else {
4156 if (parent != NULL) {
4157 if (parent->content == cur)
4158 parent->content = cur->next;
4159 else if (parent->attrs == cur)
4160 parent->attrs = cur->next;
4161 else if (parent->nameClass == cur)
4162 parent->nameClass = cur->next;
4163 } else {
4164 cur->type = XML_RELAXNG_NOOP;
4165 prev = cur;
4166 }
4167 }
4168 return(prev);
4169}
4170
4171/**
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004172 * xmlRelaxNGSimplify:
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004173 * @ctxt: a Relax-NG parser context
4174 * @nodes: grammar children nodes
4175 *
4176 * Check for simplification of empty and notAllowed
4177 */
4178static void
4179xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
4180 xmlRelaxNGDefinePtr cur,
4181 xmlRelaxNGDefinePtr parent) {
4182 xmlRelaxNGDefinePtr prev = NULL;
4183
4184 while (cur != NULL) {
4185 if ((cur->type == XML_RELAXNG_REF) ||
4186 (cur->type == XML_RELAXNG_PARENTREF)) {
4187 if (cur->depth != -3) {
4188 cur->depth = -3;
4189 xmlRelaxNGSimplify(ctxt, cur->content, cur);
4190 }
4191 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004192 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004193 if ((parent != NULL) &&
4194 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4195 (parent->type == XML_RELAXNG_LIST) ||
4196 (parent->type == XML_RELAXNG_GROUP) ||
4197 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4198 (parent->type == XML_RELAXNG_ONEORMORE) ||
4199 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4200 parent->type = XML_RELAXNG_NOT_ALLOWED;
4201 break;
4202 }
4203 if ((parent != NULL) &&
4204 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004205 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004206 } else
4207 prev = cur;
4208 } else if (cur->type == XML_RELAXNG_EMPTY){
Daniel Veillard77648bb2003-02-20 15:03:22 +00004209 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004210 if ((parent != NULL) &&
4211 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4212 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4213 parent->type = XML_RELAXNG_EMPTY;
4214 break;
4215 }
4216 if ((parent != NULL) &&
4217 ((parent->type == XML_RELAXNG_GROUP) ||
4218 (parent->type == XML_RELAXNG_INTERLEAVE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004219 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004220 } else
4221 prev = cur;
4222 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004223 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004224 if (cur->content != NULL)
4225 xmlRelaxNGSimplify(ctxt, cur->content, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004226 if (cur->attrs != NULL)
4227 xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
4228 if (cur->nameClass != NULL)
4229 xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004230 /*
4231 * This may result in a simplification
4232 */
4233 if ((cur->type == XML_RELAXNG_GROUP) ||
4234 (cur->type == XML_RELAXNG_INTERLEAVE)) {
4235 if (cur->content == NULL)
4236 cur->type = XML_RELAXNG_EMPTY;
4237 else if (cur->content->next == NULL) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004238 if ((parent == NULL) && (prev == NULL)) {
4239 cur->type = XML_RELAXNG_NOOP;
4240 } else if (prev == NULL) {
4241 parent->content = cur->content;
4242 cur->content->next = cur->next;
4243 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004244 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004245 cur->content->next = cur->next;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004246 prev->next = cur->content;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004247 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004248 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004249 }
4250 }
4251 /*
4252 * the current node may have been transformed back
4253 */
4254 if ((cur->type == XML_RELAXNG_EXCEPT) &&
4255 (cur->content != NULL) &&
4256 (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004257 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004258 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
4259 if ((parent != NULL) &&
4260 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4261 (parent->type == XML_RELAXNG_LIST) ||
4262 (parent->type == XML_RELAXNG_GROUP) ||
4263 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4264 (parent->type == XML_RELAXNG_ONEORMORE) ||
4265 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4266 parent->type = XML_RELAXNG_NOT_ALLOWED;
4267 break;
4268 }
4269 if ((parent != NULL) &&
4270 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004271 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004272 } else
4273 prev = cur;
4274 } else if (cur->type == XML_RELAXNG_EMPTY){
4275 if ((parent != NULL) &&
4276 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4277 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4278 parent->type = XML_RELAXNG_EMPTY;
4279 break;
4280 }
4281 if ((parent != NULL) &&
4282 ((parent->type == XML_RELAXNG_GROUP) ||
4283 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4284 (parent->type == XML_RELAXNG_CHOICE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004285 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004286 } else
4287 prev = cur;
4288 } else {
4289 prev = cur;
4290 }
4291 }
4292 cur = cur->next;
4293 }
4294}
4295
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004296/**
4297 * xmlRelaxNGGroupContentType:
4298 * @ct1: the first content type
4299 * @ct2: the second content type
4300 *
4301 * Try to group 2 content types
4302 *
4303 * Returns the content type
4304 */
4305static xmlRelaxNGContentType
4306xmlRelaxNGGroupContentType(xmlRelaxNGContentType ct1,
4307 xmlRelaxNGContentType ct2) {
4308 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4309 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4310 return(XML_RELAXNG_CONTENT_ERROR);
4311 if (ct1 == XML_RELAXNG_CONTENT_EMPTY)
4312 return(ct2);
4313 if (ct2 == XML_RELAXNG_CONTENT_EMPTY)
4314 return(ct1);
4315 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) &&
4316 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4317 return(XML_RELAXNG_CONTENT_COMPLEX);
4318 return(XML_RELAXNG_CONTENT_ERROR);
4319}
4320
4321/**
4322 * xmlRelaxNGMaxContentType:
4323 * @ct1: the first content type
4324 * @ct2: the second content type
4325 *
4326 * Compute the max content-type
4327 *
4328 * Returns the content type
4329 */
4330static xmlRelaxNGContentType
4331xmlRelaxNGMaxContentType(xmlRelaxNGContentType ct1,
4332 xmlRelaxNGContentType ct2) {
4333 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4334 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4335 return(XML_RELAXNG_CONTENT_ERROR);
4336 if ((ct1 == XML_RELAXNG_CONTENT_SIMPLE) ||
4337 (ct2 == XML_RELAXNG_CONTENT_SIMPLE))
4338 return(XML_RELAXNG_CONTENT_SIMPLE);
4339 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) ||
4340 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4341 return(XML_RELAXNG_CONTENT_COMPLEX);
4342 return(XML_RELAXNG_CONTENT_EMPTY);
4343}
Daniel Veillard77648bb2003-02-20 15:03:22 +00004344
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004345/**
4346 * xmlRelaxNGCheckRules:
4347 * @ctxt: a Relax-NG parser context
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004348 * @cur: the current definition
Daniel Veillard77648bb2003-02-20 15:03:22 +00004349 * @flags: some accumulated flags
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004350 * @ptype: the parent type
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004351 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004352 * Check for rules in section 7.1 and 7.2
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004353 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004354 * Returns the content type of @cur
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004355 */
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004356static xmlRelaxNGContentType
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004357xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004358 xmlRelaxNGDefinePtr cur, int flags,
4359 xmlRelaxNGType ptype) {
4360 int nflags = flags;
4361 xmlRelaxNGContentType ret, tmp, val = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004362
Daniel Veillard77648bb2003-02-20 15:03:22 +00004363 while (cur != NULL) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004364 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004365 if ((cur->type == XML_RELAXNG_REF) ||
4366 (cur->type == XML_RELAXNG_PARENTREF)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004367 if (flags & XML_RELAXNG_IN_LIST) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004368 if (ctxt->error != NULL)
4369 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00004370 "Found forbidden pattern list//ref\n");
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004371 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004372 }
4373 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4374 if (ctxt->error != NULL)
4375 ctxt->error(ctxt->userData,
4376 "Found forbidden pattern attribute//ref\n");
4377 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004378 }
4379 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4380 if (ctxt->error != NULL)
4381 ctxt->error(ctxt->userData,
4382 "Found forbidden pattern data/except//ref\n");
4383 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004384 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00004385 if (cur->depth > -4) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004386 cur->depth = -4;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004387 ret = xmlRelaxNGCheckRules(ctxt, cur->content,
4388 flags, cur->type);
4389 cur->depth = ret - 15 ;
4390 } else if (cur->depth == -4) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004391 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004392 } else {
4393 ret = (xmlRelaxNGContentType) cur->depth + 15;
4394 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004395 } else if (cur->type == XML_RELAXNG_ELEMENT) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004396 /*
4397 * The 7.3 Attribute derivation rule for groups is plugged there
4398 */
4399 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004400 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4401 if (ctxt->error != NULL)
4402 ctxt->error(ctxt->userData,
4403 "Found forbidden pattern data/except//element(ref)\n");
4404 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004405 }
4406 if (flags & XML_RELAXNG_IN_LIST) {
4407 if (ctxt->error != NULL)
4408 ctxt->error(ctxt->userData,
4409 "Found forbidden pattern list//element(ref)\n");
4410 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004411 }
4412 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4413 if (ctxt->error != NULL)
4414 ctxt->error(ctxt->userData,
4415 "Found forbidden pattern attribute//element(ref)\n");
4416 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004417 }
4418 /*
4419 * reset since in the simple form elements are only child
4420 * of grammar/define
4421 */
4422 nflags = 0;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004423 ret = xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags, cur->type);
4424 if (ret != XML_RELAXNG_CONTENT_EMPTY) {
4425 if (ctxt->error != NULL)
4426 ctxt->error(ctxt->userData,
4427 "Element %s attributes have a content type error\n",
4428 cur->name);
4429 ctxt->nbErrors++;
4430 }
4431 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4432 if (ret == XML_RELAXNG_CONTENT_ERROR) {
4433 if (ctxt->error != NULL)
4434 ctxt->error(ctxt->userData,
4435 "Element %s has a content type error\n",
4436 cur->name);
4437 ctxt->nbErrors++;
4438 } else {
4439 ret = XML_RELAXNG_CONTENT_COMPLEX;
4440 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00004441 } else if (cur->type == XML_RELAXNG_ATTRIBUTE) {
4442 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4443 if (ctxt->error != NULL)
4444 ctxt->error(ctxt->userData,
4445 "Found forbidden pattern attribute//attribute\n");
4446 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004447 }
4448 if (flags & XML_RELAXNG_IN_LIST) {
4449 if (ctxt->error != NULL)
4450 ctxt->error(ctxt->userData,
4451 "Found forbidden pattern list//attribute\n");
4452 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004453 }
4454 if (flags & XML_RELAXNG_IN_OOMGROUP) {
4455 if (ctxt->error != NULL)
4456 ctxt->error(ctxt->userData,
4457 "Found forbidden pattern oneOrMore//group//attribute\n");
4458 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004459 }
4460 if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) {
4461 if (ctxt->error != NULL)
4462 ctxt->error(ctxt->userData,
4463 "Found forbidden pattern oneOrMore//interleave//attribute\n");
4464 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004465 }
4466 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4467 if (ctxt->error != NULL)
4468 ctxt->error(ctxt->userData,
4469 "Found forbidden pattern data/except//attribute\n");
4470 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004471 }
4472 if (flags & XML_RELAXNG_IN_START) {
4473 if (ctxt->error != NULL)
4474 ctxt->error(ctxt->userData,
4475 "Found forbidden pattern start//attribute\n");
4476 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004477 }
4478 nflags = flags | XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004479 xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4480 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004481 } else if ((cur->type == XML_RELAXNG_ONEORMORE) ||
4482 (cur->type == XML_RELAXNG_ZEROORMORE)) {
4483 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4484 if (ctxt->error != NULL)
4485 ctxt->error(ctxt->userData,
4486 "Found forbidden pattern data/except//oneOrMore\n");
4487 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004488 }
4489 if (flags & XML_RELAXNG_IN_START) {
4490 if (ctxt->error != NULL)
4491 ctxt->error(ctxt->userData,
4492 "Found forbidden pattern start//oneOrMore\n");
4493 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004494 }
4495 nflags = flags | XML_RELAXNG_IN_ONEORMORE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004496 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4497 ret = xmlRelaxNGGroupContentType(ret, ret);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004498 } else if (cur->type == XML_RELAXNG_LIST) {
4499 if (flags & XML_RELAXNG_IN_LIST) {
4500 if (ctxt->error != NULL)
4501 ctxt->error(ctxt->userData,
4502 "Found forbidden pattern list//list\n");
4503 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004504 }
4505 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4506 if (ctxt->error != NULL)
4507 ctxt->error(ctxt->userData,
4508 "Found forbidden pattern data/except//list\n");
4509 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004510 }
4511 if (flags & XML_RELAXNG_IN_START) {
4512 if (ctxt->error != NULL)
4513 ctxt->error(ctxt->userData,
4514 "Found forbidden pattern start//list\n");
4515 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004516 }
4517 nflags = flags | XML_RELAXNG_IN_LIST;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004518 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004519 } else if (cur->type == XML_RELAXNG_GROUP) {
4520 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4521 if (ctxt->error != NULL)
4522 ctxt->error(ctxt->userData,
4523 "Found forbidden pattern data/except//group\n");
4524 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004525 }
4526 if (flags & XML_RELAXNG_IN_START) {
4527 if (ctxt->error != NULL)
4528 ctxt->error(ctxt->userData,
4529 "Found forbidden pattern start//group\n");
4530 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004531 }
4532 if (flags & XML_RELAXNG_IN_ONEORMORE)
4533 nflags = flags | XML_RELAXNG_IN_OOMGROUP;
4534 else
4535 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004536 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004537 /*
4538 * The 7.3 Attribute derivation rule for groups is plugged there
4539 */
4540 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004541 } else if (cur->type == XML_RELAXNG_INTERLEAVE) {
4542 if (flags & XML_RELAXNG_IN_LIST) {
4543 if (ctxt->error != NULL)
4544 ctxt->error(ctxt->userData,
4545 "Found forbidden pattern list//interleave\n");
4546 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004547 }
4548 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4549 if (ctxt->error != NULL)
4550 ctxt->error(ctxt->userData,
4551 "Found forbidden pattern data/except//interleave\n");
4552 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004553 }
4554 if (flags & XML_RELAXNG_IN_START) {
4555 if (ctxt->error != NULL)
4556 ctxt->error(ctxt->userData,
4557 "Found forbidden pattern start//interleave\n");
4558 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004559 }
4560 if (flags & XML_RELAXNG_IN_ONEORMORE)
4561 nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE;
4562 else
4563 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004564 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004565 } else if (cur->type == XML_RELAXNG_EXCEPT) {
4566 if ((cur->parent != NULL) &&
4567 (cur->parent->type == XML_RELAXNG_DATATYPE))
4568 nflags = flags | XML_RELAXNG_IN_DATAEXCEPT;
4569 else
4570 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004571 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004572 } else if (cur->type == XML_RELAXNG_DATATYPE) {
4573 if (flags & XML_RELAXNG_IN_START) {
4574 if (ctxt->error != NULL)
4575 ctxt->error(ctxt->userData,
4576 "Found forbidden pattern start//data\n");
4577 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004578 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004579 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4580 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004581 } else if (cur->type == XML_RELAXNG_VALUE) {
4582 if (flags & XML_RELAXNG_IN_START) {
4583 if (ctxt->error != NULL)
4584 ctxt->error(ctxt->userData,
4585 "Found forbidden pattern start//value\n");
4586 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004587 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004588 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4589 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004590 } else if (cur->type == XML_RELAXNG_TEXT) {
4591 if (flags & XML_RELAXNG_IN_LIST) {
4592 if (ctxt->error != NULL)
4593 ctxt->error(ctxt->userData,
4594 "Found forbidden pattern list//text\n");
4595 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004596 }
4597 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4598 if (ctxt->error != NULL)
4599 ctxt->error(ctxt->userData,
4600 "Found forbidden pattern data/except//text\n");
4601 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004602 }
4603 if (flags & XML_RELAXNG_IN_START) {
4604 if (ctxt->error != NULL)
4605 ctxt->error(ctxt->userData,
4606 "Found forbidden pattern start//text\n");
4607 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004608 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004609 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004610 } else if (cur->type == XML_RELAXNG_EMPTY) {
4611 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4612 if (ctxt->error != NULL)
4613 ctxt->error(ctxt->userData,
4614 "Found forbidden pattern data/except//empty\n");
4615 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004616 }
4617 if (flags & XML_RELAXNG_IN_START) {
4618 if (ctxt->error != NULL)
4619 ctxt->error(ctxt->userData,
4620 "Found forbidden pattern start//empty\n");
4621 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004622 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004623 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004624 } else {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004625 ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004626 }
4627 cur = cur->next;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004628 if (ptype == XML_RELAXNG_GROUP) {
4629 val = xmlRelaxNGGroupContentType(val, ret);
4630 } else if (ptype == XML_RELAXNG_INTERLEAVE) {
4631 tmp = xmlRelaxNGGroupContentType(val, ret);
4632 if (tmp != XML_RELAXNG_CONTENT_ERROR)
4633 tmp = xmlRelaxNGMaxContentType(val, ret);
4634 } else if (ptype == XML_RELAXNG_CHOICE) {
4635 val = xmlRelaxNGMaxContentType(val, ret);
4636 } else if (ptype == XML_RELAXNG_LIST) {
4637 val = XML_RELAXNG_CONTENT_SIMPLE;
4638 } else if (ptype == XML_RELAXNG_EXCEPT) {
4639 if (ret == XML_RELAXNG_CONTENT_ERROR)
4640 val = XML_RELAXNG_CONTENT_ERROR;
4641 else
4642 val = XML_RELAXNG_CONTENT_SIMPLE;
4643 } else {
4644 val = xmlRelaxNGGroupContentType(val, ret);
4645 }
4646
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004647 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004648 return(val);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004649}
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004650
4651/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004652 * xmlRelaxNGParseGrammar:
4653 * @ctxt: a Relax-NG parser context
4654 * @nodes: grammar children nodes
4655 *
4656 * parse a Relax-NG <grammar> node
4657 *
4658 * Returns the internal xmlRelaxNGGrammarPtr built or
4659 * NULL in case of error
4660 */
4661static xmlRelaxNGGrammarPtr
4662xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
4663 xmlRelaxNGGrammarPtr ret, tmp, old;
4664
Daniel Veillardc482e262003-02-26 14:48:48 +00004665#ifdef DEBUG_GRAMMAR
4666 xmlGenericError(xmlGenericErrorContext, "Parsing a new grammar\n");
4667#endif
4668
Daniel Veillard6eadf632003-01-23 18:29:16 +00004669 ret = xmlRelaxNGNewGrammar(ctxt);
4670 if (ret == NULL)
4671 return(NULL);
4672
4673 /*
4674 * Link the new grammar in the tree
4675 */
4676 ret->parent = ctxt->grammar;
4677 if (ctxt->grammar != NULL) {
4678 tmp = ctxt->grammar->children;
4679 if (tmp == NULL) {
4680 ctxt->grammar->children = ret;
4681 } else {
4682 while (tmp->next != NULL)
4683 tmp = tmp->next;
4684 tmp->next = ret;
4685 }
4686 }
4687
4688 old = ctxt->grammar;
4689 ctxt->grammar = ret;
4690 xmlRelaxNGParseGrammarContent(ctxt, nodes);
4691 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004692 if (ctxt->grammar == NULL) {
4693 if (ctxt->error != NULL)
4694 ctxt->error(ctxt->userData,
4695 "Failed to parse <grammar> content\n");
4696 ctxt->nbErrors++;
4697 } else if (ctxt->grammar->start == NULL) {
4698 if (ctxt->error != NULL)
4699 ctxt->error(ctxt->userData,
4700 "Element <grammar> has no <start>\n");
4701 ctxt->nbErrors++;
4702 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004703
4704 /*
4705 * Apply 4.17 mergingd rules to defines and starts
4706 */
4707 xmlRelaxNGCombineStart(ctxt, ret);
4708 if (ret->defs != NULL) {
4709 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
4710 ctxt);
4711 }
4712
4713 /*
4714 * link together defines and refs in this grammar
4715 */
4716 if (ret->refs != NULL) {
4717 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
4718 ctxt);
4719 }
4720 ctxt->grammar = old;
4721 return(ret);
4722}
4723
4724/**
4725 * xmlRelaxNGParseDocument:
4726 * @ctxt: a Relax-NG parser context
4727 * @node: the root node of the RelaxNG schema
4728 *
4729 * parse a Relax-NG definition resource and build an internal
4730 * xmlRelaxNG struture which can be used to validate instances.
4731 *
4732 * Returns the internal XML RelaxNG structure built or
4733 * NULL in case of error
4734 */
4735static xmlRelaxNGPtr
4736xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4737 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004738 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00004739 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004740
4741 if ((ctxt == NULL) || (node == NULL))
4742 return (NULL);
4743
4744 schema = xmlRelaxNGNewRelaxNG(ctxt);
4745 if (schema == NULL)
4746 return(NULL);
4747
Daniel Veillard276be4a2003-01-24 01:03:34 +00004748 olddefine = ctxt->define;
4749 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004750 if (IS_RELAXNG(node, "grammar")) {
4751 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
4752 } else {
Daniel Veillardc482e262003-02-26 14:48:48 +00004753 xmlRelaxNGGrammarPtr tmp, ret;
4754
4755 schema->topgrammar = ret = xmlRelaxNGNewGrammar(ctxt);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004756 if (schema->topgrammar == NULL) {
4757 return(schema);
4758 }
Daniel Veillardc482e262003-02-26 14:48:48 +00004759 /*
4760 * Link the new grammar in the tree
4761 */
4762 ret->parent = ctxt->grammar;
4763 if (ctxt->grammar != NULL) {
4764 tmp = ctxt->grammar->children;
4765 if (tmp == NULL) {
4766 ctxt->grammar->children = ret;
4767 } else {
4768 while (tmp->next != NULL)
4769 tmp = tmp->next;
4770 tmp->next = ret;
4771 }
4772 }
Daniel Veillarde431a272003-01-29 23:02:33 +00004773 old = ctxt->grammar;
Daniel Veillardc482e262003-02-26 14:48:48 +00004774 ctxt->grammar = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004775 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00004776 if (old != NULL)
4777 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004778 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004779 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00004780 if (schema->topgrammar->start != NULL) {
4781 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004782 if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) {
4783 xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
4784 while ((schema->topgrammar->start != NULL) &&
4785 (schema->topgrammar->start->type == XML_RELAXNG_NOOP) &&
4786 (schema->topgrammar->start->next != NULL))
4787 schema->topgrammar->start = schema->topgrammar->start->content;
4788 xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004789 XML_RELAXNG_IN_START, XML_RELAXNG_NOOP);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004790 }
Daniel Veillardd4310742003-02-18 21:12:46 +00004791 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004792
4793#ifdef DEBUG
4794 if (schema == NULL)
4795 xmlGenericError(xmlGenericErrorContext,
4796 "xmlRelaxNGParseDocument() failed\n");
4797#endif
4798
4799 return (schema);
4800}
4801
4802/************************************************************************
4803 * *
4804 * Reading RelaxNGs *
4805 * *
4806 ************************************************************************/
4807
4808/**
4809 * xmlRelaxNGNewParserCtxt:
4810 * @URL: the location of the schema
4811 *
4812 * Create an XML RelaxNGs parse context for that file/resource expected
4813 * to contain an XML RelaxNGs file.
4814 *
4815 * Returns the parser context or NULL in case of error
4816 */
4817xmlRelaxNGParserCtxtPtr
4818xmlRelaxNGNewParserCtxt(const char *URL) {
4819 xmlRelaxNGParserCtxtPtr ret;
4820
4821 if (URL == NULL)
4822 return(NULL);
4823
4824 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4825 if (ret == NULL) {
4826 xmlGenericError(xmlGenericErrorContext,
4827 "Failed to allocate new schama parser context for %s\n", URL);
4828 return (NULL);
4829 }
4830 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4831 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004832 ret->error = xmlGenericError;
4833 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004834 return (ret);
4835}
4836
4837/**
4838 * xmlRelaxNGNewMemParserCtxt:
4839 * @buffer: a pointer to a char array containing the schemas
4840 * @size: the size of the array
4841 *
4842 * Create an XML RelaxNGs parse context for that memory buffer expected
4843 * to contain an XML RelaxNGs file.
4844 *
4845 * Returns the parser context or NULL in case of error
4846 */
4847xmlRelaxNGParserCtxtPtr
4848xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
4849 xmlRelaxNGParserCtxtPtr ret;
4850
4851 if ((buffer == NULL) || (size <= 0))
4852 return(NULL);
4853
4854 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4855 if (ret == NULL) {
4856 xmlGenericError(xmlGenericErrorContext,
4857 "Failed to allocate new schama parser context\n");
4858 return (NULL);
4859 }
4860 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4861 ret->buffer = buffer;
4862 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004863 ret->error = xmlGenericError;
4864 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004865 return (ret);
4866}
4867
4868/**
4869 * xmlRelaxNGFreeParserCtxt:
4870 * @ctxt: the schema parser context
4871 *
4872 * Free the resources associated to the schema parser context
4873 */
4874void
4875xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
4876 if (ctxt == NULL)
4877 return;
4878 if (ctxt->URL != NULL)
4879 xmlFree(ctxt->URL);
4880 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004881 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004882 if (ctxt->interleaves != NULL)
4883 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004884 if (ctxt->documents != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +00004885 xmlRelaxNGFreeDocumentList(ctxt->documents);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00004886 if (ctxt->includes != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +00004887 xmlRelaxNGFreeIncludeList(ctxt->includes);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004888 if (ctxt->docTab != NULL)
4889 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004890 if (ctxt->incTab != NULL)
4891 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00004892 if (ctxt->defTab != NULL) {
4893 int i;
4894
4895 for (i = 0;i < ctxt->defNr;i++)
4896 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
4897 xmlFree(ctxt->defTab);
4898 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004899 xmlFree(ctxt);
4900}
4901
Daniel Veillard6eadf632003-01-23 18:29:16 +00004902/**
Daniel Veillardd2298792003-02-14 16:54:11 +00004903 * xmlRelaxNGNormExtSpace:
4904 * @value: a value
4905 *
4906 * Removes the leading and ending spaces of the value
4907 * The string is modified "in situ"
4908 */
4909static void
4910xmlRelaxNGNormExtSpace(xmlChar *value) {
4911 xmlChar *start = value;
4912 xmlChar *cur = value;
4913 if (value == NULL)
4914 return;
4915
4916 while (IS_BLANK(*cur)) cur++;
4917 if (cur == start) {
4918 do {
4919 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4920 if (*cur == 0)
4921 return;
4922 start = cur;
4923 while (IS_BLANK(*cur)) cur++;
4924 if (*cur == 0) {
4925 *start = 0;
4926 return;
4927 }
4928 } while (1);
4929 } else {
4930 do {
4931 while ((*cur != 0) && (!IS_BLANK(*cur)))
4932 *start++ = *cur++;
4933 if (*cur == 0) {
4934 *start = 0;
4935 return;
4936 }
4937 /* don't try to normalize the inner spaces */
4938 while (IS_BLANK(*cur)) cur++;
4939 *start++ = *cur++;
4940 if (*cur == 0) {
4941 *start = 0;
4942 return;
4943 }
4944 } while (1);
4945 }
4946}
4947
4948/**
4949 * xmlRelaxNGCheckAttributes:
4950 * @ctxt: a Relax-NG parser context
4951 * @node: a Relax-NG node
4952 *
4953 * Check all the attributes on the given node
4954 */
4955static void
4956xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4957 xmlAttrPtr cur, next;
4958
4959 cur = node->properties;
4960 while (cur != NULL) {
4961 next = cur->next;
4962 if ((cur->ns == NULL) ||
4963 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
4964 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4965 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
4966 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
4967 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
4968 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00004969 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00004970 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4971 if (ctxt->error != NULL)
4972 ctxt->error(ctxt->userData,
4973 "Attribute %s is not allowed on %s\n",
4974 cur->name, node->name);
4975 ctxt->nbErrors++;
4976 }
4977 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
4978 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
4979 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
4980 if (ctxt->error != NULL)
4981 ctxt->error(ctxt->userData,
4982 "Attribute %s is not allowed on %s\n",
4983 cur->name, node->name);
4984 ctxt->nbErrors++;
4985 }
4986 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
4987 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
4988 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
4989 if (ctxt->error != NULL)
4990 ctxt->error(ctxt->userData,
4991 "Attribute %s is not allowed on %s\n",
4992 cur->name, node->name);
4993 ctxt->nbErrors++;
4994 }
4995 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
4996 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
4997 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4998 if (ctxt->error != NULL)
4999 ctxt->error(ctxt->userData,
5000 "Attribute %s is not allowed on %s\n",
5001 cur->name, node->name);
5002 ctxt->nbErrors++;
5003 }
5004 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
5005 xmlChar *val;
5006 xmlURIPtr uri;
5007
5008 val = xmlNodeListGetString(node->doc, cur->children, 1);
5009 if (val != NULL) {
5010 if (val[0] != 0) {
5011 uri = xmlParseURI((const char *) val);
5012 if (uri == NULL) {
5013 if (ctxt->error != NULL)
5014 ctxt->error(ctxt->userData,
5015 "Attribute %s contains invalid URI %s\n",
5016 cur->name, val);
5017 ctxt->nbErrors++;
5018 } else {
5019 if (uri->scheme == NULL) {
5020 if (ctxt->error != NULL)
5021 ctxt->error(ctxt->userData,
5022 "Attribute %s URI %s is not absolute\n",
5023 cur->name, val);
5024 ctxt->nbErrors++;
5025 }
5026 if (uri->fragment != NULL) {
5027 if (ctxt->error != NULL)
5028 ctxt->error(ctxt->userData,
5029 "Attribute %s URI %s has a fragment ID\n",
5030 cur->name, val);
5031 ctxt->nbErrors++;
5032 }
5033 xmlFreeURI(uri);
5034 }
5035 }
5036 xmlFree(val);
5037 }
5038 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
5039 if (ctxt->error != NULL)
5040 ctxt->error(ctxt->userData,
5041 "Unknown attribute %s on %s\n",
5042 cur->name, node->name);
5043 ctxt->nbErrors++;
5044 }
5045 }
5046 cur = next;
5047 }
5048}
5049
5050/**
Daniel Veillardc5312d72003-02-21 17:14:10 +00005051 * xmlRelaxNGCleanupTree:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005052 * @ctxt: a Relax-NG parser context
Daniel Veillardc5312d72003-02-21 17:14:10 +00005053 * @root: an xmlNodePtr subtree
Daniel Veillard6eadf632003-01-23 18:29:16 +00005054 *
Daniel Veillardc5312d72003-02-21 17:14:10 +00005055 * Cleanup the subtree from unwanted nodes for parsing, resolve
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005056 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00005057 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005058static void
5059xmlRelaxNGCleanupTree(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr root) {
5060 xmlNodePtr cur, delete;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005061
Daniel Veillard6eadf632003-01-23 18:29:16 +00005062 delete = NULL;
5063 cur = root;
5064 while (cur != NULL) {
5065 if (delete != NULL) {
5066 xmlUnlinkNode(delete);
5067 xmlFreeNode(delete);
5068 delete = NULL;
5069 }
5070 if (cur->type == XML_ELEMENT_NODE) {
5071 /*
5072 * Simplification 4.1. Annotations
5073 */
5074 if ((cur->ns == NULL) ||
5075 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00005076 if ((cur->parent != NULL) &&
5077 (cur->parent->type == XML_ELEMENT_NODE) &&
5078 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
5079 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
5080 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
5081 if (ctxt->error != NULL)
5082 ctxt->error(ctxt->userData,
5083 "element %s doesn't allow foreign elements\n",
5084 cur->parent->name);
5085 ctxt->nbErrors++;
5086 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005087 delete = cur;
5088 goto skip_children;
5089 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00005090 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005091 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005092 xmlChar *href, *ns, *base, *URL;
5093 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00005094 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005095
5096 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00005097 if (ns == NULL) {
5098 tmp = cur->parent;
5099 while ((tmp != NULL) &&
5100 (tmp->type == XML_ELEMENT_NODE)) {
5101 ns = xmlGetProp(tmp, BAD_CAST "ns");
5102 if (ns != NULL)
5103 break;
5104 tmp = tmp->parent;
5105 }
5106 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005107 href = xmlGetProp(cur, BAD_CAST "href");
5108 if (href == NULL) {
5109 if (ctxt->error != NULL)
5110 ctxt->error(ctxt->userData,
5111 "xmlRelaxNGParse: externalRef has no href attribute\n");
5112 ctxt->nbErrors++;
5113 delete = cur;
5114 goto skip_children;
5115 }
5116 base = xmlNodeGetBase(cur->doc, cur);
5117 URL = xmlBuildURI(href, base);
5118 if (URL == NULL) {
5119 if (ctxt->error != NULL)
5120 ctxt->error(ctxt->userData,
5121 "Failed to compute URL for externalRef %s\n", href);
5122 ctxt->nbErrors++;
5123 if (href != NULL)
5124 xmlFree(href);
5125 if (base != NULL)
5126 xmlFree(base);
5127 delete = cur;
5128 goto skip_children;
5129 }
5130 if (href != NULL)
5131 xmlFree(href);
5132 if (base != NULL)
5133 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005134 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005135 if (docu == NULL) {
5136 if (ctxt->error != NULL)
5137 ctxt->error(ctxt->userData,
5138 "Failed to load externalRef %s\n", URL);
5139 ctxt->nbErrors++;
5140 xmlFree(URL);
5141 delete = cur;
5142 goto skip_children;
5143 }
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005144 if (ns != NULL)
5145 xmlFree(ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005146 xmlFree(URL);
5147 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005148 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005149 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005150 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00005151 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005152
5153 href = xmlGetProp(cur, BAD_CAST "href");
5154 if (href == NULL) {
5155 if (ctxt->error != NULL)
5156 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005157 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005158 ctxt->nbErrors++;
5159 delete = cur;
5160 goto skip_children;
5161 }
5162 base = xmlNodeGetBase(cur->doc, cur);
5163 URL = xmlBuildURI(href, base);
5164 if (URL == NULL) {
5165 if (ctxt->error != NULL)
5166 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005167 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005168 ctxt->nbErrors++;
5169 if (href != NULL)
5170 xmlFree(href);
5171 if (base != NULL)
5172 xmlFree(base);
5173 delete = cur;
5174 goto skip_children;
5175 }
5176 if (href != NULL)
5177 xmlFree(href);
5178 if (base != NULL)
5179 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00005180 ns = xmlGetProp(cur, BAD_CAST "ns");
5181 if (ns == NULL) {
5182 tmp = cur->parent;
5183 while ((tmp != NULL) &&
5184 (tmp->type == XML_ELEMENT_NODE)) {
5185 ns = xmlGetProp(tmp, BAD_CAST "ns");
5186 if (ns != NULL)
5187 break;
5188 tmp = tmp->parent;
5189 }
5190 }
5191 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
5192 if (ns != NULL)
5193 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005194 if (incl == NULL) {
5195 if (ctxt->error != NULL)
5196 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005197 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005198 ctxt->nbErrors++;
5199 xmlFree(URL);
5200 delete = cur;
5201 goto skip_children;
5202 }
5203 xmlFree(URL);
5204 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005205 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
5206 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00005207 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005208 xmlNodePtr text = NULL;
5209
5210 /*
5211 * Simplification 4.8. name attribute of element
5212 * and attribute elements
5213 */
5214 name = xmlGetProp(cur, BAD_CAST "name");
5215 if (name != NULL) {
5216 if (cur->children == NULL) {
5217 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
5218 name);
5219 } else {
5220 xmlNodePtr node;
5221 node = xmlNewNode(cur->ns, BAD_CAST "name");
5222 if (node != NULL) {
5223 xmlAddPrevSibling(cur->children, node);
5224 text = xmlNewText(name);
5225 xmlAddChild(node, text);
5226 text = node;
5227 }
5228 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005229 if (text == NULL) {
5230 if (ctxt->error != NULL)
5231 ctxt->error(ctxt->userData,
5232 "Failed to create a name %s element\n", name);
5233 ctxt->nbErrors++;
5234 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005235 xmlUnsetProp(cur, BAD_CAST "name");
5236 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00005237 ns = xmlGetProp(cur, BAD_CAST "ns");
5238 if (ns != NULL) {
5239 if (text != NULL) {
5240 xmlSetProp(text, BAD_CAST "ns", ns);
5241 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00005242 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005243 xmlFree(ns);
5244 } else if (xmlStrEqual(cur->name,
5245 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005246 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
5247 }
5248 }
5249 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
5250 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
5251 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
5252 /*
5253 * Simplification 4.8. name attribute of element
5254 * and attribute elements
5255 */
5256 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
5257 xmlNodePtr node;
5258 xmlChar *ns = NULL;
5259
5260 node = cur->parent;
5261 while ((node != NULL) &&
5262 (node->type == XML_ELEMENT_NODE)) {
5263 ns = xmlGetProp(node, BAD_CAST "ns");
5264 if (ns != NULL) {
5265 break;
5266 }
5267 node = node->parent;
5268 }
5269 if (ns == NULL) {
5270 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
5271 } else {
5272 xmlSetProp(cur, BAD_CAST "ns", ns);
5273 xmlFree(ns);
5274 }
5275 }
5276 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
5277 xmlChar *name, *local, *prefix;
5278
5279 /*
5280 * Simplification: 4.10. QNames
5281 */
5282 name = xmlNodeGetContent(cur);
5283 if (name != NULL) {
5284 local = xmlSplitQName2(name, &prefix);
5285 if (local != NULL) {
5286 xmlNsPtr ns;
5287
5288 ns = xmlSearchNs(cur->doc, cur, prefix);
5289 if (ns == NULL) {
5290 if (ctxt->error != NULL)
5291 ctxt->error(ctxt->userData,
5292 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
5293 ctxt->nbErrors++;
5294 } else {
5295 xmlSetProp(cur, BAD_CAST "ns", ns->href);
5296 xmlNodeSetContent(cur, local);
5297 }
5298 xmlFree(local);
5299 xmlFree(prefix);
5300 }
5301 xmlFree(name);
5302 }
5303 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005304 /*
5305 * 4.16
5306 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005307 if (xmlStrEqual(cur->name, BAD_CAST "nsName")) {
5308 if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5309 if (ctxt->error != NULL)
5310 ctxt->error(ctxt->userData,
5311 "Found nsName/except//nsName forbidden construct\n");
5312 ctxt->nbErrors++;
5313 }
5314 }
5315 } else if ((xmlStrEqual(cur->name, BAD_CAST "except")) &&
5316 (cur != root)) {
5317 int oldflags = ctxt->flags;
5318
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005319 /*
5320 * 4.16
5321 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005322 if ((cur->parent != NULL) &&
5323 (xmlStrEqual(cur->parent->name, BAD_CAST "anyName"))) {
5324 ctxt->flags |= XML_RELAXNG_IN_ANYEXCEPT;
5325 xmlRelaxNGCleanupTree(ctxt, cur);
5326 ctxt->flags = oldflags;
5327 goto skip_children;
5328 } else if ((cur->parent != NULL) &&
5329 (xmlStrEqual(cur->parent->name, BAD_CAST "nsName"))) {
5330 ctxt->flags |= XML_RELAXNG_IN_NSEXCEPT;
5331 xmlRelaxNGCleanupTree(ctxt, cur);
5332 ctxt->flags = oldflags;
5333 goto skip_children;
5334 }
5335 } else if (xmlStrEqual(cur->name, BAD_CAST "anyName")) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005336 /*
5337 * 4.16
5338 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005339 if (ctxt->flags & XML_RELAXNG_IN_ANYEXCEPT) {
5340 if (ctxt->error != NULL)
5341 ctxt->error(ctxt->userData,
5342 "Found anyName/except//anyName forbidden construct\n");
5343 ctxt->nbErrors++;
5344 } else if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5345 if (ctxt->error != NULL)
5346 ctxt->error(ctxt->userData,
5347 "Found nsName/except//anyName forbidden construct\n");
5348 ctxt->nbErrors++;
5349 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005350 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005351 /*
5352 * Thisd is not an else since "include" is transformed
5353 * into a div
5354 */
5355 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005356 xmlChar *ns;
5357 xmlNodePtr child, ins, tmp;
5358
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005359 /*
5360 * implements rule 4.11
5361 */
Daniel Veillard416589a2003-02-17 17:25:42 +00005362
5363 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005364
5365 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005366 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005367 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005368 if (ns != NULL) {
5369 if (!xmlHasProp(child, BAD_CAST "ns")) {
5370 xmlSetProp(child, BAD_CAST "ns", ns);
5371 }
5372 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005373 tmp = child->next;
5374 xmlUnlinkNode(child);
5375 ins = xmlAddNextSibling(ins, child);
5376 child = tmp;
5377 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005378 if (ns != NULL)
5379 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005380 delete = cur;
5381 goto skip_children;
5382 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005383 }
5384 }
5385 /*
5386 * Simplification 4.2 whitespaces
5387 */
5388 else if (cur->type == XML_TEXT_NODE) {
5389 if (IS_BLANK_NODE(cur)) {
5390 if (cur->parent->type == XML_ELEMENT_NODE) {
5391 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
5392 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
5393 delete = cur;
5394 } else {
5395 delete = cur;
5396 goto skip_children;
5397 }
5398 }
5399 } else if (cur->type != XML_CDATA_SECTION_NODE) {
5400 delete = cur;
5401 goto skip_children;
5402 }
5403
5404 /*
5405 * Skip to next node
5406 */
5407 if (cur->children != NULL) {
5408 if ((cur->children->type != XML_ENTITY_DECL) &&
5409 (cur->children->type != XML_ENTITY_REF_NODE) &&
5410 (cur->children->type != XML_ENTITY_NODE)) {
5411 cur = cur->children;
5412 continue;
5413 }
5414 }
5415skip_children:
5416 if (cur->next != NULL) {
5417 cur = cur->next;
5418 continue;
5419 }
5420
5421 do {
5422 cur = cur->parent;
5423 if (cur == NULL)
5424 break;
5425 if (cur == root) {
5426 cur = NULL;
5427 break;
5428 }
5429 if (cur->next != NULL) {
5430 cur = cur->next;
5431 break;
5432 }
5433 } while (cur != NULL);
5434 }
5435 if (delete != NULL) {
5436 xmlUnlinkNode(delete);
5437 xmlFreeNode(delete);
5438 delete = NULL;
5439 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00005440}
Daniel Veillard6eadf632003-01-23 18:29:16 +00005441
Daniel Veillardc5312d72003-02-21 17:14:10 +00005442/**
5443 * xmlRelaxNGCleanupDoc:
5444 * @ctxt: a Relax-NG parser context
5445 * @doc: an xmldocPtr document pointer
5446 *
5447 * Cleanup the document from unwanted nodes for parsing, resolve
5448 * Include and externalRef lookups.
5449 *
5450 * Returns the cleaned up document or NULL in case of error
5451 */
5452static xmlDocPtr
5453xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
5454 xmlNodePtr root;
5455
5456 /*
5457 * Extract the root
5458 */
5459 root = xmlDocGetRootElement(doc);
5460 if (root == NULL) {
5461 if (ctxt->error != NULL)
5462 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5463 ctxt->URL);
5464 ctxt->nbErrors++;
5465 return (NULL);
5466 }
5467 xmlRelaxNGCleanupTree(ctxt, root);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005468 return(doc);
5469}
5470
5471/**
5472 * xmlRelaxNGParse:
5473 * @ctxt: a Relax-NG parser context
5474 *
5475 * parse a schema definition resource and build an internal
5476 * XML Shema struture which can be used to validate instances.
5477 * *WARNING* this interface is highly subject to change
5478 *
5479 * Returns the internal XML RelaxNG structure built from the resource or
5480 * NULL in case of error
5481 */
5482xmlRelaxNGPtr
5483xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
5484{
5485 xmlRelaxNGPtr ret = NULL;
5486 xmlDocPtr doc;
5487 xmlNodePtr root;
5488
5489 xmlRelaxNGInitTypes();
5490
5491 if (ctxt == NULL)
5492 return (NULL);
5493
5494 /*
5495 * First step is to parse the input document into an DOM/Infoset
5496 */
5497 if (ctxt->URL != NULL) {
5498 doc = xmlParseFile((const char *) ctxt->URL);
5499 if (doc == NULL) {
5500 if (ctxt->error != NULL)
5501 ctxt->error(ctxt->userData,
5502 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
5503 ctxt->nbErrors++;
5504 return (NULL);
5505 }
5506 } else if (ctxt->buffer != NULL) {
5507 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
5508 if (doc == NULL) {
5509 if (ctxt->error != NULL)
5510 ctxt->error(ctxt->userData,
5511 "xmlRelaxNGParse: could not parse schemas\n");
5512 ctxt->nbErrors++;
5513 return (NULL);
5514 }
5515 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5516 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5517 } else {
5518 if (ctxt->error != NULL)
5519 ctxt->error(ctxt->userData,
5520 "xmlRelaxNGParse: nothing to parse\n");
5521 ctxt->nbErrors++;
5522 return (NULL);
5523 }
5524 ctxt->document = doc;
5525
5526 /*
5527 * Some preprocessing of the document content
5528 */
5529 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
5530 if (doc == NULL) {
5531 xmlFreeDoc(ctxt->document);
5532 ctxt->document = NULL;
5533 return(NULL);
5534 }
5535
Daniel Veillard6eadf632003-01-23 18:29:16 +00005536 /*
5537 * Then do the parsing for good
5538 */
5539 root = xmlDocGetRootElement(doc);
5540 if (root == NULL) {
5541 if (ctxt->error != NULL)
5542 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5543 ctxt->URL);
5544 ctxt->nbErrors++;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005545 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005546 return (NULL);
5547 }
5548 ret = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005549 if (ret == NULL) {
5550 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005551 return(NULL);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005552 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005553
5554 /*
5555 * Check the ref/defines links
5556 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005557 /*
5558 * try to preprocess interleaves
5559 */
5560 if (ctxt->interleaves != NULL) {
5561 xmlHashScan(ctxt->interleaves,
5562 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
5563 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005564
5565 /*
5566 * if there was a parsing error return NULL
5567 */
5568 if (ctxt->nbErrors > 0) {
5569 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005570 ctxt->document = NULL;
5571 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005572 return(NULL);
5573 }
5574
5575 /*
5576 * Transfer the pointer for cleanup at the schema level.
5577 */
5578 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005579 ctxt->document = NULL;
5580 ret->documents = ctxt->documents;
5581 ctxt->documents = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005582
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005583 ret->includes = ctxt->includes;
5584 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00005585 ret->defNr = ctxt->defNr;
5586 ret->defTab = ctxt->defTab;
5587 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005588
5589 return (ret);
5590}
5591
5592/**
5593 * xmlRelaxNGSetParserErrors:
5594 * @ctxt: a Relax-NG validation context
5595 * @err: the error callback
5596 * @warn: the warning callback
5597 * @ctx: contextual data for the callbacks
5598 *
5599 * Set the callback functions used to handle errors for a validation context
5600 */
5601void
5602xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
5603 xmlRelaxNGValidityErrorFunc err,
5604 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5605 if (ctxt == NULL)
5606 return;
5607 ctxt->error = err;
5608 ctxt->warning = warn;
5609 ctxt->userData = ctx;
5610}
5611/************************************************************************
5612 * *
5613 * Dump back a compiled form *
5614 * *
5615 ************************************************************************/
5616static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
5617
5618/**
5619 * xmlRelaxNGDumpDefines:
5620 * @output: the file output
5621 * @defines: a list of define structures
5622 *
5623 * Dump a RelaxNG structure back
5624 */
5625static void
5626xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
5627 while (defines != NULL) {
5628 xmlRelaxNGDumpDefine(output, defines);
5629 defines = defines->next;
5630 }
5631}
5632
5633/**
5634 * xmlRelaxNGDumpDefine:
5635 * @output: the file output
5636 * @define: a define structure
5637 *
5638 * Dump a RelaxNG structure back
5639 */
5640static void
5641xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
5642 if (define == NULL)
5643 return;
5644 switch(define->type) {
5645 case XML_RELAXNG_EMPTY:
5646 fprintf(output, "<empty/>\n");
5647 break;
5648 case XML_RELAXNG_NOT_ALLOWED:
5649 fprintf(output, "<notAllowed/>\n");
5650 break;
5651 case XML_RELAXNG_TEXT:
5652 fprintf(output, "<text/>\n");
5653 break;
5654 case XML_RELAXNG_ELEMENT:
5655 fprintf(output, "<element>\n");
5656 if (define->name != NULL) {
5657 fprintf(output, "<name");
5658 if (define->ns != NULL)
5659 fprintf(output, " ns=\"%s\"", define->ns);
5660 fprintf(output, ">%s</name>\n", define->name);
5661 }
5662 xmlRelaxNGDumpDefines(output, define->attrs);
5663 xmlRelaxNGDumpDefines(output, define->content);
5664 fprintf(output, "</element>\n");
5665 break;
5666 case XML_RELAXNG_LIST:
5667 fprintf(output, "<list>\n");
5668 xmlRelaxNGDumpDefines(output, define->content);
5669 fprintf(output, "</list>\n");
5670 break;
5671 case XML_RELAXNG_ONEORMORE:
5672 fprintf(output, "<oneOrMore>\n");
5673 xmlRelaxNGDumpDefines(output, define->content);
5674 fprintf(output, "</oneOrMore>\n");
5675 break;
5676 case XML_RELAXNG_ZEROORMORE:
5677 fprintf(output, "<zeroOrMore>\n");
5678 xmlRelaxNGDumpDefines(output, define->content);
5679 fprintf(output, "</zeroOrMore>\n");
5680 break;
5681 case XML_RELAXNG_CHOICE:
5682 fprintf(output, "<choice>\n");
5683 xmlRelaxNGDumpDefines(output, define->content);
5684 fprintf(output, "</choice>\n");
5685 break;
5686 case XML_RELAXNG_GROUP:
5687 fprintf(output, "<group>\n");
5688 xmlRelaxNGDumpDefines(output, define->content);
5689 fprintf(output, "</group>\n");
5690 break;
5691 case XML_RELAXNG_INTERLEAVE:
5692 fprintf(output, "<interleave>\n");
5693 xmlRelaxNGDumpDefines(output, define->content);
5694 fprintf(output, "</interleave>\n");
5695 break;
5696 case XML_RELAXNG_OPTIONAL:
5697 fprintf(output, "<optional>\n");
5698 xmlRelaxNGDumpDefines(output, define->content);
5699 fprintf(output, "</optional>\n");
5700 break;
5701 case XML_RELAXNG_ATTRIBUTE:
5702 fprintf(output, "<attribute>\n");
5703 xmlRelaxNGDumpDefines(output, define->content);
5704 fprintf(output, "</attribute>\n");
5705 break;
5706 case XML_RELAXNG_DEF:
5707 fprintf(output, "<define");
5708 if (define->name != NULL)
5709 fprintf(output, " name=\"%s\"", define->name);
5710 fprintf(output, ">\n");
5711 xmlRelaxNGDumpDefines(output, define->content);
5712 fprintf(output, "</define>\n");
5713 break;
5714 case XML_RELAXNG_REF:
5715 fprintf(output, "<ref");
5716 if (define->name != NULL)
5717 fprintf(output, " name=\"%s\"", define->name);
5718 fprintf(output, ">\n");
5719 xmlRelaxNGDumpDefines(output, define->content);
5720 fprintf(output, "</ref>\n");
5721 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00005722 case XML_RELAXNG_PARENTREF:
5723 fprintf(output, "<parentRef");
5724 if (define->name != NULL)
5725 fprintf(output, " name=\"%s\"", define->name);
5726 fprintf(output, ">\n");
5727 xmlRelaxNGDumpDefines(output, define->content);
5728 fprintf(output, "</parentRef>\n");
5729 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005730 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00005731 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00005732 xmlRelaxNGDumpDefines(output, define->content);
5733 fprintf(output, "</externalRef>\n");
5734 break;
5735 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005736 case XML_RELAXNG_VALUE:
5737 TODO
5738 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005739 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005740 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00005741 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005742 TODO
5743 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00005744 case XML_RELAXNG_NOOP:
5745 xmlRelaxNGDumpDefines(output, define->content);
5746 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005747 }
5748}
5749
5750/**
5751 * xmlRelaxNGDumpGrammar:
5752 * @output: the file output
5753 * @grammar: a grammar structure
5754 * @top: is this a top grammar
5755 *
5756 * Dump a RelaxNG structure back
5757 */
5758static void
5759xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
5760{
5761 if (grammar == NULL)
5762 return;
5763
5764 fprintf(output, "<grammar");
5765 if (top)
5766 fprintf(output,
5767 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
5768 switch(grammar->combine) {
5769 case XML_RELAXNG_COMBINE_UNDEFINED:
5770 break;
5771 case XML_RELAXNG_COMBINE_CHOICE:
5772 fprintf(output, " combine=\"choice\"");
5773 break;
5774 case XML_RELAXNG_COMBINE_INTERLEAVE:
5775 fprintf(output, " combine=\"interleave\"");
5776 break;
5777 default:
5778 fprintf(output, " <!-- invalid combine value -->");
5779 }
5780 fprintf(output, ">\n");
5781 if (grammar->start == NULL) {
5782 fprintf(output, " <!-- grammar had no start -->");
5783 } else {
5784 fprintf(output, "<start>\n");
5785 xmlRelaxNGDumpDefine(output, grammar->start);
5786 fprintf(output, "</start>\n");
5787 }
5788 /* TODO ? Dump the defines ? */
5789 fprintf(output, "</grammar>\n");
5790}
5791
5792/**
5793 * xmlRelaxNGDump:
5794 * @output: the file output
5795 * @schema: a schema structure
5796 *
5797 * Dump a RelaxNG structure back
5798 */
5799void
5800xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
5801{
5802 if (schema == NULL) {
5803 fprintf(output, "RelaxNG empty or failed to compile\n");
5804 return;
5805 }
5806 fprintf(output, "RelaxNG: ");
5807 if (schema->doc == NULL) {
5808 fprintf(output, "no document\n");
5809 } else if (schema->doc->URL != NULL) {
5810 fprintf(output, "%s\n", schema->doc->URL);
5811 } else {
5812 fprintf(output, "\n");
5813 }
5814 if (schema->topgrammar == NULL) {
5815 fprintf(output, "RelaxNG has no top grammar\n");
5816 return;
5817 }
5818 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
5819}
5820
Daniel Veillardfebcca42003-02-16 15:44:18 +00005821/**
5822 * xmlRelaxNGDumpTree:
5823 * @output: the file output
5824 * @schema: a schema structure
5825 *
5826 * Dump the transformed RelaxNG tree.
5827 */
5828void
5829xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
5830{
5831 if (schema == NULL) {
5832 fprintf(output, "RelaxNG empty or failed to compile\n");
5833 return;
5834 }
5835 if (schema->doc == NULL) {
5836 fprintf(output, "no document\n");
5837 } else {
5838 xmlDocDump(output, schema->doc);
5839 }
5840}
5841
Daniel Veillard6eadf632003-01-23 18:29:16 +00005842/************************************************************************
5843 * *
5844 * Validation implementation *
5845 * *
5846 ************************************************************************/
5847static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5848 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005849static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5850 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005851
5852/**
5853 * xmlRelaxNGSkipIgnored:
5854 * @ctxt: a schema validation context
5855 * @node: the top node.
5856 *
5857 * Skip ignorable nodes in that context
5858 *
5859 * Returns the new sibling or NULL in case of error.
5860 */
5861static xmlNodePtr
5862xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5863 xmlNodePtr node) {
5864 /*
5865 * TODO complete and handle entities
5866 */
5867 while ((node != NULL) &&
5868 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005869 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005870 ((node->type == XML_TEXT_NODE) &&
5871 (IS_BLANK_NODE(node))))) {
5872 node = node->next;
5873 }
5874 return(node);
5875}
5876
5877/**
Daniel Veillardedc91922003-01-26 00:52:04 +00005878 * xmlRelaxNGNormalize:
5879 * @ctxt: a schema validation context
5880 * @str: the string to normalize
5881 *
5882 * Implements the normalizeWhiteSpace( s ) function from
5883 * section 6.2.9 of the spec
5884 *
5885 * Returns the new string or NULL in case of error.
5886 */
5887static xmlChar *
5888xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
5889 xmlChar *ret, *p;
5890 const xmlChar *tmp;
5891 int len;
5892
5893 if (str == NULL)
5894 return(NULL);
5895 tmp = str;
5896 while (*tmp != 0) tmp++;
5897 len = tmp - str;
5898
5899 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
5900 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005901 if (ctxt != NULL) {
5902 VALID_CTXT();
5903 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
5904 } else {
5905 xmlGenericError(xmlGenericErrorContext,
5906 "xmlRelaxNGNormalize: out of memory\n");
5907 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005908 return(NULL);
5909 }
5910 p = ret;
5911 while (IS_BLANK(*str)) str++;
5912 while (*str != 0) {
5913 if (IS_BLANK(*str)) {
5914 while (IS_BLANK(*str)) str++;
5915 if (*str == 0)
5916 break;
5917 *p++ = ' ';
5918 } else
5919 *p++ = *str++;
5920 }
5921 *p = 0;
5922 return(ret);
5923}
5924
5925/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005926 * xmlRelaxNGValidateDatatype:
5927 * @ctxt: a Relax-NG validation context
5928 * @value: the string value
5929 * @type: the datatype definition
5930 *
5931 * Validate the given value against the dataype
5932 *
5933 * Returns 0 if the validation succeeded or an error code.
5934 */
5935static int
5936xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
5937 xmlRelaxNGDefinePtr define) {
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00005938 int ret, tmp;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005939 xmlRelaxNGTypeLibraryPtr lib;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00005940 void *result = NULL;
5941 xmlRelaxNGDefinePtr cur;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005942
5943 if ((define == NULL) || (define->data == NULL)) {
5944 return(-1);
5945 }
5946 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00005947 if (lib->check != NULL) {
5948 if ((define->attrs != NULL) &&
5949 (define->attrs->type == XML_RELAXNG_PARAM)) {
5950 ret = lib->check(lib->data, define->name, value, &result);
5951 } else {
5952 ret = lib->check(lib->data, define->name, value, NULL);
5953 }
5954 } else
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005955 ret = -1;
5956 if (ret < 0) {
5957 VALID_CTXT();
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00005958 VALID_ERROR2("failed to validate type %s\n", define->name);
5959 if ((result != NULL) && (lib != NULL) && (lib->freef != NULL))
5960 lib->freef(lib->data, result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005961 return(-1);
5962 } else if (ret == 1) {
5963 ret = 0;
5964 } else {
5965 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005966 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005967 ret = -1;
5968 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00005969 cur = define->attrs;
5970 while ((ret == 0) && (cur != NULL) && (cur->type == XML_RELAXNG_PARAM)) {
5971 if (lib->facet != NULL) {
5972 tmp = lib->facet(lib->data, define->name, cur->name,
5973 cur->value, value, result);
5974 if (tmp != 0)
5975 ret = -1;
5976 }
5977 cur = cur->next;
5978 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005979 if ((ret == 0) && (define->content != NULL)) {
5980 const xmlChar *oldvalue, *oldendvalue;
5981
5982 oldvalue = ctxt->state->value;
5983 oldendvalue = ctxt->state->endvalue;
5984 ctxt->state->value = (xmlChar *) value;
5985 ctxt->state->endvalue = NULL;
5986 ret = xmlRelaxNGValidateValue(ctxt, define->content);
5987 ctxt->state->value = (xmlChar *) oldvalue;
5988 ctxt->state->endvalue = (xmlChar *) oldendvalue;
5989 }
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00005990 if ((result != NULL) && (lib != NULL) && (lib->freef != NULL))
5991 lib->freef(lib->data, result);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005992 return(ret);
5993}
5994
5995/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005996 * xmlRelaxNGNextValue:
5997 * @ctxt: a Relax-NG validation context
5998 *
5999 * Skip to the next value when validating within a list
6000 *
6001 * Returns 0 if the operation succeeded or an error code.
6002 */
6003static int
6004xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
6005 xmlChar *cur;
6006
6007 cur = ctxt->state->value;
6008 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
6009 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006010 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006011 return(0);
6012 }
6013 while (*cur != 0) cur++;
6014 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
6015 if (cur == ctxt->state->endvalue)
6016 ctxt->state->value = NULL;
6017 else
6018 ctxt->state->value = cur;
6019 return(0);
6020}
6021
6022/**
6023 * xmlRelaxNGValidateValueList:
6024 * @ctxt: a Relax-NG validation context
6025 * @defines: the list of definitions to verify
6026 *
6027 * Validate the given set of definitions for the current value
6028 *
6029 * Returns 0 if the validation succeeded or an error code.
6030 */
6031static int
6032xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
6033 xmlRelaxNGDefinePtr defines) {
6034 int ret = 0;
6035
6036 while (defines != NULL) {
6037 ret = xmlRelaxNGValidateValue(ctxt, defines);
6038 if (ret != 0)
6039 break;
6040 defines = defines->next;
6041 }
6042 return(ret);
6043}
6044
6045/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006046 * xmlRelaxNGValidateValue:
6047 * @ctxt: a Relax-NG validation context
6048 * @define: the definition to verify
6049 *
6050 * Validate the given definition for the current value
6051 *
6052 * Returns 0 if the validation succeeded or an error code.
6053 */
6054static int
6055xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
6056 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00006057 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006058 xmlChar *value;
6059
6060 value = ctxt->state->value;
6061 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006062 case XML_RELAXNG_EMPTY: {
6063 if ((value != NULL) && (value[0] != 0)) {
6064 int idx = 0;
6065
6066 while (IS_BLANK(value[idx]))
6067 idx++;
6068 if (value[idx] != 0)
6069 ret = -1;
6070 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006071 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00006072 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006073 case XML_RELAXNG_TEXT:
6074 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00006075 case XML_RELAXNG_VALUE: {
6076 if (!xmlStrEqual(value, define->value)) {
6077 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00006078 xmlRelaxNGTypeLibraryPtr lib;
6079
6080 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
6081 if ((lib != NULL) && (lib->comp != NULL))
6082 ret = lib->comp(lib->data, define->name, value,
6083 define->value);
6084 else
6085 ret = -1;
6086 if (ret < 0) {
6087 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006088 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00006089 define->name);
6090 return(-1);
6091 } else if (ret == 1) {
6092 ret = 0;
6093 } else {
6094 ret = -1;
6095 }
Daniel Veillardedc91922003-01-26 00:52:04 +00006096 } else {
6097 xmlChar *nval, *nvalue;
6098
6099 /*
6100 * TODO: trivial optimizations are possible by
6101 * computing at compile-time
6102 */
6103 nval = xmlRelaxNGNormalize(ctxt, define->value);
6104 nvalue = xmlRelaxNGNormalize(ctxt, value);
6105
Daniel Veillardea3f3982003-01-26 19:45:18 +00006106 if ((nval == NULL) || (nvalue == NULL) ||
6107 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00006108 ret = -1;
6109 if (nval != NULL)
6110 xmlFree(nval);
6111 if (nvalue != NULL)
6112 xmlFree(nvalue);
6113 }
6114 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006115 if (ret == 0)
6116 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00006117 break;
6118 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006119 case XML_RELAXNG_DATATYPE: {
6120 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
6121 if (ret == 0)
6122 xmlRelaxNGNextValue(ctxt);
6123
6124 break;
6125 }
6126 case XML_RELAXNG_CHOICE: {
6127 xmlRelaxNGDefinePtr list = define->content;
6128 xmlChar *oldvalue;
6129
6130 oldflags = ctxt->flags;
6131 ctxt->flags |= FLAGS_IGNORABLE;
6132
6133 oldvalue = ctxt->state->value;
6134 while (list != NULL) {
6135 ret = xmlRelaxNGValidateValue(ctxt, list);
6136 if (ret == 0) {
6137 break;
6138 }
6139 ctxt->state->value = oldvalue;
6140 list = list->next;
6141 }
6142 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00006143 if (ret == 0)
6144 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006145 break;
6146 }
6147 case XML_RELAXNG_LIST: {
6148 xmlRelaxNGDefinePtr list = define->content;
6149 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00006150#ifdef DEBUG_LIST
6151 int nb_values = 0;
6152#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006153
6154 oldvalue = ctxt->state->value;
6155 oldend = ctxt->state->endvalue;
6156
6157 val = xmlStrdup(oldvalue);
6158 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006159 val = xmlStrdup(BAD_CAST "");
6160 }
6161 if (val == NULL) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006162 VALID_CTXT();
6163 VALID_ERROR("Internal: no state\n");
6164 return(-1);
6165 }
6166 cur = val;
6167 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006168 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006169 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006170 cur++;
6171#ifdef DEBUG_LIST
6172 nb_values++;
6173#endif
6174 while (IS_BLANK(*cur))
6175 *cur++ = 0;
6176 } else
6177 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006178 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006179#ifdef DEBUG_LIST
6180 xmlGenericError(xmlGenericErrorContext,
6181 "list value: '%s' found %d items\n", oldvalue, nb_values);
6182 nb_values = 0;
6183#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006184 ctxt->state->endvalue = cur;
6185 cur = val;
6186 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
6187
6188 ctxt->state->value = cur;
6189
6190 while (list != NULL) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006191 if (ctxt->state->value == ctxt->state->endvalue)
6192 ctxt->state->value = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006193 ret = xmlRelaxNGValidateValue(ctxt, list);
6194 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006195#ifdef DEBUG_LIST
6196 xmlGenericError(xmlGenericErrorContext,
6197 "Failed to validate value: '%s' with %d rule\n",
6198 ctxt->state->value, nb_values);
6199#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006200 break;
6201 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006202#ifdef DEBUG_LIST
6203 nb_values++;
6204#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006205 list = list->next;
6206 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006207
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006208 if ((ret == 0) && (ctxt->state->value != NULL) &&
6209 (ctxt->state->value != ctxt->state->endvalue)) {
6210 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006211 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006212 ret = -1;
6213 }
6214 xmlFree(val);
6215 ctxt->state->value = oldvalue;
6216 ctxt->state->endvalue = oldend;
6217 break;
6218 }
6219 case XML_RELAXNG_ONEORMORE:
6220 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6221 if (ret != 0) {
6222 break;
6223 }
6224 /* no break on purpose */
6225 case XML_RELAXNG_ZEROORMORE: {
6226 xmlChar *cur, *temp;
6227
6228 oldflags = ctxt->flags;
6229 ctxt->flags |= FLAGS_IGNORABLE;
6230 cur = ctxt->state->value;
6231 temp = NULL;
6232 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
6233 (temp != cur)) {
6234 temp = cur;
6235 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6236 if (ret != 0) {
6237 ctxt->state->value = temp;
6238 ret = 0;
6239 break;
6240 }
6241 cur = ctxt->state->value;
6242 }
6243 ctxt->flags = oldflags;
6244 break;
6245 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006246 case XML_RELAXNG_EXCEPT: {
6247 xmlRelaxNGDefinePtr list;
6248
6249 list = define->content;
6250 while (list != NULL) {
6251 ret = xmlRelaxNGValidateValue(ctxt, list);
6252 if (ret == 0) {
6253 ret = -1;
6254 break;
6255 } else
6256 ret = 0;
6257 list = list->next;
6258 }
6259 break;
6260 }
Daniel Veillardd4310742003-02-18 21:12:46 +00006261 case XML_RELAXNG_GROUP: {
6262 xmlRelaxNGDefinePtr list;
6263
6264 list = define->content;
6265 while (list != NULL) {
6266 ret = xmlRelaxNGValidateValue(ctxt, list);
6267 if (ret != 0) {
6268 ret = -1;
6269 break;
6270 } else
6271 ret = 0;
6272 list = list->next;
6273 }
6274 break;
6275 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006276 default:
6277 TODO
6278 ret = -1;
6279 }
6280 return(ret);
6281}
6282
6283/**
6284 * xmlRelaxNGValidateValueContent:
6285 * @ctxt: a Relax-NG validation context
6286 * @defines: the list of definitions to verify
6287 *
6288 * Validate the given definitions for the current value
6289 *
6290 * Returns 0 if the validation succeeded or an error code.
6291 */
6292static int
6293xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
6294 xmlRelaxNGDefinePtr defines) {
6295 int ret = 0;
6296
6297 while (defines != NULL) {
6298 ret = xmlRelaxNGValidateValue(ctxt, defines);
6299 if (ret != 0)
6300 break;
6301 defines = defines->next;
6302 }
6303 return(ret);
6304}
6305
6306/**
Daniel Veillard144fae12003-02-03 13:17:57 +00006307 * xmlRelaxNGAttributeMatch:
6308 * @ctxt: a Relax-NG validation context
6309 * @define: the definition to check
6310 * @prop: the attribute
6311 *
6312 * Check if the attribute matches the definition nameClass
6313 *
6314 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
6315 */
6316static int
6317xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
6318 xmlRelaxNGDefinePtr define,
6319 xmlAttrPtr prop) {
6320 int ret;
6321
6322 if (define->name != NULL) {
6323 if (!xmlStrEqual(define->name, prop->name))
6324 return(0);
6325 }
6326 if (define->ns != NULL) {
6327 if (define->ns[0] == 0) {
6328 if (prop->ns != NULL)
6329 return(0);
6330 } else {
6331 if ((prop->ns == NULL) ||
6332 (!xmlStrEqual(define->ns, prop->ns->href)))
6333 return(0);
6334 }
6335 }
6336 if (define->nameClass == NULL)
6337 return(1);
6338 define = define->nameClass;
6339 if (define->type == XML_RELAXNG_EXCEPT) {
6340 xmlRelaxNGDefinePtr list;
6341
6342 list = define->content;
6343 while (list != NULL) {
6344 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
6345 if (ret == 1)
6346 return(0);
6347 if (ret < 0)
6348 return(ret);
6349 list = list->next;
6350 }
6351 } else {
6352 TODO
6353 }
6354 return(1);
6355}
6356
6357/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006358 * xmlRelaxNGValidateAttribute:
6359 * @ctxt: a Relax-NG validation context
6360 * @define: the definition to verify
6361 *
6362 * Validate the given attribute definition for that node
6363 *
6364 * Returns 0 if the validation succeeded or an error code.
6365 */
6366static int
6367xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
6368 xmlRelaxNGDefinePtr define) {
6369 int ret = 0, i;
6370 xmlChar *value, *oldvalue;
6371 xmlAttrPtr prop = NULL, tmp;
6372
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006373 if (ctxt->state->nbAttrLeft <= 0)
6374 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006375 if (define->name != NULL) {
6376 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6377 tmp = ctxt->state->attrs[i];
6378 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
6379 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
6380 (tmp->ns == NULL)) ||
6381 ((tmp->ns != NULL) &&
6382 (xmlStrEqual(define->ns, tmp->ns->href)))) {
6383 prop = tmp;
6384 break;
6385 }
6386 }
6387 }
6388 if (prop != NULL) {
6389 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6390 oldvalue = ctxt->state->value;
6391 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00006392 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006393 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006394 if (ctxt->state->value != NULL)
6395 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006396 if (value != NULL)
6397 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006398 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006399 if (ret == 0) {
6400 /*
6401 * flag the attribute as processed
6402 */
6403 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006404 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006405 }
6406 } else {
6407 ret = -1;
6408 }
6409#ifdef DEBUG
6410 xmlGenericError(xmlGenericErrorContext,
6411 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
6412#endif
6413 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006414 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6415 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00006416 if ((tmp != NULL) &&
6417 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006418 prop = tmp;
6419 break;
6420 }
6421 }
6422 if (prop != NULL) {
6423 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6424 oldvalue = ctxt->state->value;
6425 ctxt->state->value = value;
6426 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006427 if (ctxt->state->value != NULL)
6428 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006429 if (value != NULL)
6430 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006431 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006432 if (ret == 0) {
6433 /*
6434 * flag the attribute as processed
6435 */
6436 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006437 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006438 }
6439 } else {
6440 ret = -1;
6441 }
6442#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00006443 if (define->ns != NULL) {
6444 xmlGenericError(xmlGenericErrorContext,
6445 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
6446 define->ns, ret);
6447 } else {
6448 xmlGenericError(xmlGenericErrorContext,
6449 "xmlRelaxNGValidateAttribute(anyName): %d\n",
6450 ret);
6451 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006452#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006453 }
6454
6455 return(ret);
6456}
6457
6458/**
6459 * xmlRelaxNGValidateAttributeList:
6460 * @ctxt: a Relax-NG validation context
6461 * @define: the list of definition to verify
6462 *
6463 * Validate the given node against the list of attribute definitions
6464 *
6465 * Returns 0 if the validation succeeded or an error code.
6466 */
6467static int
6468xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
6469 xmlRelaxNGDefinePtr defines) {
6470 int ret = 0;
6471 while (defines != NULL) {
6472 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
6473 ret = -1;
6474 defines = defines->next;
6475 }
6476 return(ret);
6477}
6478
6479/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006480 * xmlRelaxNGNodeMatchesList:
6481 * @node: the node
6482 * @list: a NULL terminated array of definitions
6483 *
6484 * Check if a node can be matched by one of the definitions
6485 *
6486 * Returns 1 if matches 0 otherwise
6487 */
6488static int
6489xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
6490 xmlRelaxNGDefinePtr cur;
6491 int i = 0;
6492
6493 if ((node == NULL) || (list == NULL))
6494 return(0);
6495
6496 cur = list[i++];
6497 while (cur != NULL) {
6498 if ((node->type == XML_ELEMENT_NODE) &&
6499 (cur->type == XML_RELAXNG_ELEMENT)) {
6500 if (cur->name == NULL) {
6501 if ((node->ns != NULL) &&
6502 (xmlStrEqual(node->ns->href, cur->ns)))
6503 return(1);
6504 } else if (xmlStrEqual(cur->name, node->name)) {
6505 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
6506 if (node->ns == NULL)
6507 return(1);
6508 } else {
6509 if ((node->ns != NULL) &&
6510 (xmlStrEqual(node->ns->href, cur->ns)))
6511 return(1);
6512 }
6513 }
6514 } else if ((node->type == XML_TEXT_NODE) &&
6515 (cur->type == XML_RELAXNG_TEXT)) {
6516 return(1);
6517 }
6518 cur = list[i++];
6519 }
6520 return(0);
6521}
6522
6523/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006524 * xmlRelaxNGValidateInterleave:
6525 * @ctxt: a Relax-NG validation context
6526 * @define: the definition to verify
6527 *
6528 * Validate an interleave definition for a node.
6529 *
6530 * Returns 0 if the validation succeeded or an error code.
6531 */
6532static int
6533xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
6534 xmlRelaxNGDefinePtr define) {
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006535 int ret = 0, i, nbgroups, left;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006536 xmlRelaxNGPartitionPtr partitions;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006537 xmlRelaxNGInterleaveGroupPtr group = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006538 xmlNodePtr cur, start, last = NULL, lastchg = NULL, lastelem;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006539 xmlNodePtr *list = NULL, *lasts = NULL;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006540
6541 if (define->data != NULL) {
6542 partitions = (xmlRelaxNGPartitionPtr) define->data;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006543 nbgroups = partitions->nbgroups;
6544 left = nbgroups;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006545 } else {
6546 VALID_CTXT();
6547 VALID_ERROR("Internal: interleave block has no data\n");
6548 return(-1);
6549 }
6550
6551 /*
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006552 * Build arrays to store the first and last node of the chain
6553 * pertaining to each group
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006554 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006555 list = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
6556 if (list == NULL) {
6557 VALID_CTXT();
6558 VALID_ERROR("Internal: out of memory in interleave check\n");
6559 return(-1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006560 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006561 memset(list, 0, nbgroups * sizeof(xmlNodePtr));
6562 lasts = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
6563 if (lasts == NULL) {
6564 VALID_CTXT();
6565 VALID_ERROR("Internal: out of memory in interleave check\n");
6566 return(-1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006567 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006568 memset(lasts, 0, nbgroups * sizeof(xmlNodePtr));
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006569
6570 /*
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006571 * Walk the sequence of children finding the right group and
6572 * sorting them in sequences.
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006573 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006574 cur = ctxt->state->seq;
6575 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
6576 start = cur;
6577 while (cur != NULL) {
6578 ctxt->state->seq = cur;
6579 for (i = 0;i < nbgroups;i++) {
6580 group = partitions->groups[i];
6581 if (group == NULL)
6582 continue;
6583 if (xmlRelaxNGNodeMatchesList(cur, group->defs))
6584 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006585 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006586 /*
6587 * We break as soon as an element not matched is found
6588 */
6589 if (i >= nbgroups) {
6590 break;
6591 }
6592 if (lasts[i] != NULL) {
6593 lasts[i]->next = cur;
6594 lasts[i] = cur;
6595 } else {
6596 list[i] = cur;
6597 lasts[i] = cur;
6598 }
6599 if (cur->next != NULL)
6600 lastchg = cur->next;
6601 else
6602 lastchg = cur;
6603 cur = xmlRelaxNGSkipIgnored(ctxt, cur->next);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006604 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006605 if (ret != 0) {
6606 VALID_CTXT();
6607 VALID_ERROR("Invalid sequence in interleave\n");
6608 ret = -1;
6609 goto done;
6610 }
6611 lastelem = cur;
6612 for (i = 0;i < nbgroups;i++) {
6613 group = partitions->groups[i];
6614 if (lasts[i] != NULL) {
6615 last = lasts[i]->next;
6616 lasts[i]->next = NULL;
6617 }
6618 ctxt->state->seq = list[i];
6619 ret = xmlRelaxNGValidateDefinition(ctxt, group->rule);
6620 if (ret != 0)
6621 break;
6622 cur = ctxt->state->seq;
6623 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
6624 if (cur != NULL) {
6625 VALID_CTXT();
6626 VALID_ERROR2("Extra element %s in interleave\n", cur->name);
6627 ret = -1;
6628 goto done;
6629 }
6630 if (lasts[i] != NULL) {
6631 lasts[i]->next = last;
6632 }
6633 }
6634 ctxt->state->seq = lastelem;
6635 if (ret != 0) {
6636 VALID_CTXT();
6637 VALID_ERROR("Invalid sequence in interleave\n");
6638 ret = -1;
6639 goto done;
6640 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006641
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006642done:
6643 /*
6644 * builds the next links chain from the prev one
6645 */
6646 cur = lastchg;
6647 while (cur != NULL) {
6648 if ((cur == start) || (cur->prev == NULL))
6649 break;
6650 cur->prev->next = cur;
6651 cur = cur->prev;
6652 }
6653
6654 xmlFree(list);
6655 xmlFree(lasts);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006656 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006657}
6658
6659/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006660 * xmlRelaxNGValidateElementContent:
6661 * @ctxt: a Relax-NG validation context
6662 * @define: the list of definition to verify
6663 *
6664 * Validate the given node content against the (list) of definitions
6665 *
6666 * Returns 0 if the validation succeeded or an error code.
6667 */
6668static int
6669xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
6670 xmlRelaxNGDefinePtr defines) {
6671 int ret = 0, res;
6672
6673 if (ctxt->state == NULL) {
6674 VALID_CTXT();
6675 VALID_ERROR("Internal: no state\n");
6676 return(-1);
6677 }
6678 while (defines != NULL) {
6679 res = xmlRelaxNGValidateDefinition(ctxt, defines);
6680 if (res < 0)
6681 ret = -1;
6682 defines = defines->next;
6683 }
6684
6685 return(ret);
6686}
6687
6688/**
Daniel Veillard416589a2003-02-17 17:25:42 +00006689 * xmlRelaxNGElementMatch:
6690 * @ctxt: a Relax-NG validation context
6691 * @define: the definition to check
6692 * @elem: the element
6693 *
6694 * Check if the element matches the definition nameClass
6695 *
6696 * Returns 1 if the element matches, 0 if no, or -1 in case of error
6697 */
6698static int
6699xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
6700 xmlRelaxNGDefinePtr define,
6701 xmlNodePtr elem) {
6702 int ret, oldflags;
6703
6704 if (define->name != NULL) {
6705 if (!xmlStrEqual(elem->name, define->name)) {
6706 VALID_CTXT();
6707 VALID_ERROR3("Expecting element %s, got %s\n",
6708 define->name, elem->name);
6709 return(0);
6710 }
6711 }
6712 if ((define->ns != NULL) && (define->ns[0] != 0)) {
6713 if (elem->ns == NULL) {
6714 VALID_CTXT();
6715 VALID_ERROR2("Expecting a namespace for element %s\n",
6716 elem->name);
6717 return(0);
6718 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
6719 VALID_CTXT();
6720 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
6721 elem->name, define->ns);
6722 return(0);
6723 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00006724 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
6725 (define->name == NULL)) {
6726 VALID_CTXT();
6727 VALID_ERROR2("Expecting no namespace for element %s\n",
6728 define->name);
6729 return(0);
6730 } else if ((elem->ns != NULL) && (define->name != NULL)) {
6731 VALID_CTXT();
6732 VALID_ERROR2("Expecting no namespace for element %s\n",
6733 define->name);
6734 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00006735 }
6736
6737 if (define->nameClass == NULL)
6738 return(1);
6739
6740 define = define->nameClass;
6741 if (define->type == XML_RELAXNG_EXCEPT) {
6742 xmlRelaxNGDefinePtr list;
6743 oldflags = ctxt->flags;
6744 ctxt->flags |= FLAGS_IGNORABLE;
6745
6746 list = define->content;
6747 while (list != NULL) {
6748 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6749 if (ret == 1) {
6750 ctxt->flags = oldflags;
6751 return(0);
6752 }
6753 if (ret < 0) {
6754 ctxt->flags = oldflags;
6755 return(ret);
6756 }
6757 list = list->next;
6758 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006759 ret = 1;
6760 ctxt->flags = oldflags;
6761 } else if (define->type == XML_RELAXNG_CHOICE) {
6762 xmlRelaxNGDefinePtr list;
6763 oldflags = ctxt->flags;
6764 ctxt->flags |= FLAGS_IGNORABLE;
6765
6766 list = define->nameClass;
6767 while (list != NULL) {
6768 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6769 if (ret == 1) {
6770 ctxt->flags = oldflags;
6771 return(1);
6772 }
6773 if (ret < 0) {
6774 ctxt->flags = oldflags;
6775 return(ret);
6776 }
6777 list = list->next;
6778 }
6779 ret = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006780 ctxt->flags = oldflags;
6781 } else {
6782 TODO
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006783 ret = -1;
Daniel Veillard416589a2003-02-17 17:25:42 +00006784 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006785 return(ret);
Daniel Veillard416589a2003-02-17 17:25:42 +00006786}
6787
6788/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006789 * xmlRelaxNGValidateDefinition:
6790 * @ctxt: a Relax-NG validation context
6791 * @define: the definition to verify
6792 *
6793 * Validate the current node against the definition
6794 *
6795 * Returns 0 if the validation succeeded or an error code.
6796 */
6797static int
6798xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
6799 xmlRelaxNGDefinePtr define) {
6800 xmlNodePtr node;
6801 int ret = 0, i, tmp, oldflags;
6802 xmlRelaxNGValidStatePtr oldstate, state;
6803
6804 if (define == NULL) {
6805 VALID_CTXT();
6806 VALID_ERROR("internal error: define == NULL\n");
6807 return(-1);
6808 }
6809 if (ctxt->state != NULL) {
6810 node = ctxt->state->seq;
6811 } else {
6812 node = NULL;
6813 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006814#ifdef DEBUG
6815 for (i = 0;i < ctxt->depth;i++)
6816 xmlGenericError(xmlGenericErrorContext, " ");
6817 xmlGenericError(xmlGenericErrorContext,
6818 "Start validating %s ", xmlRelaxNGDefName(define));
6819 if (define->name != NULL)
6820 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6821 if ((node != NULL) && (node->name != NULL))
6822 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
6823 else
6824 xmlGenericError(xmlGenericErrorContext, "\n");
6825#endif
6826 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006827 switch (define->type) {
6828 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00006829 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006830 if (node != NULL) {
6831 VALID_CTXT();
6832 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00006833 ret = -1;
6834 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006835 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006836 ret = 0;
6837 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006838 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00006839 ret = -1;
6840 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006841 case XML_RELAXNG_TEXT:
Daniel Veillardce14fa52003-02-19 17:32:48 +00006842#if 0
Daniel Veillard231d7912003-02-09 14:22:17 +00006843 if (node == NULL) {
6844 ret = 0;
6845 break;
6846 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006847#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006848 while ((node != NULL) &&
6849 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006850 (node->type == XML_COMMENT_NODE) ||
6851 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00006852 (node->type == XML_CDATA_SECTION_NODE)))
6853 node = node->next;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006854#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00006855 if (node == ctxt->state->seq) {
6856 VALID_CTXT();
6857 VALID_ERROR("Expecting text content\n");
6858 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006859 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006860#endif
Daniel Veillard276be4a2003-01-24 01:03:34 +00006861 ctxt->state->seq = node;
6862 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006863 case XML_RELAXNG_ELEMENT:
6864 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00006865 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006866 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00006867 VALID_ERROR("Expecting an element, got empty\n");
6868 ret = -1;
6869 break;
6870 }
6871 if (node->type != XML_ELEMENT_NODE) {
6872 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006873 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00006874 ret = -1;
6875 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006876 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006877 /*
6878 * This node was already validated successfully against
6879 * this definition.
6880 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006881 if (node->_private == define) {
6882 ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006883 break;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006884 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006885
6886 ret = xmlRelaxNGElementMatch(ctxt, define, node);
6887 if (ret <= 0) {
6888 ret = -1;
6889 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006890 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006891 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006892
6893 state = xmlRelaxNGNewValidState(ctxt, node);
6894 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006895 ret = -1;
6896 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006897 }
6898
6899 oldstate = ctxt->state;
6900 ctxt->state = state;
6901 if (define->attrs != NULL) {
6902 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00006903 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006904 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006905#ifdef DEBUG
6906 xmlGenericError(xmlGenericErrorContext,
6907 "E: Element %s failed to validate attributes\n",
6908 node->name);
6909#endif
6910 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006911 }
6912 if (define->content != NULL) {
6913 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006914 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006915 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006916#ifdef DEBUG
6917 xmlGenericError(xmlGenericErrorContext,
6918 "E: Element %s failed to validate element content\n",
6919 node->name);
6920#endif
6921 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006922 }
6923 state = ctxt->state;
6924 if (state->seq != NULL) {
6925 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
6926 if (state->seq != NULL) {
6927 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006928 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006929 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006930 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006931#ifdef DEBUG
6932 xmlGenericError(xmlGenericErrorContext,
6933 "E: Element %s has extra content: %s\n",
6934 node->name, state->seq->name);
6935#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006936 }
6937 }
6938 for (i = 0;i < state->nbAttrs;i++) {
6939 if (state->attrs[i] != NULL) {
6940 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006941 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006942 state->attrs[i]->name, node->name);
6943 ret = -1;
6944 }
6945 }
6946 ctxt->state = oldstate;
6947 xmlRelaxNGFreeValidState(state);
6948 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006949 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
6950 if (ret == 0)
6951 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006952
6953
6954#ifdef DEBUG
6955 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00006956 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006957 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00006958 if (oldstate == NULL)
6959 xmlGenericError(xmlGenericErrorContext, ": no state\n");
6960 else if (oldstate->seq == NULL)
6961 xmlGenericError(xmlGenericErrorContext, ": done\n");
6962 else if (oldstate->seq->type == XML_ELEMENT_NODE)
6963 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
6964 oldstate->seq->name);
6965 else
6966 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
6967 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006968#endif
6969 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006970 case XML_RELAXNG_OPTIONAL:
6971 oldflags = ctxt->flags;
6972 ctxt->flags |= FLAGS_IGNORABLE;
6973 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6974 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6975 if (ret != 0) {
6976 xmlRelaxNGFreeValidState(ctxt->state);
6977 ctxt->state = oldstate;
6978 ret = 0;
6979 break;
6980 }
6981 xmlRelaxNGFreeValidState(oldstate);
6982 ctxt->flags = oldflags;
6983 ret = 0;
6984 break;
6985 case XML_RELAXNG_ONEORMORE:
6986 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6987 if (ret != 0) {
6988 break;
6989 }
6990 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00006991 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006992 oldflags = ctxt->flags;
6993 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006994 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006995 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6996 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6997 if (ret != 0) {
6998 xmlRelaxNGFreeValidState(ctxt->state);
6999 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007000 break;
7001 }
7002 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007003 }
7004 if (ret == 0) {
7005 /*
7006 * There is no attribute left to be consumed,
7007 * we can check the closure by looking at ctxt->state->seq
7008 */
7009 xmlNodePtr cur, temp;
7010
Daniel Veillard276be4a2003-01-24 01:03:34 +00007011 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007012 temp = NULL;
7013 while ((cur != NULL) && (temp != cur)) {
7014 temp = cur;
7015 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
7016 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7017 if (ret != 0) {
7018 xmlRelaxNGFreeValidState(ctxt->state);
7019 ctxt->state = oldstate;
7020 break;
7021 }
7022 xmlRelaxNGFreeValidState(oldstate);
7023 cur = ctxt->state->seq;
7024 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007025 }
7026 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00007027 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007028 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00007029 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007030 case XML_RELAXNG_CHOICE: {
7031 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardce14fa52003-02-19 17:32:48 +00007032 int success = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007033
7034 oldflags = ctxt->flags;
7035 ctxt->flags |= FLAGS_IGNORABLE;
7036
7037 while (list != NULL) {
7038 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
7039 ret = xmlRelaxNGValidateDefinition(ctxt, list);
7040 if (ret == 0) {
Daniel Veillardce14fa52003-02-19 17:32:48 +00007041 if (xmlRelaxNGEqualValidState(ctxt, ctxt->state, oldstate)){
7042 /*
7043 * if that pattern was nullable flag it but try
7044 * to make more progresses
7045 */
7046 success = 1;
7047 } else {
7048 xmlRelaxNGFreeValidState(oldstate);
7049 break;
7050 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00007051 }
7052 xmlRelaxNGFreeValidState(ctxt->state);
7053 ctxt->state = oldstate;
7054 list = list->next;
7055 }
7056 ctxt->flags = oldflags;
Daniel Veillardce14fa52003-02-19 17:32:48 +00007057 if (success == 1)
7058 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007059 break;
7060 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00007061 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00007062 case XML_RELAXNG_GROUP: {
7063 xmlRelaxNGDefinePtr list = define->content;
7064
7065 while (list != NULL) {
7066 ret = xmlRelaxNGValidateDefinition(ctxt, list);
7067 if (ret != 0)
7068 break;
7069 list = list->next;
7070 }
7071 break;
7072 }
7073 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00007074 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007075 break;
7076 case XML_RELAXNG_ATTRIBUTE:
7077 ret = xmlRelaxNGValidateAttribute(ctxt, define);
7078 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00007079 case XML_RELAXNG_NOOP:
Daniel Veillard6eadf632003-01-23 18:29:16 +00007080 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00007081 case XML_RELAXNG_PARENTREF:
7082 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00007083 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
7084 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007085 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00007086 xmlNodePtr child;
7087 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007088
Daniel Veillardd4310742003-02-18 21:12:46 +00007089 child = node;
7090 while (child != NULL) {
7091 if (child->type == XML_ELEMENT_NODE) {
7092 VALID_CTXT();
7093 VALID_ERROR2("Element %s has child elements\n",
7094 node->parent->name);
7095 ret = -1;
7096 break;
7097 } else if ((child->type == XML_TEXT_NODE) ||
7098 (child->type == XML_CDATA_SECTION_NODE)) {
7099 content = xmlStrcat(content, child->content);
7100 }
7101 /* TODO: handle entities ... */
7102 child = child->next;
7103 }
7104 if (ret == -1) {
7105 if (content != NULL)
7106 xmlFree(content);
7107 break;
7108 }
7109 if (content == NULL) {
7110 content = xmlStrdup(BAD_CAST "");
7111 if (content == NULL) {
7112 VALID_CTXT();
7113 VALID_ERROR("Allocation failure\n");
7114 ret = -1;
7115 break;
7116 }
7117 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007118 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
7119 if (ret == -1) {
7120 VALID_CTXT();
Daniel Veillard8bc6cf92003-02-27 17:42:22 +00007121 VALID_ERROR2("Error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007122 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007123 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007124 }
7125 if (content != NULL)
7126 xmlFree(content);
7127 break;
7128 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007129 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00007130 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007131 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00007132 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007133
Daniel Veillardd4310742003-02-18 21:12:46 +00007134 child = node;
7135 while (child != NULL) {
7136 if (child->type == XML_ELEMENT_NODE) {
7137 VALID_CTXT();
7138 VALID_ERROR2("Element %s has child elements\n",
7139 node->parent->name);
7140 ret = -1;
7141 break;
7142 } else if ((child->type == XML_TEXT_NODE) ||
7143 (child->type == XML_CDATA_SECTION_NODE)) {
7144 content = xmlStrcat(content, child->content);
7145 }
7146 /* TODO: handle entities ... */
7147 child = child->next;
7148 }
7149 if (ret == -1) {
7150 if (content != NULL)
7151 xmlFree(content);
7152 break;
7153 }
7154 if (content == NULL) {
7155 content = xmlStrdup(BAD_CAST "");
7156 if (content == NULL) {
7157 VALID_CTXT();
7158 VALID_ERROR("Allocation failure\n");
7159 ret = -1;
7160 break;
7161 }
7162 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007163 oldvalue = ctxt->state->value;
7164 ctxt->state->value = content;
7165 ret = xmlRelaxNGValidateValue(ctxt, define);
7166 ctxt->state->value = oldvalue;
7167 if (ret == -1) {
7168 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00007169 if (define->name != NULL) {
7170 VALID_ERROR2("error validating value %s\n", define->name);
7171 } else {
7172 VALID_ERROR("error validating value\n");
7173 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007174 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007175 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007176 }
7177 if (content != NULL)
7178 xmlFree(content);
7179 break;
7180 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007181 case XML_RELAXNG_LIST: {
7182 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00007183 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007184 xmlChar *oldvalue, *oldendvalue;
7185 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007186
Daniel Veillardd4310742003-02-18 21:12:46 +00007187 /*
7188 * Make sure it's only text nodes
7189 */
7190
7191 content = NULL;
7192 child = node;
7193 while (child != NULL) {
7194 if (child->type == XML_ELEMENT_NODE) {
7195 VALID_CTXT();
7196 VALID_ERROR2("Element %s has child elements\n",
7197 node->parent->name);
7198 ret = -1;
7199 break;
7200 } else if ((child->type == XML_TEXT_NODE) ||
7201 (child->type == XML_CDATA_SECTION_NODE)) {
7202 content = xmlStrcat(content, child->content);
7203 }
7204 /* TODO: handle entities ... */
7205 child = child->next;
7206 }
7207 if (ret == -1) {
7208 if (content != NULL)
7209 xmlFree(content);
7210 break;
7211 }
7212 if (content == NULL) {
7213 content = xmlStrdup(BAD_CAST "");
7214 if (content == NULL) {
7215 VALID_CTXT();
7216 VALID_ERROR("Allocation failure\n");
7217 ret = -1;
7218 break;
7219 }
7220 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007221 len = xmlStrlen(content);
7222 oldvalue = ctxt->state->value;
7223 oldendvalue = ctxt->state->endvalue;
7224 ctxt->state->value = content;
7225 ctxt->state->endvalue = content + len;
7226 ret = xmlRelaxNGValidateValue(ctxt, define);
7227 ctxt->state->value = oldvalue;
7228 ctxt->state->endvalue = oldendvalue;
7229 if (ret == -1) {
7230 VALID_CTXT();
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007231 VALID_ERROR("error validating list\n");
Daniel Veillardd4310742003-02-18 21:12:46 +00007232 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007233 ctxt->state->seq = node->next;
7234 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007235 if (content != NULL)
7236 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007237 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007238 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007239 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00007240 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00007241 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007242 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00007243 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007244 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007245 }
Daniel Veillard231d7912003-02-09 14:22:17 +00007246 ctxt->depth--;
7247#ifdef DEBUG
7248 for (i = 0;i < ctxt->depth;i++)
7249 xmlGenericError(xmlGenericErrorContext, " ");
7250 xmlGenericError(xmlGenericErrorContext,
7251 "Validating %s ", xmlRelaxNGDefName(define));
7252 if (define->name != NULL)
7253 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
7254 if (ret == 0)
7255 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
7256 else
7257 xmlGenericError(xmlGenericErrorContext, "failed\n");
7258#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00007259 return(ret);
7260}
7261
7262/**
7263 * xmlRelaxNGValidateDocument:
7264 * @ctxt: a Relax-NG validation context
7265 * @doc: the document
7266 *
7267 * Validate the given document
7268 *
7269 * Returns 0 if the validation succeeded or an error code.
7270 */
7271static int
7272xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7273 int ret;
7274 xmlRelaxNGPtr schema;
7275 xmlRelaxNGGrammarPtr grammar;
7276 xmlRelaxNGValidStatePtr state;
7277
7278 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
7279 return(-1);
7280
7281 schema = ctxt->schema;
7282 grammar = schema->topgrammar;
7283 if (grammar == NULL) {
7284 VALID_CTXT();
7285 VALID_ERROR("No top grammar defined\n");
7286 return(-1);
7287 }
7288 state = xmlRelaxNGNewValidState(ctxt, NULL);
7289 ctxt->state = state;
7290 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
7291 state = ctxt->state;
7292 if ((state != NULL) && (state->seq != NULL)) {
7293 xmlNodePtr node;
7294
7295 node = state->seq;
7296 node = xmlRelaxNGSkipIgnored(ctxt, node);
7297 if (node != NULL) {
7298 VALID_CTXT();
7299 VALID_ERROR("extra data on the document\n");
7300 ret = -1;
7301 }
7302 }
7303 xmlRelaxNGFreeValidState(state);
7304
7305 return(ret);
7306}
7307
7308/************************************************************************
7309 * *
7310 * Validation interfaces *
7311 * *
7312 ************************************************************************/
7313/**
7314 * xmlRelaxNGNewValidCtxt:
7315 * @schema: a precompiled XML RelaxNGs
7316 *
7317 * Create an XML RelaxNGs validation context based on the given schema
7318 *
7319 * Returns the validation context or NULL in case of error
7320 */
7321xmlRelaxNGValidCtxtPtr
7322xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
7323 xmlRelaxNGValidCtxtPtr ret;
7324
7325 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
7326 if (ret == NULL) {
7327 xmlGenericError(xmlGenericErrorContext,
7328 "Failed to allocate new schama validation context\n");
7329 return (NULL);
7330 }
7331 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
7332 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00007333 ret->error = xmlGenericError;
7334 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007335 return (ret);
7336}
7337
7338/**
7339 * xmlRelaxNGFreeValidCtxt:
7340 * @ctxt: the schema validation context
7341 *
7342 * Free the resources associated to the schema validation context
7343 */
7344void
7345xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
7346 if (ctxt == NULL)
7347 return;
7348 xmlFree(ctxt);
7349}
7350
7351/**
7352 * xmlRelaxNGSetValidErrors:
7353 * @ctxt: a Relax-NG validation context
7354 * @err: the error function
7355 * @warn: the warning function
7356 * @ctx: the functions context
7357 *
7358 * Set the error and warning callback informations
7359 */
7360void
7361xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
7362 xmlRelaxNGValidityErrorFunc err,
7363 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
7364 if (ctxt == NULL)
7365 return;
7366 ctxt->error = err;
7367 ctxt->warning = warn;
7368 ctxt->userData = ctx;
7369}
7370
7371/**
7372 * xmlRelaxNGValidateDoc:
7373 * @ctxt: a Relax-NG validation context
7374 * @doc: a parsed document tree
7375 *
7376 * Validate a document tree in memory.
7377 *
7378 * Returns 0 if the document is valid, a positive error code
7379 * number otherwise and -1 in case of internal or API error.
7380 */
7381int
7382xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7383 int ret;
7384
7385 if ((ctxt == NULL) || (doc == NULL))
7386 return(-1);
7387
7388 ctxt->doc = doc;
7389
7390 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00007391 /*
7392 * TODO: build error codes
7393 */
7394 if (ret == -1)
7395 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007396 return(ret);
7397}
7398
7399#endif /* LIBXML_SCHEMAS_ENABLED */
7400