blob: 1e836d929636c95b22cdcfc0e61c37961624cc50 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * valid.c : part of the code use to do the DTD handling and the validity
3 * checking
4 *
5 * See Copyright for the status of this software.
6 *
Daniel Veillardc5d64342001-06-24 12:13:24 +00007 * daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +00008 */
9
Daniel Veillard34ce8be2002-03-18 19:37:11 +000010#define IN_LIBXML
Bjorn Reese70a9da52001-04-21 16:57:29 +000011#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000012
Owen Taylor3473f882001-02-23 17:55:21 +000013#include <string.h>
14
15#ifdef HAVE_STDLIB_H
16#include <stdlib.h>
17#endif
18
19#include <libxml/xmlmemory.h>
20#include <libxml/hash.h>
21#include <libxml/valid.h>
22#include <libxml/parser.h>
23#include <libxml/parserInternals.h>
24#include <libxml/xmlerror.h>
25#include <libxml/list.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000026#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000027
Daniel Veillarde62d36c2001-05-15 08:53:16 +000028/* #define DEBUG_VALID_ALGO */
29
Owen Taylor3473f882001-02-23 17:55:21 +000030/*
31 * Generic function for accessing stacks in the Validity Context
32 */
33
34#define PUSH_AND_POP(scope, type, name) \
35scope int name##VPush(xmlValidCtxtPtr ctxt, type value) { \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000036 if (ctxt->name##Max <= 0) { \
37 ctxt->name##Max = 4; \
38 ctxt->name##Tab = (type *) xmlMalloc( \
39 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
40 if (ctxt->name##Tab == NULL) { \
41 xmlGenericError(xmlGenericErrorContext, \
42 "malloc failed !\n"); \
Daniel Veillarda9142e72001-06-19 11:07:54 +000043 ctxt->name##Max = 0; \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000044 return(0); \
45 } \
46 } \
Owen Taylor3473f882001-02-23 17:55:21 +000047 if (ctxt->name##Nr >= ctxt->name##Max) { \
48 ctxt->name##Max *= 2; \
49 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
50 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
51 if (ctxt->name##Tab == NULL) { \
52 xmlGenericError(xmlGenericErrorContext, \
53 "realloc failed !\n"); \
54 return(0); \
55 } \
56 } \
57 ctxt->name##Tab[ctxt->name##Nr] = value; \
58 ctxt->name = value; \
59 return(ctxt->name##Nr++); \
60} \
61scope type name##VPop(xmlValidCtxtPtr ctxt) { \
62 type ret; \
63 if (ctxt->name##Nr <= 0) return(0); \
64 ctxt->name##Nr--; \
65 if (ctxt->name##Nr > 0) \
66 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
67 else \
68 ctxt->name = NULL; \
69 ret = ctxt->name##Tab[ctxt->name##Nr]; \
70 ctxt->name##Tab[ctxt->name##Nr] = 0; \
71 return(ret); \
72} \
73
Daniel Veillarddab4cb32001-04-20 13:03:48 +000074/*
Daniel Veillardb44025c2001-10-11 22:55:55 +000075 * I use a home made algorithm less complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +000076 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +000077 * only restriction is on the deepness of the tree limited by the
78 * size of the occurs bitfield
79 *
80 * this is the content of a saved state for rollbacks
81 */
82
83#define ROLLBACK_OR 0
84#define ROLLBACK_PARENT 1
85
Daniel Veillardb44025c2001-10-11 22:55:55 +000086typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +000087 xmlElementContentPtr cont; /* pointer to the content model subtree */
88 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +000089 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +000090 unsigned char depth; /* current depth in the overall tree */
91 unsigned char state; /* ROLLBACK_XXX */
92} _xmlValidState;
93
Daniel Veillardfc57b412002-04-29 15:50:14 +000094#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +000095#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
96#define CONT ctxt->vstate->cont
97#define NODE ctxt->vstate->node
98#define DEPTH ctxt->vstate->depth
99#define OCCURS ctxt->vstate->occurs
100#define STATE ctxt->vstate->state
101
Daniel Veillard5344c602001-12-31 16:37:34 +0000102#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
103#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000104
Daniel Veillard5344c602001-12-31 16:37:34 +0000105#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
106#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000107
108static int
109vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
110 xmlNodePtr node, unsigned char depth, long occurs,
111 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000112 int i = ctxt->vstateNr - 1;
113
Daniel Veillard940492d2002-04-15 10:15:25 +0000114 if (ctxt->vstateNr > MAX_RECURSE) {
115 return(-1);
116 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000117 if (ctxt->vstateNr >= ctxt->vstateMax) {
118 ctxt->vstateMax *= 2;
119 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
120 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
121 if (ctxt->vstateTab == NULL) {
122 xmlGenericError(xmlGenericErrorContext,
123 "realloc failed !n");
Daniel Veillard940492d2002-04-15 10:15:25 +0000124 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000125 }
Daniel Veillard06803992001-04-22 10:35:56 +0000126 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000127 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000128 /*
129 * Don't push on the stack a state already here
130 */
131 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
132 (ctxt->vstateTab[i].node == node) &&
133 (ctxt->vstateTab[i].depth == depth) &&
134 (ctxt->vstateTab[i].occurs == occurs) &&
135 (ctxt->vstateTab[i].state == state))
136 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000137 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
138 ctxt->vstateTab[ctxt->vstateNr].node = node;
139 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
140 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
141 ctxt->vstateTab[ctxt->vstateNr].state = state;
142 return(ctxt->vstateNr++);
143}
144
145static int
146vstateVPop(xmlValidCtxtPtr ctxt) {
147 if (ctxt->vstateNr <= 1) return(-1);
148 ctxt->vstateNr--;
149 ctxt->vstate = &ctxt->vstateTab[0];
150 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
151 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
152 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
153 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
154 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
155 return(ctxt->vstateNr);
156}
157
Owen Taylor3473f882001-02-23 17:55:21 +0000158PUSH_AND_POP(static, xmlNodePtr, node)
159
Owen Taylor3473f882001-02-23 17:55:21 +0000160#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000161static void
162xmlValidPrintNode(xmlNodePtr cur) {
163 if (cur == NULL) {
164 xmlGenericError(xmlGenericErrorContext, "null");
165 return;
166 }
167 switch (cur->type) {
168 case XML_ELEMENT_NODE:
169 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
170 break;
171 case XML_TEXT_NODE:
172 xmlGenericError(xmlGenericErrorContext, "text ");
173 break;
174 case XML_CDATA_SECTION_NODE:
175 xmlGenericError(xmlGenericErrorContext, "cdata ");
176 break;
177 case XML_ENTITY_REF_NODE:
178 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
179 break;
180 case XML_PI_NODE:
181 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
182 break;
183 case XML_COMMENT_NODE:
184 xmlGenericError(xmlGenericErrorContext, "comment ");
185 break;
186 case XML_ATTRIBUTE_NODE:
187 xmlGenericError(xmlGenericErrorContext, "?attr? ");
188 break;
189 case XML_ENTITY_NODE:
190 xmlGenericError(xmlGenericErrorContext, "?ent? ");
191 break;
192 case XML_DOCUMENT_NODE:
193 xmlGenericError(xmlGenericErrorContext, "?doc? ");
194 break;
195 case XML_DOCUMENT_TYPE_NODE:
196 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
197 break;
198 case XML_DOCUMENT_FRAG_NODE:
199 xmlGenericError(xmlGenericErrorContext, "?frag? ");
200 break;
201 case XML_NOTATION_NODE:
202 xmlGenericError(xmlGenericErrorContext, "?nota? ");
203 break;
204 case XML_HTML_DOCUMENT_NODE:
205 xmlGenericError(xmlGenericErrorContext, "?html? ");
206 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000207#ifdef LIBXML_DOCB_ENABLED
208 case XML_DOCB_DOCUMENT_NODE:
209 xmlGenericError(xmlGenericErrorContext, "?docb? ");
210 break;
211#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000212 case XML_DTD_NODE:
213 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
214 break;
215 case XML_ELEMENT_DECL:
216 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
217 break;
218 case XML_ATTRIBUTE_DECL:
219 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
220 break;
221 case XML_ENTITY_DECL:
222 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
223 break;
224 case XML_NAMESPACE_DECL:
225 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
226 break;
227 case XML_XINCLUDE_START:
228 xmlGenericError(xmlGenericErrorContext, "incstart ");
229 break;
230 case XML_XINCLUDE_END:
231 xmlGenericError(xmlGenericErrorContext, "incend ");
232 break;
233 }
234}
235
236static void
237xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000238 if (cur == NULL)
239 xmlGenericError(xmlGenericErrorContext, "null ");
240 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000241 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000242 cur = cur->next;
243 }
244}
245
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000246static void
247xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000248 char expr[1000];
249
250 expr[0] = 0;
251 xmlGenericError(xmlGenericErrorContext, "valid: ");
252 xmlValidPrintNodeList(cur);
253 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000254 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000255 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
256}
257
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000258static void
259xmlValidDebugState(xmlValidStatePtr state) {
260 xmlGenericError(xmlGenericErrorContext, "(");
261 if (state->cont == NULL)
262 xmlGenericError(xmlGenericErrorContext, "null,");
263 else
264 switch (state->cont->type) {
265 case XML_ELEMENT_CONTENT_PCDATA:
266 xmlGenericError(xmlGenericErrorContext, "pcdata,");
267 break;
268 case XML_ELEMENT_CONTENT_ELEMENT:
269 xmlGenericError(xmlGenericErrorContext, "%s,",
270 state->cont->name);
271 break;
272 case XML_ELEMENT_CONTENT_SEQ:
273 xmlGenericError(xmlGenericErrorContext, "seq,");
274 break;
275 case XML_ELEMENT_CONTENT_OR:
276 xmlGenericError(xmlGenericErrorContext, "or,");
277 break;
278 }
279 xmlValidPrintNode(state->node);
280 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
281 state->depth, state->occurs, state->state);
282}
283
284static void
285xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
286 int i, j;
287
288 xmlGenericError(xmlGenericErrorContext, "state: ");
289 xmlValidDebugState(ctxt->vstate);
290 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
291 ctxt->vstateNr - 1);
292 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
293 xmlValidDebugState(&ctxt->vstateTab[j]);
294 xmlGenericError(xmlGenericErrorContext, "\n");
295}
296
297/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000298#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000299 *****/
300
301#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000302#define DEBUG_VALID_MSG(m) \
303 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
304
Owen Taylor3473f882001-02-23 17:55:21 +0000305#else
306#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000307#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000308#endif
309
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000310/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000311
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000312#define VECTXT(ctxt, node) \
313 if ((ctxt != NULL) && (ctxt->error != NULL) && \
314 (node != NULL) && (node->type == XML_ELEMENT_NODE)) { \
315 if ((node->doc != NULL) && (node->doc->URL != NULL)) \
316 ctxt->error(ctxt->userData, "%s:%d:", node->doc->URL,\
317 (int) node->content); \
318 else \
319 ctxt->error(ctxt->userData, ":%d:", \
320 (int) node->content); \
321 }
322
323#define VWCTXT(ctxt, node) \
324 if ((ctxt != NULL) && (ctxt->warning != NULL) && \
325 (node != NULL) && (node->type == XML_ELEMENT_NODE)) { \
326 if ((node->doc != NULL) && (node->doc->URL != NULL)) \
327 ctxt->warning(ctxt->userData, "%s:%d:", node->doc->URL,\
328 (int) node->content); \
329 else \
330 ctxt->warning(ctxt->userData, ":%d:", \
331 (int) node->content); \
332 }
333
Owen Taylor3473f882001-02-23 17:55:21 +0000334#define VERROR \
335 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
336
337#define VWARNING \
338 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
339
340#define CHECK_DTD \
341 if (doc == NULL) return(0); \
342 else if ((doc->intSubset == NULL) && \
343 (doc->extSubset == NULL)) return(0)
344
Daniel Veillarda10efa82001-04-18 13:09:01 +0000345static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
346 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000347xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
348
349/************************************************************************
350 * *
351 * QName handling helper *
352 * *
353 ************************************************************************/
354
355/**
356 * xmlSplitQName2:
357 * @name: an XML parser context
358 * @prefix: a xmlChar **
359 *
360 * parse an XML qualified name string
361 *
362 * [NS 5] QName ::= (Prefix ':')? LocalPart
363 *
364 * [NS 6] Prefix ::= NCName
365 *
366 * [NS 7] LocalPart ::= NCName
367 *
368 * Returns NULL if not a QName, otherwise the local part, and prefix
369 * is updated to get the Prefix if any.
370 */
371
372xmlChar *
373xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
374 int len = 0;
375 xmlChar *ret = NULL;
376
377 *prefix = NULL;
378
Daniel Veillardf4309d72001-10-02 09:28:58 +0000379#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000380 /* xml: prefix is not really a namespace */
381 if ((name[0] == 'x') && (name[1] == 'm') &&
382 (name[2] == 'l') && (name[3] == ':'))
383 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000384#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000385
386 /* nasty but valid */
387 if (name[0] == ':')
388 return(NULL);
389
390 /*
391 * we are not trying to validate but just to cut, and yes it will
392 * work even if this is as set of UTF-8 encoded chars
393 */
394 while ((name[len] != 0) && (name[len] != ':'))
395 len++;
396
397 if (name[len] == 0)
398 return(NULL);
399
400 *prefix = xmlStrndup(name, len);
401 ret = xmlStrdup(&name[len + 1]);
402
403 return(ret);
404}
405
406/****************************************************************
407 * *
408 * Util functions for data allocation/deallocation *
409 * *
410 ****************************************************************/
411
412/**
413 * xmlNewElementContent:
414 * @name: the subelement name or NULL
415 * @type: the type of element content decl
416 *
417 * Allocate an element content structure.
418 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000419 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000420 */
421xmlElementContentPtr
422xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
423 xmlElementContentPtr ret;
424
425 switch(type) {
426 case XML_ELEMENT_CONTENT_ELEMENT:
427 if (name == NULL) {
428 xmlGenericError(xmlGenericErrorContext,
429 "xmlNewElementContent : name == NULL !\n");
430 }
431 break;
432 case XML_ELEMENT_CONTENT_PCDATA:
433 case XML_ELEMENT_CONTENT_SEQ:
434 case XML_ELEMENT_CONTENT_OR:
435 if (name != NULL) {
436 xmlGenericError(xmlGenericErrorContext,
437 "xmlNewElementContent : name != NULL !\n");
438 }
439 break;
440 default:
441 xmlGenericError(xmlGenericErrorContext,
442 "xmlNewElementContent: unknown type %d\n", type);
443 return(NULL);
444 }
445 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
446 if (ret == NULL) {
447 xmlGenericError(xmlGenericErrorContext,
448 "xmlNewElementContent : out of memory!\n");
449 return(NULL);
450 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000451 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000452 ret->type = type;
453 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000454 if (name != NULL) {
455 xmlChar *prefix = NULL;
456 ret->name = xmlSplitQName2(name, &prefix);
457 if (ret->name == NULL)
458 ret->name = xmlStrdup(name);
459 ret->prefix = prefix;
460 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000461 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000462 ret->prefix = NULL;
463 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000464 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000465 return(ret);
466}
467
468/**
469 * xmlCopyElementContent:
470 * @content: An element content pointer.
471 *
472 * Build a copy of an element content description.
473 *
474 * Returns the new xmlElementContentPtr or NULL in case of error.
475 */
476xmlElementContentPtr
477xmlCopyElementContent(xmlElementContentPtr cur) {
478 xmlElementContentPtr ret;
479
480 if (cur == NULL) return(NULL);
481 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
482 if (ret == NULL) {
483 xmlGenericError(xmlGenericErrorContext,
484 "xmlCopyElementContent : out of memory\n");
485 return(NULL);
486 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000487 if (cur->prefix != NULL)
488 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000489 ret->ocur = cur->ocur;
490 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000491 if (ret->c1 != NULL)
492 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000493 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000494 if (ret->c2 != NULL)
495 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000496 return(ret);
497}
498
499/**
500 * xmlFreeElementContent:
501 * @cur: the element content tree to free
502 *
503 * Free an element content structure. This is a recursive call !
504 */
505void
506xmlFreeElementContent(xmlElementContentPtr cur) {
507 if (cur == NULL) return;
508 switch (cur->type) {
509 case XML_ELEMENT_CONTENT_PCDATA:
510 case XML_ELEMENT_CONTENT_ELEMENT:
511 case XML_ELEMENT_CONTENT_SEQ:
512 case XML_ELEMENT_CONTENT_OR:
513 break;
514 default:
515 xmlGenericError(xmlGenericErrorContext,
516 "xmlFreeElementContent : type %d\n", cur->type);
517 return;
518 }
519 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
520 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
521 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000522 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000523 xmlFree(cur);
524}
525
526/**
527 * xmlDumpElementContent:
528 * @buf: An XML buffer
529 * @content: An element table
530 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
531 *
532 * This will dump the content of the element table as an XML DTD definition
533 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000534static void
Owen Taylor3473f882001-02-23 17:55:21 +0000535xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
536 if (content == NULL) return;
537
538 if (glob) xmlBufferWriteChar(buf, "(");
539 switch (content->type) {
540 case XML_ELEMENT_CONTENT_PCDATA:
541 xmlBufferWriteChar(buf, "#PCDATA");
542 break;
543 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000544 if (content->prefix != NULL) {
545 xmlBufferWriteCHAR(buf, content->prefix);
546 xmlBufferWriteChar(buf, ":");
547 }
Owen Taylor3473f882001-02-23 17:55:21 +0000548 xmlBufferWriteCHAR(buf, content->name);
549 break;
550 case XML_ELEMENT_CONTENT_SEQ:
551 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
552 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
553 xmlDumpElementContent(buf, content->c1, 1);
554 else
555 xmlDumpElementContent(buf, content->c1, 0);
556 xmlBufferWriteChar(buf, " , ");
557 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
558 xmlDumpElementContent(buf, content->c2, 1);
559 else
560 xmlDumpElementContent(buf, content->c2, 0);
561 break;
562 case XML_ELEMENT_CONTENT_OR:
563 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
564 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
565 xmlDumpElementContent(buf, content->c1, 1);
566 else
567 xmlDumpElementContent(buf, content->c1, 0);
568 xmlBufferWriteChar(buf, " | ");
569 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
570 xmlDumpElementContent(buf, content->c2, 1);
571 else
572 xmlDumpElementContent(buf, content->c2, 0);
573 break;
574 default:
575 xmlGenericError(xmlGenericErrorContext,
576 "xmlDumpElementContent: unknown type %d\n",
577 content->type);
578 }
579 if (glob)
580 xmlBufferWriteChar(buf, ")");
581 switch (content->ocur) {
582 case XML_ELEMENT_CONTENT_ONCE:
583 break;
584 case XML_ELEMENT_CONTENT_OPT:
585 xmlBufferWriteChar(buf, "?");
586 break;
587 case XML_ELEMENT_CONTENT_MULT:
588 xmlBufferWriteChar(buf, "*");
589 break;
590 case XML_ELEMENT_CONTENT_PLUS:
591 xmlBufferWriteChar(buf, "+");
592 break;
593 }
594}
595
596/**
597 * xmlSprintfElementContent:
598 * @buf: an output buffer
599 * @content: An element table
600 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
601 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000602 * Deprecated, unsafe, use xmlSnprintfElementContent
603 */
604void
605xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
606 xmlElementContentPtr content ATTRIBUTE_UNUSED,
607 int glob ATTRIBUTE_UNUSED) {
608}
609
610/**
611 * xmlSnprintfElementContent:
612 * @buf: an output buffer
613 * @size: the buffer size
614 * @content: An element table
615 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
616 *
Owen Taylor3473f882001-02-23 17:55:21 +0000617 * This will dump the content of the element content definition
618 * Intended just for the debug routine
619 */
620void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000621xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
622 int len;
623
Owen Taylor3473f882001-02-23 17:55:21 +0000624 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000625 len = strlen(buf);
626 if (size - len < 50) {
627 if ((size - len > 4) && (buf[len - 1] != '.'))
628 strcat(buf, " ...");
629 return;
630 }
Owen Taylor3473f882001-02-23 17:55:21 +0000631 if (glob) strcat(buf, "(");
632 switch (content->type) {
633 case XML_ELEMENT_CONTENT_PCDATA:
634 strcat(buf, "#PCDATA");
635 break;
636 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000637 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000638 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000639 strcat(buf, " ...");
640 return;
641 }
642 strcat(buf, (char *) content->prefix);
643 strcat(buf, ":");
644 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000645 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000646 strcat(buf, " ...");
647 return;
648 }
Owen Taylor3473f882001-02-23 17:55:21 +0000649 strcat(buf, (char *) content->name);
650 break;
651 case XML_ELEMENT_CONTENT_SEQ:
652 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
653 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000654 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000655 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000656 xmlSnprintfElementContent(buf, size, content->c1, 0);
657 len = strlen(buf);
658 if (size - len < 50) {
659 if ((size - len > 4) && (buf[len - 1] != '.'))
660 strcat(buf, " ...");
661 return;
662 }
Owen Taylor3473f882001-02-23 17:55:21 +0000663 strcat(buf, " , ");
664 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000665 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000666 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000667 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000668 break;
669 case XML_ELEMENT_CONTENT_OR:
670 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
671 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000672 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000673 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000674 xmlSnprintfElementContent(buf, size, content->c1, 0);
675 len = strlen(buf);
676 if (size - len < 50) {
677 if ((size - len > 4) && (buf[len - 1] != '.'))
678 strcat(buf, " ...");
679 return;
680 }
Owen Taylor3473f882001-02-23 17:55:21 +0000681 strcat(buf, " | ");
682 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000683 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000684 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000685 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000686 break;
687 }
688 if (glob)
689 strcat(buf, ")");
690 switch (content->ocur) {
691 case XML_ELEMENT_CONTENT_ONCE:
692 break;
693 case XML_ELEMENT_CONTENT_OPT:
694 strcat(buf, "?");
695 break;
696 case XML_ELEMENT_CONTENT_MULT:
697 strcat(buf, "*");
698 break;
699 case XML_ELEMENT_CONTENT_PLUS:
700 strcat(buf, "+");
701 break;
702 }
703}
704
705/****************************************************************
706 * *
707 * Registration of DTD declarations *
708 * *
709 ****************************************************************/
710
711/**
712 * xmlCreateElementTable:
713 *
714 * create and initialize an empty element hash table.
715 *
716 * Returns the xmlElementTablePtr just created or NULL in case of error.
717 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000718static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000719xmlCreateElementTable(void) {
720 return(xmlHashCreate(0));
721}
722
723/**
724 * xmlFreeElement:
725 * @elem: An element
726 *
727 * Deallocate the memory used by an element definition
728 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000729static void
Owen Taylor3473f882001-02-23 17:55:21 +0000730xmlFreeElement(xmlElementPtr elem) {
731 if (elem == NULL) return;
732 xmlUnlinkNode((xmlNodePtr) elem);
733 xmlFreeElementContent(elem->content);
734 if (elem->name != NULL)
735 xmlFree((xmlChar *) elem->name);
736 if (elem->prefix != NULL)
737 xmlFree((xmlChar *) elem->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000738 xmlFree(elem);
739}
740
741
742/**
743 * xmlAddElementDecl:
744 * @ctxt: the validation context
745 * @dtd: pointer to the DTD
746 * @name: the entity name
747 * @type: the element type
748 * @content: the element content tree or NULL
749 *
750 * Register a new element declaration
751 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000752 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +0000753 */
754xmlElementPtr
755xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
756 xmlElementTypeVal type,
757 xmlElementContentPtr content) {
758 xmlElementPtr ret;
759 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000760 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000761 xmlChar *ns, *uqname;
762
763 if (dtd == NULL) {
764 xmlGenericError(xmlGenericErrorContext,
765 "xmlAddElementDecl: dtd == NULL\n");
766 return(NULL);
767 }
768 if (name == NULL) {
769 xmlGenericError(xmlGenericErrorContext,
770 "xmlAddElementDecl: name == NULL\n");
771 return(NULL);
772 }
773 switch (type) {
774 case XML_ELEMENT_TYPE_EMPTY:
775 if (content != NULL) {
776 xmlGenericError(xmlGenericErrorContext,
777 "xmlAddElementDecl: content != NULL for EMPTY\n");
778 return(NULL);
779 }
780 break;
781 case XML_ELEMENT_TYPE_ANY:
782 if (content != NULL) {
783 xmlGenericError(xmlGenericErrorContext,
784 "xmlAddElementDecl: content != NULL for ANY\n");
785 return(NULL);
786 }
787 break;
788 case XML_ELEMENT_TYPE_MIXED:
789 if (content == NULL) {
790 xmlGenericError(xmlGenericErrorContext,
791 "xmlAddElementDecl: content == NULL for MIXED\n");
792 return(NULL);
793 }
794 break;
795 case XML_ELEMENT_TYPE_ELEMENT:
796 if (content == NULL) {
797 xmlGenericError(xmlGenericErrorContext,
798 "xmlAddElementDecl: content == NULL for ELEMENT\n");
799 return(NULL);
800 }
801 break;
802 default:
803 xmlGenericError(xmlGenericErrorContext,
804 "xmlAddElementDecl: unknown type %d\n", type);
805 return(NULL);
806 }
807
808 /*
809 * check if name is a QName
810 */
811 uqname = xmlSplitQName2(name, &ns);
812 if (uqname != NULL)
813 name = uqname;
814
815 /*
816 * Create the Element table if needed.
817 */
818 table = (xmlElementTablePtr) dtd->elements;
819 if (table == NULL) {
820 table = xmlCreateElementTable();
821 dtd->elements = (void *) table;
822 }
823 if (table == NULL) {
824 xmlGenericError(xmlGenericErrorContext,
825 "xmlAddElementDecl: Table creation failed!\n");
826 return(NULL);
827 }
828
Daniel Veillarda10efa82001-04-18 13:09:01 +0000829 /*
830 * lookup old attributes inserted on an undefined element in the
831 * internal subset.
832 */
833 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
834 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
835 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
836 oldAttributes = ret->attributes;
837 ret->attributes = NULL;
838 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
839 xmlFreeElement(ret);
840 }
Owen Taylor3473f882001-02-23 17:55:21 +0000841 }
Owen Taylor3473f882001-02-23 17:55:21 +0000842
843 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +0000844 * The element may already be present if one of its attribute
845 * was registered first
846 */
847 ret = xmlHashLookup2(table, name, ns);
848 if (ret != NULL) {
849 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
850 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000851 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +0000852 */
853 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
854 if (uqname != NULL)
855 xmlFree(uqname);
856 return(NULL);
857 }
858 } else {
859 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
860 if (ret == NULL) {
861 xmlGenericError(xmlGenericErrorContext,
862 "xmlAddElementDecl: out of memory\n");
863 return(NULL);
864 }
865 memset(ret, 0, sizeof(xmlElement));
866 ret->type = XML_ELEMENT_DECL;
867
868 /*
869 * fill the structure.
870 */
871 ret->name = xmlStrdup(name);
872 ret->prefix = ns;
873
874 /*
875 * Validity Check:
876 * Insertion must not fail
877 */
878 if (xmlHashAddEntry2(table, name, ns, ret)) {
879 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000880 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +0000881 */
882 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
883 xmlFreeElement(ret);
884 if (uqname != NULL)
885 xmlFree(uqname);
886 return(NULL);
887 }
888 }
889
890 /*
891 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +0000892 */
893 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +0000894 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000895 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +0000896
897 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000898 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +0000899 */
900 ret->parent = dtd;
901 ret->doc = dtd->doc;
902 if (dtd->last == NULL) {
903 dtd->children = dtd->last = (xmlNodePtr) ret;
904 } else {
905 dtd->last->next = (xmlNodePtr) ret;
906 ret->prev = dtd->last;
907 dtd->last = (xmlNodePtr) ret;
908 }
909 if (uqname != NULL)
910 xmlFree(uqname);
911 return(ret);
912}
913
914/**
915 * xmlFreeElementTable:
916 * @table: An element table
917 *
918 * Deallocate the memory used by an element hash table.
919 */
920void
921xmlFreeElementTable(xmlElementTablePtr table) {
922 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
923}
924
925/**
926 * xmlCopyElement:
927 * @elem: An element
928 *
929 * Build a copy of an element.
930 *
931 * Returns the new xmlElementPtr or NULL in case of error.
932 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000933static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000934xmlCopyElement(xmlElementPtr elem) {
935 xmlElementPtr cur;
936
937 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
938 if (cur == NULL) {
939 xmlGenericError(xmlGenericErrorContext,
940 "xmlCopyElement: out of memory !\n");
941 return(NULL);
942 }
943 memset(cur, 0, sizeof(xmlElement));
944 cur->type = XML_ELEMENT_DECL;
945 cur->etype = elem->etype;
946 if (elem->name != NULL)
947 cur->name = xmlStrdup(elem->name);
948 else
949 cur->name = NULL;
950 if (elem->prefix != NULL)
951 cur->prefix = xmlStrdup(elem->prefix);
952 else
953 cur->prefix = NULL;
954 cur->content = xmlCopyElementContent(elem->content);
955 /* TODO : rebuild the attribute list on the copy */
956 cur->attributes = NULL;
957 return(cur);
958}
959
960/**
961 * xmlCopyElementTable:
962 * @table: An element table
963 *
964 * Build a copy of an element table.
965 *
966 * Returns the new xmlElementTablePtr or NULL in case of error.
967 */
968xmlElementTablePtr
969xmlCopyElementTable(xmlElementTablePtr table) {
970 return((xmlElementTablePtr) xmlHashCopy(table,
971 (xmlHashCopier) xmlCopyElement));
972}
973
974/**
975 * xmlDumpElementDecl:
976 * @buf: the XML buffer output
977 * @elem: An element table
978 *
979 * This will dump the content of the element declaration as an XML
980 * DTD definition
981 */
982void
983xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
984 switch (elem->etype) {
985 case XML_ELEMENT_TYPE_EMPTY:
986 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000987 if (elem->prefix != NULL) {
988 xmlBufferWriteCHAR(buf, elem->prefix);
989 xmlBufferWriteChar(buf, ":");
990 }
Owen Taylor3473f882001-02-23 17:55:21 +0000991 xmlBufferWriteCHAR(buf, elem->name);
992 xmlBufferWriteChar(buf, " EMPTY>\n");
993 break;
994 case XML_ELEMENT_TYPE_ANY:
995 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000996 if (elem->prefix != NULL) {
997 xmlBufferWriteCHAR(buf, elem->prefix);
998 xmlBufferWriteChar(buf, ":");
999 }
Owen Taylor3473f882001-02-23 17:55:21 +00001000 xmlBufferWriteCHAR(buf, elem->name);
1001 xmlBufferWriteChar(buf, " ANY>\n");
1002 break;
1003 case XML_ELEMENT_TYPE_MIXED:
1004 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001005 if (elem->prefix != NULL) {
1006 xmlBufferWriteCHAR(buf, elem->prefix);
1007 xmlBufferWriteChar(buf, ":");
1008 }
Owen Taylor3473f882001-02-23 17:55:21 +00001009 xmlBufferWriteCHAR(buf, elem->name);
1010 xmlBufferWriteChar(buf, " ");
1011 xmlDumpElementContent(buf, elem->content, 1);
1012 xmlBufferWriteChar(buf, ">\n");
1013 break;
1014 case XML_ELEMENT_TYPE_ELEMENT:
1015 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001016 if (elem->prefix != NULL) {
1017 xmlBufferWriteCHAR(buf, elem->prefix);
1018 xmlBufferWriteChar(buf, ":");
1019 }
Owen Taylor3473f882001-02-23 17:55:21 +00001020 xmlBufferWriteCHAR(buf, elem->name);
1021 xmlBufferWriteChar(buf, " ");
1022 xmlDumpElementContent(buf, elem->content, 1);
1023 xmlBufferWriteChar(buf, ">\n");
1024 break;
1025 default:
1026 xmlGenericError(xmlGenericErrorContext,
1027 "xmlDumpElementDecl: internal: unknown type %d\n",
1028 elem->etype);
1029 }
1030}
1031
1032/**
1033 * xmlDumpElementTable:
1034 * @buf: the XML buffer output
1035 * @table: An element table
1036 *
1037 * This will dump the content of the element table as an XML DTD definition
1038 */
1039void
1040xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1041 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1042}
1043
1044/**
1045 * xmlCreateEnumeration:
1046 * @name: the enumeration name or NULL
1047 *
1048 * create and initialize an enumeration attribute node.
1049 *
1050 * Returns the xmlEnumerationPtr just created or NULL in case
1051 * of error.
1052 */
1053xmlEnumerationPtr
1054xmlCreateEnumeration(xmlChar *name) {
1055 xmlEnumerationPtr ret;
1056
1057 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1058 if (ret == NULL) {
1059 xmlGenericError(xmlGenericErrorContext,
1060 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1061 (long)sizeof(xmlEnumeration));
1062 return(NULL);
1063 }
1064 memset(ret, 0, sizeof(xmlEnumeration));
1065
1066 if (name != NULL)
1067 ret->name = xmlStrdup(name);
1068 return(ret);
1069}
1070
1071/**
1072 * xmlFreeEnumeration:
1073 * @cur: the tree to free.
1074 *
1075 * free an enumeration attribute node (recursive).
1076 */
1077void
1078xmlFreeEnumeration(xmlEnumerationPtr cur) {
1079 if (cur == NULL) return;
1080
1081 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1082
1083 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001084 xmlFree(cur);
1085}
1086
1087/**
1088 * xmlCopyEnumeration:
1089 * @cur: the tree to copy.
1090 *
1091 * Copy an enumeration attribute node (recursive).
1092 *
1093 * Returns the xmlEnumerationPtr just created or NULL in case
1094 * of error.
1095 */
1096xmlEnumerationPtr
1097xmlCopyEnumeration(xmlEnumerationPtr cur) {
1098 xmlEnumerationPtr ret;
1099
1100 if (cur == NULL) return(NULL);
1101 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1102
1103 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1104 else ret->next = NULL;
1105
1106 return(ret);
1107}
1108
1109/**
1110 * xmlDumpEnumeration:
1111 * @buf: the XML buffer output
1112 * @enum: An enumeration
1113 *
1114 * This will dump the content of the enumeration
1115 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001116static void
Owen Taylor3473f882001-02-23 17:55:21 +00001117xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1118 if (cur == NULL) return;
1119
1120 xmlBufferWriteCHAR(buf, cur->name);
1121 if (cur->next == NULL)
1122 xmlBufferWriteChar(buf, ")");
1123 else {
1124 xmlBufferWriteChar(buf, " | ");
1125 xmlDumpEnumeration(buf, cur->next);
1126 }
1127}
1128
1129/**
1130 * xmlCreateAttributeTable:
1131 *
1132 * create and initialize an empty attribute hash table.
1133 *
1134 * Returns the xmlAttributeTablePtr just created or NULL in case
1135 * of error.
1136 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001137static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001138xmlCreateAttributeTable(void) {
1139 return(xmlHashCreate(0));
1140}
1141
1142/**
1143 * xmlScanAttributeDeclCallback:
1144 * @attr: the attribute decl
1145 * @list: the list to update
1146 *
1147 * Callback called by xmlScanAttributeDecl when a new attribute
1148 * has to be entered in the list.
1149 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001150static void
Owen Taylor3473f882001-02-23 17:55:21 +00001151xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001152 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001153 attr->nexth = *list;
1154 *list = attr;
1155}
1156
1157/**
1158 * xmlScanAttributeDecl:
1159 * @dtd: pointer to the DTD
1160 * @elem: the element name
1161 *
1162 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001163 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001164 *
1165 * Returns the pointer to the first attribute decl in the chain,
1166 * possibly NULL.
1167 */
1168xmlAttributePtr
1169xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1170 xmlAttributePtr ret = NULL;
1171 xmlAttributeTablePtr table;
1172
1173 if (dtd == NULL) {
1174 xmlGenericError(xmlGenericErrorContext,
1175 "xmlScanAttributeDecl: dtd == NULL\n");
1176 return(NULL);
1177 }
1178 if (elem == NULL) {
1179 xmlGenericError(xmlGenericErrorContext,
1180 "xmlScanAttributeDecl: elem == NULL\n");
1181 return(NULL);
1182 }
1183 table = (xmlAttributeTablePtr) dtd->attributes;
1184 if (table == NULL)
1185 return(NULL);
1186
1187 /* WRONG !!! */
1188 xmlHashScan3(table, NULL, NULL, elem,
1189 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1190 return(ret);
1191}
1192
1193/**
1194 * xmlScanIDAttributeDecl:
1195 * @ctxt: the validation context
1196 * @elem: the element name
1197 *
1198 * Verify that the element don't have too many ID attributes
1199 * declared.
1200 *
1201 * Returns the number of ID attributes found.
1202 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001203static int
Owen Taylor3473f882001-02-23 17:55:21 +00001204xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1205 xmlAttributePtr cur;
1206 int ret = 0;
1207
1208 if (elem == NULL) return(0);
1209 cur = elem->attributes;
1210 while (cur != NULL) {
1211 if (cur->atype == XML_ATTRIBUTE_ID) {
1212 ret ++;
1213 if (ret > 1)
1214 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001215 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001216 elem->name, cur->name);
1217 }
1218 cur = cur->nexth;
1219 }
1220 return(ret);
1221}
1222
1223/**
1224 * xmlFreeAttribute:
1225 * @elem: An attribute
1226 *
1227 * Deallocate the memory used by an attribute definition
1228 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001229static void
Owen Taylor3473f882001-02-23 17:55:21 +00001230xmlFreeAttribute(xmlAttributePtr attr) {
1231 if (attr == NULL) return;
1232 xmlUnlinkNode((xmlNodePtr) attr);
1233 if (attr->tree != NULL)
1234 xmlFreeEnumeration(attr->tree);
1235 if (attr->elem != NULL)
1236 xmlFree((xmlChar *) attr->elem);
1237 if (attr->name != NULL)
1238 xmlFree((xmlChar *) attr->name);
1239 if (attr->defaultValue != NULL)
1240 xmlFree((xmlChar *) attr->defaultValue);
1241 if (attr->prefix != NULL)
1242 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001243 xmlFree(attr);
1244}
1245
1246
1247/**
1248 * xmlAddAttributeDecl:
1249 * @ctxt: the validation context
1250 * @dtd: pointer to the DTD
1251 * @elem: the element name
1252 * @name: the attribute name
1253 * @ns: the attribute namespace prefix
1254 * @type: the attribute type
1255 * @def: the attribute default type
1256 * @defaultValue: the attribute default value
1257 * @tree: if it's an enumeration, the associated list
1258 *
1259 * Register a new attribute declaration
1260 * Note that @tree becomes the ownership of the DTD
1261 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001262 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001263 */
1264xmlAttributePtr
1265xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1266 const xmlChar *name, const xmlChar *ns,
1267 xmlAttributeType type, xmlAttributeDefault def,
1268 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1269 xmlAttributePtr ret;
1270 xmlAttributeTablePtr table;
1271 xmlElementPtr elemDef;
1272
1273 if (dtd == NULL) {
1274 xmlGenericError(xmlGenericErrorContext,
1275 "xmlAddAttributeDecl: dtd == NULL\n");
1276 xmlFreeEnumeration(tree);
1277 return(NULL);
1278 }
1279 if (name == NULL) {
1280 xmlGenericError(xmlGenericErrorContext,
1281 "xmlAddAttributeDecl: name == NULL\n");
1282 xmlFreeEnumeration(tree);
1283 return(NULL);
1284 }
1285 if (elem == NULL) {
1286 xmlGenericError(xmlGenericErrorContext,
1287 "xmlAddAttributeDecl: elem == NULL\n");
1288 xmlFreeEnumeration(tree);
1289 return(NULL);
1290 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001291
Owen Taylor3473f882001-02-23 17:55:21 +00001292 /*
1293 * Check the type and possibly the default value.
1294 */
1295 switch (type) {
1296 case XML_ATTRIBUTE_CDATA:
1297 break;
1298 case XML_ATTRIBUTE_ID:
1299 break;
1300 case XML_ATTRIBUTE_IDREF:
1301 break;
1302 case XML_ATTRIBUTE_IDREFS:
1303 break;
1304 case XML_ATTRIBUTE_ENTITY:
1305 break;
1306 case XML_ATTRIBUTE_ENTITIES:
1307 break;
1308 case XML_ATTRIBUTE_NMTOKEN:
1309 break;
1310 case XML_ATTRIBUTE_NMTOKENS:
1311 break;
1312 case XML_ATTRIBUTE_ENUMERATION:
1313 break;
1314 case XML_ATTRIBUTE_NOTATION:
1315 break;
1316 default:
1317 xmlGenericError(xmlGenericErrorContext,
1318 "xmlAddAttributeDecl: unknown type %d\n", type);
1319 xmlFreeEnumeration(tree);
1320 return(NULL);
1321 }
1322 if ((defaultValue != NULL) &&
1323 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001324 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001325 elem, name, defaultValue);
1326 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001327 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001328 }
1329
1330 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001331 * Check first that an attribute defined in the external subset wasn't
1332 * already defined in the internal subset
1333 */
1334 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1335 (dtd->doc->intSubset != NULL) &&
1336 (dtd->doc->intSubset->attributes != NULL)) {
1337 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1338 if (ret != NULL)
1339 return(NULL);
1340 }
1341
1342 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001343 * Create the Attribute table if needed.
1344 */
1345 table = (xmlAttributeTablePtr) dtd->attributes;
1346 if (table == NULL) {
1347 table = xmlCreateAttributeTable();
1348 dtd->attributes = (void *) table;
1349 }
1350 if (table == NULL) {
1351 xmlGenericError(xmlGenericErrorContext,
1352 "xmlAddAttributeDecl: Table creation failed!\n");
1353 return(NULL);
1354 }
1355
1356
1357 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1358 if (ret == NULL) {
1359 xmlGenericError(xmlGenericErrorContext,
1360 "xmlAddAttributeDecl: out of memory\n");
1361 return(NULL);
1362 }
1363 memset(ret, 0, sizeof(xmlAttribute));
1364 ret->type = XML_ATTRIBUTE_DECL;
1365
1366 /*
1367 * fill the structure.
1368 */
1369 ret->atype = type;
1370 ret->name = xmlStrdup(name);
1371 ret->prefix = xmlStrdup(ns);
1372 ret->elem = xmlStrdup(elem);
1373 ret->def = def;
1374 ret->tree = tree;
1375 if (defaultValue != NULL)
1376 ret->defaultValue = xmlStrdup(defaultValue);
1377
1378 /*
1379 * Validity Check:
1380 * Search the DTD for previous declarations of the ATTLIST
1381 */
1382 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1383 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001384 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001385 */
1386 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001387 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001388 name, elem);
1389 xmlFreeAttribute(ret);
1390 return(NULL);
1391 }
1392
1393 /*
1394 * Validity Check:
1395 * Multiple ID per element
1396 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001397 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001398 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001399
Owen Taylor3473f882001-02-23 17:55:21 +00001400 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001401 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001402 VERROR(ctxt->userData,
1403 "Element %s has too may ID attributes defined : %s\n",
1404 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001405 ctxt->valid = 0;
1406 }
1407
Daniel Veillard48da9102001-08-07 01:10:10 +00001408 /*
1409 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001410 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001411 */
1412 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1413 ((ret->prefix != NULL &&
1414 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1415 ret->nexth = elemDef->attributes;
1416 elemDef->attributes = ret;
1417 } else {
1418 xmlAttributePtr tmp = elemDef->attributes;
1419
1420 while ((tmp != NULL) &&
1421 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1422 ((ret->prefix != NULL &&
1423 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1424 if (tmp->nexth == NULL)
1425 break;
1426 tmp = tmp->nexth;
1427 }
1428 if (tmp != NULL) {
1429 ret->nexth = tmp->nexth;
1430 tmp->nexth = ret;
1431 } else {
1432 ret->nexth = elemDef->attributes;
1433 elemDef->attributes = ret;
1434 }
1435 }
Owen Taylor3473f882001-02-23 17:55:21 +00001436 }
1437
1438 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001439 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001440 */
1441 ret->parent = dtd;
1442 ret->doc = dtd->doc;
1443 if (dtd->last == NULL) {
1444 dtd->children = dtd->last = (xmlNodePtr) ret;
1445 } else {
1446 dtd->last->next = (xmlNodePtr) ret;
1447 ret->prev = dtd->last;
1448 dtd->last = (xmlNodePtr) ret;
1449 }
1450 return(ret);
1451}
1452
1453/**
1454 * xmlFreeAttributeTable:
1455 * @table: An attribute table
1456 *
1457 * Deallocate the memory used by an entities hash table.
1458 */
1459void
1460xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1461 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1462}
1463
1464/**
1465 * xmlCopyAttribute:
1466 * @attr: An attribute
1467 *
1468 * Build a copy of an attribute.
1469 *
1470 * Returns the new xmlAttributePtr or NULL in case of error.
1471 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001472static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001473xmlCopyAttribute(xmlAttributePtr attr) {
1474 xmlAttributePtr cur;
1475
1476 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1477 if (cur == NULL) {
1478 xmlGenericError(xmlGenericErrorContext,
1479 "xmlCopyAttribute: out of memory !\n");
1480 return(NULL);
1481 }
1482 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001483 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001484 cur->atype = attr->atype;
1485 cur->def = attr->def;
1486 cur->tree = xmlCopyEnumeration(attr->tree);
1487 if (attr->elem != NULL)
1488 cur->elem = xmlStrdup(attr->elem);
1489 if (attr->name != NULL)
1490 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001491 if (attr->prefix != NULL)
1492 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001493 if (attr->defaultValue != NULL)
1494 cur->defaultValue = xmlStrdup(attr->defaultValue);
1495 return(cur);
1496}
1497
1498/**
1499 * xmlCopyAttributeTable:
1500 * @table: An attribute table
1501 *
1502 * Build a copy of an attribute table.
1503 *
1504 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1505 */
1506xmlAttributeTablePtr
1507xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1508 return((xmlAttributeTablePtr) xmlHashCopy(table,
1509 (xmlHashCopier) xmlCopyAttribute));
1510}
1511
1512/**
1513 * xmlDumpAttributeDecl:
1514 * @buf: the XML buffer output
1515 * @attr: An attribute declaration
1516 *
1517 * This will dump the content of the attribute declaration as an XML
1518 * DTD definition
1519 */
1520void
1521xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1522 xmlBufferWriteChar(buf, "<!ATTLIST ");
1523 xmlBufferWriteCHAR(buf, attr->elem);
1524 xmlBufferWriteChar(buf, " ");
1525 if (attr->prefix != NULL) {
1526 xmlBufferWriteCHAR(buf, attr->prefix);
1527 xmlBufferWriteChar(buf, ":");
1528 }
1529 xmlBufferWriteCHAR(buf, attr->name);
1530 switch (attr->atype) {
1531 case XML_ATTRIBUTE_CDATA:
1532 xmlBufferWriteChar(buf, " CDATA");
1533 break;
1534 case XML_ATTRIBUTE_ID:
1535 xmlBufferWriteChar(buf, " ID");
1536 break;
1537 case XML_ATTRIBUTE_IDREF:
1538 xmlBufferWriteChar(buf, " IDREF");
1539 break;
1540 case XML_ATTRIBUTE_IDREFS:
1541 xmlBufferWriteChar(buf, " IDREFS");
1542 break;
1543 case XML_ATTRIBUTE_ENTITY:
1544 xmlBufferWriteChar(buf, " ENTITY");
1545 break;
1546 case XML_ATTRIBUTE_ENTITIES:
1547 xmlBufferWriteChar(buf, " ENTITIES");
1548 break;
1549 case XML_ATTRIBUTE_NMTOKEN:
1550 xmlBufferWriteChar(buf, " NMTOKEN");
1551 break;
1552 case XML_ATTRIBUTE_NMTOKENS:
1553 xmlBufferWriteChar(buf, " NMTOKENS");
1554 break;
1555 case XML_ATTRIBUTE_ENUMERATION:
1556 xmlBufferWriteChar(buf, " (");
1557 xmlDumpEnumeration(buf, attr->tree);
1558 break;
1559 case XML_ATTRIBUTE_NOTATION:
1560 xmlBufferWriteChar(buf, " NOTATION (");
1561 xmlDumpEnumeration(buf, attr->tree);
1562 break;
1563 default:
1564 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001565 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001566 attr->atype);
1567 }
1568 switch (attr->def) {
1569 case XML_ATTRIBUTE_NONE:
1570 break;
1571 case XML_ATTRIBUTE_REQUIRED:
1572 xmlBufferWriteChar(buf, " #REQUIRED");
1573 break;
1574 case XML_ATTRIBUTE_IMPLIED:
1575 xmlBufferWriteChar(buf, " #IMPLIED");
1576 break;
1577 case XML_ATTRIBUTE_FIXED:
1578 xmlBufferWriteChar(buf, " #FIXED");
1579 break;
1580 default:
1581 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001582 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001583 attr->def);
1584 }
1585 if (attr->defaultValue != NULL) {
1586 xmlBufferWriteChar(buf, " ");
1587 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1588 }
1589 xmlBufferWriteChar(buf, ">\n");
1590}
1591
1592/**
1593 * xmlDumpAttributeTable:
1594 * @buf: the XML buffer output
1595 * @table: An attribute table
1596 *
1597 * This will dump the content of the attribute table as an XML DTD definition
1598 */
1599void
1600xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1601 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1602}
1603
1604/************************************************************************
1605 * *
1606 * NOTATIONs *
1607 * *
1608 ************************************************************************/
1609/**
1610 * xmlCreateNotationTable:
1611 *
1612 * create and initialize an empty notation hash table.
1613 *
1614 * Returns the xmlNotationTablePtr just created or NULL in case
1615 * of error.
1616 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001617static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001618xmlCreateNotationTable(void) {
1619 return(xmlHashCreate(0));
1620}
1621
1622/**
1623 * xmlFreeNotation:
1624 * @not: A notation
1625 *
1626 * Deallocate the memory used by an notation definition
1627 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001628static void
Owen Taylor3473f882001-02-23 17:55:21 +00001629xmlFreeNotation(xmlNotationPtr nota) {
1630 if (nota == NULL) return;
1631 if (nota->name != NULL)
1632 xmlFree((xmlChar *) nota->name);
1633 if (nota->PublicID != NULL)
1634 xmlFree((xmlChar *) nota->PublicID);
1635 if (nota->SystemID != NULL)
1636 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001637 xmlFree(nota);
1638}
1639
1640
1641/**
1642 * xmlAddNotationDecl:
1643 * @dtd: pointer to the DTD
1644 * @ctxt: the validation context
1645 * @name: the entity name
1646 * @PublicID: the public identifier or NULL
1647 * @SystemID: the system identifier or NULL
1648 *
1649 * Register a new notation declaration
1650 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001651 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001652 */
1653xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001654xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001655 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001656 const xmlChar *PublicID, const xmlChar *SystemID) {
1657 xmlNotationPtr ret;
1658 xmlNotationTablePtr table;
1659
1660 if (dtd == NULL) {
1661 xmlGenericError(xmlGenericErrorContext,
1662 "xmlAddNotationDecl: dtd == NULL\n");
1663 return(NULL);
1664 }
1665 if (name == NULL) {
1666 xmlGenericError(xmlGenericErrorContext,
1667 "xmlAddNotationDecl: name == NULL\n");
1668 return(NULL);
1669 }
1670 if ((PublicID == NULL) && (SystemID == NULL)) {
1671 xmlGenericError(xmlGenericErrorContext,
1672 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001673 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001674 }
1675
1676 /*
1677 * Create the Notation table if needed.
1678 */
1679 table = (xmlNotationTablePtr) dtd->notations;
1680 if (table == NULL)
1681 dtd->notations = table = xmlCreateNotationTable();
1682 if (table == NULL) {
1683 xmlGenericError(xmlGenericErrorContext,
1684 "xmlAddNotationDecl: Table creation failed!\n");
1685 return(NULL);
1686 }
1687
1688 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1689 if (ret == NULL) {
1690 xmlGenericError(xmlGenericErrorContext,
1691 "xmlAddNotationDecl: out of memory\n");
1692 return(NULL);
1693 }
1694 memset(ret, 0, sizeof(xmlNotation));
1695
1696 /*
1697 * fill the structure.
1698 */
1699 ret->name = xmlStrdup(name);
1700 if (SystemID != NULL)
1701 ret->SystemID = xmlStrdup(SystemID);
1702 if (PublicID != NULL)
1703 ret->PublicID = xmlStrdup(PublicID);
1704
1705 /*
1706 * Validity Check:
1707 * Check the DTD for previous declarations of the ATTLIST
1708 */
1709 if (xmlHashAddEntry(table, name, ret)) {
1710 xmlGenericError(xmlGenericErrorContext,
1711 "xmlAddNotationDecl: %s already defined\n", name);
1712 xmlFreeNotation(ret);
1713 return(NULL);
1714 }
1715 return(ret);
1716}
1717
1718/**
1719 * xmlFreeNotationTable:
1720 * @table: An notation table
1721 *
1722 * Deallocate the memory used by an entities hash table.
1723 */
1724void
1725xmlFreeNotationTable(xmlNotationTablePtr table) {
1726 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1727}
1728
1729/**
1730 * xmlCopyNotation:
1731 * @nota: A notation
1732 *
1733 * Build a copy of a notation.
1734 *
1735 * Returns the new xmlNotationPtr or NULL in case of error.
1736 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001737static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001738xmlCopyNotation(xmlNotationPtr nota) {
1739 xmlNotationPtr cur;
1740
1741 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1742 if (cur == NULL) {
1743 xmlGenericError(xmlGenericErrorContext,
1744 "xmlCopyNotation: out of memory !\n");
1745 return(NULL);
1746 }
1747 if (nota->name != NULL)
1748 cur->name = xmlStrdup(nota->name);
1749 else
1750 cur->name = NULL;
1751 if (nota->PublicID != NULL)
1752 cur->PublicID = xmlStrdup(nota->PublicID);
1753 else
1754 cur->PublicID = NULL;
1755 if (nota->SystemID != NULL)
1756 cur->SystemID = xmlStrdup(nota->SystemID);
1757 else
1758 cur->SystemID = NULL;
1759 return(cur);
1760}
1761
1762/**
1763 * xmlCopyNotationTable:
1764 * @table: A notation table
1765 *
1766 * Build a copy of a notation table.
1767 *
1768 * Returns the new xmlNotationTablePtr or NULL in case of error.
1769 */
1770xmlNotationTablePtr
1771xmlCopyNotationTable(xmlNotationTablePtr table) {
1772 return((xmlNotationTablePtr) xmlHashCopy(table,
1773 (xmlHashCopier) xmlCopyNotation));
1774}
1775
1776/**
1777 * xmlDumpNotationDecl:
1778 * @buf: the XML buffer output
1779 * @nota: A notation declaration
1780 *
1781 * This will dump the content the notation declaration as an XML DTD definition
1782 */
1783void
1784xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1785 xmlBufferWriteChar(buf, "<!NOTATION ");
1786 xmlBufferWriteCHAR(buf, nota->name);
1787 if (nota->PublicID != NULL) {
1788 xmlBufferWriteChar(buf, " PUBLIC ");
1789 xmlBufferWriteQuotedString(buf, nota->PublicID);
1790 if (nota->SystemID != NULL) {
1791 xmlBufferWriteChar(buf, " ");
1792 xmlBufferWriteCHAR(buf, nota->SystemID);
1793 }
1794 } else {
1795 xmlBufferWriteChar(buf, " SYSTEM ");
1796 xmlBufferWriteCHAR(buf, nota->SystemID);
1797 }
1798 xmlBufferWriteChar(buf, " >\n");
1799}
1800
1801/**
1802 * xmlDumpNotationTable:
1803 * @buf: the XML buffer output
1804 * @table: A notation table
1805 *
1806 * This will dump the content of the notation table as an XML DTD definition
1807 */
1808void
1809xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1810 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1811}
1812
1813/************************************************************************
1814 * *
1815 * IDs *
1816 * *
1817 ************************************************************************/
1818/**
1819 * xmlCreateIDTable:
1820 *
1821 * create and initialize an empty id hash table.
1822 *
1823 * Returns the xmlIDTablePtr just created or NULL in case
1824 * of error.
1825 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001826static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001827xmlCreateIDTable(void) {
1828 return(xmlHashCreate(0));
1829}
1830
1831/**
1832 * xmlFreeID:
1833 * @not: A id
1834 *
1835 * Deallocate the memory used by an id definition
1836 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001837static void
Owen Taylor3473f882001-02-23 17:55:21 +00001838xmlFreeID(xmlIDPtr id) {
1839 if (id == NULL) return;
1840 if (id->value != NULL)
1841 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001842 xmlFree(id);
1843}
1844
1845/**
1846 * xmlAddID:
1847 * @ctxt: the validation context
1848 * @doc: pointer to the document
1849 * @value: the value name
1850 * @attr: the attribute holding the ID
1851 *
1852 * Register a new id declaration
1853 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001854 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001855 */
1856xmlIDPtr
1857xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1858 xmlAttrPtr attr) {
1859 xmlIDPtr ret;
1860 xmlIDTablePtr table;
1861
1862 if (doc == NULL) {
1863 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001864 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001865 return(NULL);
1866 }
1867 if (value == NULL) {
1868 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001869 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001870 return(NULL);
1871 }
1872 if (attr == NULL) {
1873 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001874 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001875 return(NULL);
1876 }
1877
1878 /*
1879 * Create the ID table if needed.
1880 */
1881 table = (xmlIDTablePtr) doc->ids;
1882 if (table == NULL)
1883 doc->ids = table = xmlCreateIDTable();
1884 if (table == NULL) {
1885 xmlGenericError(xmlGenericErrorContext,
1886 "xmlAddID: Table creation failed!\n");
1887 return(NULL);
1888 }
1889
1890 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1891 if (ret == NULL) {
1892 xmlGenericError(xmlGenericErrorContext,
1893 "xmlAddID: out of memory\n");
1894 return(NULL);
1895 }
1896
1897 /*
1898 * fill the structure.
1899 */
1900 ret->value = xmlStrdup(value);
1901 ret->attr = attr;
1902
1903 if (xmlHashAddEntry(table, value, ret) < 0) {
1904 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001905 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001906 */
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00001907 if (ctxt != NULL)
1908 VERROR(ctxt->userData, "ID %s already defined\n", value);
Owen Taylor3473f882001-02-23 17:55:21 +00001909 xmlFreeID(ret);
1910 return(NULL);
1911 }
1912 return(ret);
1913}
1914
1915/**
1916 * xmlFreeIDTable:
1917 * @table: An id table
1918 *
1919 * Deallocate the memory used by an ID hash table.
1920 */
1921void
1922xmlFreeIDTable(xmlIDTablePtr table) {
1923 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1924}
1925
1926/**
1927 * xmlIsID:
1928 * @doc: the document
1929 * @elem: the element carrying the attribute
1930 * @attr: the attribute
1931 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001932 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00001933 * then this is simple, otherwise we use an heuristic: name ID (upper
1934 * or lowercase).
1935 *
1936 * Returns 0 or 1 depending on the lookup result
1937 */
1938int
1939xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1940 if (doc == NULL) return(0);
1941 if (attr == NULL) return(0);
1942 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1943 return(0);
1944 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1945 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1946 (xmlStrEqual(BAD_CAST "name", attr->name)))
1947 return(1);
1948 return(0);
1949 } else {
1950 xmlAttributePtr attrDecl;
1951
1952 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00001953 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
1954 /*
1955 * TODO: this sucks ... recomputing this every time is stupid
1956 */
1957 int len = xmlStrlen(elem->name) + xmlStrlen(elem->ns->prefix) + 2;
1958 xmlChar *fullname;
1959
1960 fullname = xmlMalloc(len);
1961 if (fullname == NULL)
1962 return(0);
1963 snprintf((char *) fullname, len, "%s:%s", (char *) elem->ns->prefix,
1964 (char *) elem->name);
1965 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
1966 attr->name);
1967 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1968 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
1969 attr->name);
1970 xmlFree(fullname);
1971 } else {
1972 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
1973 attr->name);
1974 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1975 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1976 attr->name);
1977 }
Owen Taylor3473f882001-02-23 17:55:21 +00001978
1979 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1980 return(1);
1981 }
1982 return(0);
1983}
1984
1985/**
1986 * xmlRemoveID
1987 * @doc: the document
1988 * @attr: the attribute
1989 *
1990 * Remove the given attribute from the ID table maintained internally.
1991 *
1992 * Returns -1 if the lookup failed and 0 otherwise
1993 */
1994int
1995xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1996 xmlAttrPtr cur;
1997 xmlIDTablePtr table;
1998 xmlChar *ID;
1999
2000 if (doc == NULL) return(-1);
2001 if (attr == NULL) return(-1);
2002 table = (xmlIDTablePtr) doc->ids;
2003 if (table == NULL)
2004 return(-1);
2005
2006 if (attr == NULL)
2007 return(-1);
2008 ID = xmlNodeListGetString(doc, attr->children, 1);
2009 if (ID == NULL)
2010 return(-1);
2011 cur = xmlHashLookup(table, ID);
2012 if (cur != attr) {
2013 xmlFree(ID);
2014 return(-1);
2015 }
2016 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2017 xmlFree(ID);
2018 return(0);
2019}
2020
2021/**
2022 * xmlGetID:
2023 * @doc: pointer to the document
2024 * @ID: the ID value
2025 *
2026 * Search the attribute declaring the given ID
2027 *
2028 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2029 */
2030xmlAttrPtr
2031xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2032 xmlIDTablePtr table;
2033 xmlIDPtr id;
2034
2035 if (doc == NULL) {
2036 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2037 return(NULL);
2038 }
2039
2040 if (ID == NULL) {
2041 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2042 return(NULL);
2043 }
2044
2045 table = (xmlIDTablePtr) doc->ids;
2046 if (table == NULL)
2047 return(NULL);
2048
2049 id = xmlHashLookup(table, ID);
2050 if (id == NULL)
2051 return(NULL);
2052 return(id->attr);
2053}
2054
2055/************************************************************************
2056 * *
2057 * Refs *
2058 * *
2059 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002060typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002061{
2062 xmlListPtr l;
2063 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002064} xmlRemoveMemo;
2065
2066typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2067
2068typedef struct xmlValidateMemo_t
2069{
2070 xmlValidCtxtPtr ctxt;
2071 const xmlChar *name;
2072} xmlValidateMemo;
2073
2074typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002075
2076/**
2077 * xmlCreateRefTable:
2078 *
2079 * create and initialize an empty ref hash table.
2080 *
2081 * Returns the xmlRefTablePtr just created or NULL in case
2082 * of error.
2083 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002084static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002085xmlCreateRefTable(void) {
2086 return(xmlHashCreate(0));
2087}
2088
2089/**
2090 * xmlFreeRef:
2091 * @lk: A list link
2092 *
2093 * Deallocate the memory used by a ref definition
2094 */
2095static void
2096xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002097 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2098 if (ref == NULL) return;
2099 if (ref->value != NULL)
2100 xmlFree((xmlChar *)ref->value);
2101 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002102}
2103
2104/**
2105 * xmlFreeRefList:
2106 * @list_ref: A list of references.
2107 *
2108 * Deallocate the memory used by a list of references
2109 */
2110static void
2111xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002112 if (list_ref == NULL) return;
2113 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002114}
2115
2116/**
2117 * xmlWalkRemoveRef:
2118 * @data: Contents of current link
2119 * @user: Value supplied by the user
2120 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002121 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002122 */
2123static int
2124xmlWalkRemoveRef(const void *data, const void *user)
2125{
Daniel Veillard37721922001-05-04 15:21:12 +00002126 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2127 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2128 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002129
Daniel Veillard37721922001-05-04 15:21:12 +00002130 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2131 xmlListRemoveFirst(ref_list, (void *)data);
2132 return 0;
2133 }
2134 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002135}
2136
2137/**
2138 * xmlAddRef:
2139 * @ctxt: the validation context
2140 * @doc: pointer to the document
2141 * @value: the value name
2142 * @attr: the attribute holding the Ref
2143 *
2144 * Register a new ref declaration
2145 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002146 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002147 */
2148xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002149xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002150 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002151 xmlRefPtr ret;
2152 xmlRefTablePtr table;
2153 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002154
Daniel Veillard37721922001-05-04 15:21:12 +00002155 if (doc == NULL) {
2156 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002157 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002158 return(NULL);
2159 }
2160 if (value == NULL) {
2161 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002162 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002163 return(NULL);
2164 }
2165 if (attr == NULL) {
2166 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002167 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002168 return(NULL);
2169 }
Owen Taylor3473f882001-02-23 17:55:21 +00002170
Daniel Veillard37721922001-05-04 15:21:12 +00002171 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002172 * Create the Ref table if needed.
2173 */
Daniel Veillard37721922001-05-04 15:21:12 +00002174 table = (xmlRefTablePtr) doc->refs;
2175 if (table == NULL)
2176 doc->refs = table = xmlCreateRefTable();
2177 if (table == NULL) {
2178 xmlGenericError(xmlGenericErrorContext,
2179 "xmlAddRef: Table creation failed!\n");
2180 return(NULL);
2181 }
Owen Taylor3473f882001-02-23 17:55:21 +00002182
Daniel Veillard37721922001-05-04 15:21:12 +00002183 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2184 if (ret == NULL) {
2185 xmlGenericError(xmlGenericErrorContext,
2186 "xmlAddRef: out of memory\n");
2187 return(NULL);
2188 }
Owen Taylor3473f882001-02-23 17:55:21 +00002189
Daniel Veillard37721922001-05-04 15:21:12 +00002190 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002191 * fill the structure.
2192 */
Daniel Veillard37721922001-05-04 15:21:12 +00002193 ret->value = xmlStrdup(value);
2194 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002195
Daniel Veillard37721922001-05-04 15:21:12 +00002196 /* To add a reference :-
2197 * References are maintained as a list of references,
2198 * Lookup the entry, if no entry create new nodelist
2199 * Add the owning node to the NodeList
2200 * Return the ref
2201 */
Owen Taylor3473f882001-02-23 17:55:21 +00002202
Daniel Veillard37721922001-05-04 15:21:12 +00002203 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2204 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2205 xmlGenericError(xmlGenericErrorContext,
2206 "xmlAddRef: Reference list creation failed!\n");
2207 return(NULL);
2208 }
2209 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2210 xmlListDelete(ref_list);
2211 xmlGenericError(xmlGenericErrorContext,
2212 "xmlAddRef: Reference list insertion failed!\n");
2213 return(NULL);
2214 }
2215 }
2216 xmlListInsert(ref_list, ret);
2217 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002218}
2219
2220/**
2221 * xmlFreeRefTable:
2222 * @table: An ref table
2223 *
2224 * Deallocate the memory used by an Ref hash table.
2225 */
2226void
2227xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002228 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002229}
2230
2231/**
2232 * xmlIsRef:
2233 * @doc: the document
2234 * @elem: the element carrying the attribute
2235 * @attr: the attribute
2236 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002237 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002238 * then this is simple, otherwise we use an heuristic: name Ref (upper
2239 * or lowercase).
2240 *
2241 * Returns 0 or 1 depending on the lookup result
2242 */
2243int
2244xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002245 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2246 return(0);
2247 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2248 /* TODO @@@ */
2249 return(0);
2250 } else {
2251 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002252
Daniel Veillard37721922001-05-04 15:21:12 +00002253 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2254 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2255 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2256 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002257
Daniel Veillard37721922001-05-04 15:21:12 +00002258 if ((attrDecl != NULL) &&
2259 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2260 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2261 return(1);
2262 }
2263 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002264}
2265
2266/**
2267 * xmlRemoveRef
2268 * @doc: the document
2269 * @attr: the attribute
2270 *
2271 * Remove the given attribute from the Ref table maintained internally.
2272 *
2273 * Returns -1 if the lookup failed and 0 otherwise
2274 */
2275int
2276xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002277 xmlListPtr ref_list;
2278 xmlRefTablePtr table;
2279 xmlChar *ID;
2280 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002281
Daniel Veillard37721922001-05-04 15:21:12 +00002282 if (doc == NULL) return(-1);
2283 if (attr == NULL) return(-1);
2284 table = (xmlRefTablePtr) doc->refs;
2285 if (table == NULL)
2286 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002287
Daniel Veillard37721922001-05-04 15:21:12 +00002288 if (attr == NULL)
2289 return(-1);
2290 ID = xmlNodeListGetString(doc, attr->children, 1);
2291 if (ID == NULL)
2292 return(-1);
2293 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002294
Daniel Veillard37721922001-05-04 15:21:12 +00002295 if(ref_list == NULL) {
2296 xmlFree(ID);
2297 return (-1);
2298 }
2299 /* At this point, ref_list refers to a list of references which
2300 * have the same key as the supplied attr. Our list of references
2301 * is ordered by reference address and we don't have that information
2302 * here to use when removing. We'll have to walk the list and
2303 * check for a matching attribute, when we find one stop the walk
2304 * and remove the entry.
2305 * The list is ordered by reference, so that means we don't have the
2306 * key. Passing the list and the reference to the walker means we
2307 * will have enough data to be able to remove the entry.
2308 */
2309 target.l = ref_list;
2310 target.ap = attr;
2311
2312 /* Remove the supplied attr from our list */
2313 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002314
Daniel Veillard37721922001-05-04 15:21:12 +00002315 /*If the list is empty then remove the list entry in the hash */
2316 if (xmlListEmpty(ref_list))
2317 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2318 xmlFreeRefList);
2319 xmlFree(ID);
2320 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002321}
2322
2323/**
2324 * xmlGetRefs:
2325 * @doc: pointer to the document
2326 * @ID: the ID value
2327 *
2328 * Find the set of references for the supplied ID.
2329 *
2330 * Returns NULL if not found, otherwise node set for the ID.
2331 */
2332xmlListPtr
2333xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002334 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002335
Daniel Veillard37721922001-05-04 15:21:12 +00002336 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002337 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002338 return(NULL);
2339 }
Owen Taylor3473f882001-02-23 17:55:21 +00002340
Daniel Veillard37721922001-05-04 15:21:12 +00002341 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002342 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002343 return(NULL);
2344 }
Owen Taylor3473f882001-02-23 17:55:21 +00002345
Daniel Veillard37721922001-05-04 15:21:12 +00002346 table = (xmlRefTablePtr) doc->refs;
2347 if (table == NULL)
2348 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002349
Daniel Veillard37721922001-05-04 15:21:12 +00002350 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002351}
2352
2353/************************************************************************
2354 * *
2355 * Routines for validity checking *
2356 * *
2357 ************************************************************************/
2358
2359/**
2360 * xmlGetDtdElementDesc:
2361 * @dtd: a pointer to the DtD to search
2362 * @name: the element name
2363 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002364 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002365 *
2366 * returns the xmlElementPtr if found or NULL
2367 */
2368
2369xmlElementPtr
2370xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2371 xmlElementTablePtr table;
2372 xmlElementPtr cur;
2373 xmlChar *uqname = NULL, *prefix = NULL;
2374
2375 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002376 if (dtd->elements == NULL)
2377 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002378 table = (xmlElementTablePtr) dtd->elements;
2379
2380 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002381 if (uqname != NULL)
2382 name = uqname;
2383 cur = xmlHashLookup2(table, name, prefix);
2384 if (prefix != NULL) xmlFree(prefix);
2385 if (uqname != NULL) xmlFree(uqname);
2386 return(cur);
2387}
2388/**
2389 * xmlGetDtdElementDesc2:
2390 * @dtd: a pointer to the DtD to search
2391 * @name: the element name
2392 * @create: create an empty description if not found
2393 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002394 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002395 *
2396 * returns the xmlElementPtr if found or NULL
2397 */
2398
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002399static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002400xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2401 xmlElementTablePtr table;
2402 xmlElementPtr cur;
2403 xmlChar *uqname = NULL, *prefix = NULL;
2404
2405 if (dtd == NULL) return(NULL);
2406 if (dtd->elements == NULL) {
2407 if (!create)
2408 return(NULL);
2409 /*
2410 * Create the Element table if needed.
2411 */
2412 table = (xmlElementTablePtr) dtd->elements;
2413 if (table == NULL) {
2414 table = xmlCreateElementTable();
2415 dtd->elements = (void *) table;
2416 }
2417 if (table == NULL) {
2418 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002419 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002420 return(NULL);
2421 }
2422 }
2423 table = (xmlElementTablePtr) dtd->elements;
2424
2425 uqname = xmlSplitQName2(name, &prefix);
2426 if (uqname != NULL)
2427 name = uqname;
2428 cur = xmlHashLookup2(table, name, prefix);
2429 if ((cur == NULL) && (create)) {
2430 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2431 if (cur == NULL) {
2432 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002433 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002434 return(NULL);
2435 }
2436 memset(cur, 0, sizeof(xmlElement));
2437 cur->type = XML_ELEMENT_DECL;
2438
2439 /*
2440 * fill the structure.
2441 */
2442 cur->name = xmlStrdup(name);
2443 cur->prefix = xmlStrdup(prefix);
2444 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2445
2446 xmlHashAddEntry2(table, name, prefix, cur);
2447 }
2448 if (prefix != NULL) xmlFree(prefix);
2449 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002450 return(cur);
2451}
2452
2453/**
2454 * xmlGetDtdQElementDesc:
2455 * @dtd: a pointer to the DtD to search
2456 * @name: the element name
2457 * @prefix: the element namespace prefix
2458 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002459 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002460 *
2461 * returns the xmlElementPtr if found or NULL
2462 */
2463
Daniel Veillard48da9102001-08-07 01:10:10 +00002464xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002465xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2466 const xmlChar *prefix) {
2467 xmlElementTablePtr table;
2468
2469 if (dtd == NULL) return(NULL);
2470 if (dtd->elements == NULL) return(NULL);
2471 table = (xmlElementTablePtr) dtd->elements;
2472
2473 return(xmlHashLookup2(table, name, prefix));
2474}
2475
2476/**
2477 * xmlGetDtdAttrDesc:
2478 * @dtd: a pointer to the DtD to search
2479 * @elem: the element name
2480 * @name: the attribute name
2481 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002482 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002483 * this element.
2484 *
2485 * returns the xmlAttributePtr if found or NULL
2486 */
2487
2488xmlAttributePtr
2489xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2490 xmlAttributeTablePtr table;
2491 xmlAttributePtr cur;
2492 xmlChar *uqname = NULL, *prefix = NULL;
2493
2494 if (dtd == NULL) return(NULL);
2495 if (dtd->attributes == NULL) return(NULL);
2496
2497 table = (xmlAttributeTablePtr) dtd->attributes;
2498 if (table == NULL)
2499 return(NULL);
2500
2501 uqname = xmlSplitQName2(name, &prefix);
2502
2503 if (uqname != NULL) {
2504 cur = xmlHashLookup3(table, uqname, prefix, elem);
2505 if (prefix != NULL) xmlFree(prefix);
2506 if (uqname != NULL) xmlFree(uqname);
2507 } else
2508 cur = xmlHashLookup3(table, name, NULL, elem);
2509 return(cur);
2510}
2511
2512/**
2513 * xmlGetDtdQAttrDesc:
2514 * @dtd: a pointer to the DtD to search
2515 * @elem: the element name
2516 * @name: the attribute name
2517 * @prefix: the attribute namespace prefix
2518 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002519 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002520 * this element.
2521 *
2522 * returns the xmlAttributePtr if found or NULL
2523 */
2524
Daniel Veillard48da9102001-08-07 01:10:10 +00002525xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002526xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2527 const xmlChar *prefix) {
2528 xmlAttributeTablePtr table;
2529
2530 if (dtd == NULL) return(NULL);
2531 if (dtd->attributes == NULL) return(NULL);
2532 table = (xmlAttributeTablePtr) dtd->attributes;
2533
2534 return(xmlHashLookup3(table, name, prefix, elem));
2535}
2536
2537/**
2538 * xmlGetDtdNotationDesc:
2539 * @dtd: a pointer to the DtD to search
2540 * @name: the notation name
2541 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002542 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002543 *
2544 * returns the xmlNotationPtr if found or NULL
2545 */
2546
2547xmlNotationPtr
2548xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2549 xmlNotationTablePtr table;
2550
2551 if (dtd == NULL) return(NULL);
2552 if (dtd->notations == NULL) return(NULL);
2553 table = (xmlNotationTablePtr) dtd->notations;
2554
2555 return(xmlHashLookup(table, name));
2556}
2557
2558/**
2559 * xmlValidateNotationUse:
2560 * @ctxt: the validation context
2561 * @doc: the document
2562 * @notationName: the notation name to check
2563 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002564 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002565 * - [ VC: Notation Declared ]
2566 *
2567 * returns 1 if valid or 0 otherwise
2568 */
2569
2570int
2571xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2572 const xmlChar *notationName) {
2573 xmlNotationPtr notaDecl;
2574 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2575
2576 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2577 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2578 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2579
2580 if (notaDecl == NULL) {
2581 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2582 notationName);
2583 return(0);
2584 }
2585 return(1);
2586}
2587
2588/**
2589 * xmlIsMixedElement
2590 * @doc: the document
2591 * @name: the element name
2592 *
2593 * Search in the DtDs whether an element accept Mixed content (or ANY)
2594 * basically if it is supposed to accept text childs
2595 *
2596 * returns 0 if no, 1 if yes, and -1 if no element description is available
2597 */
2598
2599int
2600xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2601 xmlElementPtr elemDecl;
2602
2603 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2604
2605 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2606 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2607 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2608 if (elemDecl == NULL) return(-1);
2609 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002610 case XML_ELEMENT_TYPE_UNDEFINED:
2611 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002612 case XML_ELEMENT_TYPE_ELEMENT:
2613 return(0);
2614 case XML_ELEMENT_TYPE_EMPTY:
2615 /*
2616 * return 1 for EMPTY since we want VC error to pop up
2617 * on <empty> </empty> for example
2618 */
2619 case XML_ELEMENT_TYPE_ANY:
2620 case XML_ELEMENT_TYPE_MIXED:
2621 return(1);
2622 }
2623 return(1);
2624}
2625
2626/**
2627 * xmlValidateNameValue:
2628 * @value: an Name value
2629 *
2630 * Validate that the given value match Name production
2631 *
2632 * returns 1 if valid or 0 otherwise
2633 */
2634
Daniel Veillard9b731d72002-04-14 12:56:08 +00002635int
Owen Taylor3473f882001-02-23 17:55:21 +00002636xmlValidateNameValue(const xmlChar *value) {
2637 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002638 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002639
2640 if (value == NULL) return(0);
2641 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002642 val = xmlStringCurrentChar(NULL, cur, &len);
2643 cur += len;
2644 if (!IS_LETTER(val) && (val != '_') &&
2645 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002646 return(0);
2647 }
2648
Daniel Veillardd8224e02002-01-13 15:43:22 +00002649 val = xmlStringCurrentChar(NULL, cur, &len);
2650 cur += len;
2651 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2652 (val == '.') || (val == '-') ||
2653 (val == '_') || (val == ':') ||
2654 (IS_COMBINING(val)) ||
2655 (IS_EXTENDER(val))) {
2656 val = xmlStringCurrentChar(NULL, cur, &len);
2657 cur += len;
2658 }
Owen Taylor3473f882001-02-23 17:55:21 +00002659
Daniel Veillardd8224e02002-01-13 15:43:22 +00002660 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002661
2662 return(1);
2663}
2664
2665/**
2666 * xmlValidateNamesValue:
2667 * @value: an Names value
2668 *
2669 * Validate that the given value match Names production
2670 *
2671 * returns 1 if valid or 0 otherwise
2672 */
2673
Daniel Veillard9b731d72002-04-14 12:56:08 +00002674int
Owen Taylor3473f882001-02-23 17:55:21 +00002675xmlValidateNamesValue(const xmlChar *value) {
2676 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002677 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002678
2679 if (value == NULL) return(0);
2680 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002681 val = xmlStringCurrentChar(NULL, cur, &len);
2682 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002683
Daniel Veillardd8224e02002-01-13 15:43:22 +00002684 if (!IS_LETTER(val) && (val != '_') &&
2685 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002686 return(0);
2687 }
2688
Daniel Veillardd8224e02002-01-13 15:43:22 +00002689 val = xmlStringCurrentChar(NULL, cur, &len);
2690 cur += len;
2691 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2692 (val == '.') || (val == '-') ||
2693 (val == '_') || (val == ':') ||
2694 (IS_COMBINING(val)) ||
2695 (IS_EXTENDER(val))) {
2696 val = xmlStringCurrentChar(NULL, cur, &len);
2697 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002698 }
2699
Daniel Veillardd8224e02002-01-13 15:43:22 +00002700 while (IS_BLANK(val)) {
2701 while (IS_BLANK(val)) {
2702 val = xmlStringCurrentChar(NULL, cur, &len);
2703 cur += len;
2704 }
2705
2706 if (!IS_LETTER(val) && (val != '_') &&
2707 (val != ':')) {
2708 return(0);
2709 }
2710 val = xmlStringCurrentChar(NULL, cur, &len);
2711 cur += len;
2712
2713 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2714 (val == '.') || (val == '-') ||
2715 (val == '_') || (val == ':') ||
2716 (IS_COMBINING(val)) ||
2717 (IS_EXTENDER(val))) {
2718 val = xmlStringCurrentChar(NULL, cur, &len);
2719 cur += len;
2720 }
2721 }
2722
2723 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002724
2725 return(1);
2726}
2727
2728/**
2729 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002730 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002731 *
2732 * Validate that the given value match Nmtoken production
2733 *
2734 * [ VC: Name Token ]
2735 *
2736 * returns 1 if valid or 0 otherwise
2737 */
2738
Daniel Veillard9b731d72002-04-14 12:56:08 +00002739int
Owen Taylor3473f882001-02-23 17:55:21 +00002740xmlValidateNmtokenValue(const xmlChar *value) {
2741 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002742 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002743
2744 if (value == NULL) return(0);
2745 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002746 val = xmlStringCurrentChar(NULL, cur, &len);
2747 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002748
Daniel Veillardd8224e02002-01-13 15:43:22 +00002749 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2750 (val != '.') && (val != '-') &&
2751 (val != '_') && (val != ':') &&
2752 (!IS_COMBINING(val)) &&
2753 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002754 return(0);
2755
Daniel Veillardd8224e02002-01-13 15:43:22 +00002756 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2757 (val == '.') || (val == '-') ||
2758 (val == '_') || (val == ':') ||
2759 (IS_COMBINING(val)) ||
2760 (IS_EXTENDER(val))) {
2761 val = xmlStringCurrentChar(NULL, cur, &len);
2762 cur += len;
2763 }
Owen Taylor3473f882001-02-23 17:55:21 +00002764
Daniel Veillardd8224e02002-01-13 15:43:22 +00002765 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002766
2767 return(1);
2768}
2769
2770/**
2771 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002772 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00002773 *
2774 * Validate that the given value match Nmtokens production
2775 *
2776 * [ VC: Name Token ]
2777 *
2778 * returns 1 if valid or 0 otherwise
2779 */
2780
Daniel Veillard9b731d72002-04-14 12:56:08 +00002781int
Owen Taylor3473f882001-02-23 17:55:21 +00002782xmlValidateNmtokensValue(const xmlChar *value) {
2783 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002784 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002785
2786 if (value == NULL) return(0);
2787 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002788 val = xmlStringCurrentChar(NULL, cur, &len);
2789 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002790
Daniel Veillardd8224e02002-01-13 15:43:22 +00002791 while (IS_BLANK(val)) {
2792 val = xmlStringCurrentChar(NULL, cur, &len);
2793 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002794 }
2795
Daniel Veillardd8224e02002-01-13 15:43:22 +00002796 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2797 (val != '.') && (val != '-') &&
2798 (val != '_') && (val != ':') &&
2799 (!IS_COMBINING(val)) &&
2800 (!IS_EXTENDER(val)))
2801 return(0);
2802
2803 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2804 (val == '.') || (val == '-') ||
2805 (val == '_') || (val == ':') ||
2806 (IS_COMBINING(val)) ||
2807 (IS_EXTENDER(val))) {
2808 val = xmlStringCurrentChar(NULL, cur, &len);
2809 cur += len;
2810 }
2811
2812 while (IS_BLANK(val)) {
2813 while (IS_BLANK(val)) {
2814 val = xmlStringCurrentChar(NULL, cur, &len);
2815 cur += len;
2816 }
2817 if (val == 0) return(1);
2818
2819 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2820 (val != '.') && (val != '-') &&
2821 (val != '_') && (val != ':') &&
2822 (!IS_COMBINING(val)) &&
2823 (!IS_EXTENDER(val)))
2824 return(0);
2825
2826 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2827 (val == '.') || (val == '-') ||
2828 (val == '_') || (val == ':') ||
2829 (IS_COMBINING(val)) ||
2830 (IS_EXTENDER(val))) {
2831 val = xmlStringCurrentChar(NULL, cur, &len);
2832 cur += len;
2833 }
2834 }
2835
2836 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002837
2838 return(1);
2839}
2840
2841/**
2842 * xmlValidateNotationDecl:
2843 * @ctxt: the validation context
2844 * @doc: a document instance
2845 * @nota: a notation definition
2846 *
2847 * Try to validate a single notation definition
2848 * basically it does the following checks as described by the
2849 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002850 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00002851 * But this function get called anyway ...
2852 *
2853 * returns 1 if valid or 0 otherwise
2854 */
2855
2856int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002857xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2858 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002859 int ret = 1;
2860
2861 return(ret);
2862}
2863
2864/**
2865 * xmlValidateAttributeValue:
2866 * @type: an attribute type
2867 * @value: an attribute value
2868 *
2869 * Validate that the given attribute value match the proper production
2870 *
2871 * [ VC: ID ]
2872 * Values of type ID must match the Name production....
2873 *
2874 * [ VC: IDREF ]
2875 * Values of type IDREF must match the Name production, and values
2876 * of type IDREFS must match Names ...
2877 *
2878 * [ VC: Entity Name ]
2879 * Values of type ENTITY must match the Name production, values
2880 * of type ENTITIES must match Names ...
2881 *
2882 * [ VC: Name Token ]
2883 * Values of type NMTOKEN must match the Nmtoken production; values
2884 * of type NMTOKENS must match Nmtokens.
2885 *
2886 * returns 1 if valid or 0 otherwise
2887 */
2888
2889int
2890xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2891 switch (type) {
2892 case XML_ATTRIBUTE_ENTITIES:
2893 case XML_ATTRIBUTE_IDREFS:
2894 return(xmlValidateNamesValue(value));
2895 case XML_ATTRIBUTE_ENTITY:
2896 case XML_ATTRIBUTE_IDREF:
2897 case XML_ATTRIBUTE_ID:
2898 case XML_ATTRIBUTE_NOTATION:
2899 return(xmlValidateNameValue(value));
2900 case XML_ATTRIBUTE_NMTOKENS:
2901 case XML_ATTRIBUTE_ENUMERATION:
2902 return(xmlValidateNmtokensValue(value));
2903 case XML_ATTRIBUTE_NMTOKEN:
2904 return(xmlValidateNmtokenValue(value));
2905 case XML_ATTRIBUTE_CDATA:
2906 break;
2907 }
2908 return(1);
2909}
2910
2911/**
2912 * xmlValidateAttributeValue2:
2913 * @ctxt: the validation context
2914 * @doc: the document
2915 * @name: the attribute name (used for error reporting only)
2916 * @type: the attribute type
2917 * @value: the attribute value
2918 *
2919 * Validate that the given attribute value match a given type.
2920 * This typically cannot be done before having finished parsing
2921 * the subsets.
2922 *
2923 * [ VC: IDREF ]
2924 * Values of type IDREF must match one of the declared IDs
2925 * Values of type IDREFS must match a sequence of the declared IDs
2926 * each Name must match the value of an ID attribute on some element
2927 * in the XML document; i.e. IDREF values must match the value of
2928 * some ID attribute
2929 *
2930 * [ VC: Entity Name ]
2931 * Values of type ENTITY must match one declared entity
2932 * Values of type ENTITIES must match a sequence of declared entities
2933 *
2934 * [ VC: Notation Attributes ]
2935 * all notation names in the declaration must be declared.
2936 *
2937 * returns 1 if valid or 0 otherwise
2938 */
2939
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002940static int
Owen Taylor3473f882001-02-23 17:55:21 +00002941xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2942 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2943 int ret = 1;
2944 switch (type) {
2945 case XML_ATTRIBUTE_IDREFS:
2946 case XML_ATTRIBUTE_IDREF:
2947 case XML_ATTRIBUTE_ID:
2948 case XML_ATTRIBUTE_NMTOKENS:
2949 case XML_ATTRIBUTE_ENUMERATION:
2950 case XML_ATTRIBUTE_NMTOKEN:
2951 case XML_ATTRIBUTE_CDATA:
2952 break;
2953 case XML_ATTRIBUTE_ENTITY: {
2954 xmlEntityPtr ent;
2955
2956 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00002957 if ((ent == NULL) && (doc->standalone == 1)) {
2958 doc->standalone = 0;
2959 ent = xmlGetDocEntity(doc, value);
2960 if (ent != NULL) {
2961 VERROR(ctxt->userData,
2962"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
2963 name, value);
2964 /* WAIT to get answer from the Core WG on this
2965 ret = 0;
2966 */
2967 }
2968 }
Owen Taylor3473f882001-02-23 17:55:21 +00002969 if (ent == NULL) {
2970 VERROR(ctxt->userData,
2971 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2972 name, value);
2973 ret = 0;
2974 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2975 VERROR(ctxt->userData,
2976 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2977 name, value);
2978 ret = 0;
2979 }
2980 break;
2981 }
2982 case XML_ATTRIBUTE_ENTITIES: {
2983 xmlChar *dup, *nam = NULL, *cur, save;
2984 xmlEntityPtr ent;
2985
2986 dup = xmlStrdup(value);
2987 if (dup == NULL)
2988 return(0);
2989 cur = dup;
2990 while (*cur != 0) {
2991 nam = cur;
2992 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2993 save = *cur;
2994 *cur = 0;
2995 ent = xmlGetDocEntity(doc, nam);
2996 if (ent == NULL) {
2997 VERROR(ctxt->userData,
2998 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2999 name, nam);
3000 ret = 0;
3001 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3002 VERROR(ctxt->userData,
3003 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3004 name, nam);
3005 ret = 0;
3006 }
3007 if (save == 0)
3008 break;
3009 *cur = save;
3010 while (IS_BLANK(*cur)) cur++;
3011 }
3012 xmlFree(dup);
3013 break;
3014 }
3015 case XML_ATTRIBUTE_NOTATION: {
3016 xmlNotationPtr nota;
3017
3018 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3019 if ((nota == NULL) && (doc->extSubset != NULL))
3020 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3021
3022 if (nota == NULL) {
3023 VERROR(ctxt->userData,
3024 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3025 name, value);
3026 ret = 0;
3027 }
3028 break;
3029 }
3030 }
3031 return(ret);
3032}
3033
3034/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003035 * xmlValidCtxtNormalizeAttributeValue:
3036 * @ctxt: the validation context
3037 * @doc: the document
3038 * @elem: the parent
3039 * @name: the attribute name
3040 * @value: the attribute value
3041 * @ctxt: the validation context or NULL
3042 *
3043 * Does the validation related extra step of the normalization of attribute
3044 * values:
3045 *
3046 * If the declared value is not CDATA, then the XML processor must further
3047 * process the normalized attribute value by discarding any leading and
3048 * trailing space (#x20) characters, and by replacing sequences of space
3049 * (#x20) characters by single space (#x20) character.
3050 *
3051 * Also check VC: Standalone Document Declaration in P32, and update
3052 * ctxt->valid accordingly
3053 *
3054 * returns a new normalized string if normalization is needed, NULL otherwise
3055 * the caller must free the returned value.
3056 */
3057
3058xmlChar *
3059xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3060 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3061 xmlChar *ret, *dst;
3062 const xmlChar *src;
3063 xmlAttributePtr attrDecl = NULL;
3064 int extsubset = 0;
3065
3066 if (doc == NULL) return(NULL);
3067 if (elem == NULL) return(NULL);
3068 if (name == NULL) return(NULL);
3069 if (value == NULL) return(NULL);
3070
3071 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3072 xmlChar qname[500];
3073 snprintf((char *) qname, sizeof(qname), "%s:%s",
3074 elem->ns->prefix, elem->name);
3075 qname[sizeof(qname) - 1] = 0;
3076 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3077 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3078 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3079 if (attrDecl != NULL)
3080 extsubset = 1;
3081 }
3082 }
3083 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3084 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3085 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3086 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3087 if (attrDecl != NULL)
3088 extsubset = 1;
3089 }
3090
3091 if (attrDecl == NULL)
3092 return(NULL);
3093 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3094 return(NULL);
3095
3096 ret = xmlStrdup(value);
3097 if (ret == NULL)
3098 return(NULL);
3099 src = value;
3100 dst = ret;
3101 while (*src == 0x20) src++;
3102 while (*src != 0) {
3103 if (*src == 0x20) {
3104 while (*src == 0x20) src++;
3105 if (*src != 0)
3106 *dst++ = 0x20;
3107 } else {
3108 *dst++ = *src++;
3109 }
3110 }
3111 *dst = 0;
3112 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3113 VERROR(ctxt->userData,
3114"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3115 name, elem->name);
3116 ctxt->valid = 0;
3117 }
3118 return(ret);
3119}
3120
3121/**
Owen Taylor3473f882001-02-23 17:55:21 +00003122 * xmlValidNormalizeAttributeValue:
3123 * @doc: the document
3124 * @elem: the parent
3125 * @name: the attribute name
3126 * @value: the attribute value
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003127 * @ctxt: the validation context or NULL
Owen Taylor3473f882001-02-23 17:55:21 +00003128 *
3129 * Does the validation related extra step of the normalization of attribute
3130 * values:
3131 *
3132 * If the declared value is not CDATA, then the XML processor must further
3133 * process the normalized attribute value by discarding any leading and
3134 * trailing space (#x20) characters, and by replacing sequences of space
3135 * (#x20) characters by single space (#x20) character.
3136 *
3137 * returns a new normalized string if normalization is needed, NULL otherwise
3138 * the caller must free the returned value.
3139 */
3140
3141xmlChar *
3142xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3143 const xmlChar *name, const xmlChar *value) {
3144 xmlChar *ret, *dst;
3145 const xmlChar *src;
3146 xmlAttributePtr attrDecl = NULL;
3147
3148 if (doc == NULL) return(NULL);
3149 if (elem == NULL) return(NULL);
3150 if (name == NULL) return(NULL);
3151 if (value == NULL) return(NULL);
3152
3153 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3154 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003155 snprintf((char *) qname, sizeof(qname), "%s:%s",
3156 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003157 qname[sizeof(qname) - 1] = 0;
3158 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3159 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3160 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3161 }
3162 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3163 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3164 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3165
3166 if (attrDecl == NULL)
3167 return(NULL);
3168 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3169 return(NULL);
3170
3171 ret = xmlStrdup(value);
3172 if (ret == NULL)
3173 return(NULL);
3174 src = value;
3175 dst = ret;
3176 while (*src == 0x20) src++;
3177 while (*src != 0) {
3178 if (*src == 0x20) {
3179 while (*src == 0x20) src++;
3180 if (*src != 0)
3181 *dst++ = 0x20;
3182 } else {
3183 *dst++ = *src++;
3184 }
3185 }
3186 *dst = 0;
3187 return(ret);
3188}
3189
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003190static void
Owen Taylor3473f882001-02-23 17:55:21 +00003191xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003192 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003193 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3194}
3195
3196/**
3197 * xmlValidateAttributeDecl:
3198 * @ctxt: the validation context
3199 * @doc: a document instance
3200 * @attr: an attribute definition
3201 *
3202 * Try to validate a single attribute definition
3203 * basically it does the following checks as described by the
3204 * XML-1.0 recommendation:
3205 * - [ VC: Attribute Default Legal ]
3206 * - [ VC: Enumeration ]
3207 * - [ VC: ID Attribute Default ]
3208 *
3209 * The ID/IDREF uniqueness and matching are done separately
3210 *
3211 * returns 1 if valid or 0 otherwise
3212 */
3213
3214int
3215xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3216 xmlAttributePtr attr) {
3217 int ret = 1;
3218 int val;
3219 CHECK_DTD;
3220 if(attr == NULL) return(1);
3221
3222 /* Attribute Default Legal */
3223 /* Enumeration */
3224 if (attr->defaultValue != NULL) {
3225 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3226 if (val == 0) {
3227 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003228 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003229 attr->name, attr->elem);
3230 }
3231 ret &= val;
3232 }
3233
3234 /* ID Attribute Default */
3235 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3236 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3237 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3238 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003239 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003240 attr->name, attr->elem);
3241 ret = 0;
3242 }
3243
3244 /* One ID per Element Type */
3245 if (attr->atype == XML_ATTRIBUTE_ID) {
3246 int nbId;
3247
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003248 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003249 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3250 attr->elem);
3251 if (elem != NULL) {
3252 nbId = xmlScanIDAttributeDecl(NULL, elem);
3253 } else {
3254 xmlAttributeTablePtr table;
3255
3256 /*
3257 * The attribute may be declared in the internal subset and the
3258 * element in the external subset.
3259 */
3260 nbId = 0;
3261 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3262 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3263 xmlValidateAttributeIdCallback, &nbId);
3264 }
3265 if (nbId > 1) {
3266 VERROR(ctxt->userData,
3267 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3268 attr->elem, nbId, attr->name);
3269 } else if (doc->extSubset != NULL) {
3270 int extId = 0;
3271 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3272 if (elem != NULL) {
3273 extId = xmlScanIDAttributeDecl(NULL, elem);
3274 }
3275 if (extId > 1) {
3276 VERROR(ctxt->userData,
3277 "Element %s has %d ID attribute defined in the external subset : %s\n",
3278 attr->elem, extId, attr->name);
3279 } else if (extId + nbId > 1) {
3280 VERROR(ctxt->userData,
3281"Element %s has ID attributes defined in the internal and external subset : %s\n",
3282 attr->elem, attr->name);
3283 }
3284 }
3285 }
3286
3287 /* Validity Constraint: Enumeration */
3288 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3289 xmlEnumerationPtr tree = attr->tree;
3290 while (tree != NULL) {
3291 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3292 tree = tree->next;
3293 }
3294 if (tree == NULL) {
3295 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003296"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003297 attr->defaultValue, attr->name, attr->elem);
3298 ret = 0;
3299 }
3300 }
3301
3302 return(ret);
3303}
3304
3305/**
3306 * xmlValidateElementDecl:
3307 * @ctxt: the validation context
3308 * @doc: a document instance
3309 * @elem: an element definition
3310 *
3311 * Try to validate a single element definition
3312 * basically it does the following checks as described by the
3313 * XML-1.0 recommendation:
3314 * - [ VC: One ID per Element Type ]
3315 * - [ VC: No Duplicate Types ]
3316 * - [ VC: Unique Element Type Declaration ]
3317 *
3318 * returns 1 if valid or 0 otherwise
3319 */
3320
3321int
3322xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3323 xmlElementPtr elem) {
3324 int ret = 1;
3325 xmlElementPtr tst;
3326
3327 CHECK_DTD;
3328
3329 if (elem == NULL) return(1);
3330
3331 /* No Duplicate Types */
3332 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3333 xmlElementContentPtr cur, next;
3334 const xmlChar *name;
3335
3336 cur = elem->content;
3337 while (cur != NULL) {
3338 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3339 if (cur->c1 == NULL) break;
3340 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3341 name = cur->c1->name;
3342 next = cur->c2;
3343 while (next != NULL) {
3344 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3345 if (xmlStrEqual(next->name, name)) {
3346 VERROR(ctxt->userData,
3347 "Definition of %s has duplicate references of %s\n",
3348 elem->name, name);
3349 ret = 0;
3350 }
3351 break;
3352 }
3353 if (next->c1 == NULL) break;
3354 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3355 if (xmlStrEqual(next->c1->name, name)) {
3356 VERROR(ctxt->userData,
3357 "Definition of %s has duplicate references of %s\n",
3358 elem->name, name);
3359 ret = 0;
3360 }
3361 next = next->c2;
3362 }
3363 }
3364 cur = cur->c2;
3365 }
3366 }
3367
3368 /* VC: Unique Element Type Declaration */
3369 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003370 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003371 ((tst->prefix == elem->prefix) ||
3372 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003373 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003374 VERROR(ctxt->userData, "Redefinition of element %s\n",
3375 elem->name);
3376 ret = 0;
3377 }
3378 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003379 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003380 ((tst->prefix == elem->prefix) ||
3381 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003382 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003383 VERROR(ctxt->userData, "Redefinition of element %s\n",
3384 elem->name);
3385 ret = 0;
3386 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003387 /* One ID per Element Type
3388 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003389 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3390 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003391 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003392 return(ret);
3393}
3394
3395/**
3396 * xmlValidateOneAttribute:
3397 * @ctxt: the validation context
3398 * @doc: a document instance
3399 * @elem: an element instance
3400 * @attr: an attribute instance
3401 * @value: the attribute value (without entities processing)
3402 *
3403 * Try to validate a single attribute for an element
3404 * basically it does the following checks as described by the
3405 * XML-1.0 recommendation:
3406 * - [ VC: Attribute Value Type ]
3407 * - [ VC: Fixed Attribute Default ]
3408 * - [ VC: Entity Name ]
3409 * - [ VC: Name Token ]
3410 * - [ VC: ID ]
3411 * - [ VC: IDREF ]
3412 * - [ VC: Entity Name ]
3413 * - [ VC: Notation Attributes ]
3414 *
3415 * The ID/IDREF uniqueness and matching are done separately
3416 *
3417 * returns 1 if valid or 0 otherwise
3418 */
3419
3420int
3421xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3422 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3423 /* xmlElementPtr elemDecl; */
3424 xmlAttributePtr attrDecl = NULL;
3425 int val;
3426 int ret = 1;
3427
3428 CHECK_DTD;
3429 if ((elem == NULL) || (elem->name == NULL)) return(0);
3430 if ((attr == NULL) || (attr->name == NULL)) return(0);
3431
3432 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3433 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003434 snprintf((char *) qname, sizeof(qname), "%s:%s",
3435 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003436 qname[sizeof(qname) - 1] = 0;
3437 if (attr->ns != NULL) {
3438 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3439 attr->name, attr->ns->prefix);
3440 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3441 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3442 attr->name, attr->ns->prefix);
3443 } else {
3444 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3445 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3446 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3447 qname, attr->name);
3448 }
3449 }
3450 if (attrDecl == NULL) {
3451 if (attr->ns != NULL) {
3452 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3453 attr->name, attr->ns->prefix);
3454 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3455 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3456 attr->name, attr->ns->prefix);
3457 } else {
3458 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3459 elem->name, attr->name);
3460 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3461 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3462 elem->name, attr->name);
3463 }
3464 }
3465
3466
3467 /* Validity Constraint: Attribute Value Type */
3468 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003469 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003470 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003471 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003472 attr->name, elem->name);
3473 return(0);
3474 }
3475 attr->atype = attrDecl->atype;
3476
3477 val = xmlValidateAttributeValue(attrDecl->atype, value);
3478 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003479 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003480 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003481 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003482 attr->name, elem->name);
3483 ret = 0;
3484 }
3485
3486 /* Validity constraint: Fixed Attribute Default */
3487 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3488 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003489 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003490 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003491 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003492 attr->name, elem->name, attrDecl->defaultValue);
3493 ret = 0;
3494 }
3495 }
3496
3497 /* Validity Constraint: ID uniqueness */
3498 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3499 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3500 ret = 0;
3501 }
3502
3503 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3504 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3505 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3506 ret = 0;
3507 }
3508
3509 /* Validity Constraint: Notation Attributes */
3510 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3511 xmlEnumerationPtr tree = attrDecl->tree;
3512 xmlNotationPtr nota;
3513
3514 /* First check that the given NOTATION was declared */
3515 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3516 if (nota == NULL)
3517 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3518
3519 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003520 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003521 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003522 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003523 value, attr->name, elem->name);
3524 ret = 0;
3525 }
3526
3527 /* Second, verify that it's among the list */
3528 while (tree != NULL) {
3529 if (xmlStrEqual(tree->name, value)) break;
3530 tree = tree->next;
3531 }
3532 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003533 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003534 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003535"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003536 value, attr->name, elem->name);
3537 ret = 0;
3538 }
3539 }
3540
3541 /* Validity Constraint: Enumeration */
3542 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3543 xmlEnumerationPtr tree = attrDecl->tree;
3544 while (tree != NULL) {
3545 if (xmlStrEqual(tree->name, value)) break;
3546 tree = tree->next;
3547 }
3548 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003549 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003550 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003551 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003552 value, attr->name, elem->name);
3553 ret = 0;
3554 }
3555 }
3556
3557 /* Fixed Attribute Default */
3558 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3559 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003560 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003561 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003562 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003563 attr->name, elem->name, attrDecl->defaultValue);
3564 ret = 0;
3565 }
3566
3567 /* Extra check for the attribute value */
3568 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3569 attrDecl->atype, value);
3570
3571 return(ret);
3572}
3573
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003574/**
3575 * xmlValidateSkipIgnorable:
3576 * @ctxt: the validation context
3577 * @child: the child list
3578 *
3579 * Skip ignorable elements w.r.t. the validation process
3580 *
3581 * returns the first element to consider for validation of the content model
3582 */
3583
3584static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003585xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003586 while (child != NULL) {
3587 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003588 /* These things are ignored (skipped) during validation. */
3589 case XML_PI_NODE:
3590 case XML_COMMENT_NODE:
3591 case XML_XINCLUDE_START:
3592 case XML_XINCLUDE_END:
3593 child = child->next;
3594 break;
3595 case XML_TEXT_NODE:
3596 if (xmlIsBlankNode(child))
3597 child = child->next;
3598 else
3599 return(child);
3600 break;
3601 /* keep current node */
3602 default:
3603 return(child);
3604 }
3605 }
3606 return(child);
3607}
3608
3609/**
3610 * xmlValidateElementType:
3611 * @ctxt: the validation context
3612 *
3613 * Try to validate the content model of an element internal function
3614 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003615 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3616 * reference is found and -3 if the validation succeeded but
3617 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003618 */
3619
3620static int
3621xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003622 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003623 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003624
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003625 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003626 if ((NODE == NULL) && (CONT == NULL))
3627 return(1);
3628 if ((NODE == NULL) &&
3629 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3630 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3631 return(1);
3632 }
3633 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003634 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003635 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003636
3637 /*
3638 * We arrive here when more states need to be examined
3639 */
3640cont:
3641
3642 /*
3643 * We just recovered from a rollback generated by a possible
3644 * epsilon transition, go directly to the analysis phase
3645 */
3646 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003647 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003648 DEBUG_VALID_STATE(NODE, CONT)
3649 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003650 goto analyze;
3651 }
3652
3653 DEBUG_VALID_STATE(NODE, CONT)
3654 /*
3655 * we may have to save a backup state here. This is the equivalent
3656 * of handling epsilon transition in NFAs.
3657 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003658 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003659 ((CONT->parent == NULL) ||
3660 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003661 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003662 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003663 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003664 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003665 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
3666 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003667 }
3668
3669
3670 /*
3671 * Check first if the content matches
3672 */
3673 switch (CONT->type) {
3674 case XML_ELEMENT_CONTENT_PCDATA:
3675 if (NODE == NULL) {
3676 DEBUG_VALID_MSG("pcdata failed no node");
3677 ret = 0;
3678 break;
3679 }
3680 if (NODE->type == XML_TEXT_NODE) {
3681 DEBUG_VALID_MSG("pcdata found, skip to next");
3682 /*
3683 * go to next element in the content model
3684 * skipping ignorable elems
3685 */
3686 do {
3687 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003688 NODE = xmlValidateSkipIgnorable(NODE);
3689 if ((NODE != NULL) &&
3690 (NODE->type == XML_ENTITY_REF_NODE))
3691 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003692 } while ((NODE != NULL) &&
3693 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003694 (NODE->type != XML_TEXT_NODE) &&
3695 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003696 ret = 1;
3697 break;
3698 } else {
3699 DEBUG_VALID_MSG("pcdata failed");
3700 ret = 0;
3701 break;
3702 }
3703 break;
3704 case XML_ELEMENT_CONTENT_ELEMENT:
3705 if (NODE == NULL) {
3706 DEBUG_VALID_MSG("element failed no node");
3707 ret = 0;
3708 break;
3709 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003710 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3711 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003712 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003713 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3714 ret = (CONT->prefix == NULL);
3715 } else if (CONT->prefix == NULL) {
3716 ret = 0;
3717 } else {
3718 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3719 }
3720 }
3721 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003722 DEBUG_VALID_MSG("element found, skip to next");
3723 /*
3724 * go to next element in the content model
3725 * skipping ignorable elems
3726 */
3727 do {
3728 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003729 NODE = xmlValidateSkipIgnorable(NODE);
3730 if ((NODE != NULL) &&
3731 (NODE->type == XML_ENTITY_REF_NODE))
3732 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003733 } while ((NODE != NULL) &&
3734 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003735 (NODE->type != XML_TEXT_NODE) &&
3736 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003737 } else {
3738 DEBUG_VALID_MSG("element failed");
3739 ret = 0;
3740 break;
3741 }
3742 break;
3743 case XML_ELEMENT_CONTENT_OR:
3744 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003745 * Small optimization.
3746 */
3747 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3748 if ((NODE == NULL) ||
3749 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3750 DEPTH++;
3751 CONT = CONT->c2;
3752 goto cont;
3753 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003754 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3755 ret = (CONT->c1->prefix == NULL);
3756 } else if (CONT->c1->prefix == NULL) {
3757 ret = 0;
3758 } else {
3759 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3760 }
3761 if (ret == 0) {
3762 DEPTH++;
3763 CONT = CONT->c2;
3764 goto cont;
3765 }
Daniel Veillard85349052001-04-20 13:48:21 +00003766 }
3767
3768 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003769 * save the second branch 'or' branch
3770 */
3771 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003772 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3773 OCCURS, ROLLBACK_OR) < 0)
3774 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003775 DEPTH++;
3776 CONT = CONT->c1;
3777 goto cont;
3778 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003779 /*
3780 * Small optimization.
3781 */
3782 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3783 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3784 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3785 if ((NODE == NULL) ||
3786 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3787 DEPTH++;
3788 CONT = CONT->c2;
3789 goto cont;
3790 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003791 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3792 ret = (CONT->c1->prefix == NULL);
3793 } else if (CONT->c1->prefix == NULL) {
3794 ret = 0;
3795 } else {
3796 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3797 }
3798 if (ret == 0) {
3799 DEPTH++;
3800 CONT = CONT->c2;
3801 goto cont;
3802 }
Daniel Veillard1d047672001-06-09 16:41:01 +00003803 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003804 DEPTH++;
3805 CONT = CONT->c1;
3806 goto cont;
3807 }
3808
3809 /*
3810 * At this point handle going up in the tree
3811 */
3812 if (ret == -1) {
3813 DEBUG_VALID_MSG("error found returning");
3814 return(ret);
3815 }
3816analyze:
3817 while (CONT != NULL) {
3818 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003819 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003820 * this level.
3821 */
3822 if (ret == 0) {
3823 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003824 xmlNodePtr cur;
3825
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003826 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003827 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003828 DEBUG_VALID_MSG("Once branch failed, rollback");
3829 if (vstateVPop(ctxt) < 0 ) {
3830 DEBUG_VALID_MSG("exhaustion, failed");
3831 return(0);
3832 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003833 if (cur != ctxt->vstate->node)
3834 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003835 goto cont;
3836 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00003837 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003838 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003839 DEBUG_VALID_MSG("Plus branch failed, rollback");
3840 if (vstateVPop(ctxt) < 0 ) {
3841 DEBUG_VALID_MSG("exhaustion, failed");
3842 return(0);
3843 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003844 if (cur != ctxt->vstate->node)
3845 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003846 goto cont;
3847 }
3848 DEBUG_VALID_MSG("Plus branch found");
3849 ret = 1;
3850 break;
3851 case XML_ELEMENT_CONTENT_MULT:
3852#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00003853 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003854 DEBUG_VALID_MSG("Mult branch failed");
3855 } else {
3856 DEBUG_VALID_MSG("Mult branch found");
3857 }
3858#endif
3859 ret = 1;
3860 break;
3861 case XML_ELEMENT_CONTENT_OPT:
3862 DEBUG_VALID_MSG("Option branch failed");
3863 ret = 1;
3864 break;
3865 }
3866 } else {
3867 switch (CONT->ocur) {
3868 case XML_ELEMENT_CONTENT_OPT:
3869 DEBUG_VALID_MSG("Option branch succeeded");
3870 ret = 1;
3871 break;
3872 case XML_ELEMENT_CONTENT_ONCE:
3873 DEBUG_VALID_MSG("Once branch succeeded");
3874 ret = 1;
3875 break;
3876 case XML_ELEMENT_CONTENT_PLUS:
3877 if (STATE == ROLLBACK_PARENT) {
3878 DEBUG_VALID_MSG("Plus branch rollback");
3879 ret = 1;
3880 break;
3881 }
3882 if (NODE == NULL) {
3883 DEBUG_VALID_MSG("Plus branch exhausted");
3884 ret = 1;
3885 break;
3886 }
3887 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003888 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003889 goto cont;
3890 case XML_ELEMENT_CONTENT_MULT:
3891 if (STATE == ROLLBACK_PARENT) {
3892 DEBUG_VALID_MSG("Mult branch rollback");
3893 ret = 1;
3894 break;
3895 }
3896 if (NODE == NULL) {
3897 DEBUG_VALID_MSG("Mult branch exhausted");
3898 ret = 1;
3899 break;
3900 }
3901 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003902 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003903 goto cont;
3904 }
3905 }
3906 STATE = 0;
3907
3908 /*
3909 * Then act accordingly at the parent level
3910 */
Daniel Veillard5344c602001-12-31 16:37:34 +00003911 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003912 if (CONT->parent == NULL)
3913 break;
3914
3915 switch (CONT->parent->type) {
3916 case XML_ELEMENT_CONTENT_PCDATA:
3917 DEBUG_VALID_MSG("Error: parent pcdata");
3918 return(-1);
3919 case XML_ELEMENT_CONTENT_ELEMENT:
3920 DEBUG_VALID_MSG("Error: parent element");
3921 return(-1);
3922 case XML_ELEMENT_CONTENT_OR:
3923 if (ret == 1) {
3924 DEBUG_VALID_MSG("Or succeeded");
3925 CONT = CONT->parent;
3926 DEPTH--;
3927 } else {
3928 DEBUG_VALID_MSG("Or failed");
3929 CONT = CONT->parent;
3930 DEPTH--;
3931 }
3932 break;
3933 case XML_ELEMENT_CONTENT_SEQ:
3934 if (ret == 0) {
3935 DEBUG_VALID_MSG("Sequence failed");
3936 CONT = CONT->parent;
3937 DEPTH--;
3938 } else if (CONT == CONT->parent->c1) {
3939 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3940 CONT = CONT->parent->c2;
3941 goto cont;
3942 } else {
3943 DEBUG_VALID_MSG("Sequence succeeded");
3944 CONT = CONT->parent;
3945 DEPTH--;
3946 }
3947 }
3948 }
3949 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003950 xmlNodePtr cur;
3951
3952 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003953 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3954 if (vstateVPop(ctxt) < 0 ) {
3955 DEBUG_VALID_MSG("exhaustion, failed");
3956 return(0);
3957 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003958 if (cur != ctxt->vstate->node)
3959 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003960 goto cont;
3961 }
3962 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003963 xmlNodePtr cur;
3964
3965 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003966 DEBUG_VALID_MSG("Failure, rollback");
3967 if (vstateVPop(ctxt) < 0 ) {
3968 DEBUG_VALID_MSG("exhaustion, failed");
3969 return(0);
3970 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003971 if (cur != ctxt->vstate->node)
3972 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003973 goto cont;
3974 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003975 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003976}
3977
3978/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00003979 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003980 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00003981 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003982 * @content: An element
3983 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3984 *
3985 * This will dump the list of elements to the buffer
3986 * Intended just for the debug routine
3987 */
3988static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00003989xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003990 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00003991 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003992
3993 if (node == NULL) return;
3994 if (glob) strcat(buf, "(");
3995 cur = node;
3996 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00003997 len = strlen(buf);
3998 if (size - len < 50) {
3999 if ((size - len > 4) && (buf[len - 1] != '.'))
4000 strcat(buf, " ...");
4001 return;
4002 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004003 switch (cur->type) {
4004 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004005 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004006 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004007 if ((size - len > 4) && (buf[len - 1] != '.'))
4008 strcat(buf, " ...");
4009 return;
4010 }
4011 strcat(buf, (char *) cur->ns->prefix);
4012 strcat(buf, ":");
4013 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004014 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004015 if ((size - len > 4) && (buf[len - 1] != '.'))
4016 strcat(buf, " ...");
4017 return;
4018 }
4019 strcat(buf, (char *) cur->name);
4020 if (cur->next != NULL)
4021 strcat(buf, " ");
4022 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004023 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004024 if (xmlIsBlankNode(cur))
4025 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004026 case XML_CDATA_SECTION_NODE:
4027 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004028 strcat(buf, "CDATA");
4029 if (cur->next != NULL)
4030 strcat(buf, " ");
4031 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004032 case XML_ATTRIBUTE_NODE:
4033 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004034#ifdef LIBXML_DOCB_ENABLED
4035 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004036#endif
4037 case XML_HTML_DOCUMENT_NODE:
4038 case XML_DOCUMENT_TYPE_NODE:
4039 case XML_DOCUMENT_FRAG_NODE:
4040 case XML_NOTATION_NODE:
4041 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004042 strcat(buf, "???");
4043 if (cur->next != NULL)
4044 strcat(buf, " ");
4045 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004046 case XML_ENTITY_NODE:
4047 case XML_PI_NODE:
4048 case XML_DTD_NODE:
4049 case XML_COMMENT_NODE:
4050 case XML_ELEMENT_DECL:
4051 case XML_ATTRIBUTE_DECL:
4052 case XML_ENTITY_DECL:
4053 case XML_XINCLUDE_START:
4054 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004055 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004056 }
4057 cur = cur->next;
4058 }
4059 if (glob) strcat(buf, ")");
4060}
4061
4062/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004063 * xmlValidateElementContent:
4064 * @ctxt: the validation context
4065 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004066 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004067 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004068 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004069 *
4070 * Try to validate the content model of an element
4071 *
4072 * returns 1 if valid or 0 if not and -1 in case of error
4073 */
4074
4075static int
4076xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004077 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004078 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004079 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004080 xmlElementContentPtr cont;
4081 const xmlChar *name;
4082
4083 if (elemDecl == NULL)
4084 return(-1);
4085 cont = elemDecl->content;
4086 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004087
4088 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004089 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004090 */
4091 ctxt->vstateMax = 8;
4092 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4093 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4094 if (ctxt->vstateTab == NULL) {
4095 xmlGenericError(xmlGenericErrorContext,
4096 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004097 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004098 }
4099 /*
4100 * The first entry in the stack is reserved to the current state
4101 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004102 ctxt->nodeMax = 0;
4103 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004104 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004105 ctxt->vstate = &ctxt->vstateTab[0];
4106 ctxt->vstateNr = 1;
4107 CONT = cont;
4108 NODE = child;
4109 DEPTH = 0;
4110 OCCURS = 0;
4111 STATE = 0;
4112 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004113 if ((ret == -3) && (warn)) {
4114 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004115 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004116 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004117 /*
4118 * An entities reference appeared at this level.
4119 * Buid a minimal representation of this node content
4120 * sufficient to run the validation process on it
4121 */
4122 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004123 cur = child;
4124 while (cur != NULL) {
4125 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004126 case XML_ENTITY_REF_NODE:
4127 /*
4128 * Push the current node to be able to roll back
4129 * and process within the entity
4130 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004131 if ((cur->children != NULL) &&
4132 (cur->children->children != NULL)) {
4133 nodeVPush(ctxt, cur);
4134 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004135 continue;
4136 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004137 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004138 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004139 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004140 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004141 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004142 case XML_CDATA_SECTION_NODE:
4143 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004144 case XML_ELEMENT_NODE:
4145 /*
4146 * Allocate a new node and minimally fills in
4147 * what's required
4148 */
4149 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4150 if (tmp == NULL) {
4151 xmlGenericError(xmlGenericErrorContext,
4152 "xmlValidateElementContent : malloc failed\n");
4153 xmlFreeNodeList(repl);
4154 ret = -1;
4155 goto done;
4156 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004157 tmp->type = cur->type;
4158 tmp->name = cur->name;
4159 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004160 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004161 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004162 if (repl == NULL)
4163 repl = last = tmp;
4164 else {
4165 last->next = tmp;
4166 last = tmp;
4167 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004168 if (cur->type == XML_CDATA_SECTION_NODE) {
4169 /*
4170 * E59 spaces in CDATA does not match the
4171 * nonterminal S
4172 */
4173 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4174 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004175 break;
4176 default:
4177 break;
4178 }
4179 /*
4180 * Switch to next element
4181 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004182 cur = cur->next;
4183 while (cur == NULL) {
4184 cur = nodeVPop(ctxt);
4185 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004186 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004187 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004188 }
4189 }
4190
4191 /*
4192 * Relaunch the validation
4193 */
4194 ctxt->vstate = &ctxt->vstateTab[0];
4195 ctxt->vstateNr = 1;
4196 CONT = cont;
4197 NODE = repl;
4198 DEPTH = 0;
4199 OCCURS = 0;
4200 STATE = 0;
4201 ret = xmlValidateElementType(ctxt);
4202 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004203 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004204 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4205 char expr[5000];
4206 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004207
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004208 expr[0] = 0;
4209 xmlSnprintfElementContent(expr, 5000, cont, 1);
4210 list[0] = 0;
4211 if (repl != NULL)
4212 xmlSnprintfElements(list, 5000, repl, 1);
4213 else
4214 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004215
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004216 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004217 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004218 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004219 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004220 name, expr, list);
4221 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004222 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004223 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004224 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004225 expr, list);
4226 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004227 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004228 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004229 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004230 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004231 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004232 name);
4233 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004234 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004235 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004236 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004237 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004238 }
4239 ret = 0;
4240 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004241 if (ret == -3)
4242 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004243
4244
4245done:
4246 /*
4247 * Deallocate the copy if done, and free up the validation stack
4248 */
4249 while (repl != NULL) {
4250 tmp = repl->next;
4251 xmlFree(repl);
4252 repl = tmp;
4253 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004254 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004255 if (ctxt->vstateTab != NULL) {
4256 xmlFree(ctxt->vstateTab);
4257 ctxt->vstateTab = NULL;
4258 }
4259 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004260 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004261 if (ctxt->nodeTab != NULL) {
4262 xmlFree(ctxt->nodeTab);
4263 ctxt->nodeTab = NULL;
4264 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004265 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004266
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004267}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004268
Owen Taylor3473f882001-02-23 17:55:21 +00004269/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004270 * xmlValidateCdataElement:
4271 * @ctxt: the validation context
4272 * @doc: a document instance
4273 * @elem: an element instance
4274 *
4275 * Check that an element follows #CDATA
4276 *
4277 * returns 1 if valid or 0 otherwise
4278 */
4279static int
4280xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4281 xmlNodePtr elem) {
4282 int ret = 1;
4283 xmlNodePtr cur, child;
4284
4285 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4286 return(0);
4287
4288 child = elem->children;
4289
4290 cur = child;
4291 while (cur != NULL) {
4292 switch (cur->type) {
4293 case XML_ENTITY_REF_NODE:
4294 /*
4295 * Push the current node to be able to roll back
4296 * and process within the entity
4297 */
4298 if ((cur->children != NULL) &&
4299 (cur->children->children != NULL)) {
4300 nodeVPush(ctxt, cur);
4301 cur = cur->children->children;
4302 continue;
4303 }
4304 break;
4305 case XML_COMMENT_NODE:
4306 case XML_PI_NODE:
4307 case XML_TEXT_NODE:
4308 case XML_CDATA_SECTION_NODE:
4309 break;
4310 default:
4311 ret = 0;
4312 goto done;
4313 }
4314 /*
4315 * Switch to next element
4316 */
4317 cur = cur->next;
4318 while (cur == NULL) {
4319 cur = nodeVPop(ctxt);
4320 if (cur == NULL)
4321 break;
4322 cur = cur->next;
4323 }
4324 }
4325done:
4326 ctxt->nodeMax = 0;
4327 ctxt->nodeNr = 0;
4328 if (ctxt->nodeTab != NULL) {
4329 xmlFree(ctxt->nodeTab);
4330 ctxt->nodeTab = NULL;
4331 }
4332 return(ret);
4333}
4334
4335/**
Owen Taylor3473f882001-02-23 17:55:21 +00004336 * xmlValidateOneElement:
4337 * @ctxt: the validation context
4338 * @doc: a document instance
4339 * @elem: an element instance
4340 *
4341 * Try to validate a single element and it's attributes,
4342 * basically it does the following checks as described by the
4343 * XML-1.0 recommendation:
4344 * - [ VC: Element Valid ]
4345 * - [ VC: Required Attribute ]
4346 * Then call xmlValidateOneAttribute() for each attribute present.
4347 *
4348 * The ID/IDREF checkings are done separately
4349 *
4350 * returns 1 if valid or 0 otherwise
4351 */
4352
4353int
4354xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4355 xmlNodePtr elem) {
4356 xmlElementPtr elemDecl = NULL;
4357 xmlElementContentPtr cont;
4358 xmlAttributePtr attr;
4359 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004360 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004361 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004362 const xmlChar *prefix = NULL;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004363 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004364
4365 CHECK_DTD;
4366
4367 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004368 switch (elem->type) {
4369 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004370 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004371 VERROR(ctxt->userData,
4372 "Attribute element not expected here\n");
4373 return(0);
4374 case XML_TEXT_NODE:
4375 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004376 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004377 VERROR(ctxt->userData, "Text element has childs !\n");
4378 return(0);
4379 }
4380 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004381 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004382 VERROR(ctxt->userData, "Text element has attributes !\n");
4383 return(0);
4384 }
4385 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004386 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004387 VERROR(ctxt->userData, "Text element has namespace !\n");
4388 return(0);
4389 }
4390 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004391 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004392 VERROR(ctxt->userData,
4393 "Text element carries namespace definitions !\n");
4394 return(0);
4395 }
4396 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004397 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004398 VERROR(ctxt->userData,
4399 "Text element has no content !\n");
4400 return(0);
4401 }
4402 return(1);
4403 case XML_XINCLUDE_START:
4404 case XML_XINCLUDE_END:
4405 return(1);
4406 case XML_CDATA_SECTION_NODE:
4407 case XML_ENTITY_REF_NODE:
4408 case XML_PI_NODE:
4409 case XML_COMMENT_NODE:
4410 return(1);
4411 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004412 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004413 VERROR(ctxt->userData,
4414 "Entity element not expected here\n");
4415 return(0);
4416 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004417 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004418 VERROR(ctxt->userData,
4419 "Notation element not expected here\n");
4420 return(0);
4421 case XML_DOCUMENT_NODE:
4422 case XML_DOCUMENT_TYPE_NODE:
4423 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004424 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004425 VERROR(ctxt->userData,
4426 "Document element not expected here\n");
4427 return(0);
4428 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004429 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004430 VERROR(ctxt->userData,
4431 "\n");
4432 return(0);
4433 case XML_ELEMENT_NODE:
4434 break;
4435 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004436 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004437 VERROR(ctxt->userData,
4438 "unknown element type %d\n", elem->type);
4439 return(0);
4440 }
4441 if (elem->name == NULL) return(0);
4442
4443 /*
4444 * Fetch the declaration for the qualified name
4445 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004446 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4447 prefix = elem->ns->prefix;
4448
4449 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004450 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004451 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004452 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004453 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004454 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004455 if (elemDecl != NULL)
4456 extsubset = 1;
4457 }
Owen Taylor3473f882001-02-23 17:55:21 +00004458 }
4459
4460 /*
4461 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004462 * This is "non-strict" validation should be done on the
4463 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004464 */
4465 if (elemDecl == NULL) {
4466 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004467 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004468 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004469 if (elemDecl != NULL)
4470 extsubset = 1;
4471 }
Owen Taylor3473f882001-02-23 17:55:21 +00004472 }
4473 if (elemDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004474 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004475 VERROR(ctxt->userData, "No declaration for element %s\n",
4476 elem->name);
4477 return(0);
4478 }
4479
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004480 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004481 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004482 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004483 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004484 VERROR(ctxt->userData, "No declaration for element %s\n",
4485 elem->name);
4486 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004487 case XML_ELEMENT_TYPE_EMPTY:
4488 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004489 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004490 VERROR(ctxt->userData,
4491 "Element %s was declared EMPTY this one has content\n",
4492 elem->name);
4493 ret = 0;
4494 }
4495 break;
4496 case XML_ELEMENT_TYPE_ANY:
4497 /* I don't think anything is required then */
4498 break;
4499 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004500
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004501 /* simple case of declared as #PCDATA */
4502 if ((elemDecl->content != NULL) &&
4503 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4504 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4505 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004506 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004507 VERROR(ctxt->userData,
4508 "Element %s was declared #PCDATA but contains non text nodes\n",
4509 elem->name);
4510 }
4511 break;
4512 }
Owen Taylor3473f882001-02-23 17:55:21 +00004513 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004514 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004515 while (child != NULL) {
4516 if (child->type == XML_ELEMENT_NODE) {
4517 name = child->name;
4518 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4519 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004520 snprintf((char *) qname, sizeof(qname), "%s:%s",
4521 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004522 qname[sizeof(qname) - 1] = 0;
4523 cont = elemDecl->content;
4524 while (cont != NULL) {
4525 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4526 if (xmlStrEqual(cont->name, qname)) break;
4527 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4528 (cont->c1 != NULL) &&
4529 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4530 if (xmlStrEqual(cont->c1->name, qname)) break;
4531 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4532 (cont->c1 == NULL) ||
4533 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4534 /* Internal error !!! */
4535 xmlGenericError(xmlGenericErrorContext,
4536 "Internal: MIXED struct bad\n");
4537 break;
4538 }
4539 cont = cont->c2;
4540 }
4541 if (cont != NULL)
4542 goto child_ok;
4543 }
4544 cont = elemDecl->content;
4545 while (cont != NULL) {
4546 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4547 if (xmlStrEqual(cont->name, name)) break;
4548 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4549 (cont->c1 != NULL) &&
4550 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4551 if (xmlStrEqual(cont->c1->name, name)) break;
4552 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4553 (cont->c1 == NULL) ||
4554 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4555 /* Internal error !!! */
4556 xmlGenericError(xmlGenericErrorContext,
4557 "Internal: MIXED struct bad\n");
4558 break;
4559 }
4560 cont = cont->c2;
4561 }
4562 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004563 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004564 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004565 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004566 name, elem->name);
4567 ret = 0;
4568 }
4569 }
4570child_ok:
4571 child = child->next;
4572 }
4573 break;
4574 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004575 if ((doc->standalone == 1) && (extsubset == 1)) {
4576 /*
4577 * VC: Standalone Document Declaration
4578 * - element types with element content, if white space
4579 * occurs directly within any instance of those types.
4580 */
4581 child = elem->children;
4582 while (child != NULL) {
4583 if (child->type == XML_TEXT_NODE) {
4584 const xmlChar *content = child->content;
4585
4586 while (IS_BLANK(*content))
4587 content++;
4588 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004589 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004590 VERROR(ctxt->userData,
4591"standalone: %s declared in the external subset contains white spaces nodes\n",
4592 elem->name);
4593 ret = 0;
4594 break;
4595 }
4596 }
4597 child =child->next;
4598 }
4599 }
Owen Taylor3473f882001-02-23 17:55:21 +00004600 child = elem->children;
4601 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004602 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004603 if (tmp <= 0)
4604 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004605 break;
4606 }
4607
4608 /* [ VC: Required Attribute ] */
4609 attr = elemDecl->attributes;
4610 while (attr != NULL) {
4611 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004612 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00004613
Daniel Veillarde4301c82002-02-13 13:32:35 +00004614 if ((attr->prefix == NULL) &&
4615 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4616 xmlNsPtr ns;
4617
4618 ns = elem->nsDef;
4619 while (ns != NULL) {
4620 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00004621 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004622 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00004623 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004624 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4625 xmlNsPtr ns;
4626
4627 ns = elem->nsDef;
4628 while (ns != NULL) {
4629 if (xmlStrEqual(attr->name, ns->prefix))
4630 goto found;
4631 ns = ns->next;
4632 }
4633 } else {
4634 xmlAttrPtr attrib;
4635
4636 attrib = elem->properties;
4637 while (attrib != NULL) {
4638 if (xmlStrEqual(attrib->name, attr->name)) {
4639 if (attr->prefix != NULL) {
4640 xmlNsPtr nameSpace = attrib->ns;
4641
4642 if (nameSpace == NULL)
4643 nameSpace = elem->ns;
4644 /*
4645 * qualified names handling is problematic, having a
4646 * different prefix should be possible but DTDs don't
4647 * allow to define the URI instead of the prefix :-(
4648 */
4649 if (nameSpace == NULL) {
4650 if (qualified < 0)
4651 qualified = 0;
4652 } else if (!xmlStrEqual(nameSpace->prefix,
4653 attr->prefix)) {
4654 if (qualified < 1)
4655 qualified = 1;
4656 } else
4657 goto found;
4658 } else {
4659 /*
4660 * We should allow applications to define namespaces
4661 * for their application even if the DTD doesn't
4662 * carry one, otherwise, basically we would always
4663 * break.
4664 */
4665 goto found;
4666 }
4667 }
4668 attrib = attrib->next;
4669 }
Owen Taylor3473f882001-02-23 17:55:21 +00004670 }
4671 if (qualified == -1) {
4672 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004673 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004674 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004675 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004676 elem->name, attr->name);
4677 ret = 0;
4678 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004679 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004680 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004681 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004682 elem->name, attr->prefix,attr->name);
4683 ret = 0;
4684 }
4685 } else if (qualified == 0) {
4686 VWARNING(ctxt->userData,
4687 "Element %s required attribute %s:%s has no prefix\n",
4688 elem->name, attr->prefix,attr->name);
4689 } else if (qualified == 1) {
4690 VWARNING(ctxt->userData,
4691 "Element %s required attribute %s:%s has different prefix\n",
4692 elem->name, attr->prefix,attr->name);
4693 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004694 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
4695 /*
4696 * Special tests checking #FIXED namespace declarations
4697 * have the right value since this is not done as an
4698 * attribute checking
4699 */
4700 if ((attr->prefix == NULL) &&
4701 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4702 xmlNsPtr ns;
4703
4704 ns = elem->nsDef;
4705 while (ns != NULL) {
4706 if (ns->prefix == NULL) {
4707 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004708 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00004709 VERROR(ctxt->userData,
4710 "Element %s namespace name for default namespace does not match the DTD\n",
4711 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00004712 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004713 }
4714 goto found;
4715 }
4716 ns = ns->next;
4717 }
4718 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4719 xmlNsPtr ns;
4720
4721 ns = elem->nsDef;
4722 while (ns != NULL) {
4723 if (xmlStrEqual(attr->name, ns->prefix)) {
4724 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004725 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00004726 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004727 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00004728 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00004729 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004730 }
4731 goto found;
4732 }
4733 ns = ns->next;
4734 }
4735 }
Owen Taylor3473f882001-02-23 17:55:21 +00004736 }
4737found:
4738 attr = attr->nexth;
4739 }
4740 return(ret);
4741}
4742
4743/**
4744 * xmlValidateRoot:
4745 * @ctxt: the validation context
4746 * @doc: a document instance
4747 *
4748 * Try to validate a the root element
4749 * basically it does the following check as described by the
4750 * XML-1.0 recommendation:
4751 * - [ VC: Root Element Type ]
4752 * it doesn't try to recurse or apply other check to the element
4753 *
4754 * returns 1 if valid or 0 otherwise
4755 */
4756
4757int
4758xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4759 xmlNodePtr root;
4760 if (doc == NULL) return(0);
4761
4762 root = xmlDocGetRootElement(doc);
4763 if ((root == NULL) || (root->name == NULL)) {
4764 VERROR(ctxt->userData, "Not valid: no root element\n");
4765 return(0);
4766 }
4767
4768 /*
4769 * When doing post validation against a separate DTD, those may
4770 * no internal subset has been generated
4771 */
4772 if ((doc->intSubset != NULL) &&
4773 (doc->intSubset->name != NULL)) {
4774 /*
4775 * Check first the document root against the NQName
4776 */
4777 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4778 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4779 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004780 snprintf((char *) qname, sizeof(qname), "%s:%s",
4781 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004782 qname[sizeof(qname) - 1] = 0;
4783 if (xmlStrEqual(doc->intSubset->name, qname))
4784 goto name_ok;
4785 }
4786 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4787 (xmlStrEqual(root->name, BAD_CAST "html")))
4788 goto name_ok;
4789 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004790 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004791 root->name, doc->intSubset->name);
4792 return(0);
4793
4794 }
4795 }
4796name_ok:
4797 return(1);
4798}
4799
4800
4801/**
4802 * xmlValidateElement:
4803 * @ctxt: the validation context
4804 * @doc: a document instance
4805 * @elem: an element instance
4806 *
4807 * Try to validate the subtree under an element
4808 *
4809 * returns 1 if valid or 0 otherwise
4810 */
4811
4812int
4813xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4814 xmlNodePtr child;
4815 xmlAttrPtr attr;
4816 xmlChar *value;
4817 int ret = 1;
4818
4819 if (elem == NULL) return(0);
4820
4821 /*
4822 * XInclude elements were added after parsing in the infoset,
4823 * they don't really mean anything validation wise.
4824 */
4825 if ((elem->type == XML_XINCLUDE_START) ||
4826 (elem->type == XML_XINCLUDE_END))
4827 return(1);
4828
4829 CHECK_DTD;
4830
Daniel Veillard10ea86c2001-06-20 13:55:33 +00004831 /*
4832 * Entities references have to be handled separately
4833 */
4834 if (elem->type == XML_ENTITY_REF_NODE) {
4835 return(1);
4836 }
4837
Owen Taylor3473f882001-02-23 17:55:21 +00004838 ret &= xmlValidateOneElement(ctxt, doc, elem);
4839 attr = elem->properties;
4840 while(attr != NULL) {
4841 value = xmlNodeListGetString(doc, attr->children, 0);
4842 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4843 if (value != NULL)
4844 xmlFree(value);
4845 attr= attr->next;
4846 }
4847 child = elem->children;
4848 while (child != NULL) {
4849 ret &= xmlValidateElement(ctxt, doc, child);
4850 child = child->next;
4851 }
4852
4853 return(ret);
4854}
4855
Daniel Veillard8730c562001-02-26 10:49:57 +00004856/**
4857 * xmlValidateRef:
4858 * @ref: A reference to be validated
4859 * @ctxt: Validation context
4860 * @name: Name of ID we are searching for
4861 *
4862 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004863static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004864xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004865 const xmlChar *name) {
4866 xmlAttrPtr id;
4867 xmlAttrPtr attr;
4868
4869 if (ref == NULL)
4870 return;
4871 attr = ref->attr;
4872 if (attr == NULL)
4873 return;
4874 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4875 id = xmlGetID(ctxt->doc, name);
4876 if (id == NULL) {
4877 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004878 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004879 attr->name, name);
4880 ctxt->valid = 0;
4881 }
4882 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4883 xmlChar *dup, *str = NULL, *cur, save;
4884
4885 dup = xmlStrdup(name);
4886 if (dup == NULL) {
4887 ctxt->valid = 0;
4888 return;
4889 }
4890 cur = dup;
4891 while (*cur != 0) {
4892 str = cur;
4893 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4894 save = *cur;
4895 *cur = 0;
4896 id = xmlGetID(ctxt->doc, str);
4897 if (id == NULL) {
4898 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004899 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004900 attr->name, str);
4901 ctxt->valid = 0;
4902 }
4903 if (save == 0)
4904 break;
4905 *cur = save;
4906 while (IS_BLANK(*cur)) cur++;
4907 }
4908 xmlFree(dup);
4909 }
4910}
4911
4912/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004913 * xmlWalkValidateList:
4914 * @data: Contents of current link
4915 * @user: Value supplied by the user
4916 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004917 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00004918 */
4919static int
4920xmlWalkValidateList(const void *data, const void *user)
4921{
4922 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4923 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4924 return 1;
4925}
4926
4927/**
4928 * xmlValidateCheckRefCallback:
4929 * @ref_list: List of references
4930 * @ctxt: Validation context
4931 * @name: Name of ID we are searching for
4932 *
4933 */
4934static void
4935xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4936 const xmlChar *name) {
4937 xmlValidateMemo memo;
4938
4939 if (ref_list == NULL)
4940 return;
4941 memo.ctxt = ctxt;
4942 memo.name = name;
4943
4944 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4945
4946}
4947
4948/**
Owen Taylor3473f882001-02-23 17:55:21 +00004949 * xmlValidateDocumentFinal:
4950 * @ctxt: the validation context
4951 * @doc: a document instance
4952 *
4953 * Does the final step for the document validation once all the
4954 * incremental validation steps have been completed
4955 *
4956 * basically it does the following checks described by the XML Rec
4957 *
4958 *
4959 * returns 1 if valid or 0 otherwise
4960 */
4961
4962int
4963xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4964 xmlRefTablePtr table;
4965
4966 if (doc == NULL) {
4967 xmlGenericError(xmlGenericErrorContext,
4968 "xmlValidateDocumentFinal: doc == NULL\n");
4969 return(0);
4970 }
4971
4972 /*
4973 * Check all the NOTATION/NOTATIONS attributes
4974 */
4975 /*
4976 * Check all the ENTITY/ENTITIES attributes definition for validity
4977 */
4978 /*
4979 * Check all the IDREF/IDREFS attributes definition for validity
4980 */
4981 table = (xmlRefTablePtr) doc->refs;
4982 ctxt->doc = doc;
4983 ctxt->valid = 1;
4984 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4985 return(ctxt->valid);
4986}
4987
4988/**
4989 * xmlValidateDtd:
4990 * @ctxt: the validation context
4991 * @doc: a document instance
4992 * @dtd: a dtd instance
4993 *
4994 * Try to validate the document against the dtd instance
4995 *
4996 * basically it does check all the definitions in the DtD.
4997 *
4998 * returns 1 if valid or 0 otherwise
4999 */
5000
5001int
5002xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
5003 int ret;
5004 xmlDtdPtr oldExt;
5005 xmlNodePtr root;
5006
5007 if (dtd == NULL) return(0);
5008 if (doc == NULL) return(0);
5009 oldExt = doc->extSubset;
5010 doc->extSubset = dtd;
5011 ret = xmlValidateRoot(ctxt, doc);
5012 if (ret == 0) {
5013 doc->extSubset = oldExt;
5014 return(ret);
5015 }
5016 if (doc->ids != NULL) {
5017 xmlFreeIDTable(doc->ids);
5018 doc->ids = NULL;
5019 }
5020 if (doc->refs != NULL) {
5021 xmlFreeRefTable(doc->refs);
5022 doc->refs = NULL;
5023 }
5024 root = xmlDocGetRootElement(doc);
5025 ret = xmlValidateElement(ctxt, doc, root);
5026 ret &= xmlValidateDocumentFinal(ctxt, doc);
5027 doc->extSubset = oldExt;
5028 return(ret);
5029}
5030
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005031static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005032xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
5033 const xmlChar *name ATTRIBUTE_UNUSED) {
5034 if (cur == NULL)
5035 return;
5036 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
5037 xmlChar *notation = cur->content;
5038
Daniel Veillard878eab02002-02-19 13:46:09 +00005039 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005040 int ret;
5041
5042 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
5043 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00005044 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005045 }
5046 }
5047 }
5048}
5049
5050static void
Owen Taylor3473f882001-02-23 17:55:21 +00005051xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00005052 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005053 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00005054 xmlDocPtr doc;
5055 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005056
Owen Taylor3473f882001-02-23 17:55:21 +00005057 if (cur == NULL)
5058 return;
5059 switch (cur->atype) {
5060 case XML_ATTRIBUTE_CDATA:
5061 case XML_ATTRIBUTE_ID:
5062 case XML_ATTRIBUTE_IDREF :
5063 case XML_ATTRIBUTE_IDREFS:
5064 case XML_ATTRIBUTE_NMTOKEN:
5065 case XML_ATTRIBUTE_NMTOKENS:
5066 case XML_ATTRIBUTE_ENUMERATION:
5067 break;
5068 case XML_ATTRIBUTE_ENTITY:
5069 case XML_ATTRIBUTE_ENTITIES:
5070 case XML_ATTRIBUTE_NOTATION:
5071 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005072
5073 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
5074 cur->atype, cur->defaultValue);
5075 if ((ret == 0) && (ctxt->valid == 1))
5076 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005077 }
5078 if (cur->tree != NULL) {
5079 xmlEnumerationPtr tree = cur->tree;
5080 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005081 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00005082 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005083 if ((ret == 0) && (ctxt->valid == 1))
5084 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005085 tree = tree->next;
5086 }
5087 }
5088 }
Daniel Veillard878eab02002-02-19 13:46:09 +00005089 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
5090 doc = cur->doc;
5091 if ((doc == NULL) || (cur->elem == NULL)) {
5092 VERROR(ctxt->userData,
5093 "xmlValidateAttributeCallback(%s): internal error\n",
5094 cur->name);
5095 return;
5096 }
5097 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
5098 if (elem == NULL)
5099 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
5100 if (elem == NULL) {
5101 VERROR(ctxt->userData,
5102 "attribute %s: could not find decl for element %s\n",
5103 cur->name, cur->elem);
5104 return;
5105 }
5106 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
5107 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005108 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00005109 cur->name, cur->elem);
5110 ctxt->valid = 0;
5111 }
5112 }
Owen Taylor3473f882001-02-23 17:55:21 +00005113}
5114
5115/**
5116 * xmlValidateDtdFinal:
5117 * @ctxt: the validation context
5118 * @doc: a document instance
5119 *
5120 * Does the final step for the dtds validation once all the
5121 * subsets have been parsed
5122 *
5123 * basically it does the following checks described by the XML Rec
5124 * - check that ENTITY and ENTITIES type attributes default or
5125 * possible values matches one of the defined entities.
5126 * - check that NOTATION type attributes default or
5127 * possible values matches one of the defined notations.
5128 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005129 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00005130 */
5131
5132int
5133xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00005134 xmlDtdPtr dtd;
5135 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005136 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00005137
5138 if (doc == NULL) return(0);
5139 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5140 return(0);
5141 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005142 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005143 dtd = doc->intSubset;
5144 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5145 table = (xmlAttributeTablePtr) dtd->attributes;
5146 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005147 }
5148 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005149 entities = (xmlEntitiesTablePtr) dtd->entities;
5150 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5151 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005152 }
5153 dtd = doc->extSubset;
5154 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5155 table = (xmlAttributeTablePtr) dtd->attributes;
5156 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005157 }
5158 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005159 entities = (xmlEntitiesTablePtr) dtd->entities;
5160 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5161 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005162 }
5163 return(ctxt->valid);
5164}
5165
5166/**
5167 * xmlValidateDocument:
5168 * @ctxt: the validation context
5169 * @doc: a document instance
5170 *
5171 * Try to validate the document instance
5172 *
5173 * basically it does the all the checks described by the XML Rec
5174 * i.e. validates the internal and external subset (if present)
5175 * and validate the document tree.
5176 *
5177 * returns 1 if valid or 0 otherwise
5178 */
5179
5180int
5181xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5182 int ret;
5183 xmlNodePtr root;
5184
5185 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5186 return(0);
5187 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
5188 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
5189 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
5190 doc->intSubset->SystemID);
5191 if (doc->extSubset == NULL) {
5192 if (doc->intSubset->SystemID != NULL) {
5193 VERROR(ctxt->userData,
5194 "Could not load the external subset \"%s\"\n",
5195 doc->intSubset->SystemID);
5196 } else {
5197 VERROR(ctxt->userData,
5198 "Could not load the external subset \"%s\"\n",
5199 doc->intSubset->ExternalID);
5200 }
5201 return(0);
5202 }
5203 }
5204
5205 if (doc->ids != NULL) {
5206 xmlFreeIDTable(doc->ids);
5207 doc->ids = NULL;
5208 }
5209 if (doc->refs != NULL) {
5210 xmlFreeRefTable(doc->refs);
5211 doc->refs = NULL;
5212 }
5213 ret = xmlValidateDtdFinal(ctxt, doc);
5214 if (!xmlValidateRoot(ctxt, doc)) return(0);
5215
5216 root = xmlDocGetRootElement(doc);
5217 ret &= xmlValidateElement(ctxt, doc, root);
5218 ret &= xmlValidateDocumentFinal(ctxt, doc);
5219 return(ret);
5220}
5221
5222
5223/************************************************************************
5224 * *
5225 * Routines for dynamic validation editing *
5226 * *
5227 ************************************************************************/
5228
5229/**
5230 * xmlValidGetPotentialChildren:
5231 * @ctree: an element content tree
5232 * @list: an array to store the list of child names
5233 * @len: a pointer to the number of element in the list
5234 * @max: the size of the array
5235 *
5236 * Build/extend a list of potential children allowed by the content tree
5237 *
5238 * returns the number of element in the list, or -1 in case of error.
5239 */
5240
5241int
5242xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
5243 int *len, int max) {
5244 int i;
5245
5246 if ((ctree == NULL) || (list == NULL) || (len == NULL))
5247 return(-1);
5248 if (*len >= max) return(*len);
5249
5250 switch (ctree->type) {
5251 case XML_ELEMENT_CONTENT_PCDATA:
5252 for (i = 0; i < *len;i++)
5253 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
5254 list[(*len)++] = BAD_CAST "#PCDATA";
5255 break;
5256 case XML_ELEMENT_CONTENT_ELEMENT:
5257 for (i = 0; i < *len;i++)
5258 if (xmlStrEqual(ctree->name, list[i])) return(*len);
5259 list[(*len)++] = ctree->name;
5260 break;
5261 case XML_ELEMENT_CONTENT_SEQ:
5262 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5263 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5264 break;
5265 case XML_ELEMENT_CONTENT_OR:
5266 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5267 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5268 break;
5269 }
5270
5271 return(*len);
5272}
5273
5274/**
5275 * xmlValidGetValidElements:
5276 * @prev: an element to insert after
5277 * @next: an element to insert next
5278 * @list: an array to store the list of child names
5279 * @max: the size of the array
5280 *
5281 * This function returns the list of authorized children to insert
5282 * within an existing tree while respecting the validity constraints
5283 * forced by the Dtd. The insertion point is defined using @prev and
5284 * @next in the following ways:
5285 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
5286 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
5287 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
5288 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
5289 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
5290 *
5291 * pointers to the element names are inserted at the beginning of the array
5292 * and do not need to be freed.
5293 *
5294 * returns the number of element in the list, or -1 in case of error. If
5295 * the function returns the value @max the caller is invited to grow the
5296 * receiving array and retry.
5297 */
5298
5299int
5300xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
5301 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005302 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005303 int nb_valid_elements = 0;
5304 const xmlChar *elements[256];
5305 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005306 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005307
5308 xmlNode *ref_node;
5309 xmlNode *parent;
5310 xmlNode *test_node;
5311
5312 xmlNode *prev_next;
5313 xmlNode *next_prev;
5314 xmlNode *parent_childs;
5315 xmlNode *parent_last;
5316
5317 xmlElement *element_desc;
5318
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00005319 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005320
Owen Taylor3473f882001-02-23 17:55:21 +00005321 if (prev == NULL && next == NULL)
5322 return(-1);
5323
5324 if (list == NULL) return(-1);
5325 if (max <= 0) return(-1);
5326
5327 nb_valid_elements = 0;
5328 ref_node = prev ? prev : next;
5329 parent = ref_node->parent;
5330
5331 /*
5332 * Retrieves the parent element declaration
5333 */
5334 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5335 parent->name);
5336 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5337 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5338 parent->name);
5339 if (element_desc == NULL) return(-1);
5340
5341 /*
5342 * Do a backup of the current tree structure
5343 */
5344 prev_next = prev ? prev->next : NULL;
5345 next_prev = next ? next->prev : NULL;
5346 parent_childs = parent->children;
5347 parent_last = parent->last;
5348
5349 /*
5350 * Creates a dummy node and insert it into the tree
5351 */
5352 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5353 test_node->doc = ref_node->doc;
5354 test_node->parent = parent;
5355 test_node->prev = prev;
5356 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005357 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005358
5359 if (prev) prev->next = test_node;
5360 else parent->children = test_node;
5361
5362 if (next) next->prev = test_node;
5363 else parent->last = test_node;
5364
5365 /*
5366 * Insert each potential child node and check if the parent is
5367 * still valid
5368 */
5369 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5370 elements, &nb_elements, 256);
5371
5372 for (i = 0;i < nb_elements;i++) {
5373 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005374 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005375 int j;
5376
5377 for (j = 0; j < nb_valid_elements;j++)
5378 if (xmlStrEqual(elements[i], list[j])) break;
5379 list[nb_valid_elements++] = elements[i];
5380 if (nb_valid_elements >= max) break;
5381 }
5382 }
5383
5384 /*
5385 * Restore the tree structure
5386 */
5387 if (prev) prev->next = prev_next;
5388 if (next) next->prev = next_prev;
5389 parent->children = parent_childs;
5390 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005391
5392 /*
5393 * Free up the dummy node
5394 */
5395 test_node->name = name;
5396 xmlFreeNode(test_node);
5397
Owen Taylor3473f882001-02-23 17:55:21 +00005398 return(nb_valid_elements);
5399}