blob: 29db131d915d2440b29fe0e6c8f23c6f317cfc9c [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
335 *
336 * Function provided by a type library to check if a value match a type
337 *
338 * Returns 1 if yes, 0 if no and -1 in case of error.
339 */
340typedef int (*xmlRelaxNGTypeCheck) (void *data, const xmlChar *type,
341 const xmlChar *value);
342
343/**
344 * xmlRelaxNGTypeCompare:
345 * @data: data needed for the library
346 * @type: the type name
347 * @value1: the first value
348 * @value2: the second value
349 *
350 * Function provided by a type library to compare two values accordingly
351 * to a type.
352 *
353 * Returns 1 if yes, 0 if no and -1 in case of error.
354 */
355typedef int (*xmlRelaxNGTypeCompare) (void *data, const xmlChar *type,
356 const xmlChar *value1,
357 const xmlChar *value2);
358typedef struct _xmlRelaxNGTypeLibrary xmlRelaxNGTypeLibrary;
359typedef xmlRelaxNGTypeLibrary *xmlRelaxNGTypeLibraryPtr;
360struct _xmlRelaxNGTypeLibrary {
361 const xmlChar *namespace; /* the datatypeLibrary value */
362 void *data; /* data needed for the library */
363 xmlRelaxNGTypeHave have; /* the export function */
364 xmlRelaxNGTypeCheck check; /* the checking function */
365 xmlRelaxNGTypeCompare comp; /* the compare function */
366};
367
368/************************************************************************
369 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +0000370 * Allocation functions *
371 * *
372 ************************************************************************/
Daniel Veillard6eadf632003-01-23 18:29:16 +0000373static void xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar);
374static void xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define);
Daniel Veillardd2298792003-02-14 16:54:11 +0000375static void xmlRelaxNGNormExtSpace(xmlChar *value);
Daniel Veillardc482e262003-02-26 14:48:48 +0000376static void xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000377
378/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000379 * xmlRelaxNGFreeDocument:
380 * @docu: a document structure
381 *
382 * Deallocate a RelaxNG document structure.
383 */
384static void
385xmlRelaxNGFreeDocument(xmlRelaxNGDocumentPtr docu)
386{
387 if (docu == NULL)
388 return;
389
390 if (docu->href != NULL)
391 xmlFree(docu->href);
392 if (docu->doc != NULL)
393 xmlFreeDoc(docu->doc);
394 if (docu->schema != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000395 xmlRelaxNGFreeInnerSchema(docu->schema);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000396 xmlFree(docu);
397}
398
399/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000400 * xmlRelaxNGFreeDocumentList:
401 * @docu: a list of document structure
402 *
403 * Deallocate a RelaxNG document structures.
404 */
405static void
406xmlRelaxNGFreeDocumentList(xmlRelaxNGDocumentPtr docu)
407{
408 xmlRelaxNGDocumentPtr next;
409 while (docu != NULL) {
410 next = docu->next;
411 xmlRelaxNGFreeDocument(docu);
412 docu = next;
413 }
414}
415
416/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000417 * xmlRelaxNGFreeInclude:
418 * @incl: a include structure
419 *
420 * Deallocate a RelaxNG include structure.
421 */
422static void
423xmlRelaxNGFreeInclude(xmlRelaxNGIncludePtr incl)
424{
425 if (incl == NULL)
426 return;
427
428 if (incl->href != NULL)
429 xmlFree(incl->href);
430 if (incl->doc != NULL)
431 xmlFreeDoc(incl->doc);
432 if (incl->schema != NULL)
433 xmlRelaxNGFree(incl->schema);
434 xmlFree(incl);
435}
436
437/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000438 * xmlRelaxNGFreeIncludeList:
439 * @incl: a include structure list
440 *
441 * Deallocate a RelaxNG include structure.
442 */
443static void
444xmlRelaxNGFreeIncludeList(xmlRelaxNGIncludePtr incl)
445{
446 xmlRelaxNGIncludePtr next;
447 while (incl != NULL) {
448 next = incl->next;
449 xmlRelaxNGFreeInclude(incl);
450 incl = next;
451 }
452}
453
454/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000455 * xmlRelaxNGNewRelaxNG:
456 * @ctxt: a Relax-NG validation context (optional)
457 *
458 * Allocate a new RelaxNG structure.
459 *
460 * Returns the newly allocated structure or NULL in case or error
461 */
462static xmlRelaxNGPtr
463xmlRelaxNGNewRelaxNG(xmlRelaxNGParserCtxtPtr ctxt)
464{
465 xmlRelaxNGPtr ret;
466
467 ret = (xmlRelaxNGPtr) xmlMalloc(sizeof(xmlRelaxNG));
468 if (ret == NULL) {
469 if ((ctxt != NULL) && (ctxt->error != NULL))
470 ctxt->error(ctxt->userData, "Out of memory\n");
471 ctxt->nbErrors++;
472 return (NULL);
473 }
474 memset(ret, 0, sizeof(xmlRelaxNG));
475
476 return (ret);
477}
478
479/**
Daniel Veillardc482e262003-02-26 14:48:48 +0000480 * xmlRelaxNGFreeInnerSchema:
481 * @schema: a schema structure
482 *
483 * Deallocate a RelaxNG schema structure.
484 */
485static void
486xmlRelaxNGFreeInnerSchema(xmlRelaxNGPtr schema)
487{
488 if (schema == NULL)
489 return;
490
491 if (schema->doc != NULL)
492 xmlFreeDoc(schema->doc);
493 if (schema->defTab != NULL) {
494 int i;
495
496 for (i = 0;i < schema->defNr;i++)
497 xmlRelaxNGFreeDefine(schema->defTab[i]);
498 xmlFree(schema->defTab);
499 }
500
501 xmlFree(schema);
502}
503
504/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000505 * xmlRelaxNGFree:
506 * @schema: a schema structure
507 *
508 * Deallocate a RelaxNG structure.
509 */
510void
511xmlRelaxNGFree(xmlRelaxNGPtr schema)
512{
513 if (schema == NULL)
514 return;
515
Daniel Veillard6eadf632003-01-23 18:29:16 +0000516 if (schema->topgrammar != NULL)
517 xmlRelaxNGFreeGrammar(schema->topgrammar);
518 if (schema->doc != NULL)
519 xmlFreeDoc(schema->doc);
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000520 if (schema->documents != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000521 xmlRelaxNGFreeDocumentList(schema->documents);
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000522 if (schema->includes != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +0000523 xmlRelaxNGFreeIncludeList(schema->includes);
Daniel Veillard419a7682003-02-03 23:22:49 +0000524 if (schema->defTab != NULL) {
525 int i;
526
527 for (i = 0;i < schema->defNr;i++)
528 xmlRelaxNGFreeDefine(schema->defTab[i]);
529 xmlFree(schema->defTab);
530 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000531
532 xmlFree(schema);
533}
534
535/**
536 * xmlRelaxNGNewGrammar:
537 * @ctxt: a Relax-NG validation context (optional)
538 *
539 * Allocate a new RelaxNG grammar.
540 *
541 * Returns the newly allocated structure or NULL in case or error
542 */
543static xmlRelaxNGGrammarPtr
544xmlRelaxNGNewGrammar(xmlRelaxNGParserCtxtPtr ctxt)
545{
546 xmlRelaxNGGrammarPtr ret;
547
548 ret = (xmlRelaxNGGrammarPtr) xmlMalloc(sizeof(xmlRelaxNGGrammar));
549 if (ret == NULL) {
550 if ((ctxt != NULL) && (ctxt->error != NULL))
551 ctxt->error(ctxt->userData, "Out of memory\n");
552 ctxt->nbErrors++;
553 return (NULL);
554 }
555 memset(ret, 0, sizeof(xmlRelaxNGGrammar));
556
557 return (ret);
558}
559
560/**
561 * xmlRelaxNGFreeGrammar:
562 * @grammar: a grammar structure
563 *
564 * Deallocate a RelaxNG grammar structure.
565 */
566static void
567xmlRelaxNGFreeGrammar(xmlRelaxNGGrammarPtr grammar)
568{
569 if (grammar == NULL)
570 return;
571
Daniel Veillardc482e262003-02-26 14:48:48 +0000572 if (grammar->children != NULL) {
573 xmlRelaxNGFreeGrammar(grammar->children);
574 }
Daniel Veillard419a7682003-02-03 23:22:49 +0000575 if (grammar->next != NULL) {
576 xmlRelaxNGFreeGrammar(grammar->next);
577 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000578 if (grammar->refs != NULL) {
579 xmlHashFree(grammar->refs, NULL);
580 }
581 if (grammar->defs != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000582 xmlHashFree(grammar->defs, NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000583 }
584
585 xmlFree(grammar);
586}
587
588/**
589 * xmlRelaxNGNewDefine:
590 * @ctxt: a Relax-NG validation context
591 * @node: the node in the input document.
592 *
593 * Allocate a new RelaxNG define.
594 *
595 * Returns the newly allocated structure or NULL in case or error
596 */
597static xmlRelaxNGDefinePtr
598xmlRelaxNGNewDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node)
599{
600 xmlRelaxNGDefinePtr ret;
601
Daniel Veillard419a7682003-02-03 23:22:49 +0000602 if (ctxt->defMax == 0) {
603 ctxt->defMax = 16;
604 ctxt->defNr = 0;
605 ctxt->defTab = (xmlRelaxNGDefinePtr *)
606 xmlMalloc(ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
607 if (ctxt->defTab == NULL) {
608 if ((ctxt != NULL) && (ctxt->error != NULL))
609 ctxt->error(ctxt->userData, "Out of memory\n");
610 ctxt->nbErrors++;
611 return (NULL);
612 }
613 } else if (ctxt->defMax <= ctxt->defNr) {
614 xmlRelaxNGDefinePtr *tmp;
615 ctxt->defMax *= 2;
616 tmp = (xmlRelaxNGDefinePtr *) xmlRealloc(ctxt->defTab,
617 ctxt->defMax * sizeof(xmlRelaxNGDefinePtr));
618 if (tmp == NULL) {
619 if ((ctxt != NULL) && (ctxt->error != NULL))
620 ctxt->error(ctxt->userData, "Out of memory\n");
621 ctxt->nbErrors++;
622 return (NULL);
623 }
624 ctxt->defTab = tmp;
625 }
Daniel Veillard6eadf632003-01-23 18:29:16 +0000626 ret = (xmlRelaxNGDefinePtr) xmlMalloc(sizeof(xmlRelaxNGDefine));
627 if (ret == NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +0000628 if ((ctxt != NULL) && (ctxt->error != NULL))
629 ctxt->error(ctxt->userData, "Out of memory\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +0000630 ctxt->nbErrors++;
Daniel Veillard419a7682003-02-03 23:22:49 +0000631 return(NULL);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000632 }
633 memset(ret, 0, sizeof(xmlRelaxNGDefine));
Daniel Veillard419a7682003-02-03 23:22:49 +0000634 ctxt->defTab[ctxt->defNr++] = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000635 ret->node = node;
Daniel Veillardd4310742003-02-18 21:12:46 +0000636 ret->depth = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000637 return (ret);
638}
639
640/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000641 * xmlRelaxNGFreePartition:
642 * @partitions: a partition set structure
643 *
644 * Deallocate RelaxNG partition set structures.
645 */
646static void
647xmlRelaxNGFreePartition(xmlRelaxNGPartitionPtr partitions) {
648 xmlRelaxNGInterleaveGroupPtr group;
649 int j;
650
651 if (partitions != NULL) {
652 if (partitions->groups != NULL) {
653 for (j = 0;j < partitions->nbgroups;j++) {
654 group = partitions->groups[j];
655 if (group != NULL) {
656 if (group->defs != NULL)
657 xmlFree(group->defs);
Daniel Veillard44e1dd02003-02-21 23:23:28 +0000658 if (group->attrs != NULL)
659 xmlFree(group->attrs);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +0000660 xmlFree(group);
661 }
662 }
663 xmlFree(partitions->groups);
664 }
665 xmlFree(partitions);
666 }
667}
668/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000669 * xmlRelaxNGFreeDefine:
670 * @define: a define structure
671 *
672 * Deallocate a RelaxNG define structure.
673 */
674static void
675xmlRelaxNGFreeDefine(xmlRelaxNGDefinePtr define)
676{
677 if (define == NULL)
678 return;
679
Daniel Veillard419a7682003-02-03 23:22:49 +0000680 if ((define->data != NULL) &&
681 (define->type == XML_RELAXNG_INTERLEAVE))
682 xmlRelaxNGFreePartition((xmlRelaxNGPartitionPtr) define->data);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000683 if (define->name != NULL)
684 xmlFree(define->name);
685 if (define->ns != NULL)
686 xmlFree(define->ns);
Daniel Veillardedc91922003-01-26 00:52:04 +0000687 if (define->value != NULL)
688 xmlFree(define->value);
Daniel Veillard6eadf632003-01-23 18:29:16 +0000689 xmlFree(define);
690}
691
692/**
693 * xmlRelaxNGNewValidState:
694 * @ctxt: a Relax-NG validation context
695 * @node: the current node or NULL for the document
696 *
697 * Allocate a new RelaxNG validation state
698 *
699 * Returns the newly allocated structure or NULL in case or error
700 */
701static xmlRelaxNGValidStatePtr
702xmlRelaxNGNewValidState(xmlRelaxNGValidCtxtPtr ctxt, xmlNodePtr node)
703{
704 xmlRelaxNGValidStatePtr ret;
705 xmlAttrPtr attr;
706 xmlAttrPtr attrs[MAX_ATTR];
707 int nbAttrs = 0;
708 xmlNodePtr root = NULL;
709
710 if (node == NULL) {
711 root = xmlDocGetRootElement(ctxt->doc);
712 if (root == NULL)
713 return(NULL);
714 } else {
715 attr = node->properties;
716 while (attr != NULL) {
717 if (nbAttrs < MAX_ATTR)
718 attrs[nbAttrs++] = attr;
719 else
720 nbAttrs++;
721 attr = attr->next;
722 }
723 }
724
725 if (nbAttrs < MAX_ATTR)
726 attrs[nbAttrs] = NULL;
727 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(sizeof(xmlRelaxNGValidState) +
728 nbAttrs * sizeof(xmlAttrPtr));
729 if (ret == NULL) {
730 if ((ctxt != NULL) && (ctxt->error != NULL))
731 ctxt->error(ctxt->userData, "Out of memory\n");
732 return (NULL);
733 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +0000734 ret->value = NULL;
735 ret->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000736 if (node == NULL) {
737 ret->node = (xmlNodePtr) ctxt->doc;
738 ret->seq = root;
739 ret->nbAttrs = 0;
740 } else {
741 ret->node = node;
742 ret->seq = node->children;
743 ret->nbAttrs = nbAttrs;
744 if (nbAttrs > 0) {
745 if (nbAttrs < MAX_ATTR) {
746 memcpy(&(ret->attrs[0]), attrs,
747 sizeof(xmlAttrPtr) * (nbAttrs + 1));
748 } else {
749 attr = node->properties;
750 nbAttrs = 0;
751 while (attr != NULL) {
752 ret->attrs[nbAttrs++] = attr;
753 attr = attr->next;
754 }
755 ret->attrs[nbAttrs] = NULL;
756 }
757 }
758 }
Daniel Veillard1ed7f362003-02-03 10:57:45 +0000759 ret->nbAttrLeft = ret->nbAttrs;
Daniel Veillard6eadf632003-01-23 18:29:16 +0000760 return (ret);
761}
762
763/**
764 * xmlRelaxNGCopyValidState:
765 * @ctxt: a Relax-NG validation context
766 * @state: a validation state
767 *
768 * Copy the validation state
769 *
770 * Returns the newly allocated structure or NULL in case or error
771 */
772static xmlRelaxNGValidStatePtr
773xmlRelaxNGCopyValidState(xmlRelaxNGValidCtxtPtr ctxt,
774 xmlRelaxNGValidStatePtr state)
775{
776 xmlRelaxNGValidStatePtr ret;
777 unsigned int size;
778
779 if (state == NULL)
780 return(NULL);
781
782 size = sizeof(xmlRelaxNGValidState) +
783 state->nbAttrs * sizeof(xmlAttrPtr);
784 ret = (xmlRelaxNGValidStatePtr) xmlMalloc(size);
785 if (ret == NULL) {
786 if ((ctxt != NULL) && (ctxt->error != NULL))
787 ctxt->error(ctxt->userData, "Out of memory\n");
788 return (NULL);
789 }
790 memcpy(ret, state, size);
791 return(ret);
792}
793
794/**
Daniel Veillardce14fa52003-02-19 17:32:48 +0000795 * xmlRelaxNGEqualValidState:
796 * @ctxt: a Relax-NG validation context
797 * @state1: a validation state
798 * @state2: a validation state
799 *
800 * Compare the validation states for equality
801 *
802 * Returns 1 if equald, 0 otherwise
803 */
804static int
805xmlRelaxNGEqualValidState(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
806 xmlRelaxNGValidStatePtr state1,
807 xmlRelaxNGValidStatePtr state2)
808{
809 int i;
810
811 if ((state1 == NULL) || (state2 == NULL))
812 return(0);
813 if (state1 == state2)
814 return(1);
815 if (state1->node != state2->node)
816 return(0);
817 if (state1->seq != state2->seq)
818 return(0);
819 if (state1->nbAttrLeft != state2->nbAttrLeft)
820 return(0);
821 if (state1->nbAttrs != state2->nbAttrs)
822 return(0);
823 if (state1->endvalue != state2->endvalue)
824 return(0);
825 if ((state1->value != state2->value) &&
826 (!xmlStrEqual(state1->value, state2->value)))
827 return(0);
828 for (i = 0;i < state1->nbAttrs;i++) {
829 if (state1->attrs[i] != state2->attrs[i])
830 return(0);
831 }
832 return(1);
833}
834
835/**
Daniel Veillard6eadf632003-01-23 18:29:16 +0000836 * xmlRelaxNGFreeValidState:
837 * @state: a validation state structure
838 *
839 * Deallocate a RelaxNG validation state structure.
840 */
841static void
842xmlRelaxNGFreeValidState(xmlRelaxNGValidStatePtr state)
843{
844 if (state == NULL)
845 return;
846
847 xmlFree(state);
848}
849
850/************************************************************************
851 * *
Daniel Veillardd41f4f42003-01-29 21:07:52 +0000852 * Document functions *
853 * *
854 ************************************************************************/
855static xmlDocPtr xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt,
856 xmlDocPtr doc);
857
858/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000859 * xmlRelaxNGIncludePush:
860 * @ctxt: the parser context
861 * @value: the element doc
862 *
863 * Pushes a new include on top of the include stack
864 *
865 * Returns 0 in case of error, the index in the stack otherwise
866 */
867static int
868xmlRelaxNGIncludePush(xmlRelaxNGParserCtxtPtr ctxt,
869 xmlRelaxNGIncludePtr value)
870{
871 if (ctxt->incTab == NULL) {
872 ctxt->incMax = 4;
873 ctxt->incNr = 0;
874 ctxt->incTab = (xmlRelaxNGIncludePtr *) xmlMalloc(
875 ctxt->incMax * sizeof(ctxt->incTab[0]));
876 if (ctxt->incTab == NULL) {
877 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
878 return (0);
879 }
880 }
881 if (ctxt->incNr >= ctxt->incMax) {
882 ctxt->incMax *= 2;
883 ctxt->incTab =
884 (xmlRelaxNGIncludePtr *) xmlRealloc(ctxt->incTab,
885 ctxt->incMax *
886 sizeof(ctxt->incTab[0]));
887 if (ctxt->incTab == NULL) {
888 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
889 return (0);
890 }
891 }
892 ctxt->incTab[ctxt->incNr] = value;
893 ctxt->inc = value;
894 return (ctxt->incNr++);
895}
896
897/**
898 * xmlRelaxNGIncludePop:
899 * @ctxt: the parser context
900 *
901 * Pops the top include from the include stack
902 *
903 * Returns the include just removed
904 */
905static xmlRelaxNGIncludePtr
906xmlRelaxNGIncludePop(xmlRelaxNGParserCtxtPtr ctxt)
907{
908 xmlRelaxNGIncludePtr ret;
909
910 if (ctxt->incNr <= 0)
911 return (0);
912 ctxt->incNr--;
913 if (ctxt->incNr > 0)
914 ctxt->inc = ctxt->incTab[ctxt->incNr - 1];
915 else
916 ctxt->inc = NULL;
917 ret = ctxt->incTab[ctxt->incNr];
918 ctxt->incTab[ctxt->incNr] = 0;
919 return (ret);
920}
921
922/**
923 * xmlRelaxNGLoadInclude:
924 * @ctxt: the parser context
925 * @URL: the normalized URL
926 * @node: the include node.
Daniel Veillard416589a2003-02-17 17:25:42 +0000927 * @ns: the namespace passed from the context.
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000928 *
929 * First lookup if the document is already loaded into the parser context,
930 * check against recursion. If not found the resource is loaded and
931 * the content is preprocessed before being returned back to the caller.
932 *
933 * Returns the xmlRelaxNGIncludePtr or NULL in case of error
934 */
935static xmlRelaxNGIncludePtr
936xmlRelaxNGLoadInclude(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillard416589a2003-02-17 17:25:42 +0000937 xmlNodePtr node, const xmlChar *ns) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000938 xmlRelaxNGIncludePtr ret = NULL;
939 xmlDocPtr doc;
940 int i;
941 xmlNodePtr root, tmp, tmp2, cur;
942
943 /*
944 * check against recursion in the stack
945 */
946 for (i = 0;i < ctxt->incNr;i++) {
947 if (xmlStrEqual(ctxt->incTab[i]->href, URL)) {
948 if (ctxt->error != NULL)
949 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +0000950 "Detected an Include recursion for %s\n",
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000951 URL);
952 ctxt->nbErrors++;
953 return(NULL);
954 }
955 }
956
957 /*
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000958 * load the document
959 */
960 doc = xmlParseFile((const char *) URL);
961 if (doc == NULL) {
962 if (ctxt->error != NULL)
963 ctxt->error(ctxt->userData,
964 "xmlRelaxNG: could not load %s\n", URL);
965 ctxt->nbErrors++;
966 return (NULL);
967 }
968
969 /*
970 * Allocate the document structures and register it first.
971 */
972 ret = (xmlRelaxNGIncludePtr) xmlMalloc(sizeof(xmlRelaxNGInclude));
973 if (ret == NULL) {
974 if (ctxt->error != NULL)
975 ctxt->error(ctxt->userData,
976 "xmlRelaxNG: allocate memory for doc %s\n", URL);
977 ctxt->nbErrors++;
978 xmlFreeDoc(doc);
979 return (NULL);
980 }
981 memset(ret, 0, sizeof(xmlRelaxNGInclude));
982 ret->doc = doc;
983 ret->href = xmlStrdup(URL);
Daniel Veillardc482e262003-02-26 14:48:48 +0000984 ret->next = ctxt->includes;
985 ctxt->includes = ret;
Daniel Veillarda9d912d2003-02-01 17:43:10 +0000986
987 /*
Daniel Veillard416589a2003-02-17 17:25:42 +0000988 * transmit the ns if needed
989 */
990 if (ns != NULL) {
991 root = xmlDocGetRootElement(doc);
992 if (root != NULL) {
993 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
994 xmlSetProp(root, BAD_CAST"ns", ns);
995 }
996 }
997 }
998
999 /*
Daniel Veillardc482e262003-02-26 14:48:48 +00001000 * push it on the stack
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001001 */
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001002 xmlRelaxNGIncludePush(ctxt, ret);
1003
1004 /*
1005 * Some preprocessing of the document content, this include recursing
1006 * in the include stack.
1007 */
1008 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1009 if (doc == NULL) {
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001010 ctxt->inc = NULL;
1011 return(NULL);
1012 }
1013
1014 /*
1015 * Pop up the include from the stack
1016 */
1017 xmlRelaxNGIncludePop(ctxt);
1018
1019 /*
1020 * Check that the top element is a grammar
1021 */
1022 root = xmlDocGetRootElement(doc);
1023 if (root == NULL) {
1024 if (ctxt->error != NULL)
1025 ctxt->error(ctxt->userData,
1026 "xmlRelaxNG: included document is empty %s\n", URL);
1027 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001028 return (NULL);
1029 }
1030 if (!IS_RELAXNG(root, "grammar")) {
1031 if (ctxt->error != NULL)
1032 ctxt->error(ctxt->userData,
1033 "xmlRelaxNG: included document %s root is not a grammar\n",
1034 URL);
1035 ctxt->nbErrors++;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001036 return (NULL);
1037 }
1038
1039 /*
1040 * Elimination of redefined rules in the include.
1041 */
1042 cur = node->children;
1043 while (cur != NULL) {
1044 if (IS_RELAXNG(cur, "start")) {
1045 int found = 0;
1046
1047 tmp = root->children;
1048 while (tmp != NULL) {
1049 tmp2 = tmp->next;
1050 if (IS_RELAXNG(tmp, "start")) {
1051 found = 1;
1052 xmlUnlinkNode(tmp);
1053 xmlFreeNode(tmp);
1054 }
1055 tmp = tmp2;
1056 }
1057 if (!found) {
1058 if (ctxt->error != NULL)
1059 ctxt->error(ctxt->userData,
1060 "xmlRelaxNG: include %s has a start but not the included grammar\n",
1061 URL);
1062 ctxt->nbErrors++;
1063 }
1064 } else if (IS_RELAXNG(cur, "define")) {
1065 xmlChar *name, *name2;
1066
1067 name = xmlGetProp(cur, BAD_CAST "name");
1068 if (name == NULL) {
1069 if (ctxt->error != NULL)
1070 ctxt->error(ctxt->userData,
1071 "xmlRelaxNG: include %s has define without name\n",
1072 URL);
1073 ctxt->nbErrors++;
1074 } else {
1075 int found = 0;
1076
Daniel Veillardd2298792003-02-14 16:54:11 +00001077 xmlRelaxNGNormExtSpace(name);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001078 tmp = root->children;
1079 while (tmp != NULL) {
1080 tmp2 = tmp->next;
1081 if (IS_RELAXNG(tmp, "define")) {
1082 name2 = xmlGetProp(tmp, BAD_CAST "name");
Daniel Veillardd2298792003-02-14 16:54:11 +00001083 xmlRelaxNGNormExtSpace(name2);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001084 if (name2 != NULL) {
1085 if (xmlStrEqual(name, name2)) {
1086 found = 1;
1087 xmlUnlinkNode(tmp);
1088 xmlFreeNode(tmp);
1089 }
1090 xmlFree(name2);
1091 }
1092 }
1093 tmp = tmp2;
1094 }
1095 if (!found) {
1096 if (ctxt->error != NULL)
1097 ctxt->error(ctxt->userData,
1098 "xmlRelaxNG: include %s has a define %s but not the included grammar\n",
1099 URL, name);
1100 ctxt->nbErrors++;
1101 }
1102 xmlFree(name);
1103 }
1104 }
1105 cur = cur->next;
1106 }
1107
1108
1109 return(ret);
1110}
1111
1112/**
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001113 * xmlRelaxNGDocumentPush:
1114 * @ctxt: the parser context
1115 * @value: the element doc
1116 *
1117 * Pushes a new doc on top of the doc stack
1118 *
1119 * Returns 0 in case of error, the index in the stack otherwise
1120 */
1121static int
1122xmlRelaxNGDocumentPush(xmlRelaxNGParserCtxtPtr ctxt,
1123 xmlRelaxNGDocumentPtr value)
1124{
1125 if (ctxt->docTab == NULL) {
1126 ctxt->docMax = 4;
1127 ctxt->docNr = 0;
1128 ctxt->docTab = (xmlRelaxNGDocumentPtr *) xmlMalloc(
1129 ctxt->docMax * sizeof(ctxt->docTab[0]));
1130 if (ctxt->docTab == NULL) {
1131 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
1132 return (0);
1133 }
1134 }
1135 if (ctxt->docNr >= ctxt->docMax) {
1136 ctxt->docMax *= 2;
1137 ctxt->docTab =
1138 (xmlRelaxNGDocumentPtr *) xmlRealloc(ctxt->docTab,
1139 ctxt->docMax *
1140 sizeof(ctxt->docTab[0]));
1141 if (ctxt->docTab == NULL) {
1142 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
1143 return (0);
1144 }
1145 }
1146 ctxt->docTab[ctxt->docNr] = value;
1147 ctxt->doc = value;
1148 return (ctxt->docNr++);
1149}
1150
1151/**
1152 * xmlRelaxNGDocumentPop:
1153 * @ctxt: the parser context
1154 *
1155 * Pops the top doc from the doc stack
1156 *
1157 * Returns the doc just removed
1158 */
1159static xmlRelaxNGDocumentPtr
1160xmlRelaxNGDocumentPop(xmlRelaxNGParserCtxtPtr ctxt)
1161{
1162 xmlRelaxNGDocumentPtr ret;
1163
1164 if (ctxt->docNr <= 0)
1165 return (0);
1166 ctxt->docNr--;
1167 if (ctxt->docNr > 0)
1168 ctxt->doc = ctxt->docTab[ctxt->docNr - 1];
1169 else
1170 ctxt->doc = NULL;
1171 ret = ctxt->docTab[ctxt->docNr];
1172 ctxt->docTab[ctxt->docNr] = 0;
1173 return (ret);
1174}
1175
1176/**
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001177 * xmlRelaxNGLoadExternalRef:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001178 * @ctxt: the parser context
1179 * @URL: the normalized URL
1180 * @ns: the inherited ns if any
1181 *
1182 * First lookup if the document is already loaded into the parser context,
1183 * check against recursion. If not found the resource is loaded and
1184 * the content is preprocessed before being returned back to the caller.
1185 *
1186 * Returns the xmlRelaxNGDocumentPtr or NULL in case of error
1187 */
1188static xmlRelaxNGDocumentPtr
Daniel Veillarda9d912d2003-02-01 17:43:10 +00001189xmlRelaxNGLoadExternalRef(xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *URL,
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001190 const xmlChar *ns) {
1191 xmlRelaxNGDocumentPtr ret = NULL;
1192 xmlDocPtr doc;
1193 xmlNodePtr root;
1194 int i;
1195
1196 /*
1197 * check against recursion in the stack
1198 */
1199 for (i = 0;i < ctxt->docNr;i++) {
1200 if (xmlStrEqual(ctxt->docTab[i]->href, URL)) {
1201 if (ctxt->error != NULL)
1202 ctxt->error(ctxt->userData,
1203 "Detected an externalRef recursion for %s\n",
1204 URL);
1205 ctxt->nbErrors++;
1206 return(NULL);
1207 }
1208 }
1209
1210 /*
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001211 * load the document
1212 */
1213 doc = xmlParseFile((const char *) URL);
1214 if (doc == NULL) {
1215 if (ctxt->error != NULL)
1216 ctxt->error(ctxt->userData,
1217 "xmlRelaxNG: could not load %s\n", URL);
1218 ctxt->nbErrors++;
1219 return (NULL);
1220 }
1221
1222 /*
1223 * Allocate the document structures and register it first.
1224 */
1225 ret = (xmlRelaxNGDocumentPtr) xmlMalloc(sizeof(xmlRelaxNGDocument));
1226 if (ret == NULL) {
1227 if (ctxt->error != NULL)
1228 ctxt->error(ctxt->userData,
1229 "xmlRelaxNG: allocate memory for doc %s\n", URL);
1230 ctxt->nbErrors++;
1231 xmlFreeDoc(doc);
1232 return (NULL);
1233 }
1234 memset(ret, 0, sizeof(xmlRelaxNGDocument));
1235 ret->doc = doc;
1236 ret->href = xmlStrdup(URL);
Daniel Veillardc482e262003-02-26 14:48:48 +00001237 ret->next = ctxt->documents;
1238 ctxt->documents = ret;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001239
1240 /*
1241 * transmit the ns if needed
1242 */
1243 if (ns != NULL) {
1244 root = xmlDocGetRootElement(doc);
1245 if (root != NULL) {
1246 if (xmlHasProp(root, BAD_CAST"ns") == NULL) {
1247 xmlSetProp(root, BAD_CAST"ns", ns);
1248 }
1249 }
1250 }
1251
1252 /*
1253 * push it on the stack and register it in the hash table
1254 */
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001255 xmlRelaxNGDocumentPush(ctxt, ret);
1256
1257 /*
1258 * Some preprocessing of the document content
1259 */
1260 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
1261 if (doc == NULL) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001262 ctxt->doc = NULL;
1263 return(NULL);
1264 }
1265
1266 xmlRelaxNGDocumentPop(ctxt);
1267
1268 return(ret);
1269}
1270
1271/************************************************************************
1272 * *
Daniel Veillard6eadf632003-01-23 18:29:16 +00001273 * Error functions *
1274 * *
1275 ************************************************************************/
1276
1277#define VALID_CTXT() \
Daniel Veillard231d7912003-02-09 14:22:17 +00001278 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1279 xmlGenericError(xmlGenericErrorContext, \
Daniel Veillard6eadf632003-01-23 18:29:16 +00001280 "error detected at %s:%d\n", \
1281 __FILE__, __LINE__);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001282
1283#define VALID_ERROR(a) \
Daniel Veillard231d7912003-02-09 14:22:17 +00001284 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001285 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a)
1286#define VALID_ERROR2(a, b) \
1287 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1288 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b)
1289#define VALID_ERROR3(a, b, c) \
1290 if (((ctxt->flags & 1) == 0) || (ctxt->flags & 2)) \
1291 if (ctxt->error != NULL) ctxt->error(ctxt->userData, a, b, c)
Daniel Veillard6eadf632003-01-23 18:29:16 +00001292
Daniel Veillardd2298792003-02-14 16:54:11 +00001293#ifdef DEBUG
Daniel Veillard231d7912003-02-09 14:22:17 +00001294static const char *
1295xmlRelaxNGDefName(xmlRelaxNGDefinePtr def) {
1296 if (def == NULL)
1297 return("none");
1298 switch(def->type) {
1299 case XML_RELAXNG_EMPTY: return("empty");
1300 case XML_RELAXNG_NOT_ALLOWED: return("notAllowed");
1301 case XML_RELAXNG_EXCEPT: return("except");
1302 case XML_RELAXNG_TEXT: return("text");
1303 case XML_RELAXNG_ELEMENT: return("element");
1304 case XML_RELAXNG_DATATYPE: return("datatype");
1305 case XML_RELAXNG_VALUE: return("value");
1306 case XML_RELAXNG_LIST: return("list");
1307 case XML_RELAXNG_ATTRIBUTE: return("attribute");
1308 case XML_RELAXNG_DEF: return("def");
1309 case XML_RELAXNG_REF: return("ref");
1310 case XML_RELAXNG_EXTERNALREF: return("externalRef");
1311 case XML_RELAXNG_PARENTREF: return("parentRef");
1312 case XML_RELAXNG_OPTIONAL: return("optional");
1313 case XML_RELAXNG_ZEROORMORE: return("zeroOrMore");
1314 case XML_RELAXNG_ONEORMORE: return("oneOrMore");
1315 case XML_RELAXNG_CHOICE: return("choice");
1316 case XML_RELAXNG_GROUP: return("group");
1317 case XML_RELAXNG_INTERLEAVE: return("interleave");
1318 case XML_RELAXNG_START: return("start");
1319 }
1320 return("unknown");
1321}
Daniel Veillardd2298792003-02-14 16:54:11 +00001322#endif
1323
Daniel Veillard6eadf632003-01-23 18:29:16 +00001324#if 0
1325/**
1326 * xmlRelaxNGErrorContext:
1327 * @ctxt: the parsing context
1328 * @schema: the schema being built
1329 * @node: the node being processed
1330 * @child: the child being processed
1331 *
1332 * Dump a RelaxNGType structure
1333 */
1334static void
1335xmlRelaxNGErrorContext(xmlRelaxNGParserCtxtPtr ctxt, xmlRelaxNGPtr schema,
1336 xmlNodePtr node, xmlNodePtr child)
1337{
1338 int line = 0;
1339 const xmlChar *file = NULL;
1340 const xmlChar *name = NULL;
1341 const char *type = "error";
1342
1343 if ((ctxt == NULL) || (ctxt->error == NULL))
1344 return;
1345
1346 if (child != NULL)
1347 node = child;
1348
1349 if (node != NULL) {
1350 if ((node->type == XML_DOCUMENT_NODE) ||
1351 (node->type == XML_HTML_DOCUMENT_NODE)) {
1352 xmlDocPtr doc = (xmlDocPtr) node;
1353
1354 file = doc->URL;
1355 } else {
1356 /*
1357 * Try to find contextual informations to report
1358 */
1359 if (node->type == XML_ELEMENT_NODE) {
1360 line = (int) node->content;
1361 } else if ((node->prev != NULL) &&
1362 (node->prev->type == XML_ELEMENT_NODE)) {
1363 line = (int) node->prev->content;
1364 } else if ((node->parent != NULL) &&
1365 (node->parent->type == XML_ELEMENT_NODE)) {
1366 line = (int) node->parent->content;
1367 }
1368 if ((node->doc != NULL) && (node->doc->URL != NULL))
1369 file = node->doc->URL;
1370 if (node->name != NULL)
1371 name = node->name;
1372 }
1373 }
1374
1375 if (ctxt != NULL)
1376 type = "compilation error";
1377 else if (schema != NULL)
1378 type = "runtime error";
1379
1380 if ((file != NULL) && (line != 0) && (name != NULL))
1381 ctxt->error(ctxt->userData, "%s: file %s line %d element %s\n",
1382 type, file, line, name);
1383 else if ((file != NULL) && (name != NULL))
1384 ctxt->error(ctxt->userData, "%s: file %s element %s\n",
1385 type, file, name);
1386 else if ((file != NULL) && (line != 0))
1387 ctxt->error(ctxt->userData, "%s: file %s line %d\n", type, file, line);
1388 else if (file != NULL)
1389 ctxt->error(ctxt->userData, "%s: file %s\n", type, file);
1390 else if (name != NULL)
1391 ctxt->error(ctxt->userData, "%s: element %s\n", type, name);
1392 else
1393 ctxt->error(ctxt->userData, "%s\n", type);
1394}
1395#endif
1396
1397/************************************************************************
1398 * *
1399 * Type library hooks *
1400 * *
1401 ************************************************************************/
Daniel Veillardea3f3982003-01-26 19:45:18 +00001402static xmlChar *xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt,
1403 const xmlChar *str);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001404
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001405/**
1406 * xmlRelaxNGSchemaTypeHave:
1407 * @data: data needed for the library
1408 * @type: the type name
1409 *
1410 * Check if the given type is provided by
1411 * the W3C XMLSchema Datatype library.
1412 *
1413 * Returns 1 if yes, 0 if no and -1 in case of error.
1414 */
1415static int
1416xmlRelaxNGSchemaTypeHave(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001417 const xmlChar *type) {
1418 xmlSchemaTypePtr typ;
1419
1420 if (type == NULL)
1421 return(-1);
1422 typ = xmlSchemaGetPredefinedType(type,
1423 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1424 if (typ == NULL)
1425 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001426 return(1);
1427}
1428
1429/**
1430 * xmlRelaxNGSchemaTypeCheck:
1431 * @data: data needed for the library
1432 * @type: the type name
1433 * @value: the value to check
1434 *
1435 * Check if the given type and value are validated by
1436 * the W3C XMLSchema Datatype library.
1437 *
1438 * Returns 1 if yes, 0 if no and -1 in case of error.
1439 */
1440static int
1441xmlRelaxNGSchemaTypeCheck(void *data ATTRIBUTE_UNUSED,
Daniel Veillardc6e997c2003-01-27 12:35:42 +00001442 const xmlChar *type,
1443 const xmlChar *value) {
1444 xmlSchemaTypePtr typ;
1445 int ret;
1446
1447 /*
1448 * TODO: the type should be cached ab provided back, interface subject
1449 * to changes.
1450 * TODO: handle facets, may require an additional interface and keep
1451 * the value returned from the validation.
1452 */
1453 if ((type == NULL) || (value == NULL))
1454 return(-1);
1455 typ = xmlSchemaGetPredefinedType(type,
1456 BAD_CAST "http://www.w3.org/2001/XMLSchema");
1457 if (typ == NULL)
1458 return(-1);
1459 ret = xmlSchemaValidatePredefinedType(typ, value, NULL);
1460 if (ret == 0)
1461 return(1);
1462 if (ret > 0)
1463 return(0);
1464 return(-1);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001465}
1466
1467/**
1468 * xmlRelaxNGSchemaTypeCompare:
1469 * @data: data needed for the library
1470 * @type: the type name
1471 * @value1: the first value
1472 * @value2: the second value
1473 *
1474 * Compare two values accordingly a type from the W3C XMLSchema
1475 * Datatype library.
1476 *
1477 * Returns 1 if yes, 0 if no and -1 in case of error.
1478 */
1479static int
1480xmlRelaxNGSchemaTypeCompare(void *data ATTRIBUTE_UNUSED,
1481 const xmlChar *type ATTRIBUTE_UNUSED,
1482 const xmlChar *value1 ATTRIBUTE_UNUSED,
1483 const xmlChar *value2 ATTRIBUTE_UNUSED) {
1484 TODO
1485 return(1);
1486}
1487
1488/**
1489 * xmlRelaxNGDefaultTypeHave:
1490 * @data: data needed for the library
1491 * @type: the type name
1492 *
1493 * Check if the given type is provided by
1494 * the default datatype library.
1495 *
1496 * Returns 1 if yes, 0 if no and -1 in case of error.
1497 */
1498static int
1499xmlRelaxNGDefaultTypeHave(void *data ATTRIBUTE_UNUSED, const xmlChar *type) {
1500 if (type == NULL)
1501 return(-1);
1502 if (xmlStrEqual(type, BAD_CAST "string"))
1503 return(1);
1504 if (xmlStrEqual(type, BAD_CAST "token"))
1505 return(1);
1506 return(0);
1507}
1508
1509/**
1510 * xmlRelaxNGDefaultTypeCheck:
1511 * @data: data needed for the library
1512 * @type: the type name
1513 * @value: the value to check
1514 *
1515 * Check if the given type and value are validated by
1516 * the default datatype library.
1517 *
1518 * Returns 1 if yes, 0 if no and -1 in case of error.
1519 */
1520static int
1521xmlRelaxNGDefaultTypeCheck(void *data ATTRIBUTE_UNUSED,
1522 const xmlChar *type ATTRIBUTE_UNUSED,
1523 const xmlChar *value ATTRIBUTE_UNUSED) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001524 if (value == NULL)
1525 return(-1);
1526 if (xmlStrEqual(type, BAD_CAST "string"))
1527 return(1);
1528 if (xmlStrEqual(type, BAD_CAST "token")) {
1529#if 0
1530 const xmlChar *cur = value;
1531
1532 while (*cur != 0) {
1533 if (!IS_BLANK(*cur))
1534 return(1);
1535 cur++;
1536 }
1537#endif
1538 return(1);
1539 }
1540
1541 return(0);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001542}
1543
1544/**
1545 * xmlRelaxNGDefaultTypeCompare:
1546 * @data: data needed for the library
1547 * @type: the type name
1548 * @value1: the first value
1549 * @value2: the second value
1550 *
1551 * Compare two values accordingly a type from the default
1552 * datatype library.
1553 *
1554 * Returns 1 if yes, 0 if no and -1 in case of error.
1555 */
1556static int
1557xmlRelaxNGDefaultTypeCompare(void *data ATTRIBUTE_UNUSED,
1558 const xmlChar *type ATTRIBUTE_UNUSED,
1559 const xmlChar *value1 ATTRIBUTE_UNUSED,
1560 const xmlChar *value2 ATTRIBUTE_UNUSED) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00001561 int ret = -1;
1562
1563 if (xmlStrEqual(type, BAD_CAST "string")) {
1564 ret = xmlStrEqual(value1, value2);
1565 } else if (xmlStrEqual(type, BAD_CAST "token")) {
1566 if (!xmlStrEqual(value1, value2)) {
1567 xmlChar *nval, *nvalue;
1568
1569 /*
1570 * TODO: trivial optimizations are possible by
1571 * computing at compile-time
1572 */
1573 nval = xmlRelaxNGNormalize(NULL, value1);
1574 nvalue = xmlRelaxNGNormalize(NULL, value2);
1575
Daniel Veillardd4310742003-02-18 21:12:46 +00001576 if ((nval == NULL) || (nvalue == NULL))
Daniel Veillardea3f3982003-01-26 19:45:18 +00001577 ret = -1;
Daniel Veillardd4310742003-02-18 21:12:46 +00001578 else if (xmlStrEqual(nval, nvalue))
1579 ret = 1;
1580 else
1581 ret = 0;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001582 if (nval != NULL)
1583 xmlFree(nval);
1584 if (nvalue != NULL)
1585 xmlFree(nvalue);
Daniel Veillardd4310742003-02-18 21:12:46 +00001586 } else
1587 ret = 1;
Daniel Veillardea3f3982003-01-26 19:45:18 +00001588 }
1589 return(ret);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001590}
1591
1592static int xmlRelaxNGTypeInitialized = 0;
1593static xmlHashTablePtr xmlRelaxNGRegisteredTypes = NULL;
1594
1595/**
1596 * xmlRelaxNGFreeTypeLibrary:
1597 * @lib: the type library structure
1598 * @namespace: the URI bound to the library
1599 *
1600 * Free the structure associated to the type library
1601 */
Daniel Veillard6eadf632003-01-23 18:29:16 +00001602static void
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001603xmlRelaxNGFreeTypeLibrary(xmlRelaxNGTypeLibraryPtr lib,
1604 const xmlChar *namespace ATTRIBUTE_UNUSED) {
1605 if (lib == NULL)
1606 return;
1607 if (lib->namespace != NULL)
1608 xmlFree((xmlChar *)lib->namespace);
1609 xmlFree(lib);
1610}
1611
1612/**
1613 * xmlRelaxNGRegisterTypeLibrary:
1614 * @namespace: the URI bound to the library
1615 * @data: data associated to the library
1616 * @have: the provide function
1617 * @check: the checking function
1618 * @comp: the comparison function
1619 *
1620 * Register a new type library
1621 *
1622 * Returns 0 in case of success and -1 in case of error.
1623 */
1624static int
1625xmlRelaxNGRegisterTypeLibrary(const xmlChar *namespace, void *data,
1626 xmlRelaxNGTypeHave have, xmlRelaxNGTypeCheck check,
1627 xmlRelaxNGTypeCompare comp) {
1628 xmlRelaxNGTypeLibraryPtr lib;
1629 int ret;
1630
1631 if ((xmlRelaxNGRegisteredTypes == NULL) || (namespace == NULL) ||
1632 (check == NULL) || (comp == NULL))
1633 return(-1);
1634 if (xmlHashLookup(xmlRelaxNGRegisteredTypes, namespace) != NULL) {
1635 xmlGenericError(xmlGenericErrorContext,
1636 "Relax-NG types library '%s' already registered\n",
1637 namespace);
1638 return(-1);
1639 }
1640 lib = (xmlRelaxNGTypeLibraryPtr) xmlMalloc(sizeof(xmlRelaxNGTypeLibrary));
1641 if (lib == NULL) {
1642 xmlGenericError(xmlGenericErrorContext,
1643 "Relax-NG types library '%s' malloc() failed\n",
1644 namespace);
1645 return (-1);
1646 }
1647 memset(lib, 0, sizeof(xmlRelaxNGTypeLibrary));
1648 lib->namespace = xmlStrdup(namespace);
1649 lib->data = data;
1650 lib->have = have;
1651 lib->comp = comp;
1652 lib->check = check;
1653 ret = xmlHashAddEntry(xmlRelaxNGRegisteredTypes, namespace, lib);
1654 if (ret < 0) {
1655 xmlGenericError(xmlGenericErrorContext,
1656 "Relax-NG types library failed to register '%s'\n",
1657 namespace);
1658 xmlRelaxNGFreeTypeLibrary(lib, namespace);
1659 return(-1);
1660 }
1661 return(0);
1662}
1663
1664/**
1665 * xmlRelaxNGInitTypes:
1666 *
1667 * Initilize the default type libraries.
1668 *
1669 * Returns 0 in case of success and -1 in case of error.
1670 */
1671static int
Daniel Veillard6eadf632003-01-23 18:29:16 +00001672xmlRelaxNGInitTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001673 if (xmlRelaxNGTypeInitialized != 0)
1674 return(0);
1675 xmlRelaxNGRegisteredTypes = xmlHashCreate(10);
1676 if (xmlRelaxNGRegisteredTypes == NULL) {
1677 xmlGenericError(xmlGenericErrorContext,
1678 "Failed to allocate sh table for Relax-NG types\n");
1679 return(-1);
1680 }
1681 xmlRelaxNGRegisterTypeLibrary(
1682 BAD_CAST "http://www.w3.org/2001/XMLSchema-datatypes",
1683 NULL,
1684 xmlRelaxNGSchemaTypeHave,
1685 xmlRelaxNGSchemaTypeCheck,
1686 xmlRelaxNGSchemaTypeCompare);
1687 xmlRelaxNGRegisterTypeLibrary(
1688 xmlRelaxNGNs,
1689 NULL,
1690 xmlRelaxNGDefaultTypeHave,
1691 xmlRelaxNGDefaultTypeCheck,
1692 xmlRelaxNGDefaultTypeCompare);
1693 xmlRelaxNGTypeInitialized = 1;
1694 return(0);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001695}
1696
1697/**
1698 * xmlRelaxNGCleanupTypes:
1699 *
1700 * Cleanup the default Schemas type library associated to RelaxNG
1701 */
1702void
1703xmlRelaxNGCleanupTypes(void) {
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001704 if (xmlRelaxNGTypeInitialized == 0)
1705 return;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001706 xmlSchemaCleanupTypes();
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001707 xmlHashFree(xmlRelaxNGRegisteredTypes, (xmlHashDeallocator)
1708 xmlRelaxNGFreeTypeLibrary);
1709 xmlRelaxNGTypeInitialized = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00001710}
1711
1712/************************************************************************
1713 * *
1714 * Parsing functions *
1715 * *
1716 ************************************************************************/
1717
1718static xmlRelaxNGDefinePtr xmlRelaxNGParseAttribute(
1719 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1720static xmlRelaxNGDefinePtr xmlRelaxNGParseElement(
1721 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
1722static xmlRelaxNGDefinePtr xmlRelaxNGParsePatterns(
Daniel Veillard154877e2003-01-30 12:17:05 +00001723 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes, int group);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00001724static xmlRelaxNGDefinePtr xmlRelaxNGParsePattern(
1725 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00001726static xmlRelaxNGPtr xmlRelaxNGParseDocument(
1727 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00001728static int xmlRelaxNGParseGrammarContent(
1729 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00001730static xmlRelaxNGDefinePtr xmlRelaxNGParseNameClass(
1731 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
1732 xmlRelaxNGDefinePtr def);
Daniel Veillard419a7682003-02-03 23:22:49 +00001733static xmlRelaxNGGrammarPtr xmlRelaxNGParseGrammar(
1734 xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes);
Daniel Veillard17bed982003-02-24 20:11:43 +00001735static int xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
1736 xmlRelaxNGDefinePtr define, xmlNodePtr elem);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001737
1738
1739#define IS_BLANK_NODE(n) \
1740 (((n)->type == XML_TEXT_NODE) && (xmlRelaxNGIsBlank((n)->content)))
1741
1742/**
1743 * xmlRelaxNGIsBlank:
1744 * @str: a string
1745 *
1746 * Check if a string is ignorable c.f. 4.2. Whitespace
1747 *
1748 * Returns 1 if the string is NULL or made of blanks chars, 0 otherwise
1749 */
1750static int
1751xmlRelaxNGIsBlank(xmlChar *str) {
1752 if (str == NULL)
1753 return(1);
1754 while (*str != 0) {
1755 if (!(IS_BLANK(*str))) return(0);
1756 str++;
1757 }
1758 return(1);
1759}
1760
Daniel Veillard6eadf632003-01-23 18:29:16 +00001761/**
1762 * xmlRelaxNGGetDataTypeLibrary:
1763 * @ctxt: a Relax-NG parser context
1764 * @node: the current data or value element
1765 *
1766 * Applies algorithm from 4.3. datatypeLibrary attribute
1767 *
1768 * Returns the datatypeLibary value or NULL if not found
1769 */
1770static xmlChar *
1771xmlRelaxNGGetDataTypeLibrary(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
1772 xmlNodePtr node) {
1773 xmlChar *ret, *escape;
1774
Daniel Veillard6eadf632003-01-23 18:29:16 +00001775 if ((IS_RELAXNG(node, "data")) || (IS_RELAXNG(node, "value"))) {
1776 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1777 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001778 if (ret[0] == 0) {
1779 xmlFree(ret);
1780 return(NULL);
1781 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001782 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
Daniel Veillard6eadf632003-01-23 18:29:16 +00001783 if (escape == NULL) {
1784 return(ret);
1785 }
1786 xmlFree(ret);
1787 return(escape);
1788 }
1789 }
1790 node = node->parent;
1791 while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) {
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001792 ret = xmlGetProp(node, BAD_CAST "datatypeLibrary");
1793 if (ret != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001794 if (ret[0] == 0) {
1795 xmlFree(ret);
1796 return(NULL);
1797 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001798 escape = xmlURIEscapeStr(ret, BAD_CAST ":/#?");
1799 if (escape == NULL) {
1800 return(ret);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001801 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00001802 xmlFree(ret);
1803 return(escape);
Daniel Veillard6eadf632003-01-23 18:29:16 +00001804 }
1805 node = node->parent;
1806 }
1807 return(NULL);
1808}
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001809
1810/**
Daniel Veillardedc91922003-01-26 00:52:04 +00001811 * xmlRelaxNGParseValue:
1812 * @ctxt: a Relax-NG parser context
1813 * @node: the data node.
1814 *
1815 * parse the content of a RelaxNG value node.
1816 *
1817 * Returns the definition pointer or NULL in case of error
1818 */
1819static xmlRelaxNGDefinePtr
1820xmlRelaxNGParseValue(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
1821 xmlRelaxNGDefinePtr def = NULL;
1822 xmlRelaxNGTypeLibraryPtr lib;
1823 xmlChar *type;
1824 xmlChar *library;
1825 int tmp;
1826
1827 def = xmlRelaxNGNewDefine(ctxt, node);
1828 if (def == NULL)
1829 return(NULL);
1830 def->type = XML_RELAXNG_VALUE;
1831
1832 type = xmlGetProp(node, BAD_CAST "type");
1833 if (type != NULL) {
Daniel Veillardd2298792003-02-14 16:54:11 +00001834 xmlRelaxNGNormExtSpace(type);
1835 if (xmlValidateNCName(type, 0)) {
1836 if (ctxt->error != NULL)
1837 ctxt->error(ctxt->userData,
1838 "value type '%s' is not an NCName\n",
1839 type);
1840 ctxt->nbErrors++;
1841 }
Daniel Veillardedc91922003-01-26 00:52:04 +00001842 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1843 if (library == NULL)
1844 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1845
1846 def->name = type;
1847 def->ns = library;
1848
1849 lib = (xmlRelaxNGTypeLibraryPtr)
1850 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1851 if (lib == NULL) {
1852 if (ctxt->error != NULL)
1853 ctxt->error(ctxt->userData,
1854 "Use of unregistered type library '%s'\n",
1855 library);
1856 ctxt->nbErrors++;
1857 def->data = NULL;
1858 } else {
1859 def->data = lib;
1860 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001861 if (ctxt->error != NULL)
1862 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001863 "Internal error with type library '%s': no 'have'\n",
1864 library);
1865 ctxt->nbErrors++;
1866 } else {
1867 tmp = lib->have(lib->data, def->name);
1868 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001869 if (ctxt->error != NULL)
1870 ctxt->error(ctxt->userData,
Daniel Veillardedc91922003-01-26 00:52:04 +00001871 "Error type '%s' is not exported by type library '%s'\n",
1872 def->name, library);
1873 ctxt->nbErrors++;
1874 }
1875 }
1876 }
1877 }
1878 if (node->children == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00001879 def->value = xmlStrdup(BAD_CAST "");
Daniel Veillardedc91922003-01-26 00:52:04 +00001880 } else if ((node->children->type != XML_TEXT_NODE) ||
1881 (node->children->next != NULL)) {
1882 if (ctxt->error != NULL)
1883 ctxt->error(ctxt->userData,
1884 "Expecting a single text value for <value>content\n");
1885 ctxt->nbErrors++;
1886 } else {
1887 def->value = xmlNodeGetContent(node);
1888 if (def->value == NULL) {
1889 if (ctxt->error != NULL)
1890 ctxt->error(ctxt->userData,
1891 "Element <value> has no content\n");
1892 ctxt->nbErrors++;
1893 }
1894 }
1895 /* TODO check ahead of time that the value is okay per the type */
1896 return(def);
1897}
1898
1899/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001900 * xmlRelaxNGParseData:
1901 * @ctxt: a Relax-NG parser context
1902 * @node: the data node.
1903 *
1904 * parse the content of a RelaxNG data node.
1905 *
1906 * Returns the definition pointer or NULL in case of error
1907 */
1908static xmlRelaxNGDefinePtr
1909xmlRelaxNGParseData(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001910 xmlRelaxNGDefinePtr def = NULL, except, last = NULL;
Daniel Veillard8fe98712003-02-19 00:19:14 +00001911 xmlRelaxNGDefinePtr param, lastparam = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001912 xmlRelaxNGTypeLibraryPtr lib;
1913 xmlChar *type;
1914 xmlChar *library;
1915 xmlNodePtr content;
1916 int tmp;
1917
1918 type = xmlGetProp(node, BAD_CAST "type");
1919 if (type == NULL) {
1920 if (ctxt->error != NULL)
1921 ctxt->error(ctxt->userData,
1922 "data has no type\n");
1923 ctxt->nbErrors++;
1924 return(NULL);
1925 }
Daniel Veillardd2298792003-02-14 16:54:11 +00001926 xmlRelaxNGNormExtSpace(type);
1927 if (xmlValidateNCName(type, 0)) {
1928 if (ctxt->error != NULL)
1929 ctxt->error(ctxt->userData,
1930 "data type '%s' is not an NCName\n",
1931 type);
1932 ctxt->nbErrors++;
1933 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001934 library = xmlRelaxNGGetDataTypeLibrary(ctxt, node);
1935 if (library == NULL)
1936 library = xmlStrdup(BAD_CAST "http://relaxng.org/ns/structure/1.0");
1937
1938 def = xmlRelaxNGNewDefine(ctxt, node);
1939 if (def == NULL) {
1940 xmlFree(type);
1941 return(NULL);
1942 }
1943 def->type = XML_RELAXNG_DATATYPE;
1944 def->name = type;
1945 def->ns = library;
1946
1947 lib = (xmlRelaxNGTypeLibraryPtr)
1948 xmlHashLookup(xmlRelaxNGRegisteredTypes, library);
1949 if (lib == NULL) {
1950 if (ctxt->error != NULL)
1951 ctxt->error(ctxt->userData,
1952 "Use of unregistered type library '%s'\n",
1953 library);
1954 ctxt->nbErrors++;
1955 def->data = NULL;
1956 } else {
1957 def->data = lib;
1958 if (lib->have == NULL) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001959 if (ctxt->error != NULL)
1960 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001961 "Internal error with type library '%s': no 'have'\n",
1962 library);
1963 ctxt->nbErrors++;
1964 } else {
1965 tmp = lib->have(lib->data, def->name);
1966 if (tmp != 1) {
Daniel Veillard1703c5f2003-02-10 14:28:44 +00001967 if (ctxt->error != NULL)
1968 ctxt->error(ctxt->userData,
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001969 "Error type '%s' is not exported by type library '%s'\n",
1970 def->name, library);
1971 ctxt->nbErrors++;
1972 }
1973 }
1974 }
1975 content = node->children;
Daniel Veillard416589a2003-02-17 17:25:42 +00001976
1977 /*
1978 * Handle optional params
1979 */
Daniel Veillarddd1655c2003-01-25 18:01:32 +00001980 while (content != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00001981 if (!xmlStrEqual(content->name, BAD_CAST "param"))
1982 break;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00001983 if (xmlStrEqual(library,
1984 BAD_CAST"http://relaxng.org/ns/structure/1.0")) {
1985 if (ctxt->error != NULL)
1986 ctxt->error(ctxt->userData,
1987 "Type library '%s' does not allow type parameters\n",
1988 library);
1989 ctxt->nbErrors++;
1990 content = content->next;
1991 while ((content != NULL) &&
1992 (xmlStrEqual(content->name, BAD_CAST "param")))
1993 content = content->next;
1994 } else {
1995 param = xmlRelaxNGNewDefine(ctxt, node);
1996 if (param != NULL) {
1997 param->type = XML_RELAXNG_PARAM;
1998 param->name = xmlGetProp(content, BAD_CAST "name");
1999 if (param->name == NULL) {
2000 if (ctxt->error != NULL)
2001 ctxt->error(ctxt->userData,
2002 "param has no name\n");
2003 ctxt->nbErrors++;
2004 }
2005 param->value = xmlNodeGetContent(content);
2006 if (lastparam == NULL) {
2007 def->attrs = lastparam = param;
2008 } else {
2009 lastparam->next = param;
2010 lastparam = param;
2011 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00002012 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00002013 content = content->next;
Daniel Veillard8fe98712003-02-19 00:19:14 +00002014 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002015 }
Daniel Veillard416589a2003-02-17 17:25:42 +00002016 /*
2017 * Handle optional except
2018 */
2019 if ((content != NULL) && (xmlStrEqual(content->name, BAD_CAST "except"))) {
2020 xmlNodePtr child;
2021 xmlRelaxNGDefinePtr tmp2, last2 = NULL;
2022
2023 except = xmlRelaxNGNewDefine(ctxt, node);
2024 if (except == NULL) {
2025 return(def);
2026 }
2027 except->type = XML_RELAXNG_EXCEPT;
2028 child = content->children;
2029 if (last == NULL) {
2030 def->content = except;
2031 } else {
2032 last->next = except;
2033 }
2034 if (child == NULL) {
2035 if (ctxt->error != NULL)
2036 ctxt->error(ctxt->userData,
2037 "except has no content\n");
2038 ctxt->nbErrors++;
2039 }
2040 while (child != NULL) {
2041 tmp2 = xmlRelaxNGParsePattern(ctxt, child);
2042 if (tmp2 != NULL) {
2043 if (last2 == NULL) {
2044 except->content = last2 = tmp2;
2045 } else {
2046 last2->next = tmp2;
2047 last2 = tmp2;
2048 }
2049 }
2050 child = child->next;
2051 }
2052 content = content->next;
2053 }
2054 /*
2055 * Check there is no unhandled data
2056 */
2057 if (content != NULL) {
2058 if (ctxt->error != NULL)
2059 ctxt->error(ctxt->userData,
2060 "Element data has unexpected content %s\n", content->name);
2061 ctxt->nbErrors++;
2062 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002063
2064 return(def);
2065}
2066
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002067static const xmlChar *invalidName = BAD_CAST "\1";
2068
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002069/**
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002070 * xmlRelaxNGCompareNameClasses:
2071 * @defs1: the first element/attribute defs
2072 * @defs2: the second element/attribute defs
2073 * @name: the restriction on the name
2074 * @ns: the restriction on the namespace
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002075 *
2076 * Compare the 2 lists of element definitions. The comparison is
2077 * that if both lists do not accept the same QNames, it returns 1
2078 * If the 2 lists can accept the same QName the comparison returns 0
2079 *
2080 * Returns 1 disttinct, 0 if equal
2081 */
2082static int
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002083xmlRelaxNGCompareNameClasses(xmlRelaxNGDefinePtr def1,
2084 xmlRelaxNGDefinePtr def2) {
2085 int ret = 1;
2086 xmlNode node;
2087 xmlNs ns;
2088 xmlRelaxNGValidCtxt ctxt;
2089 ctxt.flags = FLAGS_IGNORABLE;
2090
2091 if ((def1->type == XML_RELAXNG_ELEMENT) ||
2092 (def1->type == XML_RELAXNG_ATTRIBUTE)) {
2093 if (def2->type == XML_RELAXNG_TEXT)
2094 return(1);
2095 if (def1->name != NULL) {
2096 node.name = def1->name;
2097 } else {
2098 node.name = invalidName;
2099 }
2100 node.ns = &ns;
2101 if (def1->ns != NULL) {
2102 if (def1->ns[0] == 0) {
2103 node.ns = NULL;
2104 } else {
2105 ns.href = def1->ns;
2106 }
2107 } else {
2108 ns.href = invalidName;
2109 }
2110 if (xmlRelaxNGElementMatch(&ctxt, def2, &node)) {
2111 if (def1->nameClass != NULL) {
2112 ret = xmlRelaxNGCompareNameClasses(def1->nameClass, def2);
2113 } else {
2114 ret = 0;
2115 }
2116 } else {
2117 ret = 1;
2118 }
2119 } else if (def1->type == XML_RELAXNG_TEXT) {
2120 if (def2->type == XML_RELAXNG_TEXT)
2121 return(0);
2122 return(1);
2123 } else if (def1->type == XML_RELAXNG_EXCEPT) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002124 TODO
2125 ret = 0;
2126 } else {
2127 TODO
2128 ret = 0;
2129 }
2130 if (ret == 0)
2131 return(ret);
2132 if ((def2->type == XML_RELAXNG_ELEMENT) ||
2133 (def2->type == XML_RELAXNG_ATTRIBUTE)) {
2134 if (def2->name != NULL) {
2135 node.name = def2->name;
2136 } else {
2137 node.name = invalidName;
2138 }
2139 node.ns = &ns;
2140 if (def2->ns != NULL) {
2141 if (def2->ns[0] == 0) {
2142 node.ns = NULL;
2143 } else {
2144 ns.href = def2->ns;
2145 }
2146 } else {
2147 ns.href = invalidName;
2148 }
2149 if (xmlRelaxNGElementMatch(&ctxt, def1, &node)) {
2150 if (def2->nameClass != NULL) {
2151 ret = xmlRelaxNGCompareNameClasses(def2->nameClass, def1);
2152 } else {
2153 ret = 0;
2154 }
2155 } else {
2156 ret = 1;
2157 }
2158 } else {
2159 TODO
2160 ret = 0;
2161 }
2162
2163 return(ret);
2164}
2165
2166/**
2167 * xmlRelaxNGCompareElemDefLists:
2168 * @ctxt: a Relax-NG parser context
2169 * @defs1: the first list of element/attribute defs
2170 * @defs2: the second list of element/attribute defs
2171 *
2172 * Compare the 2 lists of element or attribute definitions. The comparison
2173 * is that if both lists do not accept the same QNames, it returns 1
2174 * If the 2 lists can accept the same QName the comparison returns 0
2175 *
2176 * Returns 1 disttinct, 0 if equal
2177 */
2178static int
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002179xmlRelaxNGCompareElemDefLists(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
2180 xmlRelaxNGDefinePtr *def1,
2181 xmlRelaxNGDefinePtr *def2) {
2182 xmlRelaxNGDefinePtr *basedef2 = def2;
2183
Daniel Veillard154877e2003-01-30 12:17:05 +00002184 if ((def1 == NULL) || (def2 == NULL))
2185 return(1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002186 if ((*def1 == NULL) || (*def2 == NULL))
2187 return(1);
2188 while (*def1 != NULL) {
2189 while ((*def2) != NULL) {
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00002190 if (xmlRelaxNGCompareNameClasses(*def1, *def2) == 0)
2191 return(0);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002192 def2++;
2193 }
2194 def2 = basedef2;
2195 def1++;
2196 }
2197 return(1);
2198}
2199
2200/**
2201 * xmlRelaxNGGetElements:
2202 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002203 * @def: the definition definition
2204 * @eora: gather elements (0) or attributes (1)
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002205 *
2206 * Compute the list of top elements a definition can generate
2207 *
2208 * Returns a list of elements or NULL if none was found.
2209 */
2210static xmlRelaxNGDefinePtr *
2211xmlRelaxNGGetElements(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002212 xmlRelaxNGDefinePtr def,
2213 int eora) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002214 xmlRelaxNGDefinePtr *ret = NULL, parent, cur, tmp;
2215 int len = 0;
2216 int max = 0;
2217
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002218 /*
2219 * Don't run that check in case of error. Infinite recursion
2220 * becomes possible.
2221 */
2222 if (ctxt->nbErrors != 0)
2223 return(NULL);
2224
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002225 parent = NULL;
2226 cur = def;
2227 while (cur != NULL) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002228 if (((eora == 0) && ((cur->type == XML_RELAXNG_ELEMENT) ||
2229 (cur->type == XML_RELAXNG_TEXT))) ||
2230 ((eora == 1) && (cur->type == XML_RELAXNG_ATTRIBUTE))) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002231 if (ret == NULL) {
2232 max = 10;
2233 ret = (xmlRelaxNGDefinePtr *)
2234 xmlMalloc((max + 1) * sizeof(xmlRelaxNGDefinePtr));
2235 if (ret == NULL) {
2236 if (ctxt->error != NULL)
2237 ctxt->error(ctxt->userData,
2238 "Out of memory in element search\n");
2239 ctxt->nbErrors++;
2240 return(NULL);
2241 }
2242 } else if (max <= len) {
2243 max *= 2;
2244 ret = xmlRealloc(ret, (max + 1) * sizeof(xmlRelaxNGDefinePtr));
2245 if (ret == NULL) {
2246 if (ctxt->error != NULL)
2247 ctxt->error(ctxt->userData,
2248 "Out of memory in element search\n");
2249 ctxt->nbErrors++;
2250 return(NULL);
2251 }
2252 }
Daniel Veillardb08c9812003-01-28 23:09:49 +00002253 ret[len++] = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002254 ret[len] = NULL;
2255 } else if ((cur->type == XML_RELAXNG_CHOICE) ||
2256 (cur->type == XML_RELAXNG_INTERLEAVE) ||
2257 (cur->type == XML_RELAXNG_GROUP) ||
2258 (cur->type == XML_RELAXNG_ONEORMORE) ||
Daniel Veillardb08c9812003-01-28 23:09:49 +00002259 (cur->type == XML_RELAXNG_ZEROORMORE) ||
2260 (cur->type == XML_RELAXNG_OPTIONAL) ||
2261 (cur->type == XML_RELAXNG_REF) ||
2262 (cur->type == XML_RELAXNG_DEF)) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002263 /*
2264 * Don't go within elements or attributes or string values.
2265 * Just gather the element top list
2266 */
2267 if (cur->content != NULL) {
2268 parent = cur;
2269 cur = cur->content;
2270 tmp = cur;
2271 while (tmp != NULL) {
2272 tmp->parent = parent;
2273 tmp = tmp->next;
2274 }
2275 continue;
2276 }
2277 }
Daniel Veillard154877e2003-01-30 12:17:05 +00002278 if (cur == def)
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002279 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002280 if (cur->next != NULL) {
2281 cur = cur->next;
2282 continue;
2283 }
2284 do {
2285 cur = cur->parent;
2286 if (cur == NULL) break;
2287 if (cur == def) return(ret);
2288 if (cur->next != NULL) {
2289 cur = cur->next;
2290 break;
2291 }
2292 } while (cur != NULL);
2293 }
2294 return(ret);
2295}
2296
2297/**
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002298 * xmlRelaxNGCheckGroupAttrs:
2299 * @ctxt: a Relax-NG parser context
2300 * @def: the group definition
2301 *
2302 * Detects violations of rule 7.3
2303 */
2304static void
2305xmlRelaxNGCheckGroupAttrs(xmlRelaxNGParserCtxtPtr ctxt,
2306 xmlRelaxNGDefinePtr def) {
2307 xmlRelaxNGDefinePtr **list;
2308 xmlRelaxNGDefinePtr cur;
2309 int nbchild = 0, i, j, ret;
2310
2311 if ((def == NULL) ||
2312 ((def->type != XML_RELAXNG_GROUP) &&
2313 (def->type != XML_RELAXNG_ELEMENT)))
2314 return;
2315
2316 /*
2317 * Don't run that check in case of error. Infinite recursion
2318 * becomes possible.
2319 */
2320 if (ctxt->nbErrors != 0)
2321 return;
2322
2323 cur = def->attrs;
2324 while (cur != NULL) {
2325 nbchild++;
2326 cur = cur->next;
2327 }
2328 cur = def->content;
2329 while (cur != NULL) {
2330 nbchild++;
2331 cur = cur->next;
2332 }
2333
2334 list = (xmlRelaxNGDefinePtr **) xmlMalloc(nbchild *
2335 sizeof(xmlRelaxNGDefinePtr *));
2336 if (list == NULL) {
2337 if (ctxt->error != NULL)
2338 ctxt->error(ctxt->userData,
2339 "Out of memory in group computation\n");
2340 ctxt->nbErrors++;
2341 return;
2342 }
2343 i = 0;
2344 cur = def->attrs;
2345 while (cur != NULL) {
2346 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2347 i++;
2348 cur = cur->next;
2349 }
2350 cur = def->content;
2351 while (cur != NULL) {
2352 list[i] = xmlRelaxNGGetElements(ctxt, cur, 1);
2353 i++;
2354 cur = cur->next;
2355 }
2356
2357 for (i = 0;i < nbchild;i++) {
2358 if (list[i] == NULL)
2359 continue;
2360 for (j = 0;j < i;j++) {
2361 if (list[j] == NULL)
2362 continue;
2363 ret = xmlRelaxNGCompareElemDefLists(ctxt, list[i], list[j]);
2364 if (ret == 0) {
2365 if (ctxt->error != NULL)
2366 ctxt->error(ctxt->userData,
2367 "Attributes conflicts in group\n");
2368 ctxt->nbErrors++;
2369 }
2370 }
2371 }
2372 for (i = 0;i < nbchild;i++) {
2373 if (list[i] != NULL)
2374 xmlFree(list[i]);
2375 }
2376 xmlFree(list);
2377}
2378
2379/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002380 * xmlRelaxNGComputeInterleaves:
2381 * @def: the interleave definition
2382 * @ctxt: a Relax-NG parser context
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002383 * @name: the definition name
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002384 *
2385 * A lot of work for preprocessing interleave definitions
2386 * is potentially needed to get a decent execution speed at runtime
2387 * - trying to get a total order on the element nodes generated
2388 * by the interleaves, order the list of interleave definitions
2389 * following that order.
2390 * - if <text/> is used to handle mixed content, it is better to
2391 * flag this in the define and simplify the runtime checking
2392 * algorithm
2393 */
2394static void
2395xmlRelaxNGComputeInterleaves(xmlRelaxNGDefinePtr def,
2396 xmlRelaxNGParserCtxtPtr ctxt,
2397 xmlChar *name ATTRIBUTE_UNUSED) {
2398 xmlRelaxNGDefinePtr cur;
2399
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002400 xmlRelaxNGPartitionPtr partitions = NULL;
2401 xmlRelaxNGInterleaveGroupPtr *groups = NULL;
2402 xmlRelaxNGInterleaveGroupPtr group;
2403 int i,j,ret;
2404 int nbgroups = 0;
2405 int nbchild = 0;
2406
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002407 /*
2408 * Don't run that check in case of error. Infinite recursion
2409 * becomes possible.
2410 */
2411 if (ctxt->nbErrors != 0)
2412 return;
2413
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002414#ifdef DEBUG_INTERLEAVE
2415 xmlGenericError(xmlGenericErrorContext,
2416 "xmlRelaxNGComputeInterleaves(%s)\n",
2417 name);
2418#endif
2419 cur = def->content;
2420 while (cur != NULL) {
2421 nbchild++;
2422 cur = cur->next;
2423 }
2424
2425#ifdef DEBUG_INTERLEAVE
2426 xmlGenericError(xmlGenericErrorContext, " %d child\n", nbchild);
2427#endif
2428 groups = (xmlRelaxNGInterleaveGroupPtr *)
2429 xmlMalloc(nbchild * sizeof(xmlRelaxNGInterleaveGroupPtr));
2430 if (groups == NULL)
2431 goto error;
2432 cur = def->content;
2433 while (cur != NULL) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002434 groups[nbgroups] = (xmlRelaxNGInterleaveGroupPtr)
2435 xmlMalloc(sizeof(xmlRelaxNGInterleaveGroup));
2436 if (groups[nbgroups] == NULL)
2437 goto error;
2438 groups[nbgroups]->rule = cur;
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002439 groups[nbgroups]->defs = xmlRelaxNGGetElements(ctxt, cur, 0);
2440 groups[nbgroups]->attrs = xmlRelaxNGGetElements(ctxt, cur, 1);
Daniel Veillard154877e2003-01-30 12:17:05 +00002441 nbgroups++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002442 cur = cur->next;
2443 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002444#ifdef DEBUG_INTERLEAVE
2445 xmlGenericError(xmlGenericErrorContext, " %d groups\n", nbgroups);
2446#endif
2447
2448 /*
2449 * Let's check that all rules makes a partitions according to 7.4
2450 */
2451 partitions = (xmlRelaxNGPartitionPtr)
2452 xmlMalloc(sizeof(xmlRelaxNGPartition));
2453 if (partitions == NULL)
2454 goto error;
2455 partitions->nbgroups = nbgroups;
2456 for (i = 0;i < nbgroups;i++) {
2457 group = groups[i];
2458 for (j = i+1;j < nbgroups;j++) {
2459 if (groups[j] == NULL)
2460 continue;
2461 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->defs,
2462 groups[j]->defs);
2463 if (ret == 0) {
2464 if (ctxt->error != NULL)
2465 ctxt->error(ctxt->userData,
2466 "Element or text conflicts in interleave\n");
2467 ctxt->nbErrors++;
2468 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002469 ret = xmlRelaxNGCompareElemDefLists(ctxt, group->attrs,
2470 groups[j]->attrs);
2471 if (ret == 0) {
2472 if (ctxt->error != NULL)
2473 ctxt->error(ctxt->userData,
2474 "Attributes conflicts in interleave\n");
2475 ctxt->nbErrors++;
2476 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002477 }
2478 }
2479 partitions->groups = groups;
2480
2481 /*
Daniel Veillard44e1dd02003-02-21 23:23:28 +00002482 * and save the partition list back in the def
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002483 */
2484 def->data = partitions;
2485 return;
2486
2487error:
2488 if (ctxt->error != NULL)
2489 ctxt->error(ctxt->userData,
2490 "Out of memory in interleave computation\n");
2491 ctxt->nbErrors++;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002492 if (groups != NULL) {
2493 for (i = 0;i < nbgroups;i++)
2494 if (groups[i] != NULL) {
2495 if (groups[i]->defs != NULL)
2496 xmlFree(groups[i]->defs);
2497 xmlFree(groups[i]);
2498 }
2499 xmlFree(groups);
2500 }
2501 xmlRelaxNGFreePartition(partitions);
2502}
2503
2504/**
2505 * xmlRelaxNGParseInterleave:
2506 * @ctxt: a Relax-NG parser context
2507 * @node: the data node.
2508 *
2509 * parse the content of a RelaxNG interleave node.
2510 *
2511 * Returns the definition pointer or NULL in case of error
2512 */
2513static xmlRelaxNGDefinePtr
2514xmlRelaxNGParseInterleave(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2515 xmlRelaxNGDefinePtr def = NULL;
2516 xmlRelaxNGDefinePtr last = NULL, cur;
2517 xmlNodePtr child;
2518
2519 def = xmlRelaxNGNewDefine(ctxt, node);
2520 if (def == NULL) {
2521 return(NULL);
2522 }
2523 def->type = XML_RELAXNG_INTERLEAVE;
2524
2525 if (ctxt->interleaves == NULL)
2526 ctxt->interleaves = xmlHashCreate(10);
2527 if (ctxt->interleaves == NULL) {
2528 if (ctxt->error != NULL)
2529 ctxt->error(ctxt->userData,
2530 "Failed to create interleaves hash table\n");
2531 ctxt->nbErrors++;
2532 } else {
2533 char name[32];
2534
2535 snprintf(name, 32, "interleave%d", ctxt->nbInterleaves++);
2536 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST name, def) < 0) {
2537 if (ctxt->error != NULL)
2538 ctxt->error(ctxt->userData,
2539 "Failed to add %s to hash table\n", name);
2540 ctxt->nbErrors++;
2541 }
2542 }
2543 child = node->children;
Daniel Veillardd2298792003-02-14 16:54:11 +00002544 if (child == NULL) {
2545 if (ctxt->error != NULL)
2546 ctxt->error(ctxt->userData, "Element interleave is empty\n");
2547 ctxt->nbErrors++;
2548 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002549 while (child != NULL) {
2550 if (IS_RELAXNG(child, "element")) {
2551 cur = xmlRelaxNGParseElement(ctxt, child);
2552 } else {
2553 cur = xmlRelaxNGParsePattern(ctxt, child);
2554 }
2555 if (cur != NULL) {
2556 cur->parent = def;
2557 if (last == NULL) {
2558 def->content = last = cur;
2559 } else {
2560 last->next = cur;
2561 last = cur;
2562 }
2563 }
2564 child = child->next;
2565 }
2566
2567 return(def);
2568}
Daniel Veillard6eadf632003-01-23 18:29:16 +00002569
2570/**
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002571 * xmlRelaxNGParseInclude:
2572 * @ctxt: a Relax-NG parser context
2573 * @node: the include node
2574 *
2575 * Integrate the content of an include node in the current grammar
2576 *
2577 * Returns 0 in case of success or -1 in case of error
2578 */
2579static int
2580xmlRelaxNGParseInclude(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2581 xmlRelaxNGIncludePtr incl;
2582 xmlNodePtr root;
2583 int ret = 0, tmp;
2584
2585 incl = node->_private;
2586 if (incl == NULL) {
2587 if (ctxt->error != NULL)
2588 ctxt->error(ctxt->userData,
2589 "Include node has no data\n");
2590 ctxt->nbErrors++;
2591 return(-1);
2592 }
2593 root = xmlDocGetRootElement(incl->doc);
2594 if (root == NULL) {
2595 if (ctxt->error != NULL)
2596 ctxt->error(ctxt->userData,
2597 "Include document is empty\n");
2598 ctxt->nbErrors++;
2599 return(-1);
2600 }
2601 if (!xmlStrEqual(root->name, BAD_CAST "grammar")) {
2602 if (ctxt->error != NULL)
2603 ctxt->error(ctxt->userData,
2604 "Include document root is not a grammar\n");
2605 ctxt->nbErrors++;
2606 return(-1);
2607 }
2608
2609 /*
2610 * Merge the definition from both the include and the internal list
2611 */
2612 if (root->children != NULL) {
2613 tmp = xmlRelaxNGParseGrammarContent(ctxt, root->children);
2614 if (tmp != 0)
2615 ret = -1;
2616 }
2617 if (node->children != NULL) {
2618 tmp = xmlRelaxNGParseGrammarContent(ctxt, node->children);
2619 if (tmp != 0)
2620 ret = -1;
2621 }
2622 return(ret);
2623}
2624
2625/**
Daniel Veillard276be4a2003-01-24 01:03:34 +00002626 * xmlRelaxNGParseDefine:
2627 * @ctxt: a Relax-NG parser context
2628 * @node: the define node
2629 *
2630 * parse the content of a RelaxNG define element node.
2631 *
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002632 * Returns 0 in case of success or -1 in case of error
Daniel Veillard276be4a2003-01-24 01:03:34 +00002633 */
2634static int
2635xmlRelaxNGParseDefine(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2636 xmlChar *name;
2637 int ret = 0, tmp;
2638 xmlRelaxNGDefinePtr def;
2639 const xmlChar *olddefine;
2640
2641 name = xmlGetProp(node, BAD_CAST "name");
2642 if (name == NULL) {
2643 if (ctxt->error != NULL)
2644 ctxt->error(ctxt->userData,
2645 "define has no name\n");
2646 ctxt->nbErrors++;
2647 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002648 xmlRelaxNGNormExtSpace(name);
2649 if (xmlValidateNCName(name, 0)) {
2650 if (ctxt->error != NULL)
2651 ctxt->error(ctxt->userData,
2652 "define name '%s' is not an NCName\n",
2653 name);
2654 ctxt->nbErrors++;
2655 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002656 def = xmlRelaxNGNewDefine(ctxt, node);
2657 if (def == NULL) {
2658 xmlFree(name);
2659 return(-1);
2660 }
2661 def->type = XML_RELAXNG_DEF;
2662 def->name = name;
2663 if (node->children == NULL) {
2664 if (ctxt->error != NULL)
2665 ctxt->error(ctxt->userData,
2666 "define has no children\n");
2667 ctxt->nbErrors++;
2668 } else {
2669 olddefine = ctxt->define;
2670 ctxt->define = name;
Daniel Veillard154877e2003-01-30 12:17:05 +00002671 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
Daniel Veillard276be4a2003-01-24 01:03:34 +00002672 ctxt->define = olddefine;
2673 }
2674 if (ctxt->grammar->defs == NULL)
2675 ctxt->grammar->defs = xmlHashCreate(10);
2676 if (ctxt->grammar->defs == NULL) {
2677 if (ctxt->error != NULL)
2678 ctxt->error(ctxt->userData,
2679 "Could not create definition hash\n");
2680 ctxt->nbErrors++;
2681 ret = -1;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002682 } else {
2683 tmp = xmlHashAddEntry(ctxt->grammar->defs, name, def);
2684 if (tmp < 0) {
Daniel Veillard154877e2003-01-30 12:17:05 +00002685 xmlRelaxNGDefinePtr prev;
2686
2687 prev = xmlHashLookup(ctxt->grammar->defs, name);
2688 if (prev == NULL) {
2689 if (ctxt->error != NULL)
2690 ctxt->error(ctxt->userData,
2691 "Internal error on define aggregation of %s\n",
2692 name);
2693 ctxt->nbErrors++;
2694 ret = -1;
Daniel Veillard154877e2003-01-30 12:17:05 +00002695 } else {
2696 while (prev->nextHash != NULL)
2697 prev = prev->nextHash;
2698 prev->nextHash = def;
2699 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00002700 }
2701 }
2702 }
2703 return(ret);
2704}
2705
2706/**
Daniel Veillardfebcca42003-02-16 15:44:18 +00002707 * xmlRelaxNGProcessExternalRef:
2708 * @ctxt: the parser context
2709 * @node: the externlRef node
2710 *
2711 * Process and compile an externlRef node
2712 *
2713 * Returns the xmlRelaxNGDefinePtr or NULL in case of error
2714 */
2715static xmlRelaxNGDefinePtr
2716xmlRelaxNGProcessExternalRef(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2717 xmlRelaxNGDocumentPtr docu;
2718 xmlNodePtr root, tmp;
2719 xmlChar *ns;
Daniel Veillard77648bb2003-02-20 15:03:22 +00002720 int newNs = 0, oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002721 xmlRelaxNGDefinePtr def;
2722
2723 docu = node->_private;
2724 if (docu != NULL) {
2725 def = xmlRelaxNGNewDefine(ctxt, node);
2726 if (def == NULL)
2727 return(NULL);
2728 def->type = XML_RELAXNG_EXTERNALREF;
2729
2730 if (docu->content == NULL) {
2731 /*
2732 * Then do the parsing for good
2733 */
2734 root = xmlDocGetRootElement(docu->doc);
2735 if (root == NULL) {
2736 if (ctxt->error != NULL)
2737 ctxt->error(ctxt->userData,
2738 "xmlRelaxNGParse: %s is empty\n",
2739 ctxt->URL);
2740 ctxt->nbErrors++;
2741 return (NULL);
2742 }
2743 /*
2744 * ns transmission rules
2745 */
2746 ns = xmlGetProp(root, BAD_CAST "ns");
2747 if (ns == NULL) {
2748 tmp = node;
2749 while ((tmp != NULL) &&
2750 (tmp->type == XML_ELEMENT_NODE)) {
2751 ns = xmlGetProp(tmp, BAD_CAST "ns");
2752 if (ns != NULL) {
2753 break;
2754 }
2755 tmp = tmp->parent;
2756 }
2757 if (ns != NULL) {
2758 xmlSetProp(root, BAD_CAST "ns", ns);
2759 newNs = 1;
2760 xmlFree(ns);
2761 }
2762 } else {
2763 xmlFree(ns);
2764 }
2765
2766 /*
2767 * Parsing to get a precompiled schemas.
2768 */
Daniel Veillard77648bb2003-02-20 15:03:22 +00002769 oldflags = ctxt->flags;
2770 ctxt->flags |= XML_RELAXNG_IN_EXTERNALREF;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002771 docu->schema = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard77648bb2003-02-20 15:03:22 +00002772 ctxt->flags = oldflags;
Daniel Veillardfebcca42003-02-16 15:44:18 +00002773 if ((docu->schema != NULL) &&
2774 (docu->schema->topgrammar != NULL)) {
2775 docu->content = docu->schema->topgrammar->start;
2776 }
2777
2778 /*
2779 * the externalRef may be reused in a different ns context
2780 */
2781 if (newNs == 1) {
2782 xmlUnsetProp(root, BAD_CAST "ns");
2783 }
2784 }
2785 def->content = docu->content;
2786 } else {
2787 def = NULL;
2788 }
2789 return(def);
2790}
2791
2792/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00002793 * xmlRelaxNGParsePattern:
2794 * @ctxt: a Relax-NG parser context
2795 * @node: the pattern node.
2796 *
2797 * parse the content of a RelaxNG pattern node.
2798 *
Daniel Veillard276be4a2003-01-24 01:03:34 +00002799 * Returns the definition pointer or NULL in case of error or if no
2800 * pattern is generated.
Daniel Veillard6eadf632003-01-23 18:29:16 +00002801 */
2802static xmlRelaxNGDefinePtr
2803xmlRelaxNGParsePattern(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
2804 xmlRelaxNGDefinePtr def = NULL;
2805
Daniel Veillardd2298792003-02-14 16:54:11 +00002806 if (node == NULL) {
2807 return(NULL);
2808 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002809 if (IS_RELAXNG(node, "element")) {
2810 def = xmlRelaxNGParseElement(ctxt, node);
2811 } else if (IS_RELAXNG(node, "attribute")) {
2812 def = xmlRelaxNGParseAttribute(ctxt, node);
2813 } else if (IS_RELAXNG(node, "empty")) {
2814 def = xmlRelaxNGNewDefine(ctxt, node);
2815 if (def == NULL)
2816 return(NULL);
2817 def->type = XML_RELAXNG_EMPTY;
Daniel Veillardd2298792003-02-14 16:54:11 +00002818 if (node->children != NULL) {
2819 if (ctxt->error != NULL)
2820 ctxt->error(ctxt->userData, "empty: had a child node\n");
2821 ctxt->nbErrors++;
2822 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002823 } else if (IS_RELAXNG(node, "text")) {
2824 def = xmlRelaxNGNewDefine(ctxt, node);
2825 if (def == NULL)
2826 return(NULL);
2827 def->type = XML_RELAXNG_TEXT;
2828 if (node->children != NULL) {
2829 if (ctxt->error != NULL)
2830 ctxt->error(ctxt->userData, "text: had a child node\n");
2831 ctxt->nbErrors++;
2832 }
2833 } else if (IS_RELAXNG(node, "zeroOrMore")) {
2834 def = xmlRelaxNGNewDefine(ctxt, node);
2835 if (def == NULL)
2836 return(NULL);
2837 def->type = XML_RELAXNG_ZEROORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002838 if (node->children == NULL) {
2839 if (ctxt->error != NULL)
2840 ctxt->error(ctxt->userData,
2841 "Element %s is empty\n", node->name);
2842 ctxt->nbErrors++;
2843 } else {
2844 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2845 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002846 } else if (IS_RELAXNG(node, "oneOrMore")) {
2847 def = xmlRelaxNGNewDefine(ctxt, node);
2848 if (def == NULL)
2849 return(NULL);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002850 def->type = XML_RELAXNG_ONEORMORE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002851 if (node->children == NULL) {
2852 if (ctxt->error != NULL)
2853 ctxt->error(ctxt->userData,
2854 "Element %s is empty\n", node->name);
2855 ctxt->nbErrors++;
2856 } else {
2857 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2858 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002859 } else if (IS_RELAXNG(node, "optional")) {
2860 def = xmlRelaxNGNewDefine(ctxt, node);
2861 if (def == NULL)
2862 return(NULL);
2863 def->type = XML_RELAXNG_OPTIONAL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002864 if (node->children == NULL) {
2865 if (ctxt->error != NULL)
2866 ctxt->error(ctxt->userData,
2867 "Element %s is empty\n", node->name);
2868 ctxt->nbErrors++;
2869 } else {
2870 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 1);
2871 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002872 } else if (IS_RELAXNG(node, "choice")) {
2873 def = xmlRelaxNGNewDefine(ctxt, node);
2874 if (def == NULL)
2875 return(NULL);
2876 def->type = XML_RELAXNG_CHOICE;
Daniel Veillardd2298792003-02-14 16:54:11 +00002877 if (node->children == NULL) {
2878 if (ctxt->error != NULL)
2879 ctxt->error(ctxt->userData,
2880 "Element %s is empty\n", node->name);
2881 ctxt->nbErrors++;
2882 } else {
2883 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2884 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002885 } else if (IS_RELAXNG(node, "group")) {
2886 def = xmlRelaxNGNewDefine(ctxt, node);
2887 if (def == NULL)
2888 return(NULL);
2889 def->type = XML_RELAXNG_GROUP;
Daniel Veillardd2298792003-02-14 16:54:11 +00002890 if (node->children == NULL) {
2891 if (ctxt->error != NULL)
2892 ctxt->error(ctxt->userData,
2893 "Element %s is empty\n", node->name);
2894 ctxt->nbErrors++;
2895 } else {
2896 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2897 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002898 } else if (IS_RELAXNG(node, "ref")) {
2899 def = xmlRelaxNGNewDefine(ctxt, node);
2900 if (def == NULL)
2901 return(NULL);
2902 def->type = XML_RELAXNG_REF;
2903 def->name = xmlGetProp(node, BAD_CAST "name");
2904 if (def->name == NULL) {
2905 if (ctxt->error != NULL)
2906 ctxt->error(ctxt->userData,
2907 "ref has no name\n");
2908 ctxt->nbErrors++;
Daniel Veillard276be4a2003-01-24 01:03:34 +00002909 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00002910 xmlRelaxNGNormExtSpace(def->name);
2911 if (xmlValidateNCName(def->name, 0)) {
2912 if (ctxt->error != NULL)
2913 ctxt->error(ctxt->userData,
2914 "ref name '%s' is not an NCName\n",
2915 def->name);
2916 ctxt->nbErrors++;
2917 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002918 }
2919 if (node->children != NULL) {
2920 if (ctxt->error != NULL)
2921 ctxt->error(ctxt->userData,
2922 "ref is not empty\n");
2923 ctxt->nbErrors++;
2924 }
2925 if (ctxt->grammar->refs == NULL)
2926 ctxt->grammar->refs = xmlHashCreate(10);
2927 if (ctxt->grammar->refs == NULL) {
2928 if (ctxt->error != NULL)
2929 ctxt->error(ctxt->userData,
2930 "Could not create references hash\n");
2931 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002932 def = NULL;
2933 } else {
2934 int tmp;
2935
2936 tmp = xmlHashAddEntry(ctxt->grammar->refs, def->name, def);
2937 if (tmp < 0) {
2938 xmlRelaxNGDefinePtr prev;
2939
2940 prev = (xmlRelaxNGDefinePtr)
2941 xmlHashLookup(ctxt->grammar->refs, def->name);
2942 if (prev == NULL) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002943 if (def->name != NULL) {
2944 if (ctxt->error != NULL)
2945 ctxt->error(ctxt->userData,
2946 "Error refs definitions '%s'\n",
2947 def->name);
2948 } else {
2949 if (ctxt->error != NULL)
2950 ctxt->error(ctxt->userData,
2951 "Error refs definitions\n");
2952 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00002953 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00002954 def = NULL;
2955 } else {
2956 def->nextHash = prev->nextHash;
2957 prev->nextHash = def;
2958 }
2959 }
2960 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00002961 } else if (IS_RELAXNG(node, "data")) {
2962 def = xmlRelaxNGParseData(ctxt, node);
Daniel Veillardd2298792003-02-14 16:54:11 +00002963#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00002964 } else if (IS_RELAXNG(node, "define")) {
2965 xmlRelaxNGParseDefine(ctxt, node);
2966 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00002967#endif
Daniel Veillardedc91922003-01-26 00:52:04 +00002968 } else if (IS_RELAXNG(node, "value")) {
2969 def = xmlRelaxNGParseValue(ctxt, node);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00002970 } else if (IS_RELAXNG(node, "list")) {
2971 def = xmlRelaxNGNewDefine(ctxt, node);
2972 if (def == NULL)
2973 return(NULL);
2974 def->type = XML_RELAXNG_LIST;
Daniel Veillardd2298792003-02-14 16:54:11 +00002975 if (node->children == NULL) {
2976 if (ctxt->error != NULL)
2977 ctxt->error(ctxt->userData,
2978 "Element %s is empty\n", node->name);
2979 ctxt->nbErrors++;
2980 } else {
2981 def->content = xmlRelaxNGParsePatterns(ctxt, node->children, 0);
2982 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00002983 } else if (IS_RELAXNG(node, "interleave")) {
2984 def = xmlRelaxNGParseInterleave(ctxt, node);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00002985 } else if (IS_RELAXNG(node, "externalRef")) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00002986 def = xmlRelaxNGProcessExternalRef(ctxt, node);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00002987 } else if (IS_RELAXNG(node, "notAllowed")) {
2988 def = xmlRelaxNGNewDefine(ctxt, node);
2989 if (def == NULL)
2990 return(NULL);
2991 def->type = XML_RELAXNG_NOT_ALLOWED;
2992 if (node->children != NULL) {
2993 if (ctxt->error != NULL)
2994 ctxt->error(ctxt->userData,
2995 "xmlRelaxNGParse: notAllowed element is not empty\n");
2996 ctxt->nbErrors++;
2997 }
Daniel Veillard419a7682003-02-03 23:22:49 +00002998 } else if (IS_RELAXNG(node, "grammar")) {
2999 xmlRelaxNGGrammarPtr grammar, old;
3000 xmlRelaxNGGrammarPtr oldparent;
3001
Daniel Veillardc482e262003-02-26 14:48:48 +00003002#ifdef DEBUG_GRAMMAR
3003 xmlGenericError(xmlGenericErrorContext, "Found <grammar> pattern\n");
3004#endif
3005
Daniel Veillard419a7682003-02-03 23:22:49 +00003006 oldparent = ctxt->parentgrammar;
3007 old = ctxt->grammar;
3008 ctxt->parentgrammar = old;
3009 grammar = xmlRelaxNGParseGrammar(ctxt, node->children);
3010 if (old != NULL) {
3011 ctxt->grammar = old;
3012 ctxt->parentgrammar = oldparent;
Daniel Veillardc482e262003-02-26 14:48:48 +00003013#if 0
Daniel Veillard419a7682003-02-03 23:22:49 +00003014 if (grammar != NULL) {
3015 grammar->next = old->next;
3016 old->next = grammar;
3017 }
Daniel Veillardc482e262003-02-26 14:48:48 +00003018#endif
Daniel Veillard419a7682003-02-03 23:22:49 +00003019 }
3020 if (grammar != NULL)
3021 def = grammar->start;
3022 else
3023 def = NULL;
3024 } else if (IS_RELAXNG(node, "parentRef")) {
3025 if (ctxt->parentgrammar == NULL) {
3026 if (ctxt->error != NULL)
3027 ctxt->error(ctxt->userData,
3028 "Use of parentRef without a parent grammar\n");
3029 ctxt->nbErrors++;
3030 return(NULL);
3031 }
3032 def = xmlRelaxNGNewDefine(ctxt, node);
3033 if (def == NULL)
3034 return(NULL);
3035 def->type = XML_RELAXNG_PARENTREF;
3036 def->name = xmlGetProp(node, BAD_CAST "name");
3037 if (def->name == NULL) {
3038 if (ctxt->error != NULL)
3039 ctxt->error(ctxt->userData,
3040 "parentRef has no name\n");
3041 ctxt->nbErrors++;
Daniel Veillardd2298792003-02-14 16:54:11 +00003042 } else {
3043 xmlRelaxNGNormExtSpace(def->name);
3044 if (xmlValidateNCName(def->name, 0)) {
3045 if (ctxt->error != NULL)
3046 ctxt->error(ctxt->userData,
3047 "parentRef name '%s' is not an NCName\n",
3048 def->name);
3049 ctxt->nbErrors++;
3050 }
Daniel Veillard419a7682003-02-03 23:22:49 +00003051 }
3052 if (node->children != NULL) {
3053 if (ctxt->error != NULL)
3054 ctxt->error(ctxt->userData,
3055 "parentRef is not empty\n");
3056 ctxt->nbErrors++;
3057 }
3058 if (ctxt->parentgrammar->refs == NULL)
3059 ctxt->parentgrammar->refs = xmlHashCreate(10);
3060 if (ctxt->parentgrammar->refs == NULL) {
3061 if (ctxt->error != NULL)
3062 ctxt->error(ctxt->userData,
3063 "Could not create references hash\n");
3064 ctxt->nbErrors++;
3065 def = NULL;
Daniel Veillardd2298792003-02-14 16:54:11 +00003066 } else if (def->name != NULL) {
Daniel Veillard419a7682003-02-03 23:22:49 +00003067 int tmp;
3068
3069 tmp = xmlHashAddEntry(ctxt->parentgrammar->refs, def->name, def);
3070 if (tmp < 0) {
3071 xmlRelaxNGDefinePtr prev;
3072
3073 prev = (xmlRelaxNGDefinePtr)
3074 xmlHashLookup(ctxt->parentgrammar->refs, def->name);
3075 if (prev == NULL) {
3076 if (ctxt->error != NULL)
3077 ctxt->error(ctxt->userData,
3078 "Internal error parentRef definitions '%s'\n",
3079 def->name);
3080 ctxt->nbErrors++;
3081 def = NULL;
3082 } else {
3083 def->nextHash = prev->nextHash;
3084 prev->nextHash = def;
3085 }
3086 }
3087 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003088 } else if (IS_RELAXNG(node, "mixed")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00003089 if (node->children == NULL) {
3090 if (ctxt->error != NULL)
3091 ctxt->error(ctxt->userData,
3092 "Mixed is empty\n");
3093 ctxt->nbErrors++;
3094 def = NULL;
Daniel Veillard416589a2003-02-17 17:25:42 +00003095 } else {
3096 def = xmlRelaxNGParseInterleave(ctxt, node);
3097 if (def != NULL) {
3098 xmlRelaxNGDefinePtr tmp;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003099
3100 if ((def->content != NULL) && (def->content->next != NULL)) {
3101 tmp = xmlRelaxNGNewDefine(ctxt, node);
3102 if (tmp != NULL) {
3103 tmp->type = XML_RELAXNG_GROUP;
3104 tmp->content = def->content;
3105 def->content = tmp;
3106 }
3107 }
3108
Daniel Veillard416589a2003-02-17 17:25:42 +00003109 tmp = xmlRelaxNGNewDefine(ctxt, node);
3110 if (tmp == NULL)
3111 return(def);
3112 tmp->type = XML_RELAXNG_TEXT;
3113 tmp->next = def->content;
3114 def->content = tmp;
3115 }
3116 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003117 } else {
3118 if (ctxt->error != NULL)
3119 ctxt->error(ctxt->userData,
3120 "Unexpected node %s is not a pattern\n",
3121 node->name);
3122 ctxt->nbErrors++;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003123 def = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003124 }
3125 return(def);
3126}
3127
3128/**
3129 * xmlRelaxNGParseAttribute:
3130 * @ctxt: a Relax-NG parser context
3131 * @node: the element node
3132 *
3133 * parse the content of a RelaxNG attribute node.
3134 *
3135 * Returns the definition pointer or NULL in case of error.
3136 */
3137static xmlRelaxNGDefinePtr
3138xmlRelaxNGParseAttribute(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
Daniel Veillardd2298792003-02-14 16:54:11 +00003139 xmlRelaxNGDefinePtr ret, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003140 xmlNodePtr child;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003141 int old_flags;
3142
3143 ret = xmlRelaxNGNewDefine(ctxt, node);
3144 if (ret == NULL)
3145 return(NULL);
3146 ret->type = XML_RELAXNG_ATTRIBUTE;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003147 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003148 child = node->children;
3149 if (child == NULL) {
3150 if (ctxt->error != NULL)
3151 ctxt->error(ctxt->userData,
3152 "xmlRelaxNGParseattribute: attribute has no children\n");
3153 ctxt->nbErrors++;
3154 return(ret);
3155 }
3156 old_flags = ctxt->flags;
3157 ctxt->flags |= XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003158 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3159 if (cur != NULL)
3160 child = child->next;
3161
Daniel Veillardd2298792003-02-14 16:54:11 +00003162 if (child != NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003163 cur = xmlRelaxNGParsePattern(ctxt, child);
3164 if (cur != NULL) {
3165 switch (cur->type) {
3166 case XML_RELAXNG_EMPTY:
3167 case XML_RELAXNG_NOT_ALLOWED:
3168 case XML_RELAXNG_TEXT:
3169 case XML_RELAXNG_ELEMENT:
3170 case XML_RELAXNG_DATATYPE:
3171 case XML_RELAXNG_VALUE:
3172 case XML_RELAXNG_LIST:
3173 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003174 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003175 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003176 case XML_RELAXNG_DEF:
3177 case XML_RELAXNG_ONEORMORE:
3178 case XML_RELAXNG_ZEROORMORE:
3179 case XML_RELAXNG_OPTIONAL:
3180 case XML_RELAXNG_CHOICE:
3181 case XML_RELAXNG_GROUP:
3182 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard77648bb2003-02-20 15:03:22 +00003183 case XML_RELAXNG_ATTRIBUTE:
Daniel Veillardd2298792003-02-14 16:54:11 +00003184 ret->content = cur;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003185 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003186 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003187 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003188 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003189 case XML_RELAXNG_EXCEPT:
Daniel Veillardd2298792003-02-14 16:54:11 +00003190 if (ctxt->error != NULL)
3191 ctxt->error(ctxt->userData,
3192 "attribute has invalid content\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003193 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003194 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003195 case XML_RELAXNG_NOOP:
3196 TODO
3197 if (ctxt->error != NULL)
3198 ctxt->error(ctxt->userData,
3199 "Internal error, noop found\n");
3200 ctxt->nbErrors++;
3201 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003202 }
3203 }
3204 child = child->next;
3205 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003206 if (child != NULL) {
3207 if (ctxt->error != NULL)
3208 ctxt->error(ctxt->userData, "attribute has multiple children\n");
3209 ctxt->nbErrors++;
3210 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003211 ctxt->flags = old_flags;
3212 return(ret);
3213}
3214
3215/**
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003216 * xmlRelaxNGParseExceptNameClass:
3217 * @ctxt: a Relax-NG parser context
3218 * @node: the except node
Daniel Veillard144fae12003-02-03 13:17:57 +00003219 * @attr: 1 if within an attribute, 0 if within an element
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003220 *
3221 * parse the content of a RelaxNG nameClass node.
3222 *
3223 * Returns the definition pointer or NULL in case of error.
3224 */
3225static xmlRelaxNGDefinePtr
Daniel Veillard144fae12003-02-03 13:17:57 +00003226xmlRelaxNGParseExceptNameClass(xmlRelaxNGParserCtxtPtr ctxt,
3227 xmlNodePtr node, int attr) {
3228 xmlRelaxNGDefinePtr ret, cur, last = NULL;
3229 xmlNodePtr child;
3230
Daniel Veillardd2298792003-02-14 16:54:11 +00003231 if (!IS_RELAXNG(node, "except")) {
3232 if (ctxt->error != NULL)
3233 ctxt->error(ctxt->userData,
3234 "Expecting an except node\n");
3235 ctxt->nbErrors++;
Daniel Veillard144fae12003-02-03 13:17:57 +00003236 return(NULL);
Daniel Veillardd2298792003-02-14 16:54:11 +00003237 }
3238 if (node->next != NULL) {
3239 if (ctxt->error != NULL)
3240 ctxt->error(ctxt->userData,
3241 "exceptNameClass allows only a single except node\n");
3242 ctxt->nbErrors++;
3243 }
Daniel Veillard144fae12003-02-03 13:17:57 +00003244 if (node->children == NULL) {
3245 if (ctxt->error != NULL)
3246 ctxt->error(ctxt->userData,
3247 "except has no content\n");
3248 ctxt->nbErrors++;
3249 return(NULL);
3250 }
3251
3252 ret = xmlRelaxNGNewDefine(ctxt, node);
3253 if (ret == NULL)
3254 return(NULL);
3255 ret->type = XML_RELAXNG_EXCEPT;
3256 child = node->children;
3257 while (child != NULL) {
3258 cur = xmlRelaxNGNewDefine(ctxt, child);
3259 if (cur == NULL)
3260 break;
3261 if (attr)
3262 cur->type = XML_RELAXNG_ATTRIBUTE;
3263 else
3264 cur->type = XML_RELAXNG_ELEMENT;
3265
Daniel Veillard419a7682003-02-03 23:22:49 +00003266 if (xmlRelaxNGParseNameClass(ctxt, child, cur) != NULL) {
Daniel Veillard144fae12003-02-03 13:17:57 +00003267 if (last == NULL) {
3268 ret->content = cur;
3269 } else {
3270 last->next = cur;
3271 }
3272 last = cur;
3273 }
3274 child = child->next;
3275 }
3276
3277 return(ret);
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003278}
3279
3280/**
3281 * xmlRelaxNGParseNameClass:
3282 * @ctxt: a Relax-NG parser context
3283 * @node: the nameClass node
3284 * @def: the current definition
3285 *
3286 * parse the content of a RelaxNG nameClass node.
3287 *
3288 * Returns the definition pointer or NULL in case of error.
3289 */
3290static xmlRelaxNGDefinePtr
3291xmlRelaxNGParseNameClass(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node,
3292 xmlRelaxNGDefinePtr def) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003293 xmlRelaxNGDefinePtr ret, tmp;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003294 xmlChar *val;
3295
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003296 ret = def;
3297 if ((IS_RELAXNG(node, "name")) || (IS_RELAXNG(node, "anyName")) ||
3298 (IS_RELAXNG(node, "nsName"))) {
3299 if ((def->type != XML_RELAXNG_ELEMENT) &&
3300 (def->type != XML_RELAXNG_ATTRIBUTE)) {
3301 ret = xmlRelaxNGNewDefine(ctxt, node);
3302 if (ret == NULL)
3303 return(NULL);
3304 ret->parent = def;
3305 if (ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE)
3306 ret->type = XML_RELAXNG_ATTRIBUTE;
3307 else
3308 ret->type = XML_RELAXNG_ELEMENT;
3309 }
3310 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003311 if (IS_RELAXNG(node, "name")) {
3312 val = xmlNodeGetContent(node);
Daniel Veillardd2298792003-02-14 16:54:11 +00003313 xmlRelaxNGNormExtSpace(val);
3314 if (xmlValidateNCName(val, 0)) {
3315 if (ctxt->error != NULL) {
3316 if (node->parent != NULL)
3317 ctxt->error(ctxt->userData,
3318 "Element %s name '%s' is not an NCName\n",
3319 node->parent->name, val);
3320 else
3321 ctxt->error(ctxt->userData,
3322 "name '%s' is not an NCName\n",
3323 val);
3324 }
3325 ctxt->nbErrors++;
3326 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003327 ret->name = val;
3328 val = xmlGetProp(node, BAD_CAST "ns");
3329 ret->ns = val;
Daniel Veillard416589a2003-02-17 17:25:42 +00003330 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3331 (val != NULL) &&
3332 (xmlStrEqual(val, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3333 ctxt->error(ctxt->userData,
3334 "Attribute with namespace '%s' is not allowed\n",
3335 val);
3336 ctxt->nbErrors++;
3337 }
3338 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3339 (val != NULL) &&
3340 (val[0] == 0) &&
3341 (xmlStrEqual(ret->name, BAD_CAST "xmlns"))) {
3342 ctxt->error(ctxt->userData,
3343 "Attribute with QName 'xmlns' is not allowed\n",
3344 val);
3345 ctxt->nbErrors++;
3346 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003347 } else if (IS_RELAXNG(node, "anyName")) {
3348 ret->name = NULL;
3349 ret->ns = NULL;
3350 if (node->children != NULL) {
3351 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003352 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3353 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003354 }
3355 } else if (IS_RELAXNG(node, "nsName")) {
3356 ret->name = NULL;
3357 ret->ns = xmlGetProp(node, BAD_CAST "ns");
3358 if (ret->ns == NULL) {
3359 if (ctxt->error != NULL)
3360 ctxt->error(ctxt->userData,
3361 "nsName has no ns attribute\n");
3362 ctxt->nbErrors++;
3363 }
Daniel Veillard416589a2003-02-17 17:25:42 +00003364 if ((ctxt->flags & XML_RELAXNG_IN_ATTRIBUTE) &&
3365 (ret->ns != NULL) &&
3366 (xmlStrEqual(ret->ns, BAD_CAST "http://www.w3.org/2000/xmlns"))) {
3367 ctxt->error(ctxt->userData,
3368 "Attribute with namespace '%s' is not allowed\n",
3369 ret->ns);
3370 ctxt->nbErrors++;
3371 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003372 if (node->children != NULL) {
3373 ret->nameClass =
Daniel Veillard144fae12003-02-03 13:17:57 +00003374 xmlRelaxNGParseExceptNameClass(ctxt, node->children,
3375 (def->type == XML_RELAXNG_ATTRIBUTE));
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003376 }
3377 } else if (IS_RELAXNG(node, "choice")) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003378 xmlNodePtr child;
3379 xmlRelaxNGDefinePtr last = NULL;
3380
Daniel Veillardd4310742003-02-18 21:12:46 +00003381 ret = xmlRelaxNGNewDefine(ctxt, node);
3382 if (ret == NULL)
3383 return(NULL);
3384 ret->parent = def;
3385 ret->type = XML_RELAXNG_CHOICE;
3386
Daniel Veillardd2298792003-02-14 16:54:11 +00003387 if (node->children == NULL) {
3388 if (ctxt->error != NULL)
3389 ctxt->error(ctxt->userData,
3390 "Element choice is empty\n");
3391 ctxt->nbErrors++;
3392 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003393
3394 child = node->children;
3395 while (child != NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00003396 tmp = xmlRelaxNGParseNameClass(ctxt, child, ret);
Daniel Veillardd2298792003-02-14 16:54:11 +00003397 if (tmp != NULL) {
3398 if (last == NULL) {
3399 last = ret->nameClass = tmp;
3400 } else {
3401 last->next = tmp;
3402 last = tmp;
3403 }
3404 }
3405 child = child->next;
3406 }
3407 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003408 } else {
3409 if (ctxt->error != NULL)
3410 ctxt->error(ctxt->userData,
3411 "expecting name, anyName, nsName or choice : got %s\n",
3412 node->name);
3413 ctxt->nbErrors++;
3414 return(NULL);
3415 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00003416 if (ret != def) {
3417 if (def->nameClass == NULL) {
3418 def->nameClass = ret;
3419 } else {
3420 tmp = def->nameClass;
3421 while (tmp->next != NULL) {
3422 tmp = tmp->next;
3423 }
3424 tmp->next = ret;
3425 }
3426 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003427 return(ret);
3428}
3429
3430/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00003431 * xmlRelaxNGParseElement:
3432 * @ctxt: a Relax-NG parser context
3433 * @node: the element node
3434 *
3435 * parse the content of a RelaxNG element node.
3436 *
3437 * Returns the definition pointer or NULL in case of error.
3438 */
3439static xmlRelaxNGDefinePtr
3440xmlRelaxNGParseElement(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
3441 xmlRelaxNGDefinePtr ret, cur, last;
3442 xmlNodePtr child;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003443 const xmlChar *olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003444
3445 ret = xmlRelaxNGNewDefine(ctxt, node);
3446 if (ret == NULL)
3447 return(NULL);
3448 ret->type = XML_RELAXNG_ELEMENT;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003449 ret->parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003450 child = node->children;
3451 if (child == NULL) {
3452 if (ctxt->error != NULL)
3453 ctxt->error(ctxt->userData,
3454 "xmlRelaxNGParseElement: element has no children\n");
3455 ctxt->nbErrors++;
3456 return(ret);
3457 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00003458 cur = xmlRelaxNGParseNameClass(ctxt, child, ret);
3459 if (cur != NULL)
3460 child = child->next;
3461
Daniel Veillard6eadf632003-01-23 18:29:16 +00003462 if (child == NULL) {
3463 if (ctxt->error != NULL)
3464 ctxt->error(ctxt->userData,
3465 "xmlRelaxNGParseElement: element has no content\n");
3466 ctxt->nbErrors++;
3467 return(ret);
3468 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003469 olddefine = ctxt->define;
3470 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003471 last = NULL;
3472 while (child != NULL) {
3473 cur = xmlRelaxNGParsePattern(ctxt, child);
3474 if (cur != NULL) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003475 cur->parent = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003476 switch (cur->type) {
3477 case XML_RELAXNG_EMPTY:
3478 case XML_RELAXNG_NOT_ALLOWED:
3479 case XML_RELAXNG_TEXT:
3480 case XML_RELAXNG_ELEMENT:
3481 case XML_RELAXNG_DATATYPE:
3482 case XML_RELAXNG_VALUE:
3483 case XML_RELAXNG_LIST:
3484 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00003485 case XML_RELAXNG_PARENTREF:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003486 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00003487 case XML_RELAXNG_DEF:
3488 case XML_RELAXNG_ZEROORMORE:
3489 case XML_RELAXNG_ONEORMORE:
3490 case XML_RELAXNG_OPTIONAL:
3491 case XML_RELAXNG_CHOICE:
3492 case XML_RELAXNG_GROUP:
3493 case XML_RELAXNG_INTERLEAVE:
3494 if (last == NULL) {
3495 ret->content = last = cur;
3496 } else {
3497 if ((last->type == XML_RELAXNG_ELEMENT) &&
3498 (ret->content == last)) {
3499 ret->content = xmlRelaxNGNewDefine(ctxt, node);
3500 if (ret->content != NULL) {
3501 ret->content->type = XML_RELAXNG_GROUP;
3502 ret->content->content = last;
3503 } else {
3504 ret->content = last;
3505 }
3506 }
3507 last->next = cur;
3508 last = cur;
3509 }
3510 break;
3511 case XML_RELAXNG_ATTRIBUTE:
3512 cur->next = ret->attrs;
3513 ret->attrs = cur;
3514 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003515 case XML_RELAXNG_START:
Daniel Veillard8fe98712003-02-19 00:19:14 +00003516 case XML_RELAXNG_PARAM:
Daniel Veillard144fae12003-02-03 13:17:57 +00003517 case XML_RELAXNG_EXCEPT:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003518 TODO
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003519 ctxt->nbErrors++;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00003520 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00003521 case XML_RELAXNG_NOOP:
3522 TODO
3523 if (ctxt->error != NULL)
3524 ctxt->error(ctxt->userData,
3525 "Internal error, noop found\n");
3526 ctxt->nbErrors++;
3527 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003528 }
3529 }
3530 child = child->next;
3531 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00003532 ctxt->define = olddefine;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003533 return(ret);
3534}
3535
3536/**
3537 * xmlRelaxNGParsePatterns:
3538 * @ctxt: a Relax-NG parser context
3539 * @nodes: list of nodes
Daniel Veillard154877e2003-01-30 12:17:05 +00003540 * @group: use an implicit <group> for elements
Daniel Veillard6eadf632003-01-23 18:29:16 +00003541 *
3542 * parse the content of a RelaxNG start node.
3543 *
3544 * Returns the definition pointer or NULL in case of error.
3545 */
3546static xmlRelaxNGDefinePtr
Daniel Veillard154877e2003-01-30 12:17:05 +00003547xmlRelaxNGParsePatterns(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes,
3548 int group) {
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003549 xmlRelaxNGDefinePtr def = NULL, last = NULL, cur, parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003550
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003551 parent = ctxt->def;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003552 while (nodes != NULL) {
3553 if (IS_RELAXNG(nodes, "element")) {
3554 cur = xmlRelaxNGParseElement(ctxt, nodes);
3555 if (def == NULL) {
3556 def = last = cur;
3557 } else {
Daniel Veillard154877e2003-01-30 12:17:05 +00003558 if ((group == 1) && (def->type == XML_RELAXNG_ELEMENT) &&
3559 (def == last)) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003560 def = xmlRelaxNGNewDefine(ctxt, nodes);
3561 def->type = XML_RELAXNG_GROUP;
3562 def->content = last;
3563 }
3564 last->next = cur;
3565 last = cur;
3566 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00003567 cur->parent = parent;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003568 } else {
3569 cur = xmlRelaxNGParsePattern(ctxt, nodes);
Daniel Veillard419a7682003-02-03 23:22:49 +00003570 if (cur != NULL) {
3571 if (def == NULL) {
3572 def = last = cur;
3573 } else {
3574 last->next = cur;
3575 last = cur;
3576 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003577 }
3578 }
3579 nodes = nodes->next;
3580 }
3581 return(def);
3582}
3583
3584/**
3585 * xmlRelaxNGParseStart:
3586 * @ctxt: a Relax-NG parser context
3587 * @nodes: start children nodes
3588 *
3589 * parse the content of a RelaxNG start node.
3590 *
3591 * Returns 0 in case of success, -1 in case of error
3592 */
3593static int
3594xmlRelaxNGParseStart(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
3595 int ret = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003596 xmlRelaxNGDefinePtr def = NULL, last;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003597
Daniel Veillardd2298792003-02-14 16:54:11 +00003598 if (nodes == NULL) {
3599 if (ctxt->error != NULL)
3600 ctxt->error(ctxt->userData,
3601 "start has no children\n");
3602 ctxt->nbErrors++;
3603 return(-1);
3604 }
3605 if (IS_RELAXNG(nodes, "empty")) {
3606 def = xmlRelaxNGNewDefine(ctxt, nodes);
3607 if (def == NULL)
3608 return(-1);
3609 def->type = XML_RELAXNG_EMPTY;
3610 if (nodes->children != NULL) {
3611 if (ctxt->error != NULL)
3612 ctxt->error(ctxt->userData, "element empty is not empty\n");
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003613 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003614 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003615 } else if (IS_RELAXNG(nodes, "notAllowed")) {
3616 def = xmlRelaxNGNewDefine(ctxt, nodes);
3617 if (def == NULL)
3618 return(-1);
3619 def->type = XML_RELAXNG_NOT_ALLOWED;
3620 if (nodes->children != NULL) {
3621 if (ctxt->error != NULL)
3622 ctxt->error(ctxt->userData,
3623 "element notAllowed is not empty\n");
3624 ctxt->nbErrors++;
3625 }
Daniel Veillardd2298792003-02-14 16:54:11 +00003626 } else {
3627 def = xmlRelaxNGParsePatterns(ctxt, nodes, 1);
Daniel Veillard2df2de22003-02-17 23:34:33 +00003628 }
3629 if (ctxt->grammar->start != NULL) {
3630 last = ctxt->grammar->start;
3631 while (last->next != NULL)
3632 last = last->next;
3633 last->next = def;
3634 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00003635 ctxt->grammar->start = def;
3636 }
3637 nodes = nodes->next;
3638 if (nodes != NULL) {
3639 if (ctxt->error != NULL)
3640 ctxt->error(ctxt->userData,
3641 "start more than one children\n");
3642 ctxt->nbErrors++;
3643 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003644 }
3645 return(ret);
3646}
3647
3648/**
3649 * xmlRelaxNGParseGrammarContent:
3650 * @ctxt: a Relax-NG parser context
3651 * @nodes: grammar children nodes
3652 *
3653 * parse the content of a RelaxNG grammar node.
3654 *
3655 * Returns 0 in case of success, -1 in case of error
3656 */
3657static int
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003658xmlRelaxNGParseGrammarContent(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003659{
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003660 int ret = 0, tmp;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003661
3662 if (nodes == NULL) {
3663 if (ctxt->error != NULL)
3664 ctxt->error(ctxt->userData,
3665 "grammar has no children\n");
3666 ctxt->nbErrors++;
3667 return(-1);
3668 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003669 while (nodes != NULL) {
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003670 if (IS_RELAXNG(nodes, "start")) {
3671 if (nodes->children == NULL) {
3672 if (ctxt->error != NULL)
3673 ctxt->error(ctxt->userData,
Daniel Veillardd2298792003-02-14 16:54:11 +00003674 "start has no children\n");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003675 ctxt->nbErrors++;
3676 } else {
3677 tmp = xmlRelaxNGParseStart(ctxt, nodes->children);
3678 if (tmp != 0)
3679 ret = -1;
3680 }
3681 } else if (IS_RELAXNG(nodes, "define")) {
3682 tmp = xmlRelaxNGParseDefine(ctxt, nodes);
3683 if (tmp != 0)
3684 ret = -1;
3685 } else if (IS_RELAXNG(nodes, "include")) {
3686 tmp = xmlRelaxNGParseInclude(ctxt, nodes);
3687 if (tmp != 0)
3688 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003689 } else {
3690 if (ctxt->error != NULL)
3691 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00003692 "grammar has unexpected child %s\n", nodes->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00003693 ctxt->nbErrors++;
3694 ret = -1;
3695 }
3696 nodes = nodes->next;
3697 }
3698 return (ret);
3699}
3700
3701/**
3702 * xmlRelaxNGCheckReference:
3703 * @ref: the ref
3704 * @ctxt: a Relax-NG parser context
3705 * @name: the name associated to the defines
3706 *
3707 * Applies the 4.17. combine attribute rule for all the define
3708 * element of a given grammar using the same name.
3709 */
3710static void
3711xmlRelaxNGCheckReference(xmlRelaxNGDefinePtr ref,
3712 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3713 xmlRelaxNGGrammarPtr grammar;
Daniel Veillard276be4a2003-01-24 01:03:34 +00003714 xmlRelaxNGDefinePtr def, cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003715
3716 grammar = ctxt->grammar;
3717 if (grammar == NULL) {
3718 if (ctxt->error != NULL)
3719 ctxt->error(ctxt->userData,
3720 "Internal error: no grammar in CheckReference %s\n",
3721 name);
3722 ctxt->nbErrors++;
3723 return;
3724 }
3725 if (ref->content != NULL) {
3726 if (ctxt->error != NULL)
3727 ctxt->error(ctxt->userData,
3728 "Internal error: reference has content in CheckReference %s\n",
3729 name);
3730 ctxt->nbErrors++;
3731 return;
3732 }
3733 if (grammar->defs != NULL) {
3734 def = xmlHashLookup(grammar->defs, name);
3735 if (def != NULL) {
Daniel Veillard276be4a2003-01-24 01:03:34 +00003736 cur = ref;
3737 while (cur != NULL) {
3738 cur->content = def;
3739 cur = cur->nextHash;
3740 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003741 } else {
Daniel Veillardd4310742003-02-18 21:12:46 +00003742 if (ctxt->error != NULL)
3743 ctxt->error(ctxt->userData,
3744 "Reference %s has no matching definition\n",
3745 name);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00003746 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003747 }
Daniel Veillardd4310742003-02-18 21:12:46 +00003748 } else {
3749 if (ctxt->error != NULL)
3750 ctxt->error(ctxt->userData,
3751 "Reference %s has no matching definition\n",
3752 name);
3753 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003754 }
3755 /*
3756 * TODO: make a closure and verify there is no loop !
3757 */
3758}
3759
3760/**
3761 * xmlRelaxNGCheckCombine:
3762 * @define: the define(s) list
3763 * @ctxt: a Relax-NG parser context
3764 * @name: the name associated to the defines
3765 *
3766 * Applies the 4.17. combine attribute rule for all the define
3767 * element of a given grammar using the same name.
3768 */
3769static void
3770xmlRelaxNGCheckCombine(xmlRelaxNGDefinePtr define,
3771 xmlRelaxNGParserCtxtPtr ctxt, const xmlChar *name) {
3772 xmlChar *combine;
3773 int choiceOrInterleave = -1;
3774 int missing = 0;
3775 xmlRelaxNGDefinePtr cur, last, tmp, tmp2;
3776
3777 if (define->nextHash == NULL)
3778 return;
3779 cur = define;
3780 while (cur != NULL) {
3781 combine = xmlGetProp(cur->node, BAD_CAST "combine");
3782 if (combine != NULL) {
3783 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3784 if (choiceOrInterleave == -1)
3785 choiceOrInterleave = 1;
3786 else if (choiceOrInterleave == 0) {
3787 if (ctxt->error != NULL)
3788 ctxt->error(ctxt->userData,
3789 "Defines for %s use both 'choice' and 'interleave'\n",
3790 name);
3791 ctxt->nbErrors++;
3792 }
Daniel Veillard154877e2003-01-30 12:17:05 +00003793 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003794 if (choiceOrInterleave == -1)
3795 choiceOrInterleave = 0;
3796 else if (choiceOrInterleave == 1) {
3797 if (ctxt->error != NULL)
3798 ctxt->error(ctxt->userData,
3799 "Defines for %s use both 'choice' and 'interleave'\n",
3800 name);
3801 ctxt->nbErrors++;
3802 }
3803 } else {
3804 if (ctxt->error != NULL)
3805 ctxt->error(ctxt->userData,
3806 "Defines for %s use unknown combine value '%s''\n",
3807 name, combine);
3808 ctxt->nbErrors++;
3809 }
3810 xmlFree(combine);
3811 } else {
3812 if (missing == 0)
3813 missing = 1;
3814 else {
3815 if (ctxt->error != NULL)
3816 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00003817 "Some defines for %s needs the combine attribute\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00003818 name);
3819 ctxt->nbErrors++;
3820 }
3821 }
3822
3823 cur = cur->nextHash;
3824 }
3825#ifdef DEBUG
3826 xmlGenericError(xmlGenericErrorContext,
3827 "xmlRelaxNGCheckCombine(): merging %s defines: %d\n",
3828 name, choiceOrInterleave);
3829#endif
3830 if (choiceOrInterleave == -1)
3831 choiceOrInterleave = 0;
3832 cur = xmlRelaxNGNewDefine(ctxt, define->node);
3833 if (cur == NULL)
3834 return;
3835 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003836 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard154877e2003-01-30 12:17:05 +00003837 else
3838 cur->type = XML_RELAXNG_CHOICE;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003839 tmp = define;
3840 last = NULL;
3841 while (tmp != NULL) {
3842 if (tmp->content != NULL) {
3843 if (tmp->content->next != NULL) {
3844 /*
3845 * we need first to create a wrapper.
3846 */
3847 tmp2 = xmlRelaxNGNewDefine(ctxt, tmp->content->node);
3848 if (tmp2 == NULL)
3849 break;
3850 tmp2->type = XML_RELAXNG_GROUP;
3851 tmp2->content = tmp->content;
3852 } else {
3853 tmp2 = tmp->content;
3854 }
3855 if (last == NULL) {
3856 cur->content = tmp2;
3857 } else {
3858 last->next = tmp2;
3859 }
3860 last = tmp2;
3861 tmp->content = NULL;
3862 }
3863 tmp = tmp->nextHash;
3864 }
3865 define->content = cur;
Daniel Veillard154877e2003-01-30 12:17:05 +00003866 if (choiceOrInterleave == 0) {
3867 if (ctxt->interleaves == NULL)
3868 ctxt->interleaves = xmlHashCreate(10);
3869 if (ctxt->interleaves == NULL) {
3870 if (ctxt->error != NULL)
3871 ctxt->error(ctxt->userData,
3872 "Failed to create interleaves hash table\n");
3873 ctxt->nbErrors++;
3874 } else {
3875 char tmpname[32];
3876
3877 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3878 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3879 if (ctxt->error != NULL)
3880 ctxt->error(ctxt->userData,
3881 "Failed to add %s to hash table\n", tmpname);
3882 ctxt->nbErrors++;
3883 }
3884 }
3885 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003886}
3887
3888/**
3889 * xmlRelaxNGCombineStart:
3890 * @ctxt: a Relax-NG parser context
3891 * @grammar: the grammar
3892 *
3893 * Applies the 4.17. combine rule for all the start
3894 * element of a given grammar.
3895 */
3896static void
3897xmlRelaxNGCombineStart(xmlRelaxNGParserCtxtPtr ctxt,
3898 xmlRelaxNGGrammarPtr grammar) {
3899 xmlRelaxNGDefinePtr starts;
3900 xmlChar *combine;
3901 int choiceOrInterleave = -1;
3902 int missing = 0;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003903 xmlRelaxNGDefinePtr cur;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003904
Daniel Veillard2df2de22003-02-17 23:34:33 +00003905 starts = grammar->start;
3906 if ((starts == NULL) || (starts->next == NULL))
Daniel Veillard6eadf632003-01-23 18:29:16 +00003907 return;
3908 cur = starts;
3909 while (cur != NULL) {
Daniel Veillard2df2de22003-02-17 23:34:33 +00003910 if ((cur->node == NULL) || (cur->node->parent == NULL) ||
3911 (!xmlStrEqual(cur->node->parent->name, BAD_CAST "start"))) {
3912 combine = NULL;
3913 if (ctxt->error != NULL)
3914 ctxt->error(ctxt->userData,
3915 "Internal error: start element not found\n");
3916 ctxt->nbErrors++;
3917 } else {
3918 combine = xmlGetProp(cur->node->parent, BAD_CAST "combine");
3919 }
3920
Daniel Veillard6eadf632003-01-23 18:29:16 +00003921 if (combine != NULL) {
3922 if (xmlStrEqual(combine, BAD_CAST "choice")) {
3923 if (choiceOrInterleave == -1)
3924 choiceOrInterleave = 1;
3925 else if (choiceOrInterleave == 0) {
3926 if (ctxt->error != NULL)
3927 ctxt->error(ctxt->userData,
3928 "<start> use both 'choice' and 'interleave'\n");
3929 ctxt->nbErrors++;
3930 }
Daniel Veillard2df2de22003-02-17 23:34:33 +00003931 } else if (xmlStrEqual(combine, BAD_CAST "interleave")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00003932 if (choiceOrInterleave == -1)
3933 choiceOrInterleave = 0;
3934 else if (choiceOrInterleave == 1) {
3935 if (ctxt->error != NULL)
3936 ctxt->error(ctxt->userData,
3937 "<start> use both 'choice' and 'interleave'\n");
3938 ctxt->nbErrors++;
3939 }
3940 } else {
3941 if (ctxt->error != NULL)
3942 ctxt->error(ctxt->userData,
3943 "<start> uses unknown combine value '%s''\n", combine);
3944 ctxt->nbErrors++;
3945 }
3946 xmlFree(combine);
3947 } else {
3948 if (missing == 0)
3949 missing = 1;
3950 else {
3951 if (ctxt->error != NULL)
3952 ctxt->error(ctxt->userData,
Daniel Veillardc482e262003-02-26 14:48:48 +00003953 "Some <start> element miss the combine attribute\n");
Daniel Veillard6eadf632003-01-23 18:29:16 +00003954 ctxt->nbErrors++;
3955 }
3956 }
3957
Daniel Veillard2df2de22003-02-17 23:34:33 +00003958 cur = cur->next;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003959 }
3960#ifdef DEBUG
3961 xmlGenericError(xmlGenericErrorContext,
3962 "xmlRelaxNGCombineStart(): merging <start>: %d\n",
3963 choiceOrInterleave);
3964#endif
3965 if (choiceOrInterleave == -1)
3966 choiceOrInterleave = 0;
3967 cur = xmlRelaxNGNewDefine(ctxt, starts->node);
3968 if (cur == NULL)
3969 return;
3970 if (choiceOrInterleave == 0)
Daniel Veillard6eadf632003-01-23 18:29:16 +00003971 cur->type = XML_RELAXNG_INTERLEAVE;
Daniel Veillard2df2de22003-02-17 23:34:33 +00003972 else
3973 cur->type = XML_RELAXNG_CHOICE;
3974 cur->content = grammar->start;
3975 grammar->start = cur;
3976 if (choiceOrInterleave == 0) {
3977 if (ctxt->interleaves == NULL)
3978 ctxt->interleaves = xmlHashCreate(10);
3979 if (ctxt->interleaves == NULL) {
3980 if (ctxt->error != NULL)
3981 ctxt->error(ctxt->userData,
3982 "Failed to create interleaves hash table\n");
3983 ctxt->nbErrors++;
3984 } else {
3985 char tmpname[32];
3986
3987 snprintf(tmpname, 32, "interleave%d", ctxt->nbInterleaves++);
3988 if (xmlHashAddEntry(ctxt->interleaves, BAD_CAST tmpname, cur) < 0) {
3989 if (ctxt->error != NULL)
3990 ctxt->error(ctxt->userData,
3991 "Failed to add %s to hash table\n", tmpname);
3992 ctxt->nbErrors++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00003993 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003994 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003995 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00003996}
3997
3998/**
Daniel Veillardd4310742003-02-18 21:12:46 +00003999 * xmlRelaxNGCheckCycles:
4000 * @ctxt: a Relax-NG parser context
4001 * @nodes: grammar children nodes
4002 * @depth: the counter
4003 *
4004 * Check for cycles.
4005 *
4006 * Returns 0 if check passed, and -1 in case of error
4007 */
4008static int
4009xmlRelaxNGCheckCycles(xmlRelaxNGParserCtxtPtr ctxt,
4010 xmlRelaxNGDefinePtr cur, int depth) {
4011 int ret = 0;
4012
4013 while ((ret == 0) && (cur != NULL)) {
4014 if ((cur->type == XML_RELAXNG_REF) ||
4015 (cur->type == XML_RELAXNG_PARENTREF)) {
4016 if (cur->depth == -1) {
4017 cur->depth = depth;
4018 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
4019 cur->depth = -2;
4020 } else if (depth == cur->depth) {
4021 if (ctxt->error != NULL)
4022 ctxt->error(ctxt->userData,
4023 "Detected a cycle in %s references\n", cur->name);
4024 ctxt->nbErrors++;
4025 return(-1);
4026 }
4027 } else if (cur->type == XML_RELAXNG_ELEMENT) {
4028 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth + 1);
4029 } else {
4030 ret = xmlRelaxNGCheckCycles(ctxt, cur->content, depth);
4031 }
4032 cur = cur->next;
4033 }
4034 return(ret);
4035}
4036
4037/**
Daniel Veillard77648bb2003-02-20 15:03:22 +00004038 * xmlRelaxNGTryUnlink:
4039 * @ctxt: a Relax-NG parser context
4040 * @cur: the definition to unlink
4041 * @parent: the parent definition
4042 * @prev: the previous sibling definition
4043 *
4044 * Try to unlink a definition. If not possble make it a NOOP
4045 *
4046 * Returns the new prev definition
4047 */
4048static xmlRelaxNGDefinePtr
4049xmlRelaxNGTryUnlink(xmlRelaxNGParserCtxtPtr ctxt ATTRIBUTE_UNUSED,
4050 xmlRelaxNGDefinePtr cur,
4051 xmlRelaxNGDefinePtr parent,
4052 xmlRelaxNGDefinePtr prev) {
4053 if (prev != NULL) {
4054 prev->next = cur->next;
4055 } else {
4056 if (parent != NULL) {
4057 if (parent->content == cur)
4058 parent->content = cur->next;
4059 else if (parent->attrs == cur)
4060 parent->attrs = cur->next;
4061 else if (parent->nameClass == cur)
4062 parent->nameClass = cur->next;
4063 } else {
4064 cur->type = XML_RELAXNG_NOOP;
4065 prev = cur;
4066 }
4067 }
4068 return(prev);
4069}
4070
4071/**
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004072 * xmlRelaxNGSimplify:
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004073 * @ctxt: a Relax-NG parser context
4074 * @nodes: grammar children nodes
4075 *
4076 * Check for simplification of empty and notAllowed
4077 */
4078static void
4079xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
4080 xmlRelaxNGDefinePtr cur,
4081 xmlRelaxNGDefinePtr parent) {
4082 xmlRelaxNGDefinePtr prev = NULL;
4083
4084 while (cur != NULL) {
4085 if ((cur->type == XML_RELAXNG_REF) ||
4086 (cur->type == XML_RELAXNG_PARENTREF)) {
4087 if (cur->depth != -3) {
4088 cur->depth = -3;
4089 xmlRelaxNGSimplify(ctxt, cur->content, cur);
4090 }
4091 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004092 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004093 if ((parent != NULL) &&
4094 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4095 (parent->type == XML_RELAXNG_LIST) ||
4096 (parent->type == XML_RELAXNG_GROUP) ||
4097 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4098 (parent->type == XML_RELAXNG_ONEORMORE) ||
4099 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4100 parent->type = XML_RELAXNG_NOT_ALLOWED;
4101 break;
4102 }
4103 if ((parent != NULL) &&
4104 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004105 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004106 } else
4107 prev = cur;
4108 } else if (cur->type == XML_RELAXNG_EMPTY){
Daniel Veillard77648bb2003-02-20 15:03:22 +00004109 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004110 if ((parent != NULL) &&
4111 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4112 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4113 parent->type = XML_RELAXNG_EMPTY;
4114 break;
4115 }
4116 if ((parent != NULL) &&
4117 ((parent->type == XML_RELAXNG_GROUP) ||
4118 (parent->type == XML_RELAXNG_INTERLEAVE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004119 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004120 } else
4121 prev = cur;
4122 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004123 cur->parent = parent;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004124 if (cur->content != NULL)
4125 xmlRelaxNGSimplify(ctxt, cur->content, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004126 if (cur->attrs != NULL)
4127 xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
4128 if (cur->nameClass != NULL)
4129 xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004130 /*
4131 * This may result in a simplification
4132 */
4133 if ((cur->type == XML_RELAXNG_GROUP) ||
4134 (cur->type == XML_RELAXNG_INTERLEAVE)) {
4135 if (cur->content == NULL)
4136 cur->type = XML_RELAXNG_EMPTY;
4137 else if (cur->content->next == NULL) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004138 if ((parent == NULL) && (prev == NULL)) {
4139 cur->type = XML_RELAXNG_NOOP;
4140 } else if (prev == NULL) {
4141 parent->content = cur->content;
4142 cur->content->next = cur->next;
4143 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004144 } else {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004145 cur->content->next = cur->next;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004146 prev->next = cur->content;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004147 cur = cur->content;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004148 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004149 }
4150 }
4151 /*
4152 * the current node may have been transformed back
4153 */
4154 if ((cur->type == XML_RELAXNG_EXCEPT) &&
4155 (cur->content != NULL) &&
4156 (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004157 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004158 } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
4159 if ((parent != NULL) &&
4160 ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
4161 (parent->type == XML_RELAXNG_LIST) ||
4162 (parent->type == XML_RELAXNG_GROUP) ||
4163 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4164 (parent->type == XML_RELAXNG_ONEORMORE) ||
4165 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4166 parent->type = XML_RELAXNG_NOT_ALLOWED;
4167 break;
4168 }
4169 if ((parent != NULL) &&
4170 (parent->type == XML_RELAXNG_CHOICE)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004171 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004172 } else
4173 prev = cur;
4174 } else if (cur->type == XML_RELAXNG_EMPTY){
4175 if ((parent != NULL) &&
4176 ((parent->type == XML_RELAXNG_ONEORMORE) ||
4177 (parent->type == XML_RELAXNG_ZEROORMORE))) {
4178 parent->type = XML_RELAXNG_EMPTY;
4179 break;
4180 }
4181 if ((parent != NULL) &&
4182 ((parent->type == XML_RELAXNG_GROUP) ||
4183 (parent->type == XML_RELAXNG_INTERLEAVE) ||
4184 (parent->type == XML_RELAXNG_CHOICE))) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004185 prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004186 } else
4187 prev = cur;
4188 } else {
4189 prev = cur;
4190 }
4191 }
4192 cur = cur->next;
4193 }
4194}
4195
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004196/**
4197 * xmlRelaxNGGroupContentType:
4198 * @ct1: the first content type
4199 * @ct2: the second content type
4200 *
4201 * Try to group 2 content types
4202 *
4203 * Returns the content type
4204 */
4205static xmlRelaxNGContentType
4206xmlRelaxNGGroupContentType(xmlRelaxNGContentType ct1,
4207 xmlRelaxNGContentType ct2) {
4208 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4209 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4210 return(XML_RELAXNG_CONTENT_ERROR);
4211 if (ct1 == XML_RELAXNG_CONTENT_EMPTY)
4212 return(ct2);
4213 if (ct2 == XML_RELAXNG_CONTENT_EMPTY)
4214 return(ct1);
4215 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) &&
4216 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4217 return(XML_RELAXNG_CONTENT_COMPLEX);
4218 return(XML_RELAXNG_CONTENT_ERROR);
4219}
4220
4221/**
4222 * xmlRelaxNGMaxContentType:
4223 * @ct1: the first content type
4224 * @ct2: the second content type
4225 *
4226 * Compute the max content-type
4227 *
4228 * Returns the content type
4229 */
4230static xmlRelaxNGContentType
4231xmlRelaxNGMaxContentType(xmlRelaxNGContentType ct1,
4232 xmlRelaxNGContentType ct2) {
4233 if ((ct1 == XML_RELAXNG_CONTENT_ERROR) ||
4234 (ct2 == XML_RELAXNG_CONTENT_ERROR))
4235 return(XML_RELAXNG_CONTENT_ERROR);
4236 if ((ct1 == XML_RELAXNG_CONTENT_SIMPLE) ||
4237 (ct2 == XML_RELAXNG_CONTENT_SIMPLE))
4238 return(XML_RELAXNG_CONTENT_SIMPLE);
4239 if ((ct1 == XML_RELAXNG_CONTENT_COMPLEX) ||
4240 (ct2 == XML_RELAXNG_CONTENT_COMPLEX))
4241 return(XML_RELAXNG_CONTENT_COMPLEX);
4242 return(XML_RELAXNG_CONTENT_EMPTY);
4243}
Daniel Veillard77648bb2003-02-20 15:03:22 +00004244
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004245/**
4246 * xmlRelaxNGCheckRules:
4247 * @ctxt: a Relax-NG parser context
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004248 * @cur: the current definition
Daniel Veillard77648bb2003-02-20 15:03:22 +00004249 * @flags: some accumulated flags
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004250 * @ptype: the parent type
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004251 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004252 * Check for rules in section 7.1 and 7.2
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004253 *
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004254 * Returns the content type of @cur
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004255 */
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004256static xmlRelaxNGContentType
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004257xmlRelaxNGCheckRules(xmlRelaxNGParserCtxtPtr ctxt,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004258 xmlRelaxNGDefinePtr cur, int flags,
4259 xmlRelaxNGType ptype) {
4260 int nflags = flags;
4261 xmlRelaxNGContentType ret, tmp, val = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004262
Daniel Veillard77648bb2003-02-20 15:03:22 +00004263 while (cur != NULL) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004264 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004265 if ((cur->type == XML_RELAXNG_REF) ||
4266 (cur->type == XML_RELAXNG_PARENTREF)) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004267 if (flags & XML_RELAXNG_IN_LIST) {
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004268 if (ctxt->error != NULL)
4269 ctxt->error(ctxt->userData,
Daniel Veillard77648bb2003-02-20 15:03:22 +00004270 "Found forbidden pattern list//ref\n");
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004271 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004272 }
4273 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4274 if (ctxt->error != NULL)
4275 ctxt->error(ctxt->userData,
4276 "Found forbidden pattern attribute//ref\n");
4277 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004278 }
4279 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4280 if (ctxt->error != NULL)
4281 ctxt->error(ctxt->userData,
4282 "Found forbidden pattern data/except//ref\n");
4283 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004284 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00004285 if (cur->depth > -4) {
Daniel Veillard77648bb2003-02-20 15:03:22 +00004286 cur->depth = -4;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004287 ret = xmlRelaxNGCheckRules(ctxt, cur->content,
4288 flags, cur->type);
4289 cur->depth = ret - 15 ;
4290 } else if (cur->depth == -4) {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004291 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillardc5312d72003-02-21 17:14:10 +00004292 } else {
4293 ret = (xmlRelaxNGContentType) cur->depth + 15;
4294 }
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004295 } else if (cur->type == XML_RELAXNG_ELEMENT) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004296 /*
4297 * The 7.3 Attribute derivation rule for groups is plugged there
4298 */
4299 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004300 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4301 if (ctxt->error != NULL)
4302 ctxt->error(ctxt->userData,
4303 "Found forbidden pattern data/except//element(ref)\n");
4304 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004305 }
4306 if (flags & XML_RELAXNG_IN_LIST) {
4307 if (ctxt->error != NULL)
4308 ctxt->error(ctxt->userData,
4309 "Found forbidden pattern list//element(ref)\n");
4310 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004311 }
4312 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4313 if (ctxt->error != NULL)
4314 ctxt->error(ctxt->userData,
4315 "Found forbidden pattern attribute//element(ref)\n");
4316 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004317 }
4318 /*
4319 * reset since in the simple form elements are only child
4320 * of grammar/define
4321 */
4322 nflags = 0;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004323 ret = xmlRelaxNGCheckRules(ctxt, cur->attrs, nflags, cur->type);
4324 if (ret != XML_RELAXNG_CONTENT_EMPTY) {
4325 if (ctxt->error != NULL)
4326 ctxt->error(ctxt->userData,
4327 "Element %s attributes have a content type error\n",
4328 cur->name);
4329 ctxt->nbErrors++;
4330 }
4331 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4332 if (ret == XML_RELAXNG_CONTENT_ERROR) {
4333 if (ctxt->error != NULL)
4334 ctxt->error(ctxt->userData,
4335 "Element %s has a content type error\n",
4336 cur->name);
4337 ctxt->nbErrors++;
4338 } else {
4339 ret = XML_RELAXNG_CONTENT_COMPLEX;
4340 }
Daniel Veillard77648bb2003-02-20 15:03:22 +00004341 } else if (cur->type == XML_RELAXNG_ATTRIBUTE) {
4342 if (flags & XML_RELAXNG_IN_ATTRIBUTE) {
4343 if (ctxt->error != NULL)
4344 ctxt->error(ctxt->userData,
4345 "Found forbidden pattern attribute//attribute\n");
4346 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004347 }
4348 if (flags & XML_RELAXNG_IN_LIST) {
4349 if (ctxt->error != NULL)
4350 ctxt->error(ctxt->userData,
4351 "Found forbidden pattern list//attribute\n");
4352 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004353 }
4354 if (flags & XML_RELAXNG_IN_OOMGROUP) {
4355 if (ctxt->error != NULL)
4356 ctxt->error(ctxt->userData,
4357 "Found forbidden pattern oneOrMore//group//attribute\n");
4358 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004359 }
4360 if (flags & XML_RELAXNG_IN_OOMINTERLEAVE) {
4361 if (ctxt->error != NULL)
4362 ctxt->error(ctxt->userData,
4363 "Found forbidden pattern oneOrMore//interleave//attribute\n");
4364 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004365 }
4366 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4367 if (ctxt->error != NULL)
4368 ctxt->error(ctxt->userData,
4369 "Found forbidden pattern data/except//attribute\n");
4370 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004371 }
4372 if (flags & XML_RELAXNG_IN_START) {
4373 if (ctxt->error != NULL)
4374 ctxt->error(ctxt->userData,
4375 "Found forbidden pattern start//attribute\n");
4376 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004377 }
4378 nflags = flags | XML_RELAXNG_IN_ATTRIBUTE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004379 xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4380 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004381 } else if ((cur->type == XML_RELAXNG_ONEORMORE) ||
4382 (cur->type == XML_RELAXNG_ZEROORMORE)) {
4383 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4384 if (ctxt->error != NULL)
4385 ctxt->error(ctxt->userData,
4386 "Found forbidden pattern data/except//oneOrMore\n");
4387 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004388 }
4389 if (flags & XML_RELAXNG_IN_START) {
4390 if (ctxt->error != NULL)
4391 ctxt->error(ctxt->userData,
4392 "Found forbidden pattern start//oneOrMore\n");
4393 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004394 }
4395 nflags = flags | XML_RELAXNG_IN_ONEORMORE;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004396 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
4397 ret = xmlRelaxNGGroupContentType(ret, ret);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004398 } else if (cur->type == XML_RELAXNG_LIST) {
4399 if (flags & XML_RELAXNG_IN_LIST) {
4400 if (ctxt->error != NULL)
4401 ctxt->error(ctxt->userData,
4402 "Found forbidden pattern list//list\n");
4403 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004404 }
4405 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4406 if (ctxt->error != NULL)
4407 ctxt->error(ctxt->userData,
4408 "Found forbidden pattern data/except//list\n");
4409 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004410 }
4411 if (flags & XML_RELAXNG_IN_START) {
4412 if (ctxt->error != NULL)
4413 ctxt->error(ctxt->userData,
4414 "Found forbidden pattern start//list\n");
4415 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004416 }
4417 nflags = flags | XML_RELAXNG_IN_LIST;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004418 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004419 } else if (cur->type == XML_RELAXNG_GROUP) {
4420 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4421 if (ctxt->error != NULL)
4422 ctxt->error(ctxt->userData,
4423 "Found forbidden pattern data/except//group\n");
4424 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004425 }
4426 if (flags & XML_RELAXNG_IN_START) {
4427 if (ctxt->error != NULL)
4428 ctxt->error(ctxt->userData,
4429 "Found forbidden pattern start//group\n");
4430 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004431 }
4432 if (flags & XML_RELAXNG_IN_ONEORMORE)
4433 nflags = flags | XML_RELAXNG_IN_OOMGROUP;
4434 else
4435 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004436 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard44e1dd02003-02-21 23:23:28 +00004437 /*
4438 * The 7.3 Attribute derivation rule for groups is plugged there
4439 */
4440 xmlRelaxNGCheckGroupAttrs(ctxt, cur);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004441 } else if (cur->type == XML_RELAXNG_INTERLEAVE) {
4442 if (flags & XML_RELAXNG_IN_LIST) {
4443 if (ctxt->error != NULL)
4444 ctxt->error(ctxt->userData,
4445 "Found forbidden pattern list//interleave\n");
4446 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004447 }
4448 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4449 if (ctxt->error != NULL)
4450 ctxt->error(ctxt->userData,
4451 "Found forbidden pattern data/except//interleave\n");
4452 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004453 }
4454 if (flags & XML_RELAXNG_IN_START) {
4455 if (ctxt->error != NULL)
4456 ctxt->error(ctxt->userData,
4457 "Found forbidden pattern start//interleave\n");
4458 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004459 }
4460 if (flags & XML_RELAXNG_IN_ONEORMORE)
4461 nflags = flags | XML_RELAXNG_IN_OOMINTERLEAVE;
4462 else
4463 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004464 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004465 } else if (cur->type == XML_RELAXNG_EXCEPT) {
4466 if ((cur->parent != NULL) &&
4467 (cur->parent->type == XML_RELAXNG_DATATYPE))
4468 nflags = flags | XML_RELAXNG_IN_DATAEXCEPT;
4469 else
4470 nflags = flags;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004471 ret = xmlRelaxNGCheckRules(ctxt, cur->content, nflags, cur->type);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004472 } else if (cur->type == XML_RELAXNG_DATATYPE) {
4473 if (flags & XML_RELAXNG_IN_START) {
4474 if (ctxt->error != NULL)
4475 ctxt->error(ctxt->userData,
4476 "Found forbidden pattern start//data\n");
4477 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004478 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004479 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4480 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004481 } else if (cur->type == XML_RELAXNG_VALUE) {
4482 if (flags & XML_RELAXNG_IN_START) {
4483 if (ctxt->error != NULL)
4484 ctxt->error(ctxt->userData,
4485 "Found forbidden pattern start//value\n");
4486 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004487 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004488 xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
4489 ret = XML_RELAXNG_CONTENT_SIMPLE;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004490 } else if (cur->type == XML_RELAXNG_TEXT) {
4491 if (flags & XML_RELAXNG_IN_LIST) {
4492 if (ctxt->error != NULL)
4493 ctxt->error(ctxt->userData,
4494 "Found forbidden pattern list//text\n");
4495 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004496 }
4497 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4498 if (ctxt->error != NULL)
4499 ctxt->error(ctxt->userData,
4500 "Found forbidden pattern data/except//text\n");
4501 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004502 }
4503 if (flags & XML_RELAXNG_IN_START) {
4504 if (ctxt->error != NULL)
4505 ctxt->error(ctxt->userData,
4506 "Found forbidden pattern start//text\n");
4507 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004508 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004509 ret = XML_RELAXNG_CONTENT_COMPLEX;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004510 } else if (cur->type == XML_RELAXNG_EMPTY) {
4511 if (flags & XML_RELAXNG_IN_DATAEXCEPT) {
4512 if (ctxt->error != NULL)
4513 ctxt->error(ctxt->userData,
4514 "Found forbidden pattern data/except//empty\n");
4515 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004516 }
4517 if (flags & XML_RELAXNG_IN_START) {
4518 if (ctxt->error != NULL)
4519 ctxt->error(ctxt->userData,
4520 "Found forbidden pattern start//empty\n");
4521 ctxt->nbErrors++;
Daniel Veillard77648bb2003-02-20 15:03:22 +00004522 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004523 ret = XML_RELAXNG_CONTENT_EMPTY;
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004524 } else {
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004525 ret = xmlRelaxNGCheckRules(ctxt, cur->content, flags, cur->type);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004526 }
4527 cur = cur->next;
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004528 if (ptype == XML_RELAXNG_GROUP) {
4529 val = xmlRelaxNGGroupContentType(val, ret);
4530 } else if (ptype == XML_RELAXNG_INTERLEAVE) {
4531 tmp = xmlRelaxNGGroupContentType(val, ret);
4532 if (tmp != XML_RELAXNG_CONTENT_ERROR)
4533 tmp = xmlRelaxNGMaxContentType(val, ret);
4534 } else if (ptype == XML_RELAXNG_CHOICE) {
4535 val = xmlRelaxNGMaxContentType(val, ret);
4536 } else if (ptype == XML_RELAXNG_LIST) {
4537 val = XML_RELAXNG_CONTENT_SIMPLE;
4538 } else if (ptype == XML_RELAXNG_EXCEPT) {
4539 if (ret == XML_RELAXNG_CONTENT_ERROR)
4540 val = XML_RELAXNG_CONTENT_ERROR;
4541 else
4542 val = XML_RELAXNG_CONTENT_SIMPLE;
4543 } else {
4544 val = xmlRelaxNGGroupContentType(val, ret);
4545 }
4546
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004547 }
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004548 return(val);
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004549}
Daniel Veillard1c745ad2003-02-20 00:11:02 +00004550
4551/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00004552 * xmlRelaxNGParseGrammar:
4553 * @ctxt: a Relax-NG parser context
4554 * @nodes: grammar children nodes
4555 *
4556 * parse a Relax-NG <grammar> node
4557 *
4558 * Returns the internal xmlRelaxNGGrammarPtr built or
4559 * NULL in case of error
4560 */
4561static xmlRelaxNGGrammarPtr
4562xmlRelaxNGParseGrammar(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr nodes) {
4563 xmlRelaxNGGrammarPtr ret, tmp, old;
4564
Daniel Veillardc482e262003-02-26 14:48:48 +00004565#ifdef DEBUG_GRAMMAR
4566 xmlGenericError(xmlGenericErrorContext, "Parsing a new grammar\n");
4567#endif
4568
Daniel Veillard6eadf632003-01-23 18:29:16 +00004569 ret = xmlRelaxNGNewGrammar(ctxt);
4570 if (ret == NULL)
4571 return(NULL);
4572
4573 /*
4574 * Link the new grammar in the tree
4575 */
4576 ret->parent = ctxt->grammar;
4577 if (ctxt->grammar != NULL) {
4578 tmp = ctxt->grammar->children;
4579 if (tmp == NULL) {
4580 ctxt->grammar->children = ret;
4581 } else {
4582 while (tmp->next != NULL)
4583 tmp = tmp->next;
4584 tmp->next = ret;
4585 }
4586 }
4587
4588 old = ctxt->grammar;
4589 ctxt->grammar = ret;
4590 xmlRelaxNGParseGrammarContent(ctxt, nodes);
4591 ctxt->grammar = ret;
Daniel Veillard2df2de22003-02-17 23:34:33 +00004592 if (ctxt->grammar == NULL) {
4593 if (ctxt->error != NULL)
4594 ctxt->error(ctxt->userData,
4595 "Failed to parse <grammar> content\n");
4596 ctxt->nbErrors++;
4597 } else if (ctxt->grammar->start == NULL) {
4598 if (ctxt->error != NULL)
4599 ctxt->error(ctxt->userData,
4600 "Element <grammar> has no <start>\n");
4601 ctxt->nbErrors++;
4602 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004603
4604 /*
4605 * Apply 4.17 mergingd rules to defines and starts
4606 */
4607 xmlRelaxNGCombineStart(ctxt, ret);
4608 if (ret->defs != NULL) {
4609 xmlHashScan(ret->defs, (xmlHashScanner) xmlRelaxNGCheckCombine,
4610 ctxt);
4611 }
4612
4613 /*
4614 * link together defines and refs in this grammar
4615 */
4616 if (ret->refs != NULL) {
4617 xmlHashScan(ret->refs, (xmlHashScanner) xmlRelaxNGCheckReference,
4618 ctxt);
4619 }
4620 ctxt->grammar = old;
4621 return(ret);
4622}
4623
4624/**
4625 * xmlRelaxNGParseDocument:
4626 * @ctxt: a Relax-NG parser context
4627 * @node: the root node of the RelaxNG schema
4628 *
4629 * parse a Relax-NG definition resource and build an internal
4630 * xmlRelaxNG struture which can be used to validate instances.
4631 *
4632 * Returns the internal XML RelaxNG structure built or
4633 * NULL in case of error
4634 */
4635static xmlRelaxNGPtr
4636xmlRelaxNGParseDocument(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4637 xmlRelaxNGPtr schema = NULL;
Daniel Veillard276be4a2003-01-24 01:03:34 +00004638 const xmlChar *olddefine;
Daniel Veillarde431a272003-01-29 23:02:33 +00004639 xmlRelaxNGGrammarPtr old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004640
4641 if ((ctxt == NULL) || (node == NULL))
4642 return (NULL);
4643
4644 schema = xmlRelaxNGNewRelaxNG(ctxt);
4645 if (schema == NULL)
4646 return(NULL);
4647
Daniel Veillard276be4a2003-01-24 01:03:34 +00004648 olddefine = ctxt->define;
4649 ctxt->define = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004650 if (IS_RELAXNG(node, "grammar")) {
4651 schema->topgrammar = xmlRelaxNGParseGrammar(ctxt, node->children);
4652 } else {
Daniel Veillardc482e262003-02-26 14:48:48 +00004653 xmlRelaxNGGrammarPtr tmp, ret;
4654
4655 schema->topgrammar = ret = xmlRelaxNGNewGrammar(ctxt);
Daniel Veillard6eadf632003-01-23 18:29:16 +00004656 if (schema->topgrammar == NULL) {
4657 return(schema);
4658 }
Daniel Veillardc482e262003-02-26 14:48:48 +00004659 /*
4660 * Link the new grammar in the tree
4661 */
4662 ret->parent = ctxt->grammar;
4663 if (ctxt->grammar != NULL) {
4664 tmp = ctxt->grammar->children;
4665 if (tmp == NULL) {
4666 ctxt->grammar->children = ret;
4667 } else {
4668 while (tmp->next != NULL)
4669 tmp = tmp->next;
4670 tmp->next = ret;
4671 }
4672 }
Daniel Veillarde431a272003-01-29 23:02:33 +00004673 old = ctxt->grammar;
Daniel Veillardc482e262003-02-26 14:48:48 +00004674 ctxt->grammar = ret;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004675 xmlRelaxNGParseStart(ctxt, node);
Daniel Veillarde431a272003-01-29 23:02:33 +00004676 if (old != NULL)
4677 ctxt->grammar = old;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004678 }
Daniel Veillard276be4a2003-01-24 01:03:34 +00004679 ctxt->define = olddefine;
Daniel Veillardd4310742003-02-18 21:12:46 +00004680 if (schema->topgrammar->start != NULL) {
4681 xmlRelaxNGCheckCycles(ctxt, schema->topgrammar->start, 0);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004682 if ((ctxt->flags & XML_RELAXNG_IN_EXTERNALREF) == 0) {
4683 xmlRelaxNGSimplify(ctxt, schema->topgrammar->start, NULL);
4684 while ((schema->topgrammar->start != NULL) &&
4685 (schema->topgrammar->start->type == XML_RELAXNG_NOOP) &&
4686 (schema->topgrammar->start->next != NULL))
4687 schema->topgrammar->start = schema->topgrammar->start->content;
4688 xmlRelaxNGCheckRules(ctxt, schema->topgrammar->start,
Daniel Veillard4c5cf702003-02-21 15:40:34 +00004689 XML_RELAXNG_IN_START, XML_RELAXNG_NOOP);
Daniel Veillard77648bb2003-02-20 15:03:22 +00004690 }
Daniel Veillardd4310742003-02-18 21:12:46 +00004691 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004692
4693#ifdef DEBUG
4694 if (schema == NULL)
4695 xmlGenericError(xmlGenericErrorContext,
4696 "xmlRelaxNGParseDocument() failed\n");
4697#endif
4698
4699 return (schema);
4700}
4701
4702/************************************************************************
4703 * *
4704 * Reading RelaxNGs *
4705 * *
4706 ************************************************************************/
4707
4708/**
4709 * xmlRelaxNGNewParserCtxt:
4710 * @URL: the location of the schema
4711 *
4712 * Create an XML RelaxNGs parse context for that file/resource expected
4713 * to contain an XML RelaxNGs file.
4714 *
4715 * Returns the parser context or NULL in case of error
4716 */
4717xmlRelaxNGParserCtxtPtr
4718xmlRelaxNGNewParserCtxt(const char *URL) {
4719 xmlRelaxNGParserCtxtPtr ret;
4720
4721 if (URL == NULL)
4722 return(NULL);
4723
4724 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4725 if (ret == NULL) {
4726 xmlGenericError(xmlGenericErrorContext,
4727 "Failed to allocate new schama parser context for %s\n", URL);
4728 return (NULL);
4729 }
4730 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4731 ret->URL = xmlStrdup((const xmlChar *)URL);
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004732 ret->error = xmlGenericError;
4733 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004734 return (ret);
4735}
4736
4737/**
4738 * xmlRelaxNGNewMemParserCtxt:
4739 * @buffer: a pointer to a char array containing the schemas
4740 * @size: the size of the array
4741 *
4742 * Create an XML RelaxNGs parse context for that memory buffer expected
4743 * to contain an XML RelaxNGs file.
4744 *
4745 * Returns the parser context or NULL in case of error
4746 */
4747xmlRelaxNGParserCtxtPtr
4748xmlRelaxNGNewMemParserCtxt(const char *buffer, int size) {
4749 xmlRelaxNGParserCtxtPtr ret;
4750
4751 if ((buffer == NULL) || (size <= 0))
4752 return(NULL);
4753
4754 ret = (xmlRelaxNGParserCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGParserCtxt));
4755 if (ret == NULL) {
4756 xmlGenericError(xmlGenericErrorContext,
4757 "Failed to allocate new schama parser context\n");
4758 return (NULL);
4759 }
4760 memset(ret, 0, sizeof(xmlRelaxNGParserCtxt));
4761 ret->buffer = buffer;
4762 ret->size = size;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00004763 ret->error = xmlGenericError;
4764 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004765 return (ret);
4766}
4767
4768/**
4769 * xmlRelaxNGFreeParserCtxt:
4770 * @ctxt: the schema parser context
4771 *
4772 * Free the resources associated to the schema parser context
4773 */
4774void
4775xmlRelaxNGFreeParserCtxt(xmlRelaxNGParserCtxtPtr ctxt) {
4776 if (ctxt == NULL)
4777 return;
4778 if (ctxt->URL != NULL)
4779 xmlFree(ctxt->URL);
4780 if (ctxt->doc != NULL)
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004781 xmlFreeDoc(ctxt->document);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00004782 if (ctxt->interleaves != NULL)
4783 xmlHashFree(ctxt->interleaves, NULL);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004784 if (ctxt->documents != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +00004785 xmlRelaxNGFreeDocumentList(ctxt->documents);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00004786 if (ctxt->includes != NULL)
Daniel Veillardc482e262003-02-26 14:48:48 +00004787 xmlRelaxNGFreeIncludeList(ctxt->includes);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004788 if (ctxt->docTab != NULL)
4789 xmlFree(ctxt->docTab);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00004790 if (ctxt->incTab != NULL)
4791 xmlFree(ctxt->incTab);
Daniel Veillard419a7682003-02-03 23:22:49 +00004792 if (ctxt->defTab != NULL) {
4793 int i;
4794
4795 for (i = 0;i < ctxt->defNr;i++)
4796 xmlRelaxNGFreeDefine(ctxt->defTab[i]);
4797 xmlFree(ctxt->defTab);
4798 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004799 xmlFree(ctxt);
4800}
4801
Daniel Veillard6eadf632003-01-23 18:29:16 +00004802/**
Daniel Veillardd2298792003-02-14 16:54:11 +00004803 * xmlRelaxNGNormExtSpace:
4804 * @value: a value
4805 *
4806 * Removes the leading and ending spaces of the value
4807 * The string is modified "in situ"
4808 */
4809static void
4810xmlRelaxNGNormExtSpace(xmlChar *value) {
4811 xmlChar *start = value;
4812 xmlChar *cur = value;
4813 if (value == NULL)
4814 return;
4815
4816 while (IS_BLANK(*cur)) cur++;
4817 if (cur == start) {
4818 do {
4819 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4820 if (*cur == 0)
4821 return;
4822 start = cur;
4823 while (IS_BLANK(*cur)) cur++;
4824 if (*cur == 0) {
4825 *start = 0;
4826 return;
4827 }
4828 } while (1);
4829 } else {
4830 do {
4831 while ((*cur != 0) && (!IS_BLANK(*cur)))
4832 *start++ = *cur++;
4833 if (*cur == 0) {
4834 *start = 0;
4835 return;
4836 }
4837 /* don't try to normalize the inner spaces */
4838 while (IS_BLANK(*cur)) cur++;
4839 *start++ = *cur++;
4840 if (*cur == 0) {
4841 *start = 0;
4842 return;
4843 }
4844 } while (1);
4845 }
4846}
4847
4848/**
4849 * xmlRelaxNGCheckAttributes:
4850 * @ctxt: a Relax-NG parser context
4851 * @node: a Relax-NG node
4852 *
4853 * Check all the attributes on the given node
4854 */
4855static void
4856xmlRelaxNGCleanupAttributes(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr node) {
4857 xmlAttrPtr cur, next;
4858
4859 cur = node->properties;
4860 while (cur != NULL) {
4861 next = cur->next;
4862 if ((cur->ns == NULL) ||
4863 (xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
4864 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
4865 if ((!xmlStrEqual(node->name, BAD_CAST "element")) &&
4866 (!xmlStrEqual(node->name, BAD_CAST "attribute")) &&
4867 (!xmlStrEqual(node->name, BAD_CAST "ref")) &&
4868 (!xmlStrEqual(node->name, BAD_CAST "parentRef")) &&
Daniel Veillard2df2de22003-02-17 23:34:33 +00004869 (!xmlStrEqual(node->name, BAD_CAST "param")) &&
Daniel Veillardd2298792003-02-14 16:54:11 +00004870 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4871 if (ctxt->error != NULL)
4872 ctxt->error(ctxt->userData,
4873 "Attribute %s is not allowed on %s\n",
4874 cur->name, node->name);
4875 ctxt->nbErrors++;
4876 }
4877 } else if (xmlStrEqual(cur->name, BAD_CAST "type")) {
4878 if ((!xmlStrEqual(node->name, BAD_CAST "value")) &&
4879 (!xmlStrEqual(node->name, BAD_CAST "data"))) {
4880 if (ctxt->error != NULL)
4881 ctxt->error(ctxt->userData,
4882 "Attribute %s is not allowed on %s\n",
4883 cur->name, node->name);
4884 ctxt->nbErrors++;
4885 }
4886 } else if (xmlStrEqual(cur->name, BAD_CAST "href")) {
4887 if ((!xmlStrEqual(node->name, BAD_CAST "externalRef")) &&
4888 (!xmlStrEqual(node->name, BAD_CAST "include"))) {
4889 if (ctxt->error != NULL)
4890 ctxt->error(ctxt->userData,
4891 "Attribute %s is not allowed on %s\n",
4892 cur->name, node->name);
4893 ctxt->nbErrors++;
4894 }
4895 } else if (xmlStrEqual(cur->name, BAD_CAST "combine")) {
4896 if ((!xmlStrEqual(node->name, BAD_CAST "start")) &&
4897 (!xmlStrEqual(node->name, BAD_CAST "define"))) {
4898 if (ctxt->error != NULL)
4899 ctxt->error(ctxt->userData,
4900 "Attribute %s is not allowed on %s\n",
4901 cur->name, node->name);
4902 ctxt->nbErrors++;
4903 }
4904 } else if (xmlStrEqual(cur->name, BAD_CAST "datatypeLibrary")) {
4905 xmlChar *val;
4906 xmlURIPtr uri;
4907
4908 val = xmlNodeListGetString(node->doc, cur->children, 1);
4909 if (val != NULL) {
4910 if (val[0] != 0) {
4911 uri = xmlParseURI((const char *) val);
4912 if (uri == NULL) {
4913 if (ctxt->error != NULL)
4914 ctxt->error(ctxt->userData,
4915 "Attribute %s contains invalid URI %s\n",
4916 cur->name, val);
4917 ctxt->nbErrors++;
4918 } else {
4919 if (uri->scheme == NULL) {
4920 if (ctxt->error != NULL)
4921 ctxt->error(ctxt->userData,
4922 "Attribute %s URI %s is not absolute\n",
4923 cur->name, val);
4924 ctxt->nbErrors++;
4925 }
4926 if (uri->fragment != NULL) {
4927 if (ctxt->error != NULL)
4928 ctxt->error(ctxt->userData,
4929 "Attribute %s URI %s has a fragment ID\n",
4930 cur->name, val);
4931 ctxt->nbErrors++;
4932 }
4933 xmlFreeURI(uri);
4934 }
4935 }
4936 xmlFree(val);
4937 }
4938 } else if (!xmlStrEqual(cur->name, BAD_CAST "ns")) {
4939 if (ctxt->error != NULL)
4940 ctxt->error(ctxt->userData,
4941 "Unknown attribute %s on %s\n",
4942 cur->name, node->name);
4943 ctxt->nbErrors++;
4944 }
4945 }
4946 cur = next;
4947 }
4948}
4949
4950/**
Daniel Veillardc5312d72003-02-21 17:14:10 +00004951 * xmlRelaxNGCleanupTree:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004952 * @ctxt: a Relax-NG parser context
Daniel Veillardc5312d72003-02-21 17:14:10 +00004953 * @root: an xmlNodePtr subtree
Daniel Veillard6eadf632003-01-23 18:29:16 +00004954 *
Daniel Veillardc5312d72003-02-21 17:14:10 +00004955 * Cleanup the subtree from unwanted nodes for parsing, resolve
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004956 * Include and externalRef lookups.
Daniel Veillard6eadf632003-01-23 18:29:16 +00004957 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00004958static void
4959xmlRelaxNGCleanupTree(xmlRelaxNGParserCtxtPtr ctxt, xmlNodePtr root) {
4960 xmlNodePtr cur, delete;
Daniel Veillard6eadf632003-01-23 18:29:16 +00004961
Daniel Veillard6eadf632003-01-23 18:29:16 +00004962 delete = NULL;
4963 cur = root;
4964 while (cur != NULL) {
4965 if (delete != NULL) {
4966 xmlUnlinkNode(delete);
4967 xmlFreeNode(delete);
4968 delete = NULL;
4969 }
4970 if (cur->type == XML_ELEMENT_NODE) {
4971 /*
4972 * Simplification 4.1. Annotations
4973 */
4974 if ((cur->ns == NULL) ||
4975 (!xmlStrEqual(cur->ns->href, xmlRelaxNGNs))) {
Daniel Veillardd2298792003-02-14 16:54:11 +00004976 if ((cur->parent != NULL) &&
4977 (cur->parent->type == XML_ELEMENT_NODE) &&
4978 ((xmlStrEqual(cur->parent->name, BAD_CAST "name")) ||
4979 (xmlStrEqual(cur->parent->name, BAD_CAST "value")) ||
4980 (xmlStrEqual(cur->parent->name, BAD_CAST "param")))) {
4981 if (ctxt->error != NULL)
4982 ctxt->error(ctxt->userData,
4983 "element %s doesn't allow foreign elements\n",
4984 cur->parent->name);
4985 ctxt->nbErrors++;
4986 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00004987 delete = cur;
4988 goto skip_children;
4989 } else {
Daniel Veillardd2298792003-02-14 16:54:11 +00004990 xmlRelaxNGCleanupAttributes(ctxt, cur);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00004991 if (xmlStrEqual(cur->name, BAD_CAST "externalRef")) {
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004992 xmlChar *href, *ns, *base, *URL;
4993 xmlRelaxNGDocumentPtr docu;
Daniel Veillard416589a2003-02-17 17:25:42 +00004994 xmlNodePtr tmp;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00004995
4996 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillard416589a2003-02-17 17:25:42 +00004997 if (ns == NULL) {
4998 tmp = cur->parent;
4999 while ((tmp != NULL) &&
5000 (tmp->type == XML_ELEMENT_NODE)) {
5001 ns = xmlGetProp(tmp, BAD_CAST "ns");
5002 if (ns != NULL)
5003 break;
5004 tmp = tmp->parent;
5005 }
5006 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005007 href = xmlGetProp(cur, BAD_CAST "href");
5008 if (href == NULL) {
5009 if (ctxt->error != NULL)
5010 ctxt->error(ctxt->userData,
5011 "xmlRelaxNGParse: externalRef has no href attribute\n");
5012 ctxt->nbErrors++;
5013 delete = cur;
5014 goto skip_children;
5015 }
5016 base = xmlNodeGetBase(cur->doc, cur);
5017 URL = xmlBuildURI(href, base);
5018 if (URL == NULL) {
5019 if (ctxt->error != NULL)
5020 ctxt->error(ctxt->userData,
5021 "Failed to compute URL for externalRef %s\n", href);
5022 ctxt->nbErrors++;
5023 if (href != NULL)
5024 xmlFree(href);
5025 if (base != NULL)
5026 xmlFree(base);
5027 delete = cur;
5028 goto skip_children;
5029 }
5030 if (href != NULL)
5031 xmlFree(href);
5032 if (base != NULL)
5033 xmlFree(base);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005034 docu = xmlRelaxNGLoadExternalRef(ctxt, URL, ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005035 if (docu == NULL) {
5036 if (ctxt->error != NULL)
5037 ctxt->error(ctxt->userData,
5038 "Failed to load externalRef %s\n", URL);
5039 ctxt->nbErrors++;
5040 xmlFree(URL);
5041 delete = cur;
5042 goto skip_children;
5043 }
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005044 if (ns != NULL)
5045 xmlFree(ns);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005046 xmlFree(URL);
5047 cur->_private = docu;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005048 } else if (xmlStrEqual(cur->name, BAD_CAST "include")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005049 xmlChar *href, *ns, *base, *URL;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005050 xmlRelaxNGIncludePtr incl;
Daniel Veillard416589a2003-02-17 17:25:42 +00005051 xmlNodePtr tmp;
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005052
5053 href = xmlGetProp(cur, BAD_CAST "href");
5054 if (href == NULL) {
5055 if (ctxt->error != NULL)
5056 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005057 "xmlRelaxNGParse: include has no href attribute\n");
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005058 ctxt->nbErrors++;
5059 delete = cur;
5060 goto skip_children;
5061 }
5062 base = xmlNodeGetBase(cur->doc, cur);
5063 URL = xmlBuildURI(href, base);
5064 if (URL == NULL) {
5065 if (ctxt->error != NULL)
5066 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005067 "Failed to compute URL for include %s\n", href);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005068 ctxt->nbErrors++;
5069 if (href != NULL)
5070 xmlFree(href);
5071 if (base != NULL)
5072 xmlFree(base);
5073 delete = cur;
5074 goto skip_children;
5075 }
5076 if (href != NULL)
5077 xmlFree(href);
5078 if (base != NULL)
5079 xmlFree(base);
Daniel Veillard416589a2003-02-17 17:25:42 +00005080 ns = xmlGetProp(cur, BAD_CAST "ns");
5081 if (ns == NULL) {
5082 tmp = cur->parent;
5083 while ((tmp != NULL) &&
5084 (tmp->type == XML_ELEMENT_NODE)) {
5085 ns = xmlGetProp(tmp, BAD_CAST "ns");
5086 if (ns != NULL)
5087 break;
5088 tmp = tmp->parent;
5089 }
5090 }
5091 incl = xmlRelaxNGLoadInclude(ctxt, URL, cur, ns);
5092 if (ns != NULL)
5093 xmlFree(ns);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005094 if (incl == NULL) {
5095 if (ctxt->error != NULL)
5096 ctxt->error(ctxt->userData,
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005097 "Failed to load include %s\n", URL);
Daniel Veillarda9d912d2003-02-01 17:43:10 +00005098 ctxt->nbErrors++;
5099 xmlFree(URL);
5100 delete = cur;
5101 goto skip_children;
5102 }
5103 xmlFree(URL);
5104 cur->_private = incl;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005105 } else if ((xmlStrEqual(cur->name, BAD_CAST "element")) ||
5106 (xmlStrEqual(cur->name, BAD_CAST "attribute"))) {
Daniel Veillardfebcca42003-02-16 15:44:18 +00005107 xmlChar *name, *ns;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005108 xmlNodePtr text = NULL;
5109
5110 /*
5111 * Simplification 4.8. name attribute of element
5112 * and attribute elements
5113 */
5114 name = xmlGetProp(cur, BAD_CAST "name");
5115 if (name != NULL) {
5116 if (cur->children == NULL) {
5117 text = xmlNewChild(cur, cur->ns, BAD_CAST "name",
5118 name);
5119 } else {
5120 xmlNodePtr node;
5121 node = xmlNewNode(cur->ns, BAD_CAST "name");
5122 if (node != NULL) {
5123 xmlAddPrevSibling(cur->children, node);
5124 text = xmlNewText(name);
5125 xmlAddChild(node, text);
5126 text = node;
5127 }
5128 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005129 if (text == NULL) {
5130 if (ctxt->error != NULL)
5131 ctxt->error(ctxt->userData,
5132 "Failed to create a name %s element\n", name);
5133 ctxt->nbErrors++;
5134 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005135 xmlUnsetProp(cur, BAD_CAST "name");
5136 xmlFree(name);
Daniel Veillardfebcca42003-02-16 15:44:18 +00005137 ns = xmlGetProp(cur, BAD_CAST "ns");
5138 if (ns != NULL) {
5139 if (text != NULL) {
5140 xmlSetProp(text, BAD_CAST "ns", ns);
5141 /* xmlUnsetProp(cur, BAD_CAST "ns"); */
Daniel Veillard6eadf632003-01-23 18:29:16 +00005142 }
Daniel Veillardfebcca42003-02-16 15:44:18 +00005143 xmlFree(ns);
5144 } else if (xmlStrEqual(cur->name,
5145 BAD_CAST "attribute")) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00005146 xmlSetProp(text, BAD_CAST "ns", BAD_CAST "");
5147 }
5148 }
5149 } else if ((xmlStrEqual(cur->name, BAD_CAST "name")) ||
5150 (xmlStrEqual(cur->name, BAD_CAST "nsName")) ||
5151 (xmlStrEqual(cur->name, BAD_CAST "value"))) {
5152 /*
5153 * Simplification 4.8. name attribute of element
5154 * and attribute elements
5155 */
5156 if (xmlHasProp(cur, BAD_CAST "ns") == NULL) {
5157 xmlNodePtr node;
5158 xmlChar *ns = NULL;
5159
5160 node = cur->parent;
5161 while ((node != NULL) &&
5162 (node->type == XML_ELEMENT_NODE)) {
5163 ns = xmlGetProp(node, BAD_CAST "ns");
5164 if (ns != NULL) {
5165 break;
5166 }
5167 node = node->parent;
5168 }
5169 if (ns == NULL) {
5170 xmlSetProp(cur, BAD_CAST "ns", BAD_CAST "");
5171 } else {
5172 xmlSetProp(cur, BAD_CAST "ns", ns);
5173 xmlFree(ns);
5174 }
5175 }
5176 if (xmlStrEqual(cur->name, BAD_CAST "name")) {
5177 xmlChar *name, *local, *prefix;
5178
5179 /*
5180 * Simplification: 4.10. QNames
5181 */
5182 name = xmlNodeGetContent(cur);
5183 if (name != NULL) {
5184 local = xmlSplitQName2(name, &prefix);
5185 if (local != NULL) {
5186 xmlNsPtr ns;
5187
5188 ns = xmlSearchNs(cur->doc, cur, prefix);
5189 if (ns == NULL) {
5190 if (ctxt->error != NULL)
5191 ctxt->error(ctxt->userData,
5192 "xmlRelaxNGParse: no namespace for prefix %s\n", prefix);
5193 ctxt->nbErrors++;
5194 } else {
5195 xmlSetProp(cur, BAD_CAST "ns", ns->href);
5196 xmlNodeSetContent(cur, local);
5197 }
5198 xmlFree(local);
5199 xmlFree(prefix);
5200 }
5201 xmlFree(name);
5202 }
5203 }
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005204 /*
5205 * 4.16
5206 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005207 if (xmlStrEqual(cur->name, BAD_CAST "nsName")) {
5208 if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5209 if (ctxt->error != NULL)
5210 ctxt->error(ctxt->userData,
5211 "Found nsName/except//nsName forbidden construct\n");
5212 ctxt->nbErrors++;
5213 }
5214 }
5215 } else if ((xmlStrEqual(cur->name, BAD_CAST "except")) &&
5216 (cur != root)) {
5217 int oldflags = ctxt->flags;
5218
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005219 /*
5220 * 4.16
5221 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005222 if ((cur->parent != NULL) &&
5223 (xmlStrEqual(cur->parent->name, BAD_CAST "anyName"))) {
5224 ctxt->flags |= XML_RELAXNG_IN_ANYEXCEPT;
5225 xmlRelaxNGCleanupTree(ctxt, cur);
5226 ctxt->flags = oldflags;
5227 goto skip_children;
5228 } else if ((cur->parent != NULL) &&
5229 (xmlStrEqual(cur->parent->name, BAD_CAST "nsName"))) {
5230 ctxt->flags |= XML_RELAXNG_IN_NSEXCEPT;
5231 xmlRelaxNGCleanupTree(ctxt, cur);
5232 ctxt->flags = oldflags;
5233 goto skip_children;
5234 }
5235 } else if (xmlStrEqual(cur->name, BAD_CAST "anyName")) {
Daniel Veillard44e1dd02003-02-21 23:23:28 +00005236 /*
5237 * 4.16
5238 */
Daniel Veillardc5312d72003-02-21 17:14:10 +00005239 if (ctxt->flags & XML_RELAXNG_IN_ANYEXCEPT) {
5240 if (ctxt->error != NULL)
5241 ctxt->error(ctxt->userData,
5242 "Found anyName/except//anyName forbidden construct\n");
5243 ctxt->nbErrors++;
5244 } else if (ctxt->flags & XML_RELAXNG_IN_NSEXCEPT) {
5245 if (ctxt->error != NULL)
5246 ctxt->error(ctxt->userData,
5247 "Found nsName/except//anyName forbidden construct\n");
5248 ctxt->nbErrors++;
5249 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005250 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005251 /*
5252 * Thisd is not an else since "include" is transformed
5253 * into a div
5254 */
5255 if (xmlStrEqual(cur->name, BAD_CAST "div")) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005256 xmlChar *ns;
5257 xmlNodePtr child, ins, tmp;
5258
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005259 /*
5260 * implements rule 4.11
5261 */
Daniel Veillard416589a2003-02-17 17:25:42 +00005262
5263 ns = xmlGetProp(cur, BAD_CAST "ns");
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005264
5265 child = cur->children;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005266 ins = cur;
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005267 while (child != NULL) {
Daniel Veillard416589a2003-02-17 17:25:42 +00005268 if (ns != NULL) {
5269 if (!xmlHasProp(child, BAD_CAST "ns")) {
5270 xmlSetProp(child, BAD_CAST "ns", ns);
5271 }
5272 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005273 tmp = child->next;
5274 xmlUnlinkNode(child);
5275 ins = xmlAddNextSibling(ins, child);
5276 child = tmp;
5277 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005278 if (ns != NULL)
5279 xmlFree(ns);
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005280 delete = cur;
5281 goto skip_children;
5282 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005283 }
5284 }
5285 /*
5286 * Simplification 4.2 whitespaces
5287 */
5288 else if (cur->type == XML_TEXT_NODE) {
5289 if (IS_BLANK_NODE(cur)) {
5290 if (cur->parent->type == XML_ELEMENT_NODE) {
5291 if ((!xmlStrEqual(cur->parent->name, BAD_CAST "value")) &&
5292 (!xmlStrEqual(cur->parent->name, BAD_CAST "param")))
5293 delete = cur;
5294 } else {
5295 delete = cur;
5296 goto skip_children;
5297 }
5298 }
5299 } else if (cur->type != XML_CDATA_SECTION_NODE) {
5300 delete = cur;
5301 goto skip_children;
5302 }
5303
5304 /*
5305 * Skip to next node
5306 */
5307 if (cur->children != NULL) {
5308 if ((cur->children->type != XML_ENTITY_DECL) &&
5309 (cur->children->type != XML_ENTITY_REF_NODE) &&
5310 (cur->children->type != XML_ENTITY_NODE)) {
5311 cur = cur->children;
5312 continue;
5313 }
5314 }
5315skip_children:
5316 if (cur->next != NULL) {
5317 cur = cur->next;
5318 continue;
5319 }
5320
5321 do {
5322 cur = cur->parent;
5323 if (cur == NULL)
5324 break;
5325 if (cur == root) {
5326 cur = NULL;
5327 break;
5328 }
5329 if (cur->next != NULL) {
5330 cur = cur->next;
5331 break;
5332 }
5333 } while (cur != NULL);
5334 }
5335 if (delete != NULL) {
5336 xmlUnlinkNode(delete);
5337 xmlFreeNode(delete);
5338 delete = NULL;
5339 }
Daniel Veillardc5312d72003-02-21 17:14:10 +00005340}
Daniel Veillard6eadf632003-01-23 18:29:16 +00005341
Daniel Veillardc5312d72003-02-21 17:14:10 +00005342/**
5343 * xmlRelaxNGCleanupDoc:
5344 * @ctxt: a Relax-NG parser context
5345 * @doc: an xmldocPtr document pointer
5346 *
5347 * Cleanup the document from unwanted nodes for parsing, resolve
5348 * Include and externalRef lookups.
5349 *
5350 * Returns the cleaned up document or NULL in case of error
5351 */
5352static xmlDocPtr
5353xmlRelaxNGCleanupDoc(xmlRelaxNGParserCtxtPtr ctxt, xmlDocPtr doc) {
5354 xmlNodePtr root;
5355
5356 /*
5357 * Extract the root
5358 */
5359 root = xmlDocGetRootElement(doc);
5360 if (root == NULL) {
5361 if (ctxt->error != NULL)
5362 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5363 ctxt->URL);
5364 ctxt->nbErrors++;
5365 return (NULL);
5366 }
5367 xmlRelaxNGCleanupTree(ctxt, root);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005368 return(doc);
5369}
5370
5371/**
5372 * xmlRelaxNGParse:
5373 * @ctxt: a Relax-NG parser context
5374 *
5375 * parse a schema definition resource and build an internal
5376 * XML Shema struture which can be used to validate instances.
5377 * *WARNING* this interface is highly subject to change
5378 *
5379 * Returns the internal XML RelaxNG structure built from the resource or
5380 * NULL in case of error
5381 */
5382xmlRelaxNGPtr
5383xmlRelaxNGParse(xmlRelaxNGParserCtxtPtr ctxt)
5384{
5385 xmlRelaxNGPtr ret = NULL;
5386 xmlDocPtr doc;
5387 xmlNodePtr root;
5388
5389 xmlRelaxNGInitTypes();
5390
5391 if (ctxt == NULL)
5392 return (NULL);
5393
5394 /*
5395 * First step is to parse the input document into an DOM/Infoset
5396 */
5397 if (ctxt->URL != NULL) {
5398 doc = xmlParseFile((const char *) ctxt->URL);
5399 if (doc == NULL) {
5400 if (ctxt->error != NULL)
5401 ctxt->error(ctxt->userData,
5402 "xmlRelaxNGParse: could not load %s\n", ctxt->URL);
5403 ctxt->nbErrors++;
5404 return (NULL);
5405 }
5406 } else if (ctxt->buffer != NULL) {
5407 doc = xmlParseMemory(ctxt->buffer, ctxt->size);
5408 if (doc == NULL) {
5409 if (ctxt->error != NULL)
5410 ctxt->error(ctxt->userData,
5411 "xmlRelaxNGParse: could not parse schemas\n");
5412 ctxt->nbErrors++;
5413 return (NULL);
5414 }
5415 doc->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5416 ctxt->URL = xmlStrdup(BAD_CAST "in_memory_buffer");
5417 } else {
5418 if (ctxt->error != NULL)
5419 ctxt->error(ctxt->userData,
5420 "xmlRelaxNGParse: nothing to parse\n");
5421 ctxt->nbErrors++;
5422 return (NULL);
5423 }
5424 ctxt->document = doc;
5425
5426 /*
5427 * Some preprocessing of the document content
5428 */
5429 doc = xmlRelaxNGCleanupDoc(ctxt, doc);
5430 if (doc == NULL) {
5431 xmlFreeDoc(ctxt->document);
5432 ctxt->document = NULL;
5433 return(NULL);
5434 }
5435
Daniel Veillard6eadf632003-01-23 18:29:16 +00005436 /*
5437 * Then do the parsing for good
5438 */
5439 root = xmlDocGetRootElement(doc);
5440 if (root == NULL) {
5441 if (ctxt->error != NULL)
5442 ctxt->error(ctxt->userData, "xmlRelaxNGParse: %s is empty\n",
5443 ctxt->URL);
5444 ctxt->nbErrors++;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005445 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005446 return (NULL);
5447 }
5448 ret = xmlRelaxNGParseDocument(ctxt, root);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005449 if (ret == NULL) {
5450 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005451 return(NULL);
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005452 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005453
5454 /*
5455 * Check the ref/defines links
5456 */
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00005457 /*
5458 * try to preprocess interleaves
5459 */
5460 if (ctxt->interleaves != NULL) {
5461 xmlHashScan(ctxt->interleaves,
5462 (xmlHashScanner)xmlRelaxNGComputeInterleaves, ctxt);
5463 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005464
5465 /*
5466 * if there was a parsing error return NULL
5467 */
5468 if (ctxt->nbErrors > 0) {
5469 xmlRelaxNGFree(ret);
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005470 ctxt->document = NULL;
5471 xmlFreeDoc(doc);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005472 return(NULL);
5473 }
5474
5475 /*
5476 * Transfer the pointer for cleanup at the schema level.
5477 */
5478 ret->doc = doc;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005479 ctxt->document = NULL;
5480 ret->documents = ctxt->documents;
5481 ctxt->documents = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00005482
Daniel Veillarde2a5a082003-02-02 14:35:17 +00005483 ret->includes = ctxt->includes;
5484 ctxt->includes = NULL;
Daniel Veillard419a7682003-02-03 23:22:49 +00005485 ret->defNr = ctxt->defNr;
5486 ret->defTab = ctxt->defTab;
5487 ctxt->defTab = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005488
5489 return (ret);
5490}
5491
5492/**
5493 * xmlRelaxNGSetParserErrors:
5494 * @ctxt: a Relax-NG validation context
5495 * @err: the error callback
5496 * @warn: the warning callback
5497 * @ctx: contextual data for the callbacks
5498 *
5499 * Set the callback functions used to handle errors for a validation context
5500 */
5501void
5502xmlRelaxNGSetParserErrors(xmlRelaxNGParserCtxtPtr ctxt,
5503 xmlRelaxNGValidityErrorFunc err,
5504 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
5505 if (ctxt == NULL)
5506 return;
5507 ctxt->error = err;
5508 ctxt->warning = warn;
5509 ctxt->userData = ctx;
5510}
5511/************************************************************************
5512 * *
5513 * Dump back a compiled form *
5514 * *
5515 ************************************************************************/
5516static void xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define);
5517
5518/**
5519 * xmlRelaxNGDumpDefines:
5520 * @output: the file output
5521 * @defines: a list of define structures
5522 *
5523 * Dump a RelaxNG structure back
5524 */
5525static void
5526xmlRelaxNGDumpDefines(FILE * output, xmlRelaxNGDefinePtr defines) {
5527 while (defines != NULL) {
5528 xmlRelaxNGDumpDefine(output, defines);
5529 defines = defines->next;
5530 }
5531}
5532
5533/**
5534 * xmlRelaxNGDumpDefine:
5535 * @output: the file output
5536 * @define: a define structure
5537 *
5538 * Dump a RelaxNG structure back
5539 */
5540static void
5541xmlRelaxNGDumpDefine(FILE * output, xmlRelaxNGDefinePtr define) {
5542 if (define == NULL)
5543 return;
5544 switch(define->type) {
5545 case XML_RELAXNG_EMPTY:
5546 fprintf(output, "<empty/>\n");
5547 break;
5548 case XML_RELAXNG_NOT_ALLOWED:
5549 fprintf(output, "<notAllowed/>\n");
5550 break;
5551 case XML_RELAXNG_TEXT:
5552 fprintf(output, "<text/>\n");
5553 break;
5554 case XML_RELAXNG_ELEMENT:
5555 fprintf(output, "<element>\n");
5556 if (define->name != NULL) {
5557 fprintf(output, "<name");
5558 if (define->ns != NULL)
5559 fprintf(output, " ns=\"%s\"", define->ns);
5560 fprintf(output, ">%s</name>\n", define->name);
5561 }
5562 xmlRelaxNGDumpDefines(output, define->attrs);
5563 xmlRelaxNGDumpDefines(output, define->content);
5564 fprintf(output, "</element>\n");
5565 break;
5566 case XML_RELAXNG_LIST:
5567 fprintf(output, "<list>\n");
5568 xmlRelaxNGDumpDefines(output, define->content);
5569 fprintf(output, "</list>\n");
5570 break;
5571 case XML_RELAXNG_ONEORMORE:
5572 fprintf(output, "<oneOrMore>\n");
5573 xmlRelaxNGDumpDefines(output, define->content);
5574 fprintf(output, "</oneOrMore>\n");
5575 break;
5576 case XML_RELAXNG_ZEROORMORE:
5577 fprintf(output, "<zeroOrMore>\n");
5578 xmlRelaxNGDumpDefines(output, define->content);
5579 fprintf(output, "</zeroOrMore>\n");
5580 break;
5581 case XML_RELAXNG_CHOICE:
5582 fprintf(output, "<choice>\n");
5583 xmlRelaxNGDumpDefines(output, define->content);
5584 fprintf(output, "</choice>\n");
5585 break;
5586 case XML_RELAXNG_GROUP:
5587 fprintf(output, "<group>\n");
5588 xmlRelaxNGDumpDefines(output, define->content);
5589 fprintf(output, "</group>\n");
5590 break;
5591 case XML_RELAXNG_INTERLEAVE:
5592 fprintf(output, "<interleave>\n");
5593 xmlRelaxNGDumpDefines(output, define->content);
5594 fprintf(output, "</interleave>\n");
5595 break;
5596 case XML_RELAXNG_OPTIONAL:
5597 fprintf(output, "<optional>\n");
5598 xmlRelaxNGDumpDefines(output, define->content);
5599 fprintf(output, "</optional>\n");
5600 break;
5601 case XML_RELAXNG_ATTRIBUTE:
5602 fprintf(output, "<attribute>\n");
5603 xmlRelaxNGDumpDefines(output, define->content);
5604 fprintf(output, "</attribute>\n");
5605 break;
5606 case XML_RELAXNG_DEF:
5607 fprintf(output, "<define");
5608 if (define->name != NULL)
5609 fprintf(output, " name=\"%s\"", define->name);
5610 fprintf(output, ">\n");
5611 xmlRelaxNGDumpDefines(output, define->content);
5612 fprintf(output, "</define>\n");
5613 break;
5614 case XML_RELAXNG_REF:
5615 fprintf(output, "<ref");
5616 if (define->name != NULL)
5617 fprintf(output, " name=\"%s\"", define->name);
5618 fprintf(output, ">\n");
5619 xmlRelaxNGDumpDefines(output, define->content);
5620 fprintf(output, "</ref>\n");
5621 break;
Daniel Veillard419a7682003-02-03 23:22:49 +00005622 case XML_RELAXNG_PARENTREF:
5623 fprintf(output, "<parentRef");
5624 if (define->name != NULL)
5625 fprintf(output, " name=\"%s\"", define->name);
5626 fprintf(output, ">\n");
5627 xmlRelaxNGDumpDefines(output, define->content);
5628 fprintf(output, "</parentRef>\n");
5629 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005630 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard416589a2003-02-17 17:25:42 +00005631 fprintf(output, "<externalRef>");
Daniel Veillarde431a272003-01-29 23:02:33 +00005632 xmlRelaxNGDumpDefines(output, define->content);
5633 fprintf(output, "</externalRef>\n");
5634 break;
5635 case XML_RELAXNG_DATATYPE:
Daniel Veillard6eadf632003-01-23 18:29:16 +00005636 case XML_RELAXNG_VALUE:
5637 TODO
5638 break;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005639 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00005640 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00005641 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00005642 TODO
5643 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00005644 case XML_RELAXNG_NOOP:
5645 xmlRelaxNGDumpDefines(output, define->content);
5646 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005647 }
5648}
5649
5650/**
5651 * xmlRelaxNGDumpGrammar:
5652 * @output: the file output
5653 * @grammar: a grammar structure
5654 * @top: is this a top grammar
5655 *
5656 * Dump a RelaxNG structure back
5657 */
5658static void
5659xmlRelaxNGDumpGrammar(FILE * output, xmlRelaxNGGrammarPtr grammar, int top)
5660{
5661 if (grammar == NULL)
5662 return;
5663
5664 fprintf(output, "<grammar");
5665 if (top)
5666 fprintf(output,
5667 " xmlns=\"http://relaxng.org/ns/structure/1.0\"");
5668 switch(grammar->combine) {
5669 case XML_RELAXNG_COMBINE_UNDEFINED:
5670 break;
5671 case XML_RELAXNG_COMBINE_CHOICE:
5672 fprintf(output, " combine=\"choice\"");
5673 break;
5674 case XML_RELAXNG_COMBINE_INTERLEAVE:
5675 fprintf(output, " combine=\"interleave\"");
5676 break;
5677 default:
5678 fprintf(output, " <!-- invalid combine value -->");
5679 }
5680 fprintf(output, ">\n");
5681 if (grammar->start == NULL) {
5682 fprintf(output, " <!-- grammar had no start -->");
5683 } else {
5684 fprintf(output, "<start>\n");
5685 xmlRelaxNGDumpDefine(output, grammar->start);
5686 fprintf(output, "</start>\n");
5687 }
5688 /* TODO ? Dump the defines ? */
5689 fprintf(output, "</grammar>\n");
5690}
5691
5692/**
5693 * xmlRelaxNGDump:
5694 * @output: the file output
5695 * @schema: a schema structure
5696 *
5697 * Dump a RelaxNG structure back
5698 */
5699void
5700xmlRelaxNGDump(FILE * output, xmlRelaxNGPtr schema)
5701{
5702 if (schema == NULL) {
5703 fprintf(output, "RelaxNG empty or failed to compile\n");
5704 return;
5705 }
5706 fprintf(output, "RelaxNG: ");
5707 if (schema->doc == NULL) {
5708 fprintf(output, "no document\n");
5709 } else if (schema->doc->URL != NULL) {
5710 fprintf(output, "%s\n", schema->doc->URL);
5711 } else {
5712 fprintf(output, "\n");
5713 }
5714 if (schema->topgrammar == NULL) {
5715 fprintf(output, "RelaxNG has no top grammar\n");
5716 return;
5717 }
5718 xmlRelaxNGDumpGrammar(output, schema->topgrammar, 1);
5719}
5720
Daniel Veillardfebcca42003-02-16 15:44:18 +00005721/**
5722 * xmlRelaxNGDumpTree:
5723 * @output: the file output
5724 * @schema: a schema structure
5725 *
5726 * Dump the transformed RelaxNG tree.
5727 */
5728void
5729xmlRelaxNGDumpTree(FILE * output, xmlRelaxNGPtr schema)
5730{
5731 if (schema == NULL) {
5732 fprintf(output, "RelaxNG empty or failed to compile\n");
5733 return;
5734 }
5735 if (schema->doc == NULL) {
5736 fprintf(output, "no document\n");
5737 } else {
5738 xmlDocDump(output, schema->doc);
5739 }
5740}
5741
Daniel Veillard6eadf632003-01-23 18:29:16 +00005742/************************************************************************
5743 * *
5744 * Validation implementation *
5745 * *
5746 ************************************************************************/
5747static int xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
5748 xmlRelaxNGDefinePtr define);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005749static int xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5750 xmlRelaxNGDefinePtr define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00005751
5752/**
5753 * xmlRelaxNGSkipIgnored:
5754 * @ctxt: a schema validation context
5755 * @node: the top node.
5756 *
5757 * Skip ignorable nodes in that context
5758 *
5759 * Returns the new sibling or NULL in case of error.
5760 */
5761static xmlNodePtr
5762xmlRelaxNGSkipIgnored(xmlRelaxNGValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5763 xmlNodePtr node) {
5764 /*
5765 * TODO complete and handle entities
5766 */
5767 while ((node != NULL) &&
5768 ((node->type == XML_COMMENT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00005769 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00005770 ((node->type == XML_TEXT_NODE) &&
5771 (IS_BLANK_NODE(node))))) {
5772 node = node->next;
5773 }
5774 return(node);
5775}
5776
5777/**
Daniel Veillardedc91922003-01-26 00:52:04 +00005778 * xmlRelaxNGNormalize:
5779 * @ctxt: a schema validation context
5780 * @str: the string to normalize
5781 *
5782 * Implements the normalizeWhiteSpace( s ) function from
5783 * section 6.2.9 of the spec
5784 *
5785 * Returns the new string or NULL in case of error.
5786 */
5787static xmlChar *
5788xmlRelaxNGNormalize(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *str) {
5789 xmlChar *ret, *p;
5790 const xmlChar *tmp;
5791 int len;
5792
5793 if (str == NULL)
5794 return(NULL);
5795 tmp = str;
5796 while (*tmp != 0) tmp++;
5797 len = tmp - str;
5798
5799 ret = (xmlChar *) xmlMalloc((len + 1) * sizeof(xmlChar));
5800 if (ret == NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005801 if (ctxt != NULL) {
5802 VALID_CTXT();
5803 VALID_ERROR("xmlRelaxNGNormalize: out of memory\n");
5804 } else {
5805 xmlGenericError(xmlGenericErrorContext,
5806 "xmlRelaxNGNormalize: out of memory\n");
5807 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005808 return(NULL);
5809 }
5810 p = ret;
5811 while (IS_BLANK(*str)) str++;
5812 while (*str != 0) {
5813 if (IS_BLANK(*str)) {
5814 while (IS_BLANK(*str)) str++;
5815 if (*str == 0)
5816 break;
5817 *p++ = ' ';
5818 } else
5819 *p++ = *str++;
5820 }
5821 *p = 0;
5822 return(ret);
5823}
5824
5825/**
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005826 * xmlRelaxNGValidateDatatype:
5827 * @ctxt: a Relax-NG validation context
5828 * @value: the string value
5829 * @type: the datatype definition
5830 *
5831 * Validate the given value against the dataype
5832 *
5833 * Returns 0 if the validation succeeded or an error code.
5834 */
5835static int
5836xmlRelaxNGValidateDatatype(xmlRelaxNGValidCtxtPtr ctxt, const xmlChar *value,
5837 xmlRelaxNGDefinePtr define) {
5838 int ret;
5839 xmlRelaxNGTypeLibraryPtr lib;
5840
5841 if ((define == NULL) || (define->data == NULL)) {
5842 return(-1);
5843 }
5844 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5845 if (lib->check != NULL)
5846 ret = lib->check(lib->data, define->name, value);
5847 else
5848 ret = -1;
5849 if (ret < 0) {
5850 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005851 VALID_ERROR2("Internal: failed to validate type %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005852 return(-1);
5853 } else if (ret == 1) {
5854 ret = 0;
5855 } else {
5856 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005857 VALID_ERROR3("Type %s doesn't allow value %s\n", define->name, value);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005858 return(-1);
5859 ret = -1;
5860 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005861 if ((ret == 0) && (define->content != NULL)) {
5862 const xmlChar *oldvalue, *oldendvalue;
5863
5864 oldvalue = ctxt->state->value;
5865 oldendvalue = ctxt->state->endvalue;
5866 ctxt->state->value = (xmlChar *) value;
5867 ctxt->state->endvalue = NULL;
5868 ret = xmlRelaxNGValidateValue(ctxt, define->content);
5869 ctxt->state->value = (xmlChar *) oldvalue;
5870 ctxt->state->endvalue = (xmlChar *) oldendvalue;
5871 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00005872 return(ret);
5873}
5874
5875/**
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005876 * xmlRelaxNGNextValue:
5877 * @ctxt: a Relax-NG validation context
5878 *
5879 * Skip to the next value when validating within a list
5880 *
5881 * Returns 0 if the operation succeeded or an error code.
5882 */
5883static int
5884xmlRelaxNGNextValue(xmlRelaxNGValidCtxtPtr ctxt) {
5885 xmlChar *cur;
5886
5887 cur = ctxt->state->value;
5888 if ((cur == NULL) || (ctxt->state->endvalue == NULL)) {
5889 ctxt->state->value = NULL;
Daniel Veillarde5b110b2003-02-04 14:43:39 +00005890 ctxt->state->endvalue = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005891 return(0);
5892 }
5893 while (*cur != 0) cur++;
5894 while ((cur != ctxt->state->endvalue) && (*cur == 0)) cur++;
5895 if (cur == ctxt->state->endvalue)
5896 ctxt->state->value = NULL;
5897 else
5898 ctxt->state->value = cur;
5899 return(0);
5900}
5901
5902/**
5903 * xmlRelaxNGValidateValueList:
5904 * @ctxt: a Relax-NG validation context
5905 * @defines: the list of definitions to verify
5906 *
5907 * Validate the given set of definitions for the current value
5908 *
5909 * Returns 0 if the validation succeeded or an error code.
5910 */
5911static int
5912xmlRelaxNGValidateValueList(xmlRelaxNGValidCtxtPtr ctxt,
5913 xmlRelaxNGDefinePtr defines) {
5914 int ret = 0;
5915
5916 while (defines != NULL) {
5917 ret = xmlRelaxNGValidateValue(ctxt, defines);
5918 if (ret != 0)
5919 break;
5920 defines = defines->next;
5921 }
5922 return(ret);
5923}
5924
5925/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00005926 * xmlRelaxNGValidateValue:
5927 * @ctxt: a Relax-NG validation context
5928 * @define: the definition to verify
5929 *
5930 * Validate the given definition for the current value
5931 *
5932 * Returns 0 if the validation succeeded or an error code.
5933 */
5934static int
5935xmlRelaxNGValidateValue(xmlRelaxNGValidCtxtPtr ctxt,
5936 xmlRelaxNGDefinePtr define) {
Daniel Veillardedc91922003-01-26 00:52:04 +00005937 int ret = 0, oldflags;
Daniel Veillard6eadf632003-01-23 18:29:16 +00005938 xmlChar *value;
5939
5940 value = ctxt->state->value;
5941 switch (define->type) {
Daniel Veillardd4310742003-02-18 21:12:46 +00005942 case XML_RELAXNG_EMPTY: {
5943 if ((value != NULL) && (value[0] != 0)) {
5944 int idx = 0;
5945
5946 while (IS_BLANK(value[idx]))
5947 idx++;
5948 if (value[idx] != 0)
5949 ret = -1;
5950 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005951 break;
Daniel Veillardd4310742003-02-18 21:12:46 +00005952 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00005953 case XML_RELAXNG_TEXT:
5954 break;
Daniel Veillardedc91922003-01-26 00:52:04 +00005955 case XML_RELAXNG_VALUE: {
5956 if (!xmlStrEqual(value, define->value)) {
5957 if (define->name != NULL) {
Daniel Veillardea3f3982003-01-26 19:45:18 +00005958 xmlRelaxNGTypeLibraryPtr lib;
5959
5960 lib = (xmlRelaxNGTypeLibraryPtr) define->data;
5961 if ((lib != NULL) && (lib->comp != NULL))
5962 ret = lib->comp(lib->data, define->name, value,
5963 define->value);
5964 else
5965 ret = -1;
5966 if (ret < 0) {
5967 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00005968 VALID_ERROR2("Internal: failed to compare type %s\n",
Daniel Veillardea3f3982003-01-26 19:45:18 +00005969 define->name);
5970 return(-1);
5971 } else if (ret == 1) {
5972 ret = 0;
5973 } else {
5974 ret = -1;
5975 }
Daniel Veillardedc91922003-01-26 00:52:04 +00005976 } else {
5977 xmlChar *nval, *nvalue;
5978
5979 /*
5980 * TODO: trivial optimizations are possible by
5981 * computing at compile-time
5982 */
5983 nval = xmlRelaxNGNormalize(ctxt, define->value);
5984 nvalue = xmlRelaxNGNormalize(ctxt, value);
5985
Daniel Veillardea3f3982003-01-26 19:45:18 +00005986 if ((nval == NULL) || (nvalue == NULL) ||
5987 (!xmlStrEqual(nval, nvalue)))
Daniel Veillardedc91922003-01-26 00:52:04 +00005988 ret = -1;
5989 if (nval != NULL)
5990 xmlFree(nval);
5991 if (nvalue != NULL)
5992 xmlFree(nvalue);
5993 }
5994 }
Daniel Veillard416589a2003-02-17 17:25:42 +00005995 if (ret == 0)
5996 xmlRelaxNGNextValue(ctxt);
Daniel Veillardedc91922003-01-26 00:52:04 +00005997 break;
5998 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00005999 case XML_RELAXNG_DATATYPE: {
6000 ret = xmlRelaxNGValidateDatatype(ctxt, value, define);
6001 if (ret == 0)
6002 xmlRelaxNGNextValue(ctxt);
6003
6004 break;
6005 }
6006 case XML_RELAXNG_CHOICE: {
6007 xmlRelaxNGDefinePtr list = define->content;
6008 xmlChar *oldvalue;
6009
6010 oldflags = ctxt->flags;
6011 ctxt->flags |= FLAGS_IGNORABLE;
6012
6013 oldvalue = ctxt->state->value;
6014 while (list != NULL) {
6015 ret = xmlRelaxNGValidateValue(ctxt, list);
6016 if (ret == 0) {
6017 break;
6018 }
6019 ctxt->state->value = oldvalue;
6020 list = list->next;
6021 }
6022 ctxt->flags = oldflags;
Daniel Veillard416589a2003-02-17 17:25:42 +00006023 if (ret == 0)
6024 xmlRelaxNGNextValue(ctxt);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006025 break;
6026 }
6027 case XML_RELAXNG_LIST: {
6028 xmlRelaxNGDefinePtr list = define->content;
6029 xmlChar *oldvalue, *oldend, *val, *cur;
Daniel Veillard416589a2003-02-17 17:25:42 +00006030#ifdef DEBUG_LIST
6031 int nb_values = 0;
6032#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006033
6034 oldvalue = ctxt->state->value;
6035 oldend = ctxt->state->endvalue;
6036
6037 val = xmlStrdup(oldvalue);
6038 if (val == NULL) {
Daniel Veillardd4310742003-02-18 21:12:46 +00006039 val = xmlStrdup(BAD_CAST "");
6040 }
6041 if (val == NULL) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006042 VALID_CTXT();
6043 VALID_ERROR("Internal: no state\n");
6044 return(-1);
6045 }
6046 cur = val;
6047 while (*cur != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006048 if (IS_BLANK(*cur)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006049 *cur = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006050 cur++;
6051#ifdef DEBUG_LIST
6052 nb_values++;
6053#endif
6054 while (IS_BLANK(*cur))
6055 *cur++ = 0;
6056 } else
6057 cur++;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006058 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006059#ifdef DEBUG_LIST
6060 xmlGenericError(xmlGenericErrorContext,
6061 "list value: '%s' found %d items\n", oldvalue, nb_values);
6062 nb_values = 0;
6063#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006064 ctxt->state->endvalue = cur;
6065 cur = val;
6066 while ((*cur == 0) && (cur != ctxt->state->endvalue)) cur++;
6067
6068 ctxt->state->value = cur;
6069
6070 while (list != NULL) {
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006071 if (ctxt->state->value == ctxt->state->endvalue)
6072 ctxt->state->value = NULL;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006073 ret = xmlRelaxNGValidateValue(ctxt, list);
6074 if (ret != 0) {
Daniel Veillard416589a2003-02-17 17:25:42 +00006075#ifdef DEBUG_LIST
6076 xmlGenericError(xmlGenericErrorContext,
6077 "Failed to validate value: '%s' with %d rule\n",
6078 ctxt->state->value, nb_values);
6079#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006080 break;
6081 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006082#ifdef DEBUG_LIST
6083 nb_values++;
6084#endif
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006085 list = list->next;
6086 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006087
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006088 if ((ret == 0) && (ctxt->state->value != NULL) &&
6089 (ctxt->state->value != ctxt->state->endvalue)) {
6090 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006091 VALID_ERROR2("Extra data in list: %s\n", ctxt->state->value);
Daniel Veillardc6e997c2003-01-27 12:35:42 +00006092 ret = -1;
6093 }
6094 xmlFree(val);
6095 ctxt->state->value = oldvalue;
6096 ctxt->state->endvalue = oldend;
6097 break;
6098 }
6099 case XML_RELAXNG_ONEORMORE:
6100 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6101 if (ret != 0) {
6102 break;
6103 }
6104 /* no break on purpose */
6105 case XML_RELAXNG_ZEROORMORE: {
6106 xmlChar *cur, *temp;
6107
6108 oldflags = ctxt->flags;
6109 ctxt->flags |= FLAGS_IGNORABLE;
6110 cur = ctxt->state->value;
6111 temp = NULL;
6112 while ((cur != NULL) && (cur != ctxt->state->endvalue) &&
6113 (temp != cur)) {
6114 temp = cur;
6115 ret = xmlRelaxNGValidateValueList(ctxt, define->content);
6116 if (ret != 0) {
6117 ctxt->state->value = temp;
6118 ret = 0;
6119 break;
6120 }
6121 cur = ctxt->state->value;
6122 }
6123 ctxt->flags = oldflags;
6124 break;
6125 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006126 case XML_RELAXNG_EXCEPT: {
6127 xmlRelaxNGDefinePtr list;
6128
6129 list = define->content;
6130 while (list != NULL) {
6131 ret = xmlRelaxNGValidateValue(ctxt, list);
6132 if (ret == 0) {
6133 ret = -1;
6134 break;
6135 } else
6136 ret = 0;
6137 list = list->next;
6138 }
6139 break;
6140 }
Daniel Veillardd4310742003-02-18 21:12:46 +00006141 case XML_RELAXNG_GROUP: {
6142 xmlRelaxNGDefinePtr list;
6143
6144 list = define->content;
6145 while (list != NULL) {
6146 ret = xmlRelaxNGValidateValue(ctxt, list);
6147 if (ret != 0) {
6148 ret = -1;
6149 break;
6150 } else
6151 ret = 0;
6152 list = list->next;
6153 }
6154 break;
6155 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006156 default:
6157 TODO
6158 ret = -1;
6159 }
6160 return(ret);
6161}
6162
6163/**
6164 * xmlRelaxNGValidateValueContent:
6165 * @ctxt: a Relax-NG validation context
6166 * @defines: the list of definitions to verify
6167 *
6168 * Validate the given definitions for the current value
6169 *
6170 * Returns 0 if the validation succeeded or an error code.
6171 */
6172static int
6173xmlRelaxNGValidateValueContent(xmlRelaxNGValidCtxtPtr ctxt,
6174 xmlRelaxNGDefinePtr defines) {
6175 int ret = 0;
6176
6177 while (defines != NULL) {
6178 ret = xmlRelaxNGValidateValue(ctxt, defines);
6179 if (ret != 0)
6180 break;
6181 defines = defines->next;
6182 }
6183 return(ret);
6184}
6185
6186/**
Daniel Veillard144fae12003-02-03 13:17:57 +00006187 * xmlRelaxNGAttributeMatch:
6188 * @ctxt: a Relax-NG validation context
6189 * @define: the definition to check
6190 * @prop: the attribute
6191 *
6192 * Check if the attribute matches the definition nameClass
6193 *
6194 * Returns 1 if the attribute matches, 0 if no, or -1 in case of error
6195 */
6196static int
6197xmlRelaxNGAttributeMatch(xmlRelaxNGValidCtxtPtr ctxt,
6198 xmlRelaxNGDefinePtr define,
6199 xmlAttrPtr prop) {
6200 int ret;
6201
6202 if (define->name != NULL) {
6203 if (!xmlStrEqual(define->name, prop->name))
6204 return(0);
6205 }
6206 if (define->ns != NULL) {
6207 if (define->ns[0] == 0) {
6208 if (prop->ns != NULL)
6209 return(0);
6210 } else {
6211 if ((prop->ns == NULL) ||
6212 (!xmlStrEqual(define->ns, prop->ns->href)))
6213 return(0);
6214 }
6215 }
6216 if (define->nameClass == NULL)
6217 return(1);
6218 define = define->nameClass;
6219 if (define->type == XML_RELAXNG_EXCEPT) {
6220 xmlRelaxNGDefinePtr list;
6221
6222 list = define->content;
6223 while (list != NULL) {
6224 ret = xmlRelaxNGAttributeMatch(ctxt, list, prop);
6225 if (ret == 1)
6226 return(0);
6227 if (ret < 0)
6228 return(ret);
6229 list = list->next;
6230 }
6231 } else {
6232 TODO
6233 }
6234 return(1);
6235}
6236
6237/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006238 * xmlRelaxNGValidateAttribute:
6239 * @ctxt: a Relax-NG validation context
6240 * @define: the definition to verify
6241 *
6242 * Validate the given attribute definition for that node
6243 *
6244 * Returns 0 if the validation succeeded or an error code.
6245 */
6246static int
6247xmlRelaxNGValidateAttribute(xmlRelaxNGValidCtxtPtr ctxt,
6248 xmlRelaxNGDefinePtr define) {
6249 int ret = 0, i;
6250 xmlChar *value, *oldvalue;
6251 xmlAttrPtr prop = NULL, tmp;
6252
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006253 if (ctxt->state->nbAttrLeft <= 0)
6254 return(-1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006255 if (define->name != NULL) {
6256 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6257 tmp = ctxt->state->attrs[i];
6258 if ((tmp != NULL) && (xmlStrEqual(define->name, tmp->name))) {
6259 if ((((define->ns == NULL) || (define->ns[0] == 0)) &&
6260 (tmp->ns == NULL)) ||
6261 ((tmp->ns != NULL) &&
6262 (xmlStrEqual(define->ns, tmp->ns->href)))) {
6263 prop = tmp;
6264 break;
6265 }
6266 }
6267 }
6268 if (prop != NULL) {
6269 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6270 oldvalue = ctxt->state->value;
6271 ctxt->state->value = value;
Daniel Veillard231d7912003-02-09 14:22:17 +00006272 ctxt->state->endvalue = NULL;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006273 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006274 if (ctxt->state->value != NULL)
6275 value = ctxt->state->value;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006276 if (value != NULL)
6277 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006278 ctxt->state->value = oldvalue;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006279 if (ret == 0) {
6280 /*
6281 * flag the attribute as processed
6282 */
6283 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006284 ctxt->state->nbAttrLeft--;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006285 }
6286 } else {
6287 ret = -1;
6288 }
6289#ifdef DEBUG
6290 xmlGenericError(xmlGenericErrorContext,
6291 "xmlRelaxNGValidateAttribute(%s): %d\n", define->name, ret);
6292#endif
6293 } else {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006294 for (i = 0;i < ctxt->state->nbAttrs;i++) {
6295 tmp = ctxt->state->attrs[i];
Daniel Veillard144fae12003-02-03 13:17:57 +00006296 if ((tmp != NULL) &&
6297 (xmlRelaxNGAttributeMatch(ctxt, define, tmp) == 1)) {
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006298 prop = tmp;
6299 break;
6300 }
6301 }
6302 if (prop != NULL) {
6303 value = xmlNodeListGetString(prop->doc, prop->children, 1);
6304 oldvalue = ctxt->state->value;
6305 ctxt->state->value = value;
6306 ret = xmlRelaxNGValidateValueContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006307 if (ctxt->state->value != NULL)
6308 value = ctxt->state->value;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006309 if (value != NULL)
6310 xmlFree(value);
Daniel Veillard231d7912003-02-09 14:22:17 +00006311 ctxt->state->value = oldvalue;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006312 if (ret == 0) {
6313 /*
6314 * flag the attribute as processed
6315 */
6316 ctxt->state->attrs[i] = NULL;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006317 ctxt->state->nbAttrLeft--;
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006318 }
6319 } else {
6320 ret = -1;
6321 }
6322#ifdef DEBUG
Daniel Veillard144fae12003-02-03 13:17:57 +00006323 if (define->ns != NULL) {
6324 xmlGenericError(xmlGenericErrorContext,
6325 "xmlRelaxNGValidateAttribute(nsName ns = %s): %d\n",
6326 define->ns, ret);
6327 } else {
6328 xmlGenericError(xmlGenericErrorContext,
6329 "xmlRelaxNGValidateAttribute(anyName): %d\n",
6330 ret);
6331 }
Daniel Veillard3b2e4e12003-02-03 08:52:58 +00006332#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006333 }
6334
6335 return(ret);
6336}
6337
6338/**
6339 * xmlRelaxNGValidateAttributeList:
6340 * @ctxt: a Relax-NG validation context
6341 * @define: the list of definition to verify
6342 *
6343 * Validate the given node against the list of attribute definitions
6344 *
6345 * Returns 0 if the validation succeeded or an error code.
6346 */
6347static int
6348xmlRelaxNGValidateAttributeList(xmlRelaxNGValidCtxtPtr ctxt,
6349 xmlRelaxNGDefinePtr defines) {
6350 int ret = 0;
6351 while (defines != NULL) {
6352 if (xmlRelaxNGValidateAttribute(ctxt, defines) != 0)
6353 ret = -1;
6354 defines = defines->next;
6355 }
6356 return(ret);
6357}
6358
6359/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006360 * xmlRelaxNGNodeMatchesList:
6361 * @node: the node
6362 * @list: a NULL terminated array of definitions
6363 *
6364 * Check if a node can be matched by one of the definitions
6365 *
6366 * Returns 1 if matches 0 otherwise
6367 */
6368static int
6369xmlRelaxNGNodeMatchesList(xmlNodePtr node, xmlRelaxNGDefinePtr *list) {
6370 xmlRelaxNGDefinePtr cur;
6371 int i = 0;
6372
6373 if ((node == NULL) || (list == NULL))
6374 return(0);
6375
6376 cur = list[i++];
6377 while (cur != NULL) {
6378 if ((node->type == XML_ELEMENT_NODE) &&
6379 (cur->type == XML_RELAXNG_ELEMENT)) {
6380 if (cur->name == NULL) {
6381 if ((node->ns != NULL) &&
6382 (xmlStrEqual(node->ns->href, cur->ns)))
6383 return(1);
6384 } else if (xmlStrEqual(cur->name, node->name)) {
6385 if ((cur->ns == NULL) || (cur->ns[0] == 0)) {
6386 if (node->ns == NULL)
6387 return(1);
6388 } else {
6389 if ((node->ns != NULL) &&
6390 (xmlStrEqual(node->ns->href, cur->ns)))
6391 return(1);
6392 }
6393 }
6394 } else if ((node->type == XML_TEXT_NODE) &&
6395 (cur->type == XML_RELAXNG_TEXT)) {
6396 return(1);
6397 }
6398 cur = list[i++];
6399 }
6400 return(0);
6401}
6402
6403/**
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006404 * xmlRelaxNGValidateInterleave:
6405 * @ctxt: a Relax-NG validation context
6406 * @define: the definition to verify
6407 *
6408 * Validate an interleave definition for a node.
6409 *
6410 * Returns 0 if the validation succeeded or an error code.
6411 */
6412static int
6413xmlRelaxNGValidateInterleave(xmlRelaxNGValidCtxtPtr ctxt,
6414 xmlRelaxNGDefinePtr define) {
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006415 int ret = 0, i, nbgroups, left;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006416 xmlRelaxNGPartitionPtr partitions;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006417 xmlRelaxNGInterleaveGroupPtr group = NULL;
Daniel Veillard3ebc7d42003-02-24 17:17:58 +00006418 xmlNodePtr cur, start, last = NULL, lastchg = NULL, lastelem;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006419 xmlNodePtr *list = NULL, *lasts = NULL;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006420
6421 if (define->data != NULL) {
6422 partitions = (xmlRelaxNGPartitionPtr) define->data;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006423 nbgroups = partitions->nbgroups;
6424 left = nbgroups;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006425 } else {
6426 VALID_CTXT();
6427 VALID_ERROR("Internal: interleave block has no data\n");
6428 return(-1);
6429 }
6430
6431 /*
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006432 * Build arrays to store the first and last node of the chain
6433 * pertaining to each group
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006434 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006435 list = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
6436 if (list == NULL) {
6437 VALID_CTXT();
6438 VALID_ERROR("Internal: out of memory in interleave check\n");
6439 return(-1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006440 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006441 memset(list, 0, nbgroups * sizeof(xmlNodePtr));
6442 lasts = (xmlNodePtr *) xmlMalloc(nbgroups * sizeof(xmlNodePtr));
6443 if (lasts == NULL) {
6444 VALID_CTXT();
6445 VALID_ERROR("Internal: out of memory in interleave check\n");
6446 return(-1);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006447 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006448 memset(lasts, 0, nbgroups * sizeof(xmlNodePtr));
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006449
6450 /*
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006451 * Walk the sequence of children finding the right group and
6452 * sorting them in sequences.
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006453 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006454 cur = ctxt->state->seq;
6455 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
6456 start = cur;
6457 while (cur != NULL) {
6458 ctxt->state->seq = cur;
6459 for (i = 0;i < nbgroups;i++) {
6460 group = partitions->groups[i];
6461 if (group == NULL)
6462 continue;
6463 if (xmlRelaxNGNodeMatchesList(cur, group->defs))
6464 break;
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006465 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006466 /*
6467 * We break as soon as an element not matched is found
6468 */
6469 if (i >= nbgroups) {
6470 break;
6471 }
6472 if (lasts[i] != NULL) {
6473 lasts[i]->next = cur;
6474 lasts[i] = cur;
6475 } else {
6476 list[i] = cur;
6477 lasts[i] = cur;
6478 }
6479 if (cur->next != NULL)
6480 lastchg = cur->next;
6481 else
6482 lastchg = cur;
6483 cur = xmlRelaxNGSkipIgnored(ctxt, cur->next);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006484 }
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006485 if (ret != 0) {
6486 VALID_CTXT();
6487 VALID_ERROR("Invalid sequence in interleave\n");
6488 ret = -1;
6489 goto done;
6490 }
6491 lastelem = cur;
6492 for (i = 0;i < nbgroups;i++) {
6493 group = partitions->groups[i];
6494 if (lasts[i] != NULL) {
6495 last = lasts[i]->next;
6496 lasts[i]->next = NULL;
6497 }
6498 ctxt->state->seq = list[i];
6499 ret = xmlRelaxNGValidateDefinition(ctxt, group->rule);
6500 if (ret != 0)
6501 break;
6502 cur = ctxt->state->seq;
6503 cur = xmlRelaxNGSkipIgnored(ctxt, cur);
6504 if (cur != NULL) {
6505 VALID_CTXT();
6506 VALID_ERROR2("Extra element %s in interleave\n", cur->name);
6507 ret = -1;
6508 goto done;
6509 }
6510 if (lasts[i] != NULL) {
6511 lasts[i]->next = last;
6512 }
6513 }
6514 ctxt->state->seq = lastelem;
6515 if (ret != 0) {
6516 VALID_CTXT();
6517 VALID_ERROR("Invalid sequence in interleave\n");
6518 ret = -1;
6519 goto done;
6520 }
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006521
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006522done:
6523 /*
6524 * builds the next links chain from the prev one
6525 */
6526 cur = lastchg;
6527 while (cur != NULL) {
6528 if ((cur == start) || (cur->prev == NULL))
6529 break;
6530 cur->prev->next = cur;
6531 cur = cur->prev;
6532 }
6533
6534 xmlFree(list);
6535 xmlFree(lasts);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006536 return(ret);
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006537}
6538
6539/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006540 * xmlRelaxNGValidateElementContent:
6541 * @ctxt: a Relax-NG validation context
6542 * @define: the list of definition to verify
6543 *
6544 * Validate the given node content against the (list) of definitions
6545 *
6546 * Returns 0 if the validation succeeded or an error code.
6547 */
6548static int
6549xmlRelaxNGValidateElementContent(xmlRelaxNGValidCtxtPtr ctxt,
6550 xmlRelaxNGDefinePtr defines) {
6551 int ret = 0, res;
6552
6553 if (ctxt->state == NULL) {
6554 VALID_CTXT();
6555 VALID_ERROR("Internal: no state\n");
6556 return(-1);
6557 }
6558 while (defines != NULL) {
6559 res = xmlRelaxNGValidateDefinition(ctxt, defines);
6560 if (res < 0)
6561 ret = -1;
6562 defines = defines->next;
6563 }
6564
6565 return(ret);
6566}
6567
6568/**
Daniel Veillard416589a2003-02-17 17:25:42 +00006569 * xmlRelaxNGElementMatch:
6570 * @ctxt: a Relax-NG validation context
6571 * @define: the definition to check
6572 * @elem: the element
6573 *
6574 * Check if the element matches the definition nameClass
6575 *
6576 * Returns 1 if the element matches, 0 if no, or -1 in case of error
6577 */
6578static int
6579xmlRelaxNGElementMatch(xmlRelaxNGValidCtxtPtr ctxt,
6580 xmlRelaxNGDefinePtr define,
6581 xmlNodePtr elem) {
6582 int ret, oldflags;
6583
6584 if (define->name != NULL) {
6585 if (!xmlStrEqual(elem->name, define->name)) {
6586 VALID_CTXT();
6587 VALID_ERROR3("Expecting element %s, got %s\n",
6588 define->name, elem->name);
6589 return(0);
6590 }
6591 }
6592 if ((define->ns != NULL) && (define->ns[0] != 0)) {
6593 if (elem->ns == NULL) {
6594 VALID_CTXT();
6595 VALID_ERROR2("Expecting a namespace for element %s\n",
6596 elem->name);
6597 return(0);
6598 } else if (!xmlStrEqual(elem->ns->href, define->ns)) {
6599 VALID_CTXT();
6600 VALID_ERROR3("Expecting element %s has wrong namespace: expecting %s\n",
6601 elem->name, define->ns);
6602 return(0);
6603 }
Daniel Veillard8fe98712003-02-19 00:19:14 +00006604 } else if ((elem->ns != NULL) && (define->ns != NULL) &&
6605 (define->name == NULL)) {
6606 VALID_CTXT();
6607 VALID_ERROR2("Expecting no namespace for element %s\n",
6608 define->name);
6609 return(0);
6610 } else if ((elem->ns != NULL) && (define->name != NULL)) {
6611 VALID_CTXT();
6612 VALID_ERROR2("Expecting no namespace for element %s\n",
6613 define->name);
6614 return(0);
Daniel Veillard416589a2003-02-17 17:25:42 +00006615 }
6616
6617 if (define->nameClass == NULL)
6618 return(1);
6619
6620 define = define->nameClass;
6621 if (define->type == XML_RELAXNG_EXCEPT) {
6622 xmlRelaxNGDefinePtr list;
6623 oldflags = ctxt->flags;
6624 ctxt->flags |= FLAGS_IGNORABLE;
6625
6626 list = define->content;
6627 while (list != NULL) {
6628 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6629 if (ret == 1) {
6630 ctxt->flags = oldflags;
6631 return(0);
6632 }
6633 if (ret < 0) {
6634 ctxt->flags = oldflags;
6635 return(ret);
6636 }
6637 list = list->next;
6638 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006639 ret = 1;
6640 ctxt->flags = oldflags;
6641 } else if (define->type == XML_RELAXNG_CHOICE) {
6642 xmlRelaxNGDefinePtr list;
6643 oldflags = ctxt->flags;
6644 ctxt->flags |= FLAGS_IGNORABLE;
6645
6646 list = define->nameClass;
6647 while (list != NULL) {
6648 ret = xmlRelaxNGElementMatch(ctxt, list, elem);
6649 if (ret == 1) {
6650 ctxt->flags = oldflags;
6651 return(1);
6652 }
6653 if (ret < 0) {
6654 ctxt->flags = oldflags;
6655 return(ret);
6656 }
6657 list = list->next;
6658 }
6659 ret = 0;
Daniel Veillard416589a2003-02-17 17:25:42 +00006660 ctxt->flags = oldflags;
6661 } else {
6662 TODO
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006663 ret = -1;
Daniel Veillard416589a2003-02-17 17:25:42 +00006664 }
Daniel Veillard2e9b1652003-02-19 13:29:45 +00006665 return(ret);
Daniel Veillard416589a2003-02-17 17:25:42 +00006666}
6667
6668/**
Daniel Veillard6eadf632003-01-23 18:29:16 +00006669 * xmlRelaxNGValidateDefinition:
6670 * @ctxt: a Relax-NG validation context
6671 * @define: the definition to verify
6672 *
6673 * Validate the current node against the definition
6674 *
6675 * Returns 0 if the validation succeeded or an error code.
6676 */
6677static int
6678xmlRelaxNGValidateDefinition(xmlRelaxNGValidCtxtPtr ctxt,
6679 xmlRelaxNGDefinePtr define) {
6680 xmlNodePtr node;
6681 int ret = 0, i, tmp, oldflags;
6682 xmlRelaxNGValidStatePtr oldstate, state;
6683
6684 if (define == NULL) {
6685 VALID_CTXT();
6686 VALID_ERROR("internal error: define == NULL\n");
6687 return(-1);
6688 }
6689 if (ctxt->state != NULL) {
6690 node = ctxt->state->seq;
6691 } else {
6692 node = NULL;
6693 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006694#ifdef DEBUG
6695 for (i = 0;i < ctxt->depth;i++)
6696 xmlGenericError(xmlGenericErrorContext, " ");
6697 xmlGenericError(xmlGenericErrorContext,
6698 "Start validating %s ", xmlRelaxNGDefName(define));
6699 if (define->name != NULL)
6700 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
6701 if ((node != NULL) && (node->name != NULL))
6702 xmlGenericError(xmlGenericErrorContext, "on %s\n", node->name);
6703 else
6704 xmlGenericError(xmlGenericErrorContext, "\n");
6705#endif
6706 ctxt->depth++;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006707 switch (define->type) {
6708 case XML_RELAXNG_EMPTY:
Daniel Veillardd4310742003-02-18 21:12:46 +00006709 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006710 if (node != NULL) {
6711 VALID_CTXT();
6712 VALID_ERROR("Expecting an empty element\n");
Daniel Veillard231d7912003-02-09 14:22:17 +00006713 ret = -1;
6714 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006715 }
Daniel Veillard231d7912003-02-09 14:22:17 +00006716 ret = 0;
6717 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006718 case XML_RELAXNG_NOT_ALLOWED:
Daniel Veillard231d7912003-02-09 14:22:17 +00006719 ret = -1;
6720 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006721 case XML_RELAXNG_TEXT:
Daniel Veillardce14fa52003-02-19 17:32:48 +00006722#if 0
Daniel Veillard231d7912003-02-09 14:22:17 +00006723 if (node == NULL) {
6724 ret = 0;
6725 break;
6726 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006727#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006728 while ((node != NULL) &&
6729 ((node->type == XML_TEXT_NODE) ||
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006730 (node->type == XML_COMMENT_NODE) ||
6731 (node->type == XML_PI_NODE) ||
Daniel Veillard6eadf632003-01-23 18:29:16 +00006732 (node->type == XML_CDATA_SECTION_NODE)))
6733 node = node->next;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006734#if 0
Daniel Veillard276be4a2003-01-24 01:03:34 +00006735 if (node == ctxt->state->seq) {
6736 VALID_CTXT();
6737 VALID_ERROR("Expecting text content\n");
6738 ret = -1;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006739 }
Daniel Veillardce14fa52003-02-19 17:32:48 +00006740#endif
Daniel Veillard276be4a2003-01-24 01:03:34 +00006741 ctxt->state->seq = node;
6742 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006743 case XML_RELAXNG_ELEMENT:
6744 node = xmlRelaxNGSkipIgnored(ctxt, node);
Daniel Veillard231d7912003-02-09 14:22:17 +00006745 if (node == NULL) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006746 VALID_CTXT();
Daniel Veillard231d7912003-02-09 14:22:17 +00006747 VALID_ERROR("Expecting an element, got empty\n");
6748 ret = -1;
6749 break;
6750 }
6751 if (node->type != XML_ELEMENT_NODE) {
6752 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006753 VALID_ERROR2("Expecting an element got %d type\n", node->type);
Daniel Veillard231d7912003-02-09 14:22:17 +00006754 ret = -1;
6755 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006756 }
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006757 /*
6758 * This node was already validated successfully against
6759 * this definition.
6760 */
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006761 if (node->_private == define) {
6762 ctxt->state->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006763 break;
Daniel Veillardc64b8e92003-02-24 11:47:13 +00006764 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006765
6766 ret = xmlRelaxNGElementMatch(ctxt, define, node);
6767 if (ret <= 0) {
6768 ret = -1;
6769 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006770 }
Daniel Veillard416589a2003-02-17 17:25:42 +00006771 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006772
6773 state = xmlRelaxNGNewValidState(ctxt, node);
6774 if (state == NULL) {
Daniel Veillard231d7912003-02-09 14:22:17 +00006775 ret = -1;
6776 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006777 }
6778
6779 oldstate = ctxt->state;
6780 ctxt->state = state;
6781 if (define->attrs != NULL) {
6782 tmp = xmlRelaxNGValidateAttributeList(ctxt, define->attrs);
Daniel Veillard231d7912003-02-09 14:22:17 +00006783 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006784 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006785#ifdef DEBUG
6786 xmlGenericError(xmlGenericErrorContext,
6787 "E: Element %s failed to validate attributes\n",
6788 node->name);
6789#endif
6790 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006791 }
6792 if (define->content != NULL) {
6793 tmp = xmlRelaxNGValidateElementContent(ctxt, define->content);
Daniel Veillard231d7912003-02-09 14:22:17 +00006794 if (tmp != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006795 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006796#ifdef DEBUG
6797 xmlGenericError(xmlGenericErrorContext,
6798 "E: Element %s failed to validate element content\n",
6799 node->name);
6800#endif
6801 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006802 }
6803 state = ctxt->state;
6804 if (state->seq != NULL) {
6805 state->seq = xmlRelaxNGSkipIgnored(ctxt, state->seq);
6806 if (state->seq != NULL) {
6807 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006808 VALID_ERROR3("Extra content for element %s: %s\n",
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006809 node->name, state->seq->name);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006810 ret = -1;
Daniel Veillard231d7912003-02-09 14:22:17 +00006811#ifdef DEBUG
6812 xmlGenericError(xmlGenericErrorContext,
6813 "E: Element %s has extra content: %s\n",
6814 node->name, state->seq->name);
6815#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00006816 }
6817 }
6818 for (i = 0;i < state->nbAttrs;i++) {
6819 if (state->attrs[i] != NULL) {
6820 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00006821 VALID_ERROR3("Invalid attribute %s for element %s\n",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006822 state->attrs[i]->name, node->name);
6823 ret = -1;
6824 }
6825 }
6826 ctxt->state = oldstate;
6827 xmlRelaxNGFreeValidState(state);
6828 if (oldstate != NULL)
Daniel Veillarde5b110b2003-02-04 14:43:39 +00006829 oldstate->seq = xmlRelaxNGSkipIgnored(ctxt, node->next);
6830 if (ret == 0)
6831 node->_private = define;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006832
6833
6834#ifdef DEBUG
6835 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard231d7912003-02-09 14:22:17 +00006836 "xmlRelaxNGValidateDefinition(): validated %s : %d",
Daniel Veillard6eadf632003-01-23 18:29:16 +00006837 node->name, ret);
Daniel Veillard231d7912003-02-09 14:22:17 +00006838 if (oldstate == NULL)
6839 xmlGenericError(xmlGenericErrorContext, ": no state\n");
6840 else if (oldstate->seq == NULL)
6841 xmlGenericError(xmlGenericErrorContext, ": done\n");
6842 else if (oldstate->seq->type == XML_ELEMENT_NODE)
6843 xmlGenericError(xmlGenericErrorContext, ": next elem %s\n",
6844 oldstate->seq->name);
6845 else
6846 xmlGenericError(xmlGenericErrorContext, ": next %s %d\n",
6847 oldstate->seq->name, oldstate->seq->type);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006848#endif
6849 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006850 case XML_RELAXNG_OPTIONAL:
6851 oldflags = ctxt->flags;
6852 ctxt->flags |= FLAGS_IGNORABLE;
6853 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6854 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6855 if (ret != 0) {
6856 xmlRelaxNGFreeValidState(ctxt->state);
6857 ctxt->state = oldstate;
6858 ret = 0;
6859 break;
6860 }
6861 xmlRelaxNGFreeValidState(oldstate);
6862 ctxt->flags = oldflags;
6863 ret = 0;
6864 break;
6865 case XML_RELAXNG_ONEORMORE:
6866 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6867 if (ret != 0) {
6868 break;
6869 }
6870 /* no break on purpose */
Daniel Veillard276be4a2003-01-24 01:03:34 +00006871 case XML_RELAXNG_ZEROORMORE: {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006872 oldflags = ctxt->flags;
6873 ctxt->flags |= FLAGS_IGNORABLE;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006874 while (ctxt->state->nbAttrLeft != 0) {
Daniel Veillard6eadf632003-01-23 18:29:16 +00006875 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6876 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6877 if (ret != 0) {
6878 xmlRelaxNGFreeValidState(ctxt->state);
6879 ctxt->state = oldstate;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006880 break;
6881 }
6882 xmlRelaxNGFreeValidState(oldstate);
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006883 }
6884 if (ret == 0) {
6885 /*
6886 * There is no attribute left to be consumed,
6887 * we can check the closure by looking at ctxt->state->seq
6888 */
6889 xmlNodePtr cur, temp;
6890
Daniel Veillard276be4a2003-01-24 01:03:34 +00006891 cur = ctxt->state->seq;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006892 temp = NULL;
6893 while ((cur != NULL) && (temp != cur)) {
6894 temp = cur;
6895 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6896 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6897 if (ret != 0) {
6898 xmlRelaxNGFreeValidState(ctxt->state);
6899 ctxt->state = oldstate;
6900 break;
6901 }
6902 xmlRelaxNGFreeValidState(oldstate);
6903 cur = ctxt->state->seq;
6904 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006905 }
6906 ctxt->flags = oldflags;
Daniel Veillard1ed7f362003-02-03 10:57:45 +00006907 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006908 break;
Daniel Veillard276be4a2003-01-24 01:03:34 +00006909 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006910 case XML_RELAXNG_CHOICE: {
6911 xmlRelaxNGDefinePtr list = define->content;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006912 int success = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006913
6914 oldflags = ctxt->flags;
6915 ctxt->flags |= FLAGS_IGNORABLE;
6916
6917 while (list != NULL) {
6918 oldstate = xmlRelaxNGCopyValidState(ctxt, ctxt->state);
6919 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6920 if (ret == 0) {
Daniel Veillardce14fa52003-02-19 17:32:48 +00006921 if (xmlRelaxNGEqualValidState(ctxt, ctxt->state, oldstate)){
6922 /*
6923 * if that pattern was nullable flag it but try
6924 * to make more progresses
6925 */
6926 success = 1;
6927 } else {
6928 xmlRelaxNGFreeValidState(oldstate);
6929 break;
6930 }
Daniel Veillard6eadf632003-01-23 18:29:16 +00006931 }
6932 xmlRelaxNGFreeValidState(ctxt->state);
6933 ctxt->state = oldstate;
6934 list = list->next;
6935 }
6936 ctxt->flags = oldflags;
Daniel Veillardce14fa52003-02-19 17:32:48 +00006937 if (success == 1)
6938 ret = 0;
Daniel Veillard6eadf632003-01-23 18:29:16 +00006939 break;
6940 }
Daniel Veillarde2a5a082003-02-02 14:35:17 +00006941 case XML_RELAXNG_DEF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006942 case XML_RELAXNG_GROUP: {
6943 xmlRelaxNGDefinePtr list = define->content;
6944
6945 while (list != NULL) {
6946 ret = xmlRelaxNGValidateDefinition(ctxt, list);
6947 if (ret != 0)
6948 break;
6949 list = list->next;
6950 }
6951 break;
6952 }
6953 case XML_RELAXNG_INTERLEAVE:
Daniel Veillard76fc5ed2003-01-28 20:58:15 +00006954 ret = xmlRelaxNGValidateInterleave(ctxt, define);
Daniel Veillard6eadf632003-01-23 18:29:16 +00006955 break;
6956 case XML_RELAXNG_ATTRIBUTE:
6957 ret = xmlRelaxNGValidateAttribute(ctxt, define);
6958 break;
Daniel Veillard77648bb2003-02-20 15:03:22 +00006959 case XML_RELAXNG_NOOP:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006960 case XML_RELAXNG_REF:
Daniel Veillard419a7682003-02-03 23:22:49 +00006961 case XML_RELAXNG_PARENTREF:
6962 case XML_RELAXNG_EXTERNALREF:
Daniel Veillard6eadf632003-01-23 18:29:16 +00006963 ret = xmlRelaxNGValidateDefinition(ctxt, define->content);
6964 break;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006965 case XML_RELAXNG_DATATYPE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00006966 xmlNodePtr child;
6967 xmlChar *content = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006968
Daniel Veillardd4310742003-02-18 21:12:46 +00006969 child = node;
6970 while (child != NULL) {
6971 if (child->type == XML_ELEMENT_NODE) {
6972 VALID_CTXT();
6973 VALID_ERROR2("Element %s has child elements\n",
6974 node->parent->name);
6975 ret = -1;
6976 break;
6977 } else if ((child->type == XML_TEXT_NODE) ||
6978 (child->type == XML_CDATA_SECTION_NODE)) {
6979 content = xmlStrcat(content, child->content);
6980 }
6981 /* TODO: handle entities ... */
6982 child = child->next;
6983 }
6984 if (ret == -1) {
6985 if (content != NULL)
6986 xmlFree(content);
6987 break;
6988 }
6989 if (content == NULL) {
6990 content = xmlStrdup(BAD_CAST "");
6991 if (content == NULL) {
6992 VALID_CTXT();
6993 VALID_ERROR("Allocation failure\n");
6994 ret = -1;
6995 break;
6996 }
6997 }
Daniel Veillarddd1655c2003-01-25 18:01:32 +00006998 ret = xmlRelaxNGValidateDatatype(ctxt, content, define);
6999 if (ret == -1) {
7000 VALID_CTXT();
Daniel Veillard1703c5f2003-02-10 14:28:44 +00007001 VALID_ERROR2("internal error validating %s\n", define->name);
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007002 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007003 ctxt->state->seq = NULL;
Daniel Veillarddd1655c2003-01-25 18:01:32 +00007004 }
7005 if (content != NULL)
7006 xmlFree(content);
7007 break;
7008 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007009 case XML_RELAXNG_VALUE: {
Daniel Veillardd4310742003-02-18 21:12:46 +00007010 xmlChar *content = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007011 xmlChar *oldvalue;
Daniel Veillardd4310742003-02-18 21:12:46 +00007012 xmlNodePtr child;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007013
Daniel Veillardd4310742003-02-18 21:12:46 +00007014 child = node;
7015 while (child != NULL) {
7016 if (child->type == XML_ELEMENT_NODE) {
7017 VALID_CTXT();
7018 VALID_ERROR2("Element %s has child elements\n",
7019 node->parent->name);
7020 ret = -1;
7021 break;
7022 } else if ((child->type == XML_TEXT_NODE) ||
7023 (child->type == XML_CDATA_SECTION_NODE)) {
7024 content = xmlStrcat(content, child->content);
7025 }
7026 /* TODO: handle entities ... */
7027 child = child->next;
7028 }
7029 if (ret == -1) {
7030 if (content != NULL)
7031 xmlFree(content);
7032 break;
7033 }
7034 if (content == NULL) {
7035 content = xmlStrdup(BAD_CAST "");
7036 if (content == NULL) {
7037 VALID_CTXT();
7038 VALID_ERROR("Allocation failure\n");
7039 ret = -1;
7040 break;
7041 }
7042 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007043 oldvalue = ctxt->state->value;
7044 ctxt->state->value = content;
7045 ret = xmlRelaxNGValidateValue(ctxt, define);
7046 ctxt->state->value = oldvalue;
7047 if (ret == -1) {
7048 VALID_CTXT();
Daniel Veillardd2298792003-02-14 16:54:11 +00007049 if (define->name != NULL) {
7050 VALID_ERROR2("error validating value %s\n", define->name);
7051 } else {
7052 VALID_ERROR("error validating value\n");
7053 }
Daniel Veillardea3f3982003-01-26 19:45:18 +00007054 } else if (ret == 0) {
Daniel Veillardd4310742003-02-18 21:12:46 +00007055 ctxt->state->seq = NULL;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007056 }
7057 if (content != NULL)
7058 xmlFree(content);
7059 break;
7060 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007061 case XML_RELAXNG_LIST: {
7062 xmlChar *content;
Daniel Veillardd4310742003-02-18 21:12:46 +00007063 xmlNodePtr child;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007064 xmlChar *oldvalue, *oldendvalue;
7065 int len;
Daniel Veillardea3f3982003-01-26 19:45:18 +00007066
Daniel Veillardd4310742003-02-18 21:12:46 +00007067 /*
7068 * Make sure it's only text nodes
7069 */
7070
7071 content = NULL;
7072 child = node;
7073 while (child != NULL) {
7074 if (child->type == XML_ELEMENT_NODE) {
7075 VALID_CTXT();
7076 VALID_ERROR2("Element %s has child elements\n",
7077 node->parent->name);
7078 ret = -1;
7079 break;
7080 } else if ((child->type == XML_TEXT_NODE) ||
7081 (child->type == XML_CDATA_SECTION_NODE)) {
7082 content = xmlStrcat(content, child->content);
7083 }
7084 /* TODO: handle entities ... */
7085 child = child->next;
7086 }
7087 if (ret == -1) {
7088 if (content != NULL)
7089 xmlFree(content);
7090 break;
7091 }
7092 if (content == NULL) {
7093 content = xmlStrdup(BAD_CAST "");
7094 if (content == NULL) {
7095 VALID_CTXT();
7096 VALID_ERROR("Allocation failure\n");
7097 ret = -1;
7098 break;
7099 }
7100 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007101 len = xmlStrlen(content);
7102 oldvalue = ctxt->state->value;
7103 oldendvalue = ctxt->state->endvalue;
7104 ctxt->state->value = content;
7105 ctxt->state->endvalue = content + len;
7106 ret = xmlRelaxNGValidateValue(ctxt, define);
7107 ctxt->state->value = oldvalue;
7108 ctxt->state->endvalue = oldendvalue;
7109 if (ret == -1) {
7110 VALID_CTXT();
Daniel Veillard2e9b1652003-02-19 13:29:45 +00007111 VALID_ERROR("error validating list\n");
Daniel Veillardd4310742003-02-18 21:12:46 +00007112 } else if ((ret == 0) && (node != NULL)) {
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007113 ctxt->state->seq = node->next;
7114 }
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007115 if (content != NULL)
7116 xmlFree(content);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007117 break;
Daniel Veillardc6e997c2003-01-27 12:35:42 +00007118 }
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007119 case XML_RELAXNG_START:
Daniel Veillard144fae12003-02-03 13:17:57 +00007120 case XML_RELAXNG_EXCEPT:
Daniel Veillard8fe98712003-02-19 00:19:14 +00007121 case XML_RELAXNG_PARAM:
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007122 TODO
Daniel Veillard416589a2003-02-17 17:25:42 +00007123 ret = -1;
Daniel Veillardd41f4f42003-01-29 21:07:52 +00007124 break;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007125 }
Daniel Veillard231d7912003-02-09 14:22:17 +00007126 ctxt->depth--;
7127#ifdef DEBUG
7128 for (i = 0;i < ctxt->depth;i++)
7129 xmlGenericError(xmlGenericErrorContext, " ");
7130 xmlGenericError(xmlGenericErrorContext,
7131 "Validating %s ", xmlRelaxNGDefName(define));
7132 if (define->name != NULL)
7133 xmlGenericError(xmlGenericErrorContext, "%s ", define->name);
7134 if (ret == 0)
7135 xmlGenericError(xmlGenericErrorContext, "suceeded\n");
7136 else
7137 xmlGenericError(xmlGenericErrorContext, "failed\n");
7138#endif
Daniel Veillard6eadf632003-01-23 18:29:16 +00007139 return(ret);
7140}
7141
7142/**
7143 * xmlRelaxNGValidateDocument:
7144 * @ctxt: a Relax-NG validation context
7145 * @doc: the document
7146 *
7147 * Validate the given document
7148 *
7149 * Returns 0 if the validation succeeded or an error code.
7150 */
7151static int
7152xmlRelaxNGValidateDocument(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7153 int ret;
7154 xmlRelaxNGPtr schema;
7155 xmlRelaxNGGrammarPtr grammar;
7156 xmlRelaxNGValidStatePtr state;
7157
7158 if ((ctxt == NULL) || (ctxt->schema == NULL) || (doc == NULL))
7159 return(-1);
7160
7161 schema = ctxt->schema;
7162 grammar = schema->topgrammar;
7163 if (grammar == NULL) {
7164 VALID_CTXT();
7165 VALID_ERROR("No top grammar defined\n");
7166 return(-1);
7167 }
7168 state = xmlRelaxNGNewValidState(ctxt, NULL);
7169 ctxt->state = state;
7170 ret = xmlRelaxNGValidateDefinition(ctxt, grammar->start);
7171 state = ctxt->state;
7172 if ((state != NULL) && (state->seq != NULL)) {
7173 xmlNodePtr node;
7174
7175 node = state->seq;
7176 node = xmlRelaxNGSkipIgnored(ctxt, node);
7177 if (node != NULL) {
7178 VALID_CTXT();
7179 VALID_ERROR("extra data on the document\n");
7180 ret = -1;
7181 }
7182 }
7183 xmlRelaxNGFreeValidState(state);
7184
7185 return(ret);
7186}
7187
7188/************************************************************************
7189 * *
7190 * Validation interfaces *
7191 * *
7192 ************************************************************************/
7193/**
7194 * xmlRelaxNGNewValidCtxt:
7195 * @schema: a precompiled XML RelaxNGs
7196 *
7197 * Create an XML RelaxNGs validation context based on the given schema
7198 *
7199 * Returns the validation context or NULL in case of error
7200 */
7201xmlRelaxNGValidCtxtPtr
7202xmlRelaxNGNewValidCtxt(xmlRelaxNGPtr schema) {
7203 xmlRelaxNGValidCtxtPtr ret;
7204
7205 ret = (xmlRelaxNGValidCtxtPtr) xmlMalloc(sizeof(xmlRelaxNGValidCtxt));
7206 if (ret == NULL) {
7207 xmlGenericError(xmlGenericErrorContext,
7208 "Failed to allocate new schama validation context\n");
7209 return (NULL);
7210 }
7211 memset(ret, 0, sizeof(xmlRelaxNGValidCtxt));
7212 ret->schema = schema;
Daniel Veillard1703c5f2003-02-10 14:28:44 +00007213 ret->error = xmlGenericError;
7214 ret->userData = xmlGenericErrorContext;
Daniel Veillard6eadf632003-01-23 18:29:16 +00007215 return (ret);
7216}
7217
7218/**
7219 * xmlRelaxNGFreeValidCtxt:
7220 * @ctxt: the schema validation context
7221 *
7222 * Free the resources associated to the schema validation context
7223 */
7224void
7225xmlRelaxNGFreeValidCtxt(xmlRelaxNGValidCtxtPtr ctxt) {
7226 if (ctxt == NULL)
7227 return;
7228 xmlFree(ctxt);
7229}
7230
7231/**
7232 * xmlRelaxNGSetValidErrors:
7233 * @ctxt: a Relax-NG validation context
7234 * @err: the error function
7235 * @warn: the warning function
7236 * @ctx: the functions context
7237 *
7238 * Set the error and warning callback informations
7239 */
7240void
7241xmlRelaxNGSetValidErrors(xmlRelaxNGValidCtxtPtr ctxt,
7242 xmlRelaxNGValidityErrorFunc err,
7243 xmlRelaxNGValidityWarningFunc warn, void *ctx) {
7244 if (ctxt == NULL)
7245 return;
7246 ctxt->error = err;
7247 ctxt->warning = warn;
7248 ctxt->userData = ctx;
7249}
7250
7251/**
7252 * xmlRelaxNGValidateDoc:
7253 * @ctxt: a Relax-NG validation context
7254 * @doc: a parsed document tree
7255 *
7256 * Validate a document tree in memory.
7257 *
7258 * Returns 0 if the document is valid, a positive error code
7259 * number otherwise and -1 in case of internal or API error.
7260 */
7261int
7262xmlRelaxNGValidateDoc(xmlRelaxNGValidCtxtPtr ctxt, xmlDocPtr doc) {
7263 int ret;
7264
7265 if ((ctxt == NULL) || (doc == NULL))
7266 return(-1);
7267
7268 ctxt->doc = doc;
7269
7270 ret = xmlRelaxNGValidateDocument(ctxt, doc);
Daniel Veillard71531f32003-02-05 13:19:53 +00007271 /*
7272 * TODO: build error codes
7273 */
7274 if (ret == -1)
7275 return(1);
Daniel Veillard6eadf632003-01-23 18:29:16 +00007276 return(ret);
7277}
7278
7279#endif /* LIBXML_SCHEMAS_ENABLED */
7280