blob: de441c2b384c1aac718318673ecef5f825546a11 [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 Veillard4432df22003-09-28 18:58:27 +000028static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
29 int create);
Daniel Veillarde62d36c2001-05-15 08:53:16 +000030/* #define DEBUG_VALID_ALGO */
Daniel Veillard5acfd6b2002-09-18 16:29:02 +000031/* #define DEBUG_REGEXP_ALGO */
Daniel Veillarde62d36c2001-05-15 08:53:16 +000032
Daniel Veillarda646cfd2002-09-17 21:50:03 +000033#define TODO \
34 xmlGenericError(xmlGenericErrorContext, \
35 "Unimplemented block at %s:%d\n", \
36 __FILE__, __LINE__);
Owen Taylor3473f882001-02-23 17:55:21 +000037
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000038/************************************************************************
39 * *
40 * Error handling routines *
41 * *
42 ************************************************************************/
43
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000044/**
Daniel Veillardce9457f2003-10-05 21:33:18 +000045 * xmlVErrMemory:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000046 * @ctxt: an XML validation parser context
47 * @extra: extra informations
48 *
49 * Handle an out of memory error
50 */
51static void
Daniel Veillardce9457f2003-10-05 21:33:18 +000052xmlVErrMemory(xmlValidCtxtPtr ctxt, const char *extra)
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000053{
Daniel Veillardbb5abab2003-10-03 22:21:51 +000054 xmlGenericErrorFunc channel = NULL;
55 xmlParserCtxtPtr pctxt = NULL;
56 void *data = NULL;
57
58 if (ctxt != NULL) {
59 channel = ctxt->error;
60 data = ctxt->userData;
61 pctxt = ctxt->userData;
62 }
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000063 if (extra)
Daniel Veillard73000572003-10-11 11:26:42 +000064 __xmlRaiseError(NULL, channel, data,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000065 pctxt, NULL, XML_FROM_DTD, XML_ERR_NO_MEMORY,
66 XML_ERR_FATAL, NULL, 0, extra, NULL, NULL, 0, 0,
67 "Memory allocation failed : %s\n", extra);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000068 else
Daniel Veillard73000572003-10-11 11:26:42 +000069 __xmlRaiseError(NULL, channel, data,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000070 pctxt, NULL, XML_FROM_DTD, XML_ERR_NO_MEMORY,
71 XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, 0, 0,
72 "Memory allocation failed\n");
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000073}
74
75/**
76 * xmlErrValid:
77 * @ctxt: an XML validation parser context
Daniel Veillardbb5abab2003-10-03 22:21:51 +000078 * @error: the error number
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000079 * @extra: extra informations
80 *
81 * Handle a validation error
82 */
83static void
84xmlErrValid(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlParserErrors error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000085 const char *msg, const char *extra)
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000086{
Daniel Veillardbb5abab2003-10-03 22:21:51 +000087 xmlGenericErrorFunc channel = NULL;
88 xmlParserCtxtPtr pctxt = NULL;
89 void *data = NULL;
90
91 if (ctxt != NULL) {
92 channel = ctxt->error;
93 data = ctxt->userData;
94 pctxt = ctxt->userData;
95 }
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000096 if (extra)
Daniel Veillard73000572003-10-11 11:26:42 +000097 __xmlRaiseError(NULL, channel, data,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000098 pctxt, NULL, XML_FROM_DTD, error,
99 XML_ERR_ERROR, NULL, 0, extra, NULL, NULL, 0, 0,
100 msg, extra);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000101 else
Daniel Veillard73000572003-10-11 11:26:42 +0000102 __xmlRaiseError(NULL, channel, data,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000103 pctxt, NULL, XML_FROM_DTD, error,
104 XML_ERR_ERROR, NULL, 0, NULL, NULL, NULL, 0, 0,
105 msg);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000106}
107
Daniel Veillardd61e8fb2003-10-19 21:59:17 +0000108#ifdef LIBXML_VALID_ENABLED
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000109/**
110 * xmlErrValidNodeNr:
111 * @ctxt: an XML validation parser context
112 * @node: the node raising the error
113 * @error: the error number
114 * @str1: extra informations
115 * @int2: extra informations
116 * @str3: extra informations
117 *
118 * Handle a validation error, provide contextual informations
119 */
120static void
121xmlErrValidNodeNr(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
122 xmlNodePtr node, xmlParserErrors error,
123 const char *msg, const xmlChar * str1,
124 int int2, const xmlChar * str3)
125{
Daniel Veillard659e71e2003-10-10 14:10:40 +0000126 xmlStructuredErrorFunc schannel = NULL;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000127 xmlGenericErrorFunc channel = NULL;
128 xmlParserCtxtPtr pctxt = NULL;
129 void *data = NULL;
130
131 if (ctxt != NULL) {
132 channel = ctxt->error;
133 data = ctxt->userData;
134 pctxt = ctxt->userData;
Daniel Veillard659e71e2003-10-10 14:10:40 +0000135 pctxt = ctxt->userData;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000136 }
Daniel Veillard659e71e2003-10-10 14:10:40 +0000137 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_DTD, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000138 XML_ERR_ERROR, NULL, 0,
139 (const char *) str1,
140 (const char *) str3,
141 NULL, int2, 0, msg, str1, int2, str3);
142}
143/**
144 * xmlErrValidNode:
145 * @ctxt: an XML validation parser context
146 * @node: the node raising the error
147 * @error: the error number
148 * @str1: extra informations
149 * @str2: extra informations
150 * @str3: extra informations
151 *
152 * Handle a validation error, provide contextual informations
153 */
154static void
155xmlErrValidNode(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
156 xmlNodePtr node, xmlParserErrors error,
157 const char *msg, const xmlChar * str1,
158 const xmlChar * str2, const xmlChar * str3)
159{
Daniel Veillard659e71e2003-10-10 14:10:40 +0000160 xmlStructuredErrorFunc schannel = NULL;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000161 xmlGenericErrorFunc channel = NULL;
162 xmlParserCtxtPtr pctxt = NULL;
163 void *data = NULL;
164
165 if (ctxt != NULL) {
166 channel = ctxt->error;
167 data = ctxt->userData;
168 pctxt = ctxt->userData;
Daniel Veillard659e71e2003-10-10 14:10:40 +0000169 pctxt = ctxt->userData;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000170 }
Daniel Veillard659e71e2003-10-10 14:10:40 +0000171 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_DTD, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000172 XML_ERR_ERROR, NULL, 0,
173 (const char *) str1,
174 (const char *) str1,
175 (const char *) str3, 0, 0, msg, str1, str2, str3);
176}
177/**
178 * xmlErrValidWarning:
179 * @ctxt: an XML validation parser context
180 * @node: the node raising the error
181 * @error: the error number
182 * @str1: extra informations
183 * @str2: extra informations
184 * @str3: extra informations
185 *
186 * Handle a validation error, provide contextual informations
187 */
188static void
189xmlErrValidWarning(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
190 xmlNodePtr node, xmlParserErrors error,
191 const char *msg, const xmlChar * str1,
192 const xmlChar * str2, const xmlChar * str3)
193{
Daniel Veillard659e71e2003-10-10 14:10:40 +0000194 xmlStructuredErrorFunc schannel = NULL;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000195 xmlGenericErrorFunc channel = NULL;
196 xmlParserCtxtPtr pctxt = NULL;
197 void *data = NULL;
198
199 if (ctxt != NULL) {
200 channel = ctxt->error;
201 data = ctxt->userData;
202 pctxt = ctxt->userData;
Daniel Veillard659e71e2003-10-10 14:10:40 +0000203 pctxt = ctxt->userData;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000204 }
Daniel Veillard659e71e2003-10-10 14:10:40 +0000205 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_DTD, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000206 XML_ERR_WARNING, NULL, 0,
207 (const char *) str1,
208 (const char *) str1,
209 (const char *) str3, 0, 0, msg, str1, str2, str3);
210}
211
212
Daniel Veillardea7751d2002-12-20 00:16:24 +0000213
214#ifdef LIBXML_REGEXP_ENABLED
215/*
216 * If regexp are enabled we can do continuous validation without the
217 * need of a tree to validate the content model. this is done in each
218 * callbacks.
219 * Each xmlValidState represent the validation state associated to the
220 * set of nodes currently open from the document root to the current element.
221 */
222
223
224typedef struct _xmlValidState {
225 xmlElementPtr elemDecl; /* pointer to the content model */
226 xmlNodePtr node; /* pointer to the current node */
227 xmlRegExecCtxtPtr exec; /* regexp runtime */
228} _xmlValidState;
229
230
231static int
232vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000233 if ((ctxt->vstateMax == 0) || (ctxt->vstateTab == NULL)) {
Daniel Veillardea7751d2002-12-20 00:16:24 +0000234 ctxt->vstateMax = 10;
235 ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax *
236 sizeof(ctxt->vstateTab[0]));
237 if (ctxt->vstateTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000238 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillardea7751d2002-12-20 00:16:24 +0000239 return(-1);
240 }
241 }
242
243 if (ctxt->vstateNr >= ctxt->vstateMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000244 xmlValidState *tmp;
245
246 tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
247 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
248 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000249 xmlVErrMemory(ctxt, "realloc failed");
Daniel Veillardea7751d2002-12-20 00:16:24 +0000250 return(-1);
251 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000252 ctxt->vstateMax *= 2;
253 ctxt->vstateTab = tmp;
Daniel Veillardea7751d2002-12-20 00:16:24 +0000254 }
255 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr];
256 ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl;
257 ctxt->vstateTab[ctxt->vstateNr].node = node;
258 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
259 if (elemDecl->contModel == NULL)
260 xmlValidBuildContentModel(ctxt, elemDecl);
261 if (elemDecl->contModel != NULL) {
262 ctxt->vstateTab[ctxt->vstateNr].exec =
263 xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
264 } else {
265 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000266 xmlErrValidNode(ctxt, (xmlNodePtr) elemDecl,
267 XML_ERR_INTERNAL_ERROR,
268 "Failed to build content model regexp for %s\n",
269 node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +0000270 }
271 }
272 return(ctxt->vstateNr++);
273}
274
275static int
276vstateVPop(xmlValidCtxtPtr ctxt) {
277 xmlElementPtr elemDecl;
278
Daniel Veillard336fc7d2002-12-27 19:37:04 +0000279 if (ctxt->vstateNr < 1) return(-1);
Daniel Veillardea7751d2002-12-20 00:16:24 +0000280 ctxt->vstateNr--;
281 elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
282 ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
283 ctxt->vstateTab[ctxt->vstateNr].node = NULL;
284 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
285 xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
286 }
287 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
288 if (ctxt->vstateNr >= 1)
289 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
290 else
291 ctxt->vstate = NULL;
292 return(ctxt->vstateNr);
293}
294
295#else /* not LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000296/*
Daniel Veillard1c732d22002-11-30 11:22:59 +0000297 * If regexp are not enabled, it uses a home made algorithm less
298 * complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000299 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000300 * only restriction is on the deepness of the tree limited by the
301 * size of the occurs bitfield
302 *
303 * this is the content of a saved state for rollbacks
304 */
305
306#define ROLLBACK_OR 0
307#define ROLLBACK_PARENT 1
308
Daniel Veillardb44025c2001-10-11 22:55:55 +0000309typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000310 xmlElementContentPtr cont; /* pointer to the content model subtree */
311 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000312 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000313 unsigned char depth; /* current depth in the overall tree */
314 unsigned char state; /* ROLLBACK_XXX */
315} _xmlValidState;
316
Daniel Veillardfc57b412002-04-29 15:50:14 +0000317#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000318#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
319#define CONT ctxt->vstate->cont
320#define NODE ctxt->vstate->node
321#define DEPTH ctxt->vstate->depth
322#define OCCURS ctxt->vstate->occurs
323#define STATE ctxt->vstate->state
324
Daniel Veillard5344c602001-12-31 16:37:34 +0000325#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
326#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000327
Daniel Veillard5344c602001-12-31 16:37:34 +0000328#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
329#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000330
331static int
332vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
333 xmlNodePtr node, unsigned char depth, long occurs,
334 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000335 int i = ctxt->vstateNr - 1;
336
Daniel Veillard940492d2002-04-15 10:15:25 +0000337 if (ctxt->vstateNr > MAX_RECURSE) {
338 return(-1);
339 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000340 if (ctxt->vstateTab == NULL) {
341 ctxt->vstateMax = 8;
342 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
343 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
344 if (ctxt->vstateTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000345 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000346 return(-1);
347 }
348 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000349 if (ctxt->vstateNr >= ctxt->vstateMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000350 xmlValidState *tmp;
351
352 tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
353 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
354 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000355 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard940492d2002-04-15 10:15:25 +0000356 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000357 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000358 ctxt->vstateMax *= 2;
359 ctxt->vstateTab = tmp;
Daniel Veillard06803992001-04-22 10:35:56 +0000360 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000361 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000362 /*
363 * Don't push on the stack a state already here
364 */
365 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
366 (ctxt->vstateTab[i].node == node) &&
367 (ctxt->vstateTab[i].depth == depth) &&
368 (ctxt->vstateTab[i].occurs == occurs) &&
369 (ctxt->vstateTab[i].state == state))
370 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000371 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
372 ctxt->vstateTab[ctxt->vstateNr].node = node;
373 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
374 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
375 ctxt->vstateTab[ctxt->vstateNr].state = state;
376 return(ctxt->vstateNr++);
377}
378
379static int
380vstateVPop(xmlValidCtxtPtr ctxt) {
381 if (ctxt->vstateNr <= 1) return(-1);
382 ctxt->vstateNr--;
383 ctxt->vstate = &ctxt->vstateTab[0];
384 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
385 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
386 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
387 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
388 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
389 return(ctxt->vstateNr);
390}
391
Daniel Veillard118aed72002-09-24 14:13:13 +0000392#endif /* LIBXML_REGEXP_ENABLED */
393
Daniel Veillard1c732d22002-11-30 11:22:59 +0000394static int
395nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value)
396{
397 if (ctxt->nodeMax <= 0) {
398 ctxt->nodeMax = 4;
399 ctxt->nodeTab =
400 (xmlNodePtr *) xmlMalloc(ctxt->nodeMax *
401 sizeof(ctxt->nodeTab[0]));
402 if (ctxt->nodeTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000403 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c732d22002-11-30 11:22:59 +0000404 ctxt->nodeMax = 0;
405 return (0);
406 }
407 }
408 if (ctxt->nodeNr >= ctxt->nodeMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000409 xmlNodePtr *tmp;
410 tmp = (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
411 ctxt->nodeMax * 2 * sizeof(ctxt->nodeTab[0]));
412 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000413 xmlVErrMemory(ctxt, "realloc failed");
Daniel Veillard1c732d22002-11-30 11:22:59 +0000414 return (0);
415 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000416 ctxt->nodeMax *= 2;
417 ctxt->nodeTab = tmp;
Daniel Veillard1c732d22002-11-30 11:22:59 +0000418 }
419 ctxt->nodeTab[ctxt->nodeNr] = value;
420 ctxt->node = value;
421 return (ctxt->nodeNr++);
422}
423static xmlNodePtr
424nodeVPop(xmlValidCtxtPtr ctxt)
425{
426 xmlNodePtr ret;
427
428 if (ctxt->nodeNr <= 0)
429 return (0);
430 ctxt->nodeNr--;
431 if (ctxt->nodeNr > 0)
432 ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1];
433 else
434 ctxt->node = NULL;
435 ret = ctxt->nodeTab[ctxt->nodeNr];
436 ctxt->nodeTab[ctxt->nodeNr] = 0;
437 return (ret);
438}
Owen Taylor3473f882001-02-23 17:55:21 +0000439
Owen Taylor3473f882001-02-23 17:55:21 +0000440#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000441static void
442xmlValidPrintNode(xmlNodePtr cur) {
443 if (cur == NULL) {
444 xmlGenericError(xmlGenericErrorContext, "null");
445 return;
446 }
447 switch (cur->type) {
448 case XML_ELEMENT_NODE:
449 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
450 break;
451 case XML_TEXT_NODE:
452 xmlGenericError(xmlGenericErrorContext, "text ");
453 break;
454 case XML_CDATA_SECTION_NODE:
455 xmlGenericError(xmlGenericErrorContext, "cdata ");
456 break;
457 case XML_ENTITY_REF_NODE:
458 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
459 break;
460 case XML_PI_NODE:
461 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
462 break;
463 case XML_COMMENT_NODE:
464 xmlGenericError(xmlGenericErrorContext, "comment ");
465 break;
466 case XML_ATTRIBUTE_NODE:
467 xmlGenericError(xmlGenericErrorContext, "?attr? ");
468 break;
469 case XML_ENTITY_NODE:
470 xmlGenericError(xmlGenericErrorContext, "?ent? ");
471 break;
472 case XML_DOCUMENT_NODE:
473 xmlGenericError(xmlGenericErrorContext, "?doc? ");
474 break;
475 case XML_DOCUMENT_TYPE_NODE:
476 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
477 break;
478 case XML_DOCUMENT_FRAG_NODE:
479 xmlGenericError(xmlGenericErrorContext, "?frag? ");
480 break;
481 case XML_NOTATION_NODE:
482 xmlGenericError(xmlGenericErrorContext, "?nota? ");
483 break;
484 case XML_HTML_DOCUMENT_NODE:
485 xmlGenericError(xmlGenericErrorContext, "?html? ");
486 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000487#ifdef LIBXML_DOCB_ENABLED
488 case XML_DOCB_DOCUMENT_NODE:
489 xmlGenericError(xmlGenericErrorContext, "?docb? ");
490 break;
491#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000492 case XML_DTD_NODE:
493 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
494 break;
495 case XML_ELEMENT_DECL:
496 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
497 break;
498 case XML_ATTRIBUTE_DECL:
499 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
500 break;
501 case XML_ENTITY_DECL:
502 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
503 break;
504 case XML_NAMESPACE_DECL:
505 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
506 break;
507 case XML_XINCLUDE_START:
508 xmlGenericError(xmlGenericErrorContext, "incstart ");
509 break;
510 case XML_XINCLUDE_END:
511 xmlGenericError(xmlGenericErrorContext, "incend ");
512 break;
513 }
514}
515
516static void
517xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000518 if (cur == NULL)
519 xmlGenericError(xmlGenericErrorContext, "null ");
520 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000521 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000522 cur = cur->next;
523 }
524}
525
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000526static void
527xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Daniel Veillard83391282003-03-06 21:37:30 +0000528 char expr[5000];
Owen Taylor3473f882001-02-23 17:55:21 +0000529
530 expr[0] = 0;
531 xmlGenericError(xmlGenericErrorContext, "valid: ");
532 xmlValidPrintNodeList(cur);
533 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000534 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000535 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
536}
537
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000538static void
539xmlValidDebugState(xmlValidStatePtr state) {
540 xmlGenericError(xmlGenericErrorContext, "(");
541 if (state->cont == NULL)
542 xmlGenericError(xmlGenericErrorContext, "null,");
543 else
544 switch (state->cont->type) {
545 case XML_ELEMENT_CONTENT_PCDATA:
546 xmlGenericError(xmlGenericErrorContext, "pcdata,");
547 break;
548 case XML_ELEMENT_CONTENT_ELEMENT:
549 xmlGenericError(xmlGenericErrorContext, "%s,",
550 state->cont->name);
551 break;
552 case XML_ELEMENT_CONTENT_SEQ:
553 xmlGenericError(xmlGenericErrorContext, "seq,");
554 break;
555 case XML_ELEMENT_CONTENT_OR:
556 xmlGenericError(xmlGenericErrorContext, "or,");
557 break;
558 }
559 xmlValidPrintNode(state->node);
560 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
561 state->depth, state->occurs, state->state);
562}
563
564static void
565xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
566 int i, j;
567
568 xmlGenericError(xmlGenericErrorContext, "state: ");
569 xmlValidDebugState(ctxt->vstate);
570 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
571 ctxt->vstateNr - 1);
572 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
573 xmlValidDebugState(&ctxt->vstateTab[j]);
574 xmlGenericError(xmlGenericErrorContext, "\n");
575}
576
577/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000578#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000579 *****/
580
581#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000582#define DEBUG_VALID_MSG(m) \
583 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
584
Owen Taylor3473f882001-02-23 17:55:21 +0000585#else
586#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000587#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000588#endif
589
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000590/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000591
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000592
Owen Taylor3473f882001-02-23 17:55:21 +0000593#define CHECK_DTD \
594 if (doc == NULL) return(0); \
595 else if ((doc->intSubset == NULL) && \
596 (doc->extSubset == NULL)) return(0)
597
Owen Taylor3473f882001-02-23 17:55:21 +0000598xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
599
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000600#ifdef LIBXML_REGEXP_ENABLED
601
602/************************************************************************
603 * *
604 * Content model validation based on the regexps *
605 * *
606 ************************************************************************/
607
608/**
609 * xmlValidBuildAContentModel:
610 * @content: the content model
611 * @ctxt: the schema parser context
612 * @name: the element name whose content is being built
613 *
614 * Generate the automata sequence needed for that type
615 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000616 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000617 */
618static int
619xmlValidBuildAContentModel(xmlElementContentPtr content,
620 xmlValidCtxtPtr ctxt,
621 const xmlChar *name) {
622 if (content == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000623 xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
624 "Found NULL content in content model of %s\n",
625 name, NULL, NULL);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000626 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000627 }
628 switch (content->type) {
629 case XML_ELEMENT_CONTENT_PCDATA:
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000630 xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
631 "Found PCDATA in content model of %s\n",
632 name, NULL, NULL);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000633 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000634 break;
635 case XML_ELEMENT_CONTENT_ELEMENT: {
636 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillardc00cda82003-04-07 10:22:39 +0000637 xmlChar fn[50];
638 xmlChar *fullname;
639
640 fullname = xmlBuildQName(content->name, content->prefix, fn, 50);
641 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000642 xmlVErrMemory(ctxt, "Building content model");
Daniel Veillardc00cda82003-04-07 10:22:39 +0000643 return(0);
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000644 }
645
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000646 switch (content->ocur) {
647 case XML_ELEMENT_CONTENT_ONCE:
648 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000649 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000650 break;
651 case XML_ELEMENT_CONTENT_OPT:
652 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000653 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000654 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
655 break;
656 case XML_ELEMENT_CONTENT_PLUS:
657 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000658 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000659 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000660 ctxt->state, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000661 break;
662 case XML_ELEMENT_CONTENT_MULT:
663 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000664 ctxt->state, fullname, NULL);
Daniel Veillard57e79b32003-02-04 15:33:12 +0000665 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, ctxt->state,
666 NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000667 break;
668 }
Daniel Veillardc00cda82003-04-07 10:22:39 +0000669 if ((fullname != fn) && (fullname != content->name))
670 xmlFree(fullname);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000671 break;
672 }
673 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000674 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000675 xmlElementContentOccur ocur;
676
677 /*
678 * Simply iterate over the content
679 */
680 oldstate = ctxt->state;
681 ocur = content->ocur;
Daniel Veillardf4be0182003-02-24 19:54:33 +0000682 if (ocur != XML_ELEMENT_CONTENT_ONCE) {
683 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
684 oldstate = ctxt->state;
685 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000686 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000687 xmlValidBuildAContentModel(content->c1, ctxt, name);
688 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000689 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
690 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
691 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000692 oldend = ctxt->state;
693 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000694 switch (ocur) {
695 case XML_ELEMENT_CONTENT_ONCE:
696 break;
697 case XML_ELEMENT_CONTENT_OPT:
698 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
699 break;
700 case XML_ELEMENT_CONTENT_MULT:
701 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000702 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000703 break;
704 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000705 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000706 break;
707 }
708 break;
709 }
710 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000711 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000712 xmlElementContentOccur ocur;
713
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000714 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000715 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
716 (ocur == XML_ELEMENT_CONTENT_MULT)) {
717 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
718 ctxt->state, NULL);
719 }
720 oldstate = ctxt->state;
721 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000722
723 /*
724 * iterate over the subtypes and remerge the end with an
725 * epsilon transition
726 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000727 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000728 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000729 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000730 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000731 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000732 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
733 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000734 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000735 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000736 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
737 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000738 switch (ocur) {
739 case XML_ELEMENT_CONTENT_ONCE:
740 break;
741 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000742 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000743 break;
744 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000745 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
746 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000747 break;
748 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000749 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000750 break;
751 }
752 break;
753 }
754 default:
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000755 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
756 "ContentModel broken for element %s\n",
757 (const char *) name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000758 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000759 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000760 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000761}
762/**
763 * xmlValidBuildContentModel:
764 * @ctxt: a validation context
765 * @elem: an element declaration node
766 *
767 * (Re)Build the automata associated to the content model of this
768 * element
769 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000770 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000771 */
772int
773xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000774
775 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000776 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000777 if (elem->type != XML_ELEMENT_DECL)
778 return(0);
779 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
780 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000781 /* TODO: should we rebuild in this case ? */
Daniel Veillardec498e12003-02-05 11:01:50 +0000782 if (elem->contModel != NULL) {
783 if (!xmlRegexpIsDeterminist(elem->contModel)) {
784 ctxt->valid = 0;
785 return(0);
786 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000787 return(1);
Daniel Veillardec498e12003-02-05 11:01:50 +0000788 }
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000789
790 ctxt->am = xmlNewAutomata();
791 if (ctxt->am == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000792 xmlErrValidNode(ctxt, (xmlNodePtr) elem,
793 XML_ERR_INTERNAL_ERROR,
794 "Cannot create automata for element %s\n",
795 elem->name, NULL, NULL);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000796 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000797 }
William M. Brack78637da2003-07-31 14:47:38 +0000798 ctxt->state = xmlAutomataGetInitState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000799 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
800 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000801 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000802 if (xmlRegexpIsDeterminist(elem->contModel) != 1) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000803 char expr[5000];
804 expr[0] = 0;
805 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000806 xmlErrValidNode(ctxt, (xmlNodePtr) elem,
807 XML_DTD_CONTENT_NOT_DETERMINIST,
808 "Content model of %s is not determinist: %s\n",
809 elem->name, BAD_CAST expr, NULL);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000810#ifdef DEBUG_REGEXP_ALGO
811 xmlRegexpPrint(stderr, elem->contModel);
812#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000813 ctxt->valid = 0;
Daniel Veillardec498e12003-02-05 11:01:50 +0000814 ctxt->state = NULL;
815 xmlFreeAutomata(ctxt->am);
816 ctxt->am = NULL;
817 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000818 }
819 ctxt->state = NULL;
820 xmlFreeAutomata(ctxt->am);
821 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000822 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000823}
824
825#endif /* LIBXML_REGEXP_ENABLED */
826
Owen Taylor3473f882001-02-23 17:55:21 +0000827/****************************************************************
828 * *
829 * Util functions for data allocation/deallocation *
830 * *
831 ****************************************************************/
832
833/**
Daniel Veillarda37aab82003-06-09 09:10:36 +0000834 * xmlNewValidCtxt:
835 *
836 * Allocate a validation context structure.
837 *
838 * Returns NULL if not, otherwise the new validation context structure
839 */
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000840xmlValidCtxtPtr xmlNewValidCtxt(void) {
Daniel Veillarda37aab82003-06-09 09:10:36 +0000841 xmlValidCtxtPtr ret;
842
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000843 if ((ret = xmlMalloc(sizeof (xmlValidCtxt))) == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000844 xmlVErrMemory(NULL, "malloc failed");
Daniel Veillarda37aab82003-06-09 09:10:36 +0000845 return (NULL);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000846 }
Daniel Veillarda37aab82003-06-09 09:10:36 +0000847
848 (void) memset(ret, 0, sizeof (xmlValidCtxt));
849
850 return (ret);
851}
852
853/**
854 * xmlFreeValidCtxt:
855 * @cur: the validation context to free
856 *
857 * Free a validation context structure.
858 */
859void
860xmlFreeValidCtxt(xmlValidCtxtPtr cur) {
861 xmlFree(cur);
862}
863
Daniel Veillard4432df22003-09-28 18:58:27 +0000864#endif /* LIBXML_VALID_ENABLED */
865
Daniel Veillarda37aab82003-06-09 09:10:36 +0000866/**
Owen Taylor3473f882001-02-23 17:55:21 +0000867 * xmlNewElementContent:
868 * @name: the subelement name or NULL
869 * @type: the type of element content decl
870 *
871 * Allocate an element content structure.
872 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000873 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000874 */
875xmlElementContentPtr
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000876xmlNewElementContent(const xmlChar *name, xmlElementContentType type) {
Owen Taylor3473f882001-02-23 17:55:21 +0000877 xmlElementContentPtr ret;
878
879 switch(type) {
880 case XML_ELEMENT_CONTENT_ELEMENT:
881 if (name == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000882 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
883 "xmlNewElementContent : name == NULL !\n",
884 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000885 }
886 break;
887 case XML_ELEMENT_CONTENT_PCDATA:
888 case XML_ELEMENT_CONTENT_SEQ:
889 case XML_ELEMENT_CONTENT_OR:
890 if (name != NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000891 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
892 "xmlNewElementContent : name != NULL !\n",
893 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000894 }
895 break;
896 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000897 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
898 "Internal: ELEMENT content corrupted invalid type\n",
899 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000900 return(NULL);
901 }
902 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
903 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000904 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +0000905 return(NULL);
906 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000907 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000908 ret->type = type;
909 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000910 if (name != NULL) {
911 xmlChar *prefix = NULL;
912 ret->name = xmlSplitQName2(name, &prefix);
913 if (ret->name == NULL)
914 ret->name = xmlStrdup(name);
915 ret->prefix = prefix;
916 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000917 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000918 ret->prefix = NULL;
919 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000920 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000921 return(ret);
922}
923
924/**
925 * xmlCopyElementContent:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000926 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +0000927 *
928 * Build a copy of an element content description.
929 *
930 * Returns the new xmlElementContentPtr or NULL in case of error.
931 */
932xmlElementContentPtr
933xmlCopyElementContent(xmlElementContentPtr cur) {
934 xmlElementContentPtr ret;
935
936 if (cur == NULL) return(NULL);
937 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
938 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000939 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +0000940 return(NULL);
941 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000942 if (cur->prefix != NULL)
943 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000944 ret->ocur = cur->ocur;
945 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000946 if (ret->c1 != NULL)
947 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000948 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000949 if (ret->c2 != NULL)
950 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000951 return(ret);
952}
953
954/**
955 * xmlFreeElementContent:
956 * @cur: the element content tree to free
957 *
958 * Free an element content structure. This is a recursive call !
959 */
960void
961xmlFreeElementContent(xmlElementContentPtr cur) {
962 if (cur == NULL) return;
963 switch (cur->type) {
964 case XML_ELEMENT_CONTENT_PCDATA:
965 case XML_ELEMENT_CONTENT_ELEMENT:
966 case XML_ELEMENT_CONTENT_SEQ:
967 case XML_ELEMENT_CONTENT_OR:
968 break;
969 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000970 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
971 "Internal: ELEMENT content corrupted invalid type\n",
972 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000973 return;
974 }
975 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
976 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
977 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000978 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000979 xmlFree(cur);
980}
981
Daniel Veillarda9cce9c2003-09-29 13:20:24 +0000982#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +0000983/**
984 * xmlDumpElementContent:
985 * @buf: An XML buffer
986 * @content: An element table
987 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
988 *
989 * This will dump the content of the element table as an XML DTD definition
990 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000991static void
Owen Taylor3473f882001-02-23 17:55:21 +0000992xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
993 if (content == NULL) return;
994
995 if (glob) xmlBufferWriteChar(buf, "(");
996 switch (content->type) {
997 case XML_ELEMENT_CONTENT_PCDATA:
998 xmlBufferWriteChar(buf, "#PCDATA");
999 break;
1000 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001001 if (content->prefix != NULL) {
1002 xmlBufferWriteCHAR(buf, content->prefix);
1003 xmlBufferWriteChar(buf, ":");
1004 }
Owen Taylor3473f882001-02-23 17:55:21 +00001005 xmlBufferWriteCHAR(buf, content->name);
1006 break;
1007 case XML_ELEMENT_CONTENT_SEQ:
1008 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1009 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
1010 xmlDumpElementContent(buf, content->c1, 1);
1011 else
1012 xmlDumpElementContent(buf, content->c1, 0);
1013 xmlBufferWriteChar(buf, " , ");
1014 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
1015 xmlDumpElementContent(buf, content->c2, 1);
1016 else
1017 xmlDumpElementContent(buf, content->c2, 0);
1018 break;
1019 case XML_ELEMENT_CONTENT_OR:
1020 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1021 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
1022 xmlDumpElementContent(buf, content->c1, 1);
1023 else
1024 xmlDumpElementContent(buf, content->c1, 0);
1025 xmlBufferWriteChar(buf, " | ");
1026 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
1027 xmlDumpElementContent(buf, content->c2, 1);
1028 else
1029 xmlDumpElementContent(buf, content->c2, 0);
1030 break;
1031 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001032 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
1033 "Internal: ELEMENT content corrupted invalid type\n",
1034 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001035 }
1036 if (glob)
1037 xmlBufferWriteChar(buf, ")");
1038 switch (content->ocur) {
1039 case XML_ELEMENT_CONTENT_ONCE:
1040 break;
1041 case XML_ELEMENT_CONTENT_OPT:
1042 xmlBufferWriteChar(buf, "?");
1043 break;
1044 case XML_ELEMENT_CONTENT_MULT:
1045 xmlBufferWriteChar(buf, "*");
1046 break;
1047 case XML_ELEMENT_CONTENT_PLUS:
1048 xmlBufferWriteChar(buf, "+");
1049 break;
1050 }
1051}
1052
1053/**
1054 * xmlSprintfElementContent:
1055 * @buf: an output buffer
1056 * @content: An element table
1057 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
1058 *
Daniel Veillardd3d06722001-08-15 12:06:36 +00001059 * Deprecated, unsafe, use xmlSnprintfElementContent
1060 */
1061void
1062xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
1063 xmlElementContentPtr content ATTRIBUTE_UNUSED,
1064 int glob ATTRIBUTE_UNUSED) {
1065}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001066#endif /* LIBXML_OUTPUT_ENABLED */
Daniel Veillardd3d06722001-08-15 12:06:36 +00001067
1068/**
1069 * xmlSnprintfElementContent:
1070 * @buf: an output buffer
1071 * @size: the buffer size
1072 * @content: An element table
1073 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
1074 *
Owen Taylor3473f882001-02-23 17:55:21 +00001075 * This will dump the content of the element content definition
1076 * Intended just for the debug routine
1077 */
1078void
Daniel Veillardd3d06722001-08-15 12:06:36 +00001079xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
1080 int len;
1081
Owen Taylor3473f882001-02-23 17:55:21 +00001082 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +00001083 len = strlen(buf);
1084 if (size - len < 50) {
1085 if ((size - len > 4) && (buf[len - 1] != '.'))
1086 strcat(buf, " ...");
1087 return;
1088 }
Owen Taylor3473f882001-02-23 17:55:21 +00001089 if (glob) strcat(buf, "(");
1090 switch (content->type) {
1091 case XML_ELEMENT_CONTENT_PCDATA:
1092 strcat(buf, "#PCDATA");
1093 break;
1094 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001095 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00001096 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001097 strcat(buf, " ...");
1098 return;
1099 }
1100 strcat(buf, (char *) content->prefix);
1101 strcat(buf, ":");
1102 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00001103 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00001104 strcat(buf, " ...");
1105 return;
1106 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001107 if (content->name != NULL)
1108 strcat(buf, (char *) content->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001109 break;
1110 case XML_ELEMENT_CONTENT_SEQ:
1111 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1112 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001113 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001114 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001115 xmlSnprintfElementContent(buf, size, content->c1, 0);
1116 len = strlen(buf);
1117 if (size - len < 50) {
1118 if ((size - len > 4) && (buf[len - 1] != '.'))
1119 strcat(buf, " ...");
1120 return;
1121 }
Owen Taylor3473f882001-02-23 17:55:21 +00001122 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00001123 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
1124 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1125 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001126 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001127 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001128 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001129 break;
1130 case XML_ELEMENT_CONTENT_OR:
1131 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1132 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001133 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001134 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001135 xmlSnprintfElementContent(buf, size, content->c1, 0);
1136 len = strlen(buf);
1137 if (size - len < 50) {
1138 if ((size - len > 4) && (buf[len - 1] != '.'))
1139 strcat(buf, " ...");
1140 return;
1141 }
Owen Taylor3473f882001-02-23 17:55:21 +00001142 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00001143 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
1144 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1145 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001146 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001147 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001148 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001149 break;
1150 }
1151 if (glob)
1152 strcat(buf, ")");
1153 switch (content->ocur) {
1154 case XML_ELEMENT_CONTENT_ONCE:
1155 break;
1156 case XML_ELEMENT_CONTENT_OPT:
1157 strcat(buf, "?");
1158 break;
1159 case XML_ELEMENT_CONTENT_MULT:
1160 strcat(buf, "*");
1161 break;
1162 case XML_ELEMENT_CONTENT_PLUS:
1163 strcat(buf, "+");
1164 break;
1165 }
1166}
1167
1168/****************************************************************
1169 * *
1170 * Registration of DTD declarations *
1171 * *
1172 ****************************************************************/
1173
1174/**
1175 * xmlCreateElementTable:
1176 *
1177 * create and initialize an empty element hash table.
1178 *
1179 * Returns the xmlElementTablePtr just created or NULL in case of error.
1180 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001181static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001182xmlCreateElementTable(void) {
1183 return(xmlHashCreate(0));
1184}
1185
1186/**
1187 * xmlFreeElement:
1188 * @elem: An element
1189 *
1190 * Deallocate the memory used by an element definition
1191 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001192static void
Owen Taylor3473f882001-02-23 17:55:21 +00001193xmlFreeElement(xmlElementPtr elem) {
1194 if (elem == NULL) return;
1195 xmlUnlinkNode((xmlNodePtr) elem);
1196 xmlFreeElementContent(elem->content);
1197 if (elem->name != NULL)
1198 xmlFree((xmlChar *) elem->name);
1199 if (elem->prefix != NULL)
1200 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001201#ifdef LIBXML_REGEXP_ENABLED
1202 if (elem->contModel != NULL)
1203 xmlRegFreeRegexp(elem->contModel);
1204#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001205 xmlFree(elem);
1206}
1207
1208
1209/**
1210 * xmlAddElementDecl:
1211 * @ctxt: the validation context
1212 * @dtd: pointer to the DTD
1213 * @name: the entity name
1214 * @type: the element type
1215 * @content: the element content tree or NULL
1216 *
1217 * Register a new element declaration
1218 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001219 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001220 */
1221xmlElementPtr
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001222xmlAddElementDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1223 xmlDtdPtr dtd, const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001224 xmlElementTypeVal type,
1225 xmlElementContentPtr content) {
1226 xmlElementPtr ret;
1227 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001228 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001229 xmlChar *ns, *uqname;
1230
1231 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001232 return(NULL);
1233 }
1234 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001235 return(NULL);
1236 }
1237 switch (type) {
1238 case XML_ELEMENT_TYPE_EMPTY:
1239 if (content != NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001240 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1241 "xmlAddElementDecl: content != NULL for EMPTY\n",
1242 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001243 return(NULL);
1244 }
1245 break;
1246 case XML_ELEMENT_TYPE_ANY:
1247 if (content != NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001248 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1249 "xmlAddElementDecl: content != NULL for ANY\n",
1250 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001251 return(NULL);
1252 }
1253 break;
1254 case XML_ELEMENT_TYPE_MIXED:
1255 if (content == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001256 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1257 "xmlAddElementDecl: content == NULL for MIXED\n",
1258 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001259 return(NULL);
1260 }
1261 break;
1262 case XML_ELEMENT_TYPE_ELEMENT:
1263 if (content == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001264 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1265 "xmlAddElementDecl: content == NULL for ELEMENT\n",
1266 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001267 return(NULL);
1268 }
1269 break;
1270 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001271 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1272 "Internal: ELEMENT decl corrupted invalid type\n",
1273 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001274 return(NULL);
1275 }
1276
1277 /*
1278 * check if name is a QName
1279 */
1280 uqname = xmlSplitQName2(name, &ns);
1281 if (uqname != NULL)
1282 name = uqname;
1283
1284 /*
1285 * Create the Element table if needed.
1286 */
1287 table = (xmlElementTablePtr) dtd->elements;
1288 if (table == NULL) {
1289 table = xmlCreateElementTable();
1290 dtd->elements = (void *) table;
1291 }
1292 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001293 xmlVErrMemory(ctxt,
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001294 "xmlAddElementDecl: Table creation failed!\n");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001295 if (uqname != NULL)
1296 xmlFree(uqname);
1297 if (ns != NULL)
1298 xmlFree(ns);
Owen Taylor3473f882001-02-23 17:55:21 +00001299 return(NULL);
1300 }
1301
Daniel Veillarda10efa82001-04-18 13:09:01 +00001302 /*
1303 * lookup old attributes inserted on an undefined element in the
1304 * internal subset.
1305 */
1306 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1307 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1308 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1309 oldAttributes = ret->attributes;
1310 ret->attributes = NULL;
1311 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1312 xmlFreeElement(ret);
1313 }
Owen Taylor3473f882001-02-23 17:55:21 +00001314 }
Owen Taylor3473f882001-02-23 17:55:21 +00001315
1316 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001317 * The element may already be present if one of its attribute
1318 * was registered first
1319 */
1320 ret = xmlHashLookup2(table, name, ns);
1321 if (ret != NULL) {
1322 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001323#ifdef LIBXML_VALID_ENABLED
Daniel Veillarda10efa82001-04-18 13:09:01 +00001324 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001325 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001326 */
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001327 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED,
1328 "Redefinition of element %s\n",
1329 name, NULL, NULL);
Daniel Veillard4432df22003-09-28 18:58:27 +00001330#endif /* LIBXML_VALID_ENABLED */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001331 if (uqname != NULL)
1332 xmlFree(uqname);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001333 if (ns != NULL)
1334 xmlFree(ns);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001335 return(NULL);
1336 }
1337 } else {
1338 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1339 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001340 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001341 if (uqname != NULL)
1342 xmlFree(uqname);
1343 if (ns != NULL)
1344 xmlFree(ns);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001345 return(NULL);
1346 }
1347 memset(ret, 0, sizeof(xmlElement));
1348 ret->type = XML_ELEMENT_DECL;
1349
1350 /*
1351 * fill the structure.
1352 */
1353 ret->name = xmlStrdup(name);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001354 if (ret->name == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001355 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001356 if (uqname != NULL)
1357 xmlFree(uqname);
1358 if (ns != NULL)
1359 xmlFree(ns);
1360 xmlFree(ret);
1361 return(NULL);
1362 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00001363 ret->prefix = ns;
1364
1365 /*
1366 * Validity Check:
1367 * Insertion must not fail
1368 */
1369 if (xmlHashAddEntry2(table, name, ns, ret)) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001370#ifdef LIBXML_VALID_ENABLED
Daniel Veillarda10efa82001-04-18 13:09:01 +00001371 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001372 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001373 */
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001374 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED,
1375 "Redefinition of element %s\n",
1376 name, NULL, NULL);
Daniel Veillard4432df22003-09-28 18:58:27 +00001377#endif /* LIBXML_VALID_ENABLED */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001378 xmlFreeElement(ret);
1379 if (uqname != NULL)
1380 xmlFree(uqname);
1381 return(NULL);
1382 }
William M. Brack4e52f2f2003-09-14 18:07:39 +00001383 /*
1384 * For new element, may have attributes from earlier
1385 * definition in internal subset
1386 */
1387 ret->attributes = oldAttributes;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001388 }
1389
1390 /*
1391 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001392 */
1393 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001394 ret->content = xmlCopyElementContent(content);
Owen Taylor3473f882001-02-23 17:55:21 +00001395
1396 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001397 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001398 */
1399 ret->parent = dtd;
1400 ret->doc = dtd->doc;
1401 if (dtd->last == NULL) {
1402 dtd->children = dtd->last = (xmlNodePtr) ret;
1403 } else {
1404 dtd->last->next = (xmlNodePtr) ret;
1405 ret->prev = dtd->last;
1406 dtd->last = (xmlNodePtr) ret;
1407 }
1408 if (uqname != NULL)
1409 xmlFree(uqname);
1410 return(ret);
1411}
1412
1413/**
1414 * xmlFreeElementTable:
1415 * @table: An element table
1416 *
1417 * Deallocate the memory used by an element hash table.
1418 */
1419void
1420xmlFreeElementTable(xmlElementTablePtr table) {
1421 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1422}
1423
Daniel Veillard652327a2003-09-29 18:02:38 +00001424#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001425/**
1426 * xmlCopyElement:
1427 * @elem: An element
1428 *
1429 * Build a copy of an element.
1430 *
1431 * Returns the new xmlElementPtr or NULL in case of error.
1432 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001433static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001434xmlCopyElement(xmlElementPtr elem) {
1435 xmlElementPtr cur;
1436
1437 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1438 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001439 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001440 return(NULL);
1441 }
1442 memset(cur, 0, sizeof(xmlElement));
1443 cur->type = XML_ELEMENT_DECL;
1444 cur->etype = elem->etype;
1445 if (elem->name != NULL)
1446 cur->name = xmlStrdup(elem->name);
1447 else
1448 cur->name = NULL;
1449 if (elem->prefix != NULL)
1450 cur->prefix = xmlStrdup(elem->prefix);
1451 else
1452 cur->prefix = NULL;
1453 cur->content = xmlCopyElementContent(elem->content);
1454 /* TODO : rebuild the attribute list on the copy */
1455 cur->attributes = NULL;
1456 return(cur);
1457}
1458
1459/**
1460 * xmlCopyElementTable:
1461 * @table: An element table
1462 *
1463 * Build a copy of an element table.
1464 *
1465 * Returns the new xmlElementTablePtr or NULL in case of error.
1466 */
1467xmlElementTablePtr
1468xmlCopyElementTable(xmlElementTablePtr table) {
1469 return((xmlElementTablePtr) xmlHashCopy(table,
1470 (xmlHashCopier) xmlCopyElement));
1471}
Daniel Veillard652327a2003-09-29 18:02:38 +00001472#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001473
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001474#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001475/**
1476 * xmlDumpElementDecl:
1477 * @buf: the XML buffer output
1478 * @elem: An element table
1479 *
1480 * This will dump the content of the element declaration as an XML
1481 * DTD definition
1482 */
1483void
1484xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1485 switch (elem->etype) {
1486 case XML_ELEMENT_TYPE_EMPTY:
1487 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001488 if (elem->prefix != NULL) {
1489 xmlBufferWriteCHAR(buf, elem->prefix);
1490 xmlBufferWriteChar(buf, ":");
1491 }
Owen Taylor3473f882001-02-23 17:55:21 +00001492 xmlBufferWriteCHAR(buf, elem->name);
1493 xmlBufferWriteChar(buf, " EMPTY>\n");
1494 break;
1495 case XML_ELEMENT_TYPE_ANY:
1496 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001497 if (elem->prefix != NULL) {
1498 xmlBufferWriteCHAR(buf, elem->prefix);
1499 xmlBufferWriteChar(buf, ":");
1500 }
Owen Taylor3473f882001-02-23 17:55:21 +00001501 xmlBufferWriteCHAR(buf, elem->name);
1502 xmlBufferWriteChar(buf, " ANY>\n");
1503 break;
1504 case XML_ELEMENT_TYPE_MIXED:
1505 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001506 if (elem->prefix != NULL) {
1507 xmlBufferWriteCHAR(buf, elem->prefix);
1508 xmlBufferWriteChar(buf, ":");
1509 }
Owen Taylor3473f882001-02-23 17:55:21 +00001510 xmlBufferWriteCHAR(buf, elem->name);
1511 xmlBufferWriteChar(buf, " ");
1512 xmlDumpElementContent(buf, elem->content, 1);
1513 xmlBufferWriteChar(buf, ">\n");
1514 break;
1515 case XML_ELEMENT_TYPE_ELEMENT:
1516 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001517 if (elem->prefix != NULL) {
1518 xmlBufferWriteCHAR(buf, elem->prefix);
1519 xmlBufferWriteChar(buf, ":");
1520 }
Owen Taylor3473f882001-02-23 17:55:21 +00001521 xmlBufferWriteCHAR(buf, elem->name);
1522 xmlBufferWriteChar(buf, " ");
1523 xmlDumpElementContent(buf, elem->content, 1);
1524 xmlBufferWriteChar(buf, ">\n");
1525 break;
1526 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001527 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
1528 "Internal: ELEMENT struct corrupted invalid type\n",
1529 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001530 }
1531}
1532
1533/**
1534 * xmlDumpElementTable:
1535 * @buf: the XML buffer output
1536 * @table: An element table
1537 *
1538 * This will dump the content of the element table as an XML DTD definition
1539 */
1540void
1541xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1542 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1543}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001544#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001545
1546/**
1547 * xmlCreateEnumeration:
1548 * @name: the enumeration name or NULL
1549 *
1550 * create and initialize an enumeration attribute node.
1551 *
1552 * Returns the xmlEnumerationPtr just created or NULL in case
1553 * of error.
1554 */
1555xmlEnumerationPtr
Daniel Veillard2fdbd322003-08-18 12:15:38 +00001556xmlCreateEnumeration(const xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00001557 xmlEnumerationPtr ret;
1558
1559 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1560 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001561 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001562 return(NULL);
1563 }
1564 memset(ret, 0, sizeof(xmlEnumeration));
1565
1566 if (name != NULL)
1567 ret->name = xmlStrdup(name);
1568 return(ret);
1569}
1570
1571/**
1572 * xmlFreeEnumeration:
1573 * @cur: the tree to free.
1574 *
1575 * free an enumeration attribute node (recursive).
1576 */
1577void
1578xmlFreeEnumeration(xmlEnumerationPtr cur) {
1579 if (cur == NULL) return;
1580
1581 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1582
1583 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001584 xmlFree(cur);
1585}
1586
Daniel Veillard652327a2003-09-29 18:02:38 +00001587#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001588/**
1589 * xmlCopyEnumeration:
1590 * @cur: the tree to copy.
1591 *
1592 * Copy an enumeration attribute node (recursive).
1593 *
1594 * Returns the xmlEnumerationPtr just created or NULL in case
1595 * of error.
1596 */
1597xmlEnumerationPtr
1598xmlCopyEnumeration(xmlEnumerationPtr cur) {
1599 xmlEnumerationPtr ret;
1600
1601 if (cur == NULL) return(NULL);
1602 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1603
1604 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1605 else ret->next = NULL;
1606
1607 return(ret);
1608}
Daniel Veillard652327a2003-09-29 18:02:38 +00001609#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001610
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001611#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001612/**
1613 * xmlDumpEnumeration:
1614 * @buf: the XML buffer output
1615 * @enum: An enumeration
1616 *
1617 * This will dump the content of the enumeration
1618 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001619static void
Owen Taylor3473f882001-02-23 17:55:21 +00001620xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1621 if (cur == NULL) return;
1622
1623 xmlBufferWriteCHAR(buf, cur->name);
1624 if (cur->next == NULL)
1625 xmlBufferWriteChar(buf, ")");
1626 else {
1627 xmlBufferWriteChar(buf, " | ");
1628 xmlDumpEnumeration(buf, cur->next);
1629 }
1630}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001631#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001632
1633/**
1634 * xmlCreateAttributeTable:
1635 *
1636 * create and initialize an empty attribute hash table.
1637 *
1638 * Returns the xmlAttributeTablePtr just created or NULL in case
1639 * of error.
1640 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001641static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001642xmlCreateAttributeTable(void) {
1643 return(xmlHashCreate(0));
1644}
1645
Daniel Veillard4432df22003-09-28 18:58:27 +00001646#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001647/**
1648 * xmlScanAttributeDeclCallback:
1649 * @attr: the attribute decl
1650 * @list: the list to update
1651 *
1652 * Callback called by xmlScanAttributeDecl when a new attribute
1653 * has to be entered in the list.
1654 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001655static void
Owen Taylor3473f882001-02-23 17:55:21 +00001656xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001657 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001658 attr->nexth = *list;
1659 *list = attr;
1660}
1661
1662/**
1663 * xmlScanAttributeDecl:
1664 * @dtd: pointer to the DTD
1665 * @elem: the element name
1666 *
1667 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001668 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001669 *
1670 * Returns the pointer to the first attribute decl in the chain,
1671 * possibly NULL.
1672 */
1673xmlAttributePtr
1674xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1675 xmlAttributePtr ret = NULL;
1676 xmlAttributeTablePtr table;
1677
1678 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001679 return(NULL);
1680 }
1681 if (elem == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001682 return(NULL);
1683 }
1684 table = (xmlAttributeTablePtr) dtd->attributes;
1685 if (table == NULL)
1686 return(NULL);
1687
1688 /* WRONG !!! */
1689 xmlHashScan3(table, NULL, NULL, elem,
1690 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1691 return(ret);
1692}
1693
1694/**
1695 * xmlScanIDAttributeDecl:
1696 * @ctxt: the validation context
1697 * @elem: the element name
1698 *
1699 * Verify that the element don't have too many ID attributes
1700 * declared.
1701 *
1702 * Returns the number of ID attributes found.
1703 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001704static int
Owen Taylor3473f882001-02-23 17:55:21 +00001705xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1706 xmlAttributePtr cur;
1707 int ret = 0;
1708
1709 if (elem == NULL) return(0);
1710 cur = elem->attributes;
1711 while (cur != NULL) {
1712 if (cur->atype == XML_ATTRIBUTE_ID) {
1713 ret ++;
1714 if (ret > 1)
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001715 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_MULTIPLE_ID,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001716 "Element %s has too many ID attributes defined : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001717 elem->name, cur->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001718 }
1719 cur = cur->nexth;
1720 }
1721 return(ret);
1722}
Daniel Veillard4432df22003-09-28 18:58:27 +00001723#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001724
1725/**
1726 * xmlFreeAttribute:
1727 * @elem: An attribute
1728 *
1729 * Deallocate the memory used by an attribute definition
1730 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001731static void
Owen Taylor3473f882001-02-23 17:55:21 +00001732xmlFreeAttribute(xmlAttributePtr attr) {
1733 if (attr == NULL) return;
1734 xmlUnlinkNode((xmlNodePtr) attr);
1735 if (attr->tree != NULL)
1736 xmlFreeEnumeration(attr->tree);
1737 if (attr->elem != NULL)
1738 xmlFree((xmlChar *) attr->elem);
1739 if (attr->name != NULL)
1740 xmlFree((xmlChar *) attr->name);
1741 if (attr->defaultValue != NULL)
1742 xmlFree((xmlChar *) attr->defaultValue);
1743 if (attr->prefix != NULL)
1744 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001745 xmlFree(attr);
1746}
1747
1748
1749/**
1750 * xmlAddAttributeDecl:
1751 * @ctxt: the validation context
1752 * @dtd: pointer to the DTD
1753 * @elem: the element name
1754 * @name: the attribute name
1755 * @ns: the attribute namespace prefix
1756 * @type: the attribute type
1757 * @def: the attribute default type
1758 * @defaultValue: the attribute default value
1759 * @tree: if it's an enumeration, the associated list
1760 *
1761 * Register a new attribute declaration
1762 * Note that @tree becomes the ownership of the DTD
1763 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001764 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001765 */
1766xmlAttributePtr
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001767xmlAddAttributeDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1768 xmlDtdPtr dtd, const xmlChar *elem,
Owen Taylor3473f882001-02-23 17:55:21 +00001769 const xmlChar *name, const xmlChar *ns,
1770 xmlAttributeType type, xmlAttributeDefault def,
1771 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1772 xmlAttributePtr ret;
1773 xmlAttributeTablePtr table;
1774 xmlElementPtr elemDef;
1775
1776 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001777 xmlFreeEnumeration(tree);
1778 return(NULL);
1779 }
1780 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001781 xmlFreeEnumeration(tree);
1782 return(NULL);
1783 }
1784 if (elem == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001785 xmlFreeEnumeration(tree);
1786 return(NULL);
1787 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001788
Daniel Veillard4432df22003-09-28 18:58:27 +00001789#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001790 /*
1791 * Check the type and possibly the default value.
1792 */
1793 switch (type) {
1794 case XML_ATTRIBUTE_CDATA:
1795 break;
1796 case XML_ATTRIBUTE_ID:
1797 break;
1798 case XML_ATTRIBUTE_IDREF:
1799 break;
1800 case XML_ATTRIBUTE_IDREFS:
1801 break;
1802 case XML_ATTRIBUTE_ENTITY:
1803 break;
1804 case XML_ATTRIBUTE_ENTITIES:
1805 break;
1806 case XML_ATTRIBUTE_NMTOKEN:
1807 break;
1808 case XML_ATTRIBUTE_NMTOKENS:
1809 break;
1810 case XML_ATTRIBUTE_ENUMERATION:
1811 break;
1812 case XML_ATTRIBUTE_NOTATION:
1813 break;
1814 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001815 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1816 "Internal: ATTRIBUTE struct corrupted invalid type\n",
1817 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001818 xmlFreeEnumeration(tree);
1819 return(NULL);
1820 }
1821 if ((defaultValue != NULL) &&
1822 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001823 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_DEFAULT,
1824 "Attribute %s of %s: invalid default value\n",
1825 elem, name, defaultValue);
Owen Taylor3473f882001-02-23 17:55:21 +00001826 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001827 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001828 }
Daniel Veillard4432df22003-09-28 18:58:27 +00001829#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001830
1831 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001832 * Check first that an attribute defined in the external subset wasn't
1833 * already defined in the internal subset
1834 */
1835 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1836 (dtd->doc->intSubset != NULL) &&
1837 (dtd->doc->intSubset->attributes != NULL)) {
1838 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1839 if (ret != NULL)
1840 return(NULL);
1841 }
1842
1843 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001844 * Create the Attribute table if needed.
1845 */
1846 table = (xmlAttributeTablePtr) dtd->attributes;
1847 if (table == NULL) {
1848 table = xmlCreateAttributeTable();
1849 dtd->attributes = (void *) table;
1850 }
1851 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001852 xmlVErrMemory(ctxt,
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001853 "xmlAddAttributeDecl: Table creation failed!\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001854 return(NULL);
1855 }
1856
1857
1858 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1859 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001860 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001861 return(NULL);
1862 }
1863 memset(ret, 0, sizeof(xmlAttribute));
1864 ret->type = XML_ATTRIBUTE_DECL;
1865
1866 /*
1867 * fill the structure.
1868 */
1869 ret->atype = type;
1870 ret->name = xmlStrdup(name);
1871 ret->prefix = xmlStrdup(ns);
1872 ret->elem = xmlStrdup(elem);
1873 ret->def = def;
1874 ret->tree = tree;
1875 if (defaultValue != NULL)
1876 ret->defaultValue = xmlStrdup(defaultValue);
1877
1878 /*
1879 * Validity Check:
1880 * Search the DTD for previous declarations of the ATTLIST
1881 */
1882 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001883#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001884 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001885 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001886 */
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001887 xmlErrValidWarning(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_REDEFINED,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001888 "Attribute %s of element %s: already defined\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001889 name, elem, NULL);
Daniel Veillard4432df22003-09-28 18:58:27 +00001890#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001891 xmlFreeAttribute(ret);
1892 return(NULL);
1893 }
1894
1895 /*
1896 * Validity Check:
1897 * Multiple ID per element
1898 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001899 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001900 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001901
Daniel Veillard4432df22003-09-28 18:58:27 +00001902#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001903 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001904 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001905 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID,
Owen Taylor3473f882001-02-23 17:55:21 +00001906 "Element %s has too may ID attributes defined : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001907 elem, name, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00001908 ctxt->valid = 0;
1909 }
Daniel Veillard4432df22003-09-28 18:58:27 +00001910#endif /* LIBXML_VALID_ENABLED */
Daniel Veillardc7612992002-02-17 22:47:37 +00001911
Daniel Veillard48da9102001-08-07 01:10:10 +00001912 /*
1913 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001914 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001915 */
1916 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1917 ((ret->prefix != NULL &&
1918 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1919 ret->nexth = elemDef->attributes;
1920 elemDef->attributes = ret;
1921 } else {
1922 xmlAttributePtr tmp = elemDef->attributes;
1923
1924 while ((tmp != NULL) &&
1925 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1926 ((ret->prefix != NULL &&
1927 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1928 if (tmp->nexth == NULL)
1929 break;
1930 tmp = tmp->nexth;
1931 }
1932 if (tmp != NULL) {
1933 ret->nexth = tmp->nexth;
1934 tmp->nexth = ret;
1935 } else {
1936 ret->nexth = elemDef->attributes;
1937 elemDef->attributes = ret;
1938 }
1939 }
Owen Taylor3473f882001-02-23 17:55:21 +00001940 }
1941
1942 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001943 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001944 */
1945 ret->parent = dtd;
1946 ret->doc = dtd->doc;
1947 if (dtd->last == NULL) {
1948 dtd->children = dtd->last = (xmlNodePtr) ret;
1949 } else {
1950 dtd->last->next = (xmlNodePtr) ret;
1951 ret->prev = dtd->last;
1952 dtd->last = (xmlNodePtr) ret;
1953 }
1954 return(ret);
1955}
1956
1957/**
1958 * xmlFreeAttributeTable:
1959 * @table: An attribute table
1960 *
1961 * Deallocate the memory used by an entities hash table.
1962 */
1963void
1964xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1965 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1966}
1967
Daniel Veillard652327a2003-09-29 18:02:38 +00001968#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001969/**
1970 * xmlCopyAttribute:
1971 * @attr: An attribute
1972 *
1973 * Build a copy of an attribute.
1974 *
1975 * Returns the new xmlAttributePtr or NULL in case of error.
1976 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001977static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001978xmlCopyAttribute(xmlAttributePtr attr) {
1979 xmlAttributePtr cur;
1980
1981 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1982 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001983 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001984 return(NULL);
1985 }
1986 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001987 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001988 cur->atype = attr->atype;
1989 cur->def = attr->def;
1990 cur->tree = xmlCopyEnumeration(attr->tree);
1991 if (attr->elem != NULL)
1992 cur->elem = xmlStrdup(attr->elem);
1993 if (attr->name != NULL)
1994 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001995 if (attr->prefix != NULL)
1996 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001997 if (attr->defaultValue != NULL)
1998 cur->defaultValue = xmlStrdup(attr->defaultValue);
1999 return(cur);
2000}
2001
2002/**
2003 * xmlCopyAttributeTable:
2004 * @table: An attribute table
2005 *
2006 * Build a copy of an attribute table.
2007 *
2008 * Returns the new xmlAttributeTablePtr or NULL in case of error.
2009 */
2010xmlAttributeTablePtr
2011xmlCopyAttributeTable(xmlAttributeTablePtr table) {
2012 return((xmlAttributeTablePtr) xmlHashCopy(table,
2013 (xmlHashCopier) xmlCopyAttribute));
2014}
Daniel Veillard652327a2003-09-29 18:02:38 +00002015#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002016
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002017#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002018/**
2019 * xmlDumpAttributeDecl:
2020 * @buf: the XML buffer output
2021 * @attr: An attribute declaration
2022 *
2023 * This will dump the content of the attribute declaration as an XML
2024 * DTD definition
2025 */
2026void
2027xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
2028 xmlBufferWriteChar(buf, "<!ATTLIST ");
2029 xmlBufferWriteCHAR(buf, attr->elem);
2030 xmlBufferWriteChar(buf, " ");
2031 if (attr->prefix != NULL) {
2032 xmlBufferWriteCHAR(buf, attr->prefix);
2033 xmlBufferWriteChar(buf, ":");
2034 }
2035 xmlBufferWriteCHAR(buf, attr->name);
2036 switch (attr->atype) {
2037 case XML_ATTRIBUTE_CDATA:
2038 xmlBufferWriteChar(buf, " CDATA");
2039 break;
2040 case XML_ATTRIBUTE_ID:
2041 xmlBufferWriteChar(buf, " ID");
2042 break;
2043 case XML_ATTRIBUTE_IDREF:
2044 xmlBufferWriteChar(buf, " IDREF");
2045 break;
2046 case XML_ATTRIBUTE_IDREFS:
2047 xmlBufferWriteChar(buf, " IDREFS");
2048 break;
2049 case XML_ATTRIBUTE_ENTITY:
2050 xmlBufferWriteChar(buf, " ENTITY");
2051 break;
2052 case XML_ATTRIBUTE_ENTITIES:
2053 xmlBufferWriteChar(buf, " ENTITIES");
2054 break;
2055 case XML_ATTRIBUTE_NMTOKEN:
2056 xmlBufferWriteChar(buf, " NMTOKEN");
2057 break;
2058 case XML_ATTRIBUTE_NMTOKENS:
2059 xmlBufferWriteChar(buf, " NMTOKENS");
2060 break;
2061 case XML_ATTRIBUTE_ENUMERATION:
2062 xmlBufferWriteChar(buf, " (");
2063 xmlDumpEnumeration(buf, attr->tree);
2064 break;
2065 case XML_ATTRIBUTE_NOTATION:
2066 xmlBufferWriteChar(buf, " NOTATION (");
2067 xmlDumpEnumeration(buf, attr->tree);
2068 break;
2069 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002070 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2071 "Internal: ATTRIBUTE struct corrupted invalid type\n",
2072 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002073 }
2074 switch (attr->def) {
2075 case XML_ATTRIBUTE_NONE:
2076 break;
2077 case XML_ATTRIBUTE_REQUIRED:
2078 xmlBufferWriteChar(buf, " #REQUIRED");
2079 break;
2080 case XML_ATTRIBUTE_IMPLIED:
2081 xmlBufferWriteChar(buf, " #IMPLIED");
2082 break;
2083 case XML_ATTRIBUTE_FIXED:
2084 xmlBufferWriteChar(buf, " #FIXED");
2085 break;
2086 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002087 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2088 "Internal: ATTRIBUTE struct corrupted invalid def\n",
2089 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002090 }
2091 if (attr->defaultValue != NULL) {
2092 xmlBufferWriteChar(buf, " ");
2093 xmlBufferWriteQuotedString(buf, attr->defaultValue);
2094 }
2095 xmlBufferWriteChar(buf, ">\n");
2096}
2097
2098/**
2099 * xmlDumpAttributeTable:
2100 * @buf: the XML buffer output
2101 * @table: An attribute table
2102 *
2103 * This will dump the content of the attribute table as an XML DTD definition
2104 */
2105void
2106xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
2107 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
2108}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002109#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002110
2111/************************************************************************
2112 * *
2113 * NOTATIONs *
2114 * *
2115 ************************************************************************/
2116/**
2117 * xmlCreateNotationTable:
2118 *
2119 * create and initialize an empty notation hash table.
2120 *
2121 * Returns the xmlNotationTablePtr just created or NULL in case
2122 * of error.
2123 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002124static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002125xmlCreateNotationTable(void) {
2126 return(xmlHashCreate(0));
2127}
2128
2129/**
2130 * xmlFreeNotation:
2131 * @not: A notation
2132 *
2133 * Deallocate the memory used by an notation definition
2134 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002135static void
Owen Taylor3473f882001-02-23 17:55:21 +00002136xmlFreeNotation(xmlNotationPtr nota) {
2137 if (nota == NULL) return;
2138 if (nota->name != NULL)
2139 xmlFree((xmlChar *) nota->name);
2140 if (nota->PublicID != NULL)
2141 xmlFree((xmlChar *) nota->PublicID);
2142 if (nota->SystemID != NULL)
2143 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002144 xmlFree(nota);
2145}
2146
2147
2148/**
2149 * xmlAddNotationDecl:
2150 * @dtd: pointer to the DTD
2151 * @ctxt: the validation context
2152 * @name: the entity name
2153 * @PublicID: the public identifier or NULL
2154 * @SystemID: the system identifier or NULL
2155 *
2156 * Register a new notation declaration
2157 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002158 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00002159 */
2160xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002161xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002162 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00002163 const xmlChar *PublicID, const xmlChar *SystemID) {
2164 xmlNotationPtr ret;
2165 xmlNotationTablePtr table;
2166
2167 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002168 return(NULL);
2169 }
2170 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002171 return(NULL);
2172 }
2173 if ((PublicID == NULL) && (SystemID == NULL)) {
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002174 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002175 }
2176
2177 /*
2178 * Create the Notation table if needed.
2179 */
2180 table = (xmlNotationTablePtr) dtd->notations;
2181 if (table == NULL)
2182 dtd->notations = table = xmlCreateNotationTable();
2183 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002184 xmlVErrMemory(ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00002185 "xmlAddNotationDecl: Table creation failed!\n");
2186 return(NULL);
2187 }
2188
2189 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2190 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002191 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002192 return(NULL);
2193 }
2194 memset(ret, 0, sizeof(xmlNotation));
2195
2196 /*
2197 * fill the structure.
2198 */
2199 ret->name = xmlStrdup(name);
2200 if (SystemID != NULL)
2201 ret->SystemID = xmlStrdup(SystemID);
2202 if (PublicID != NULL)
2203 ret->PublicID = xmlStrdup(PublicID);
2204
2205 /*
2206 * Validity Check:
2207 * Check the DTD for previous declarations of the ATTLIST
2208 */
2209 if (xmlHashAddEntry(table, name, ret)) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002210#ifdef LIBXML_VALID_ENABLED
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002211 xmlErrValid(NULL, XML_DTD_NOTATION_REDEFINED,
2212 "xmlAddNotationDecl: %s already defined\n",
2213 (const char *) name);
Daniel Veillard4432df22003-09-28 18:58:27 +00002214#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002215 xmlFreeNotation(ret);
2216 return(NULL);
2217 }
2218 return(ret);
2219}
2220
2221/**
2222 * xmlFreeNotationTable:
2223 * @table: An notation table
2224 *
2225 * Deallocate the memory used by an entities hash table.
2226 */
2227void
2228xmlFreeNotationTable(xmlNotationTablePtr table) {
2229 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2230}
2231
Daniel Veillard652327a2003-09-29 18:02:38 +00002232#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002233/**
2234 * xmlCopyNotation:
2235 * @nota: A notation
2236 *
2237 * Build a copy of a notation.
2238 *
2239 * Returns the new xmlNotationPtr or NULL in case of error.
2240 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002241static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002242xmlCopyNotation(xmlNotationPtr nota) {
2243 xmlNotationPtr cur;
2244
2245 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2246 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002247 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002248 return(NULL);
2249 }
2250 if (nota->name != NULL)
2251 cur->name = xmlStrdup(nota->name);
2252 else
2253 cur->name = NULL;
2254 if (nota->PublicID != NULL)
2255 cur->PublicID = xmlStrdup(nota->PublicID);
2256 else
2257 cur->PublicID = NULL;
2258 if (nota->SystemID != NULL)
2259 cur->SystemID = xmlStrdup(nota->SystemID);
2260 else
2261 cur->SystemID = NULL;
2262 return(cur);
2263}
2264
2265/**
2266 * xmlCopyNotationTable:
2267 * @table: A notation table
2268 *
2269 * Build a copy of a notation table.
2270 *
2271 * Returns the new xmlNotationTablePtr or NULL in case of error.
2272 */
2273xmlNotationTablePtr
2274xmlCopyNotationTable(xmlNotationTablePtr table) {
2275 return((xmlNotationTablePtr) xmlHashCopy(table,
2276 (xmlHashCopier) xmlCopyNotation));
2277}
Daniel Veillard652327a2003-09-29 18:02:38 +00002278#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002279
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002280#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002281/**
2282 * xmlDumpNotationDecl:
2283 * @buf: the XML buffer output
2284 * @nota: A notation declaration
2285 *
2286 * This will dump the content the notation declaration as an XML DTD definition
2287 */
2288void
2289xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2290 xmlBufferWriteChar(buf, "<!NOTATION ");
2291 xmlBufferWriteCHAR(buf, nota->name);
2292 if (nota->PublicID != NULL) {
2293 xmlBufferWriteChar(buf, " PUBLIC ");
2294 xmlBufferWriteQuotedString(buf, nota->PublicID);
2295 if (nota->SystemID != NULL) {
2296 xmlBufferWriteChar(buf, " ");
2297 xmlBufferWriteCHAR(buf, nota->SystemID);
2298 }
2299 } else {
2300 xmlBufferWriteChar(buf, " SYSTEM ");
2301 xmlBufferWriteCHAR(buf, nota->SystemID);
2302 }
2303 xmlBufferWriteChar(buf, " >\n");
2304}
2305
2306/**
2307 * xmlDumpNotationTable:
2308 * @buf: the XML buffer output
2309 * @table: A notation table
2310 *
2311 * This will dump the content of the notation table as an XML DTD definition
2312 */
2313void
2314xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2315 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2316}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002317#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002318
2319/************************************************************************
2320 * *
2321 * IDs *
2322 * *
2323 ************************************************************************/
2324/**
2325 * xmlCreateIDTable:
2326 *
2327 * create and initialize an empty id hash table.
2328 *
2329 * Returns the xmlIDTablePtr just created or NULL in case
2330 * of error.
2331 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002332static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002333xmlCreateIDTable(void) {
2334 return(xmlHashCreate(0));
2335}
2336
2337/**
2338 * xmlFreeID:
2339 * @not: A id
2340 *
2341 * Deallocate the memory used by an id definition
2342 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002343static void
Owen Taylor3473f882001-02-23 17:55:21 +00002344xmlFreeID(xmlIDPtr id) {
2345 if (id == NULL) return;
2346 if (id->value != NULL)
2347 xmlFree((xmlChar *) id->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002348 if (id->name != NULL)
2349 xmlFree((xmlChar *) id->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002350 xmlFree(id);
2351}
2352
2353/**
2354 * xmlAddID:
2355 * @ctxt: the validation context
2356 * @doc: pointer to the document
2357 * @value: the value name
2358 * @attr: the attribute holding the ID
2359 *
2360 * Register a new id declaration
2361 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002362 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002363 */
2364xmlIDPtr
2365xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2366 xmlAttrPtr attr) {
2367 xmlIDPtr ret;
2368 xmlIDTablePtr table;
2369
2370 if (doc == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002371 return(NULL);
2372 }
2373 if (value == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002374 return(NULL);
2375 }
2376 if (attr == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002377 return(NULL);
2378 }
2379
2380 /*
2381 * Create the ID table if needed.
2382 */
2383 table = (xmlIDTablePtr) doc->ids;
2384 if (table == NULL)
2385 doc->ids = table = xmlCreateIDTable();
2386 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002387 xmlVErrMemory(ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00002388 "xmlAddID: Table creation failed!\n");
2389 return(NULL);
2390 }
2391
2392 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2393 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002394 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002395 return(NULL);
2396 }
2397
2398 /*
2399 * fill the structure.
2400 */
2401 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002402 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2403 /*
2404 * Operating in streaming mode, attr is gonna disapear
2405 */
2406 ret->name = xmlStrdup(attr->name);
2407 ret->attr = NULL;
2408 } else {
2409 ret->attr = attr;
2410 ret->name = NULL;
2411 }
2412 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002413
2414 if (xmlHashAddEntry(table, value, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002415#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002416 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002417 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002418 */
Daniel Veillard76575762002-09-05 14:21:15 +00002419 if (ctxt != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00002420 xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED,
2421 "ID %s already defined\n",
2422 value, NULL, NULL);
Daniel Veillard76575762002-09-05 14:21:15 +00002423 }
Daniel Veillard4432df22003-09-28 18:58:27 +00002424#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002425 xmlFreeID(ret);
2426 return(NULL);
2427 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002428 if (attr != NULL)
2429 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002430 return(ret);
2431}
2432
2433/**
2434 * xmlFreeIDTable:
2435 * @table: An id table
2436 *
2437 * Deallocate the memory used by an ID hash table.
2438 */
2439void
2440xmlFreeIDTable(xmlIDTablePtr table) {
2441 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2442}
2443
2444/**
2445 * xmlIsID:
2446 * @doc: the document
2447 * @elem: the element carrying the attribute
2448 * @attr: the attribute
2449 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002450 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002451 * then this is done if DTD loading has been requested. In the case
2452 * of HTML documents parsed with the HTML parser, then ID detection is
2453 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002454 *
2455 * Returns 0 or 1 depending on the lookup result
2456 */
2457int
2458xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2459 if (doc == NULL) return(0);
2460 if (attr == NULL) return(0);
2461 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2462 return(0);
2463 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2464 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2465 (xmlStrEqual(BAD_CAST "name", attr->name)))
2466 return(1);
2467 return(0);
2468 } else {
2469 xmlAttributePtr attrDecl;
2470
2471 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002472 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00002473 xmlChar fn[50];
Daniel Veillard37f961d2002-07-06 17:53:56 +00002474 xmlChar *fullname;
Daniel Veillardc00cda82003-04-07 10:22:39 +00002475
2476 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002477 if (fullname == NULL)
2478 return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002479 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2480 attr->name);
2481 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2482 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2483 attr->name);
Daniel Veillardc00cda82003-04-07 10:22:39 +00002484 if ((fullname != fn) && (fullname != elem->name))
2485 xmlFree(fullname);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002486 } else {
2487 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2488 attr->name);
2489 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2490 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2491 attr->name);
2492 }
Owen Taylor3473f882001-02-23 17:55:21 +00002493
2494 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2495 return(1);
2496 }
2497 return(0);
2498}
2499
2500/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002501 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002502 * @doc: the document
2503 * @attr: the attribute
2504 *
2505 * Remove the given attribute from the ID table maintained internally.
2506 *
2507 * Returns -1 if the lookup failed and 0 otherwise
2508 */
2509int
2510xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2511 xmlAttrPtr cur;
2512 xmlIDTablePtr table;
2513 xmlChar *ID;
2514
2515 if (doc == NULL) return(-1);
2516 if (attr == NULL) return(-1);
2517 table = (xmlIDTablePtr) doc->ids;
2518 if (table == NULL)
2519 return(-1);
2520
2521 if (attr == NULL)
2522 return(-1);
2523 ID = xmlNodeListGetString(doc, attr->children, 1);
2524 if (ID == NULL)
2525 return(-1);
2526 cur = xmlHashLookup(table, ID);
2527 if (cur != attr) {
2528 xmlFree(ID);
2529 return(-1);
2530 }
2531 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2532 xmlFree(ID);
2533 return(0);
2534}
2535
2536/**
2537 * xmlGetID:
2538 * @doc: pointer to the document
2539 * @ID: the ID value
2540 *
2541 * Search the attribute declaring the given ID
2542 *
2543 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2544 */
2545xmlAttrPtr
2546xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2547 xmlIDTablePtr table;
2548 xmlIDPtr id;
2549
2550 if (doc == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002551 return(NULL);
2552 }
2553
2554 if (ID == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002555 return(NULL);
2556 }
2557
2558 table = (xmlIDTablePtr) doc->ids;
2559 if (table == NULL)
2560 return(NULL);
2561
2562 id = xmlHashLookup(table, ID);
2563 if (id == NULL)
2564 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002565 if (id->attr == NULL) {
2566 /*
2567 * We are operating on a stream, return a well known reference
2568 * since the attribute node doesn't exist anymore
2569 */
2570 return((xmlAttrPtr) doc);
2571 }
Owen Taylor3473f882001-02-23 17:55:21 +00002572 return(id->attr);
2573}
2574
2575/************************************************************************
2576 * *
2577 * Refs *
2578 * *
2579 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002580typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002581{
2582 xmlListPtr l;
2583 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002584} xmlRemoveMemo;
2585
2586typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2587
2588typedef struct xmlValidateMemo_t
2589{
2590 xmlValidCtxtPtr ctxt;
2591 const xmlChar *name;
2592} xmlValidateMemo;
2593
2594typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002595
2596/**
2597 * xmlCreateRefTable:
2598 *
2599 * create and initialize an empty ref hash table.
2600 *
2601 * Returns the xmlRefTablePtr just created or NULL in case
2602 * of error.
2603 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002604static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002605xmlCreateRefTable(void) {
2606 return(xmlHashCreate(0));
2607}
2608
2609/**
2610 * xmlFreeRef:
2611 * @lk: A list link
2612 *
2613 * Deallocate the memory used by a ref definition
2614 */
2615static void
2616xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002617 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2618 if (ref == NULL) return;
2619 if (ref->value != NULL)
2620 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002621 if (ref->name != NULL)
2622 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002623 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002624}
2625
2626/**
2627 * xmlFreeRefList:
2628 * @list_ref: A list of references.
2629 *
2630 * Deallocate the memory used by a list of references
2631 */
2632static void
2633xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002634 if (list_ref == NULL) return;
2635 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002636}
2637
2638/**
2639 * xmlWalkRemoveRef:
2640 * @data: Contents of current link
2641 * @user: Value supplied by the user
2642 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002643 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002644 */
2645static int
2646xmlWalkRemoveRef(const void *data, const void *user)
2647{
Daniel Veillard37721922001-05-04 15:21:12 +00002648 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2649 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2650 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002651
Daniel Veillard37721922001-05-04 15:21:12 +00002652 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2653 xmlListRemoveFirst(ref_list, (void *)data);
2654 return 0;
2655 }
2656 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002657}
2658
2659/**
2660 * xmlAddRef:
2661 * @ctxt: the validation context
2662 * @doc: pointer to the document
2663 * @value: the value name
2664 * @attr: the attribute holding the Ref
2665 *
2666 * Register a new ref declaration
2667 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002668 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002669 */
2670xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002671xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002672 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002673 xmlRefPtr ret;
2674 xmlRefTablePtr table;
2675 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002676
Daniel Veillard37721922001-05-04 15:21:12 +00002677 if (doc == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002678 return(NULL);
2679 }
2680 if (value == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002681 return(NULL);
2682 }
2683 if (attr == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002684 return(NULL);
2685 }
Owen Taylor3473f882001-02-23 17:55:21 +00002686
Daniel Veillard37721922001-05-04 15:21:12 +00002687 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002688 * Create the Ref table if needed.
2689 */
Daniel Veillard37721922001-05-04 15:21:12 +00002690 table = (xmlRefTablePtr) doc->refs;
2691 if (table == NULL)
2692 doc->refs = table = xmlCreateRefTable();
2693 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002694 xmlVErrMemory(ctxt,
Daniel Veillard37721922001-05-04 15:21:12 +00002695 "xmlAddRef: Table creation failed!\n");
2696 return(NULL);
2697 }
Owen Taylor3473f882001-02-23 17:55:21 +00002698
Daniel Veillard37721922001-05-04 15:21:12 +00002699 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2700 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002701 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard37721922001-05-04 15:21:12 +00002702 return(NULL);
2703 }
Owen Taylor3473f882001-02-23 17:55:21 +00002704
Daniel Veillard37721922001-05-04 15:21:12 +00002705 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002706 * fill the structure.
2707 */
Daniel Veillard37721922001-05-04 15:21:12 +00002708 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002709 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2710 /*
2711 * Operating in streaming mode, attr is gonna disapear
2712 */
2713 ret->name = xmlStrdup(attr->name);
2714 ret->attr = NULL;
2715 } else {
2716 ret->name = NULL;
2717 ret->attr = attr;
2718 }
2719 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002720
Daniel Veillard37721922001-05-04 15:21:12 +00002721 /* To add a reference :-
2722 * References are maintained as a list of references,
2723 * Lookup the entry, if no entry create new nodelist
2724 * Add the owning node to the NodeList
2725 * Return the ref
2726 */
Owen Taylor3473f882001-02-23 17:55:21 +00002727
Daniel Veillard37721922001-05-04 15:21:12 +00002728 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2729 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002730 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2731 "xmlAddRef: Reference list creation failed!\n",
2732 NULL);
Daniel Veillard37721922001-05-04 15:21:12 +00002733 return(NULL);
2734 }
2735 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2736 xmlListDelete(ref_list);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002737 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2738 "xmlAddRef: Reference list insertion failed!\n",
2739 NULL);
Daniel Veillard37721922001-05-04 15:21:12 +00002740 return(NULL);
2741 }
2742 }
2743 xmlListInsert(ref_list, ret);
2744 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002745}
2746
2747/**
2748 * xmlFreeRefTable:
2749 * @table: An ref table
2750 *
2751 * Deallocate the memory used by an Ref hash table.
2752 */
2753void
2754xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002755 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002756}
2757
2758/**
2759 * xmlIsRef:
2760 * @doc: the document
2761 * @elem: the element carrying the attribute
2762 * @attr: the attribute
2763 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002764 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002765 * then this is simple, otherwise we use an heuristic: name Ref (upper
2766 * or lowercase).
2767 *
2768 * Returns 0 or 1 depending on the lookup result
2769 */
2770int
2771xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002772 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2773 return(0);
2774 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2775 /* TODO @@@ */
2776 return(0);
2777 } else {
2778 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002779
Daniel Veillard37721922001-05-04 15:21:12 +00002780 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2781 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2782 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2783 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002784
Daniel Veillard37721922001-05-04 15:21:12 +00002785 if ((attrDecl != NULL) &&
2786 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2787 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2788 return(1);
2789 }
2790 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002791}
2792
2793/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002794 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002795 * @doc: the document
2796 * @attr: the attribute
2797 *
2798 * Remove the given attribute from the Ref table maintained internally.
2799 *
2800 * Returns -1 if the lookup failed and 0 otherwise
2801 */
2802int
2803xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002804 xmlListPtr ref_list;
2805 xmlRefTablePtr table;
2806 xmlChar *ID;
2807 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002808
Daniel Veillard37721922001-05-04 15:21:12 +00002809 if (doc == NULL) return(-1);
2810 if (attr == NULL) return(-1);
2811 table = (xmlRefTablePtr) doc->refs;
2812 if (table == NULL)
2813 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002814
Daniel Veillard37721922001-05-04 15:21:12 +00002815 if (attr == NULL)
2816 return(-1);
2817 ID = xmlNodeListGetString(doc, attr->children, 1);
2818 if (ID == NULL)
2819 return(-1);
2820 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002821
Daniel Veillard37721922001-05-04 15:21:12 +00002822 if(ref_list == NULL) {
2823 xmlFree(ID);
2824 return (-1);
2825 }
2826 /* At this point, ref_list refers to a list of references which
2827 * have the same key as the supplied attr. Our list of references
2828 * is ordered by reference address and we don't have that information
2829 * here to use when removing. We'll have to walk the list and
2830 * check for a matching attribute, when we find one stop the walk
2831 * and remove the entry.
2832 * The list is ordered by reference, so that means we don't have the
2833 * key. Passing the list and the reference to the walker means we
2834 * will have enough data to be able to remove the entry.
2835 */
2836 target.l = ref_list;
2837 target.ap = attr;
2838
2839 /* Remove the supplied attr from our list */
2840 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002841
Daniel Veillard37721922001-05-04 15:21:12 +00002842 /*If the list is empty then remove the list entry in the hash */
2843 if (xmlListEmpty(ref_list))
2844 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2845 xmlFreeRefList);
2846 xmlFree(ID);
2847 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002848}
2849
2850/**
2851 * xmlGetRefs:
2852 * @doc: pointer to the document
2853 * @ID: the ID value
2854 *
2855 * Find the set of references for the supplied ID.
2856 *
2857 * Returns NULL if not found, otherwise node set for the ID.
2858 */
2859xmlListPtr
2860xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002861 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002862
Daniel Veillard37721922001-05-04 15:21:12 +00002863 if (doc == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002864 return(NULL);
2865 }
Owen Taylor3473f882001-02-23 17:55:21 +00002866
Daniel Veillard37721922001-05-04 15:21:12 +00002867 if (ID == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002868 return(NULL);
2869 }
Owen Taylor3473f882001-02-23 17:55:21 +00002870
Daniel Veillard37721922001-05-04 15:21:12 +00002871 table = (xmlRefTablePtr) doc->refs;
2872 if (table == NULL)
2873 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002874
Daniel Veillard37721922001-05-04 15:21:12 +00002875 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002876}
2877
2878/************************************************************************
2879 * *
2880 * Routines for validity checking *
2881 * *
2882 ************************************************************************/
2883
2884/**
2885 * xmlGetDtdElementDesc:
2886 * @dtd: a pointer to the DtD to search
2887 * @name: the element name
2888 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002889 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002890 *
2891 * returns the xmlElementPtr if found or NULL
2892 */
2893
2894xmlElementPtr
2895xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2896 xmlElementTablePtr table;
2897 xmlElementPtr cur;
2898 xmlChar *uqname = NULL, *prefix = NULL;
2899
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00002900 if ((dtd == NULL) || (name == NULL)) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002901 if (dtd->elements == NULL)
2902 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002903 table = (xmlElementTablePtr) dtd->elements;
2904
2905 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002906 if (uqname != NULL)
2907 name = uqname;
2908 cur = xmlHashLookup2(table, name, prefix);
2909 if (prefix != NULL) xmlFree(prefix);
2910 if (uqname != NULL) xmlFree(uqname);
2911 return(cur);
2912}
2913/**
2914 * xmlGetDtdElementDesc2:
2915 * @dtd: a pointer to the DtD to search
2916 * @name: the element name
2917 * @create: create an empty description if not found
2918 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002919 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002920 *
2921 * returns the xmlElementPtr if found or NULL
2922 */
2923
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002924static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002925xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2926 xmlElementTablePtr table;
2927 xmlElementPtr cur;
2928 xmlChar *uqname = NULL, *prefix = NULL;
2929
2930 if (dtd == NULL) return(NULL);
2931 if (dtd->elements == NULL) {
2932 if (!create)
2933 return(NULL);
2934 /*
2935 * Create the Element table if needed.
2936 */
2937 table = (xmlElementTablePtr) dtd->elements;
2938 if (table == NULL) {
2939 table = xmlCreateElementTable();
2940 dtd->elements = (void *) table;
2941 }
2942 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002943 xmlVErrMemory(NULL, "element table allocation failed");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002944 return(NULL);
2945 }
2946 }
2947 table = (xmlElementTablePtr) dtd->elements;
2948
2949 uqname = xmlSplitQName2(name, &prefix);
2950 if (uqname != NULL)
2951 name = uqname;
2952 cur = xmlHashLookup2(table, name, prefix);
2953 if ((cur == NULL) && (create)) {
2954 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2955 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002956 xmlVErrMemory(NULL, "malloc failed");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002957 return(NULL);
2958 }
2959 memset(cur, 0, sizeof(xmlElement));
2960 cur->type = XML_ELEMENT_DECL;
2961
2962 /*
2963 * fill the structure.
2964 */
2965 cur->name = xmlStrdup(name);
2966 cur->prefix = xmlStrdup(prefix);
2967 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2968
2969 xmlHashAddEntry2(table, name, prefix, cur);
2970 }
2971 if (prefix != NULL) xmlFree(prefix);
2972 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002973 return(cur);
2974}
2975
2976/**
2977 * xmlGetDtdQElementDesc:
2978 * @dtd: a pointer to the DtD to search
2979 * @name: the element name
2980 * @prefix: the element namespace prefix
2981 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002982 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002983 *
2984 * returns the xmlElementPtr if found or NULL
2985 */
2986
Daniel Veillard48da9102001-08-07 01:10:10 +00002987xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002988xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2989 const xmlChar *prefix) {
2990 xmlElementTablePtr table;
2991
2992 if (dtd == NULL) return(NULL);
2993 if (dtd->elements == NULL) return(NULL);
2994 table = (xmlElementTablePtr) dtd->elements;
2995
2996 return(xmlHashLookup2(table, name, prefix));
2997}
2998
2999/**
3000 * xmlGetDtdAttrDesc:
3001 * @dtd: a pointer to the DtD to search
3002 * @elem: the element name
3003 * @name: the attribute name
3004 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003005 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00003006 * this element.
3007 *
3008 * returns the xmlAttributePtr if found or NULL
3009 */
3010
3011xmlAttributePtr
3012xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
3013 xmlAttributeTablePtr table;
3014 xmlAttributePtr cur;
3015 xmlChar *uqname = NULL, *prefix = NULL;
3016
3017 if (dtd == NULL) return(NULL);
3018 if (dtd->attributes == NULL) return(NULL);
3019
3020 table = (xmlAttributeTablePtr) dtd->attributes;
3021 if (table == NULL)
3022 return(NULL);
3023
3024 uqname = xmlSplitQName2(name, &prefix);
3025
3026 if (uqname != NULL) {
3027 cur = xmlHashLookup3(table, uqname, prefix, elem);
3028 if (prefix != NULL) xmlFree(prefix);
3029 if (uqname != NULL) xmlFree(uqname);
3030 } else
3031 cur = xmlHashLookup3(table, name, NULL, elem);
3032 return(cur);
3033}
3034
3035/**
3036 * xmlGetDtdQAttrDesc:
3037 * @dtd: a pointer to the DtD to search
3038 * @elem: the element name
3039 * @name: the attribute name
3040 * @prefix: the attribute namespace prefix
3041 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003042 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00003043 * this element.
3044 *
3045 * returns the xmlAttributePtr if found or NULL
3046 */
3047
Daniel Veillard48da9102001-08-07 01:10:10 +00003048xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00003049xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
3050 const xmlChar *prefix) {
3051 xmlAttributeTablePtr table;
3052
3053 if (dtd == NULL) return(NULL);
3054 if (dtd->attributes == NULL) return(NULL);
3055 table = (xmlAttributeTablePtr) dtd->attributes;
3056
3057 return(xmlHashLookup3(table, name, prefix, elem));
3058}
3059
3060/**
3061 * xmlGetDtdNotationDesc:
3062 * @dtd: a pointer to the DtD to search
3063 * @name: the notation name
3064 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003065 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00003066 *
3067 * returns the xmlNotationPtr if found or NULL
3068 */
3069
3070xmlNotationPtr
3071xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
3072 xmlNotationTablePtr table;
3073
3074 if (dtd == NULL) return(NULL);
3075 if (dtd->notations == NULL) return(NULL);
3076 table = (xmlNotationTablePtr) dtd->notations;
3077
3078 return(xmlHashLookup(table, name));
3079}
3080
Daniel Veillard4432df22003-09-28 18:58:27 +00003081#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00003082/**
3083 * xmlValidateNotationUse:
3084 * @ctxt: the validation context
3085 * @doc: the document
3086 * @notationName: the notation name to check
3087 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003088 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00003089 * - [ VC: Notation Declared ]
3090 *
3091 * returns 1 if valid or 0 otherwise
3092 */
3093
3094int
3095xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3096 const xmlChar *notationName) {
3097 xmlNotationPtr notaDecl;
3098 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3099
3100 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
3101 if ((notaDecl == NULL) && (doc->extSubset != NULL))
3102 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
3103
Daniel Veillarde637c4a2003-03-30 21:10:09 +00003104 if ((notaDecl == NULL) && (ctxt != NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003105 xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_NOTATION,
3106 "NOTATION %s is not declared\n",
3107 notationName, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003108 return(0);
3109 }
3110 return(1);
3111}
Daniel Veillard4432df22003-09-28 18:58:27 +00003112#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00003113
3114/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00003115 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00003116 * @doc: the document
3117 * @name: the element name
3118 *
3119 * Search in the DtDs whether an element accept Mixed content (or ANY)
3120 * basically if it is supposed to accept text childs
3121 *
3122 * returns 0 if no, 1 if yes, and -1 if no element description is available
3123 */
3124
3125int
3126xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
3127 xmlElementPtr elemDecl;
3128
3129 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3130
3131 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
3132 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3133 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
3134 if (elemDecl == NULL) return(-1);
3135 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003136 case XML_ELEMENT_TYPE_UNDEFINED:
3137 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003138 case XML_ELEMENT_TYPE_ELEMENT:
3139 return(0);
3140 case XML_ELEMENT_TYPE_EMPTY:
3141 /*
3142 * return 1 for EMPTY since we want VC error to pop up
3143 * on <empty> </empty> for example
3144 */
3145 case XML_ELEMENT_TYPE_ANY:
3146 case XML_ELEMENT_TYPE_MIXED:
3147 return(1);
3148 }
3149 return(1);
3150}
3151
Daniel Veillard4432df22003-09-28 18:58:27 +00003152#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00003153/**
3154 * xmlValidateNameValue:
3155 * @value: an Name value
3156 *
3157 * Validate that the given value match Name production
3158 *
3159 * returns 1 if valid or 0 otherwise
3160 */
3161
Daniel Veillard9b731d72002-04-14 12:56:08 +00003162int
Owen Taylor3473f882001-02-23 17:55:21 +00003163xmlValidateNameValue(const xmlChar *value) {
3164 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003165 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003166
3167 if (value == NULL) return(0);
3168 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003169 val = xmlStringCurrentChar(NULL, cur, &len);
3170 cur += len;
3171 if (!IS_LETTER(val) && (val != '_') &&
3172 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003173 return(0);
3174 }
3175
Daniel Veillardd8224e02002-01-13 15:43:22 +00003176 val = xmlStringCurrentChar(NULL, cur, &len);
3177 cur += len;
3178 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3179 (val == '.') || (val == '-') ||
3180 (val == '_') || (val == ':') ||
3181 (IS_COMBINING(val)) ||
3182 (IS_EXTENDER(val))) {
3183 val = xmlStringCurrentChar(NULL, cur, &len);
3184 cur += len;
3185 }
Owen Taylor3473f882001-02-23 17:55:21 +00003186
Daniel Veillardd8224e02002-01-13 15:43:22 +00003187 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003188
3189 return(1);
3190}
3191
3192/**
3193 * xmlValidateNamesValue:
3194 * @value: an Names value
3195 *
3196 * Validate that the given value match Names production
3197 *
3198 * returns 1 if valid or 0 otherwise
3199 */
3200
Daniel Veillard9b731d72002-04-14 12:56:08 +00003201int
Owen Taylor3473f882001-02-23 17:55:21 +00003202xmlValidateNamesValue(const xmlChar *value) {
3203 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003204 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003205
3206 if (value == NULL) return(0);
3207 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003208 val = xmlStringCurrentChar(NULL, cur, &len);
3209 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003210
Daniel Veillardd8224e02002-01-13 15:43:22 +00003211 if (!IS_LETTER(val) && (val != '_') &&
3212 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003213 return(0);
3214 }
3215
Daniel Veillardd8224e02002-01-13 15:43:22 +00003216 val = xmlStringCurrentChar(NULL, cur, &len);
3217 cur += len;
3218 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3219 (val == '.') || (val == '-') ||
3220 (val == '_') || (val == ':') ||
3221 (IS_COMBINING(val)) ||
3222 (IS_EXTENDER(val))) {
3223 val = xmlStringCurrentChar(NULL, cur, &len);
3224 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003225 }
3226
Daniel Veillardd8224e02002-01-13 15:43:22 +00003227 while (IS_BLANK(val)) {
3228 while (IS_BLANK(val)) {
3229 val = xmlStringCurrentChar(NULL, cur, &len);
3230 cur += len;
3231 }
3232
3233 if (!IS_LETTER(val) && (val != '_') &&
3234 (val != ':')) {
3235 return(0);
3236 }
3237 val = xmlStringCurrentChar(NULL, cur, &len);
3238 cur += len;
3239
3240 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3241 (val == '.') || (val == '-') ||
3242 (val == '_') || (val == ':') ||
3243 (IS_COMBINING(val)) ||
3244 (IS_EXTENDER(val))) {
3245 val = xmlStringCurrentChar(NULL, cur, &len);
3246 cur += len;
3247 }
3248 }
3249
3250 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003251
3252 return(1);
3253}
3254
3255/**
3256 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003257 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003258 *
3259 * Validate that the given value match Nmtoken production
3260 *
3261 * [ VC: Name Token ]
3262 *
3263 * returns 1 if valid or 0 otherwise
3264 */
3265
Daniel Veillard9b731d72002-04-14 12:56:08 +00003266int
Owen Taylor3473f882001-02-23 17:55:21 +00003267xmlValidateNmtokenValue(const xmlChar *value) {
3268 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003269 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003270
3271 if (value == NULL) return(0);
3272 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003273 val = xmlStringCurrentChar(NULL, cur, &len);
3274 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003275
Daniel Veillardd8224e02002-01-13 15:43:22 +00003276 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3277 (val != '.') && (val != '-') &&
3278 (val != '_') && (val != ':') &&
3279 (!IS_COMBINING(val)) &&
3280 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003281 return(0);
3282
Daniel Veillardd8224e02002-01-13 15:43:22 +00003283 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3284 (val == '.') || (val == '-') ||
3285 (val == '_') || (val == ':') ||
3286 (IS_COMBINING(val)) ||
3287 (IS_EXTENDER(val))) {
3288 val = xmlStringCurrentChar(NULL, cur, &len);
3289 cur += len;
3290 }
Owen Taylor3473f882001-02-23 17:55:21 +00003291
Daniel Veillardd8224e02002-01-13 15:43:22 +00003292 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003293
3294 return(1);
3295}
3296
3297/**
3298 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003299 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003300 *
3301 * Validate that the given value match Nmtokens production
3302 *
3303 * [ VC: Name Token ]
3304 *
3305 * returns 1 if valid or 0 otherwise
3306 */
3307
Daniel Veillard9b731d72002-04-14 12:56:08 +00003308int
Owen Taylor3473f882001-02-23 17:55:21 +00003309xmlValidateNmtokensValue(const xmlChar *value) {
3310 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003311 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003312
3313 if (value == NULL) return(0);
3314 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003315 val = xmlStringCurrentChar(NULL, cur, &len);
3316 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003317
Daniel Veillardd8224e02002-01-13 15:43:22 +00003318 while (IS_BLANK(val)) {
3319 val = xmlStringCurrentChar(NULL, cur, &len);
3320 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003321 }
3322
Daniel Veillardd8224e02002-01-13 15:43:22 +00003323 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3324 (val != '.') && (val != '-') &&
3325 (val != '_') && (val != ':') &&
3326 (!IS_COMBINING(val)) &&
3327 (!IS_EXTENDER(val)))
3328 return(0);
3329
3330 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3331 (val == '.') || (val == '-') ||
3332 (val == '_') || (val == ':') ||
3333 (IS_COMBINING(val)) ||
3334 (IS_EXTENDER(val))) {
3335 val = xmlStringCurrentChar(NULL, cur, &len);
3336 cur += len;
3337 }
3338
3339 while (IS_BLANK(val)) {
3340 while (IS_BLANK(val)) {
3341 val = xmlStringCurrentChar(NULL, cur, &len);
3342 cur += len;
3343 }
3344 if (val == 0) return(1);
3345
3346 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3347 (val != '.') && (val != '-') &&
3348 (val != '_') && (val != ':') &&
3349 (!IS_COMBINING(val)) &&
3350 (!IS_EXTENDER(val)))
3351 return(0);
3352
3353 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3354 (val == '.') || (val == '-') ||
3355 (val == '_') || (val == ':') ||
3356 (IS_COMBINING(val)) ||
3357 (IS_EXTENDER(val))) {
3358 val = xmlStringCurrentChar(NULL, cur, &len);
3359 cur += len;
3360 }
3361 }
3362
3363 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003364
3365 return(1);
3366}
3367
3368/**
3369 * xmlValidateNotationDecl:
3370 * @ctxt: the validation context
3371 * @doc: a document instance
3372 * @nota: a notation definition
3373 *
3374 * Try to validate a single notation definition
3375 * basically it does the following checks as described by the
3376 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003377 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003378 * But this function get called anyway ...
3379 *
3380 * returns 1 if valid or 0 otherwise
3381 */
3382
3383int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003384xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3385 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003386 int ret = 1;
3387
3388 return(ret);
3389}
3390
3391/**
3392 * xmlValidateAttributeValue:
3393 * @type: an attribute type
3394 * @value: an attribute value
3395 *
3396 * Validate that the given attribute value match the proper production
3397 *
3398 * [ VC: ID ]
3399 * Values of type ID must match the Name production....
3400 *
3401 * [ VC: IDREF ]
3402 * Values of type IDREF must match the Name production, and values
3403 * of type IDREFS must match Names ...
3404 *
3405 * [ VC: Entity Name ]
3406 * Values of type ENTITY must match the Name production, values
3407 * of type ENTITIES must match Names ...
3408 *
3409 * [ VC: Name Token ]
3410 * Values of type NMTOKEN must match the Nmtoken production; values
3411 * of type NMTOKENS must match Nmtokens.
3412 *
3413 * returns 1 if valid or 0 otherwise
3414 */
3415
3416int
3417xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3418 switch (type) {
3419 case XML_ATTRIBUTE_ENTITIES:
3420 case XML_ATTRIBUTE_IDREFS:
3421 return(xmlValidateNamesValue(value));
3422 case XML_ATTRIBUTE_ENTITY:
3423 case XML_ATTRIBUTE_IDREF:
3424 case XML_ATTRIBUTE_ID:
3425 case XML_ATTRIBUTE_NOTATION:
3426 return(xmlValidateNameValue(value));
3427 case XML_ATTRIBUTE_NMTOKENS:
3428 case XML_ATTRIBUTE_ENUMERATION:
3429 return(xmlValidateNmtokensValue(value));
3430 case XML_ATTRIBUTE_NMTOKEN:
3431 return(xmlValidateNmtokenValue(value));
3432 case XML_ATTRIBUTE_CDATA:
3433 break;
3434 }
3435 return(1);
3436}
3437
3438/**
3439 * xmlValidateAttributeValue2:
3440 * @ctxt: the validation context
3441 * @doc: the document
3442 * @name: the attribute name (used for error reporting only)
3443 * @type: the attribute type
3444 * @value: the attribute value
3445 *
3446 * Validate that the given attribute value match a given type.
3447 * This typically cannot be done before having finished parsing
3448 * the subsets.
3449 *
3450 * [ VC: IDREF ]
3451 * Values of type IDREF must match one of the declared IDs
3452 * Values of type IDREFS must match a sequence of the declared IDs
3453 * each Name must match the value of an ID attribute on some element
3454 * in the XML document; i.e. IDREF values must match the value of
3455 * some ID attribute
3456 *
3457 * [ VC: Entity Name ]
3458 * Values of type ENTITY must match one declared entity
3459 * Values of type ENTITIES must match a sequence of declared entities
3460 *
3461 * [ VC: Notation Attributes ]
3462 * all notation names in the declaration must be declared.
3463 *
3464 * returns 1 if valid or 0 otherwise
3465 */
3466
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003467static int
Owen Taylor3473f882001-02-23 17:55:21 +00003468xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3469 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3470 int ret = 1;
3471 switch (type) {
3472 case XML_ATTRIBUTE_IDREFS:
3473 case XML_ATTRIBUTE_IDREF:
3474 case XML_ATTRIBUTE_ID:
3475 case XML_ATTRIBUTE_NMTOKENS:
3476 case XML_ATTRIBUTE_ENUMERATION:
3477 case XML_ATTRIBUTE_NMTOKEN:
3478 case XML_ATTRIBUTE_CDATA:
3479 break;
3480 case XML_ATTRIBUTE_ENTITY: {
3481 xmlEntityPtr ent;
3482
3483 ent = xmlGetDocEntity(doc, value);
Daniel Veillard62998c02003-09-15 12:56:36 +00003484 /* yeah it's a bit messy... */
Daniel Veillard878eab02002-02-19 13:46:09 +00003485 if ((ent == NULL) && (doc->standalone == 1)) {
3486 doc->standalone = 0;
3487 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003488 }
Owen Taylor3473f882001-02-23 17:55:21 +00003489 if (ent == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003490 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3491 XML_DTD_UNKNOWN_ENTITY,
Owen Taylor3473f882001-02-23 17:55:21 +00003492 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003493 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003494 ret = 0;
3495 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003496 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3497 XML_DTD_ENTITY_TYPE,
Owen Taylor3473f882001-02-23 17:55:21 +00003498 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003499 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003500 ret = 0;
3501 }
3502 break;
3503 }
3504 case XML_ATTRIBUTE_ENTITIES: {
3505 xmlChar *dup, *nam = NULL, *cur, save;
3506 xmlEntityPtr ent;
3507
3508 dup = xmlStrdup(value);
3509 if (dup == NULL)
3510 return(0);
3511 cur = dup;
3512 while (*cur != 0) {
3513 nam = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00003514 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00003515 save = *cur;
3516 *cur = 0;
3517 ent = xmlGetDocEntity(doc, nam);
3518 if (ent == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003519 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3520 XML_DTD_UNKNOWN_ENTITY,
Owen Taylor3473f882001-02-23 17:55:21 +00003521 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003522 name, nam, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003523 ret = 0;
3524 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003525 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3526 XML_DTD_ENTITY_TYPE,
Owen Taylor3473f882001-02-23 17:55:21 +00003527 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003528 name, nam, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003529 ret = 0;
3530 }
3531 if (save == 0)
3532 break;
3533 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00003534 while (IS_BLANK_CH(*cur)) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00003535 }
3536 xmlFree(dup);
3537 break;
3538 }
3539 case XML_ATTRIBUTE_NOTATION: {
3540 xmlNotationPtr nota;
3541
3542 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3543 if ((nota == NULL) && (doc->extSubset != NULL))
3544 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3545
3546 if (nota == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003547 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3548 XML_DTD_UNKNOWN_NOTATION,
Owen Taylor3473f882001-02-23 17:55:21 +00003549 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003550 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003551 ret = 0;
3552 }
3553 break;
3554 }
3555 }
3556 return(ret);
3557}
3558
3559/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003560 * xmlValidCtxtNormalizeAttributeValue:
3561 * @ctxt: the validation context
3562 * @doc: the document
3563 * @elem: the parent
3564 * @name: the attribute name
3565 * @value: the attribute value
3566 * @ctxt: the validation context or NULL
3567 *
3568 * Does the validation related extra step of the normalization of attribute
3569 * values:
3570 *
3571 * If the declared value is not CDATA, then the XML processor must further
3572 * process the normalized attribute value by discarding any leading and
3573 * trailing space (#x20) characters, and by replacing sequences of space
3574 * (#x20) characters by single space (#x20) character.
3575 *
3576 * Also check VC: Standalone Document Declaration in P32, and update
3577 * ctxt->valid accordingly
3578 *
3579 * returns a new normalized string if normalization is needed, NULL otherwise
3580 * the caller must free the returned value.
3581 */
3582
3583xmlChar *
3584xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3585 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3586 xmlChar *ret, *dst;
3587 const xmlChar *src;
3588 xmlAttributePtr attrDecl = NULL;
3589 int extsubset = 0;
3590
3591 if (doc == NULL) return(NULL);
3592 if (elem == NULL) return(NULL);
3593 if (name == NULL) return(NULL);
3594 if (value == NULL) return(NULL);
3595
3596 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003597 xmlChar fn[50];
3598 xmlChar *fullname;
3599
3600 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3601 if (fullname == NULL)
3602 return(0);
3603 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003604 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003605 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003606 if (attrDecl != NULL)
3607 extsubset = 1;
3608 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003609 if ((fullname != fn) && (fullname != elem->name))
3610 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003611 }
3612 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3613 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3614 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3615 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3616 if (attrDecl != NULL)
3617 extsubset = 1;
3618 }
3619
3620 if (attrDecl == NULL)
3621 return(NULL);
3622 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3623 return(NULL);
3624
3625 ret = xmlStrdup(value);
3626 if (ret == NULL)
3627 return(NULL);
3628 src = value;
3629 dst = ret;
3630 while (*src == 0x20) src++;
3631 while (*src != 0) {
3632 if (*src == 0x20) {
3633 while (*src == 0x20) src++;
3634 if (*src != 0)
3635 *dst++ = 0x20;
3636 } else {
3637 *dst++ = *src++;
3638 }
3639 }
3640 *dst = 0;
3641 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003642 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE,
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003643"standalone: %s on %s value had to be normalized based on external subset declaration\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003644 name, elem->name, NULL);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003645 ctxt->valid = 0;
3646 }
3647 return(ret);
3648}
3649
3650/**
Owen Taylor3473f882001-02-23 17:55:21 +00003651 * xmlValidNormalizeAttributeValue:
3652 * @doc: the document
3653 * @elem: the parent
3654 * @name: the attribute name
3655 * @value: the attribute value
3656 *
3657 * Does the validation related extra step of the normalization of attribute
3658 * values:
3659 *
3660 * If the declared value is not CDATA, then the XML processor must further
3661 * process the normalized attribute value by discarding any leading and
3662 * trailing space (#x20) characters, and by replacing sequences of space
3663 * (#x20) characters by single space (#x20) character.
3664 *
Daniel Veillard652327a2003-09-29 18:02:38 +00003665 * Returns a new normalized string if normalization is needed, NULL otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00003666 * the caller must free the returned value.
3667 */
3668
3669xmlChar *
3670xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3671 const xmlChar *name, const xmlChar *value) {
3672 xmlChar *ret, *dst;
3673 const xmlChar *src;
3674 xmlAttributePtr attrDecl = NULL;
3675
3676 if (doc == NULL) return(NULL);
3677 if (elem == NULL) return(NULL);
3678 if (name == NULL) return(NULL);
3679 if (value == NULL) return(NULL);
3680
3681 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003682 xmlChar fn[50];
3683 xmlChar *fullname;
3684
3685 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3686 if (fullname == NULL)
3687 return(0);
3688 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003689 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003690 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3691 if ((fullname != fn) && (fullname != elem->name))
3692 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003693 }
3694 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3695 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3696 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3697
3698 if (attrDecl == NULL)
3699 return(NULL);
3700 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3701 return(NULL);
3702
3703 ret = xmlStrdup(value);
3704 if (ret == NULL)
3705 return(NULL);
3706 src = value;
3707 dst = ret;
3708 while (*src == 0x20) src++;
3709 while (*src != 0) {
3710 if (*src == 0x20) {
3711 while (*src == 0x20) src++;
3712 if (*src != 0)
3713 *dst++ = 0x20;
3714 } else {
3715 *dst++ = *src++;
3716 }
3717 }
3718 *dst = 0;
3719 return(ret);
3720}
3721
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003722static void
Owen Taylor3473f882001-02-23 17:55:21 +00003723xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003724 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003725 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3726}
3727
3728/**
3729 * xmlValidateAttributeDecl:
3730 * @ctxt: the validation context
3731 * @doc: a document instance
3732 * @attr: an attribute definition
3733 *
3734 * Try to validate a single attribute definition
3735 * basically it does the following checks as described by the
3736 * XML-1.0 recommendation:
3737 * - [ VC: Attribute Default Legal ]
3738 * - [ VC: Enumeration ]
3739 * - [ VC: ID Attribute Default ]
3740 *
3741 * The ID/IDREF uniqueness and matching are done separately
3742 *
3743 * returns 1 if valid or 0 otherwise
3744 */
3745
3746int
3747xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3748 xmlAttributePtr attr) {
3749 int ret = 1;
3750 int val;
3751 CHECK_DTD;
3752 if(attr == NULL) return(1);
3753
3754 /* Attribute Default Legal */
3755 /* Enumeration */
3756 if (attr->defaultValue != NULL) {
3757 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3758 if (val == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003759 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003760 "Syntax of default value for attribute %s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003761 attr->name, attr->elem, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003762 }
3763 ret &= val;
3764 }
3765
3766 /* ID Attribute Default */
3767 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3768 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3769 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003770 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_FIXED,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003771 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003772 attr->name, attr->elem, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003773 ret = 0;
3774 }
3775
3776 /* One ID per Element Type */
3777 if (attr->atype == XML_ATTRIBUTE_ID) {
3778 int nbId;
3779
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003780 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003781 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3782 attr->elem);
3783 if (elem != NULL) {
3784 nbId = xmlScanIDAttributeDecl(NULL, elem);
3785 } else {
3786 xmlAttributeTablePtr table;
3787
3788 /*
3789 * The attribute may be declared in the internal subset and the
3790 * element in the external subset.
3791 */
3792 nbId = 0;
3793 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3794 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3795 xmlValidateAttributeIdCallback, &nbId);
3796 }
3797 if (nbId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003798
3799 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003800 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3801 attr->elem, nbId, attr->name);
3802 } else if (doc->extSubset != NULL) {
3803 int extId = 0;
3804 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3805 if (elem != NULL) {
3806 extId = xmlScanIDAttributeDecl(NULL, elem);
3807 }
3808 if (extId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003809 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003810 "Element %s has %d ID attribute defined in the external subset : %s\n",
3811 attr->elem, extId, attr->name);
3812 } else if (extId + nbId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003813 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003814"Element %s has ID attributes defined in the internal and external subset : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003815 attr->elem, attr->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003816 }
3817 }
3818 }
3819
3820 /* Validity Constraint: Enumeration */
3821 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3822 xmlEnumerationPtr tree = attr->tree;
3823 while (tree != NULL) {
3824 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3825 tree = tree->next;
3826 }
3827 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003828 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003829"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003830 attr->defaultValue, attr->name, attr->elem);
3831 ret = 0;
3832 }
3833 }
3834
3835 return(ret);
3836}
3837
3838/**
3839 * xmlValidateElementDecl:
3840 * @ctxt: the validation context
3841 * @doc: a document instance
3842 * @elem: an element definition
3843 *
3844 * Try to validate a single element definition
3845 * basically it does the following checks as described by the
3846 * XML-1.0 recommendation:
3847 * - [ VC: One ID per Element Type ]
3848 * - [ VC: No Duplicate Types ]
3849 * - [ VC: Unique Element Type Declaration ]
3850 *
3851 * returns 1 if valid or 0 otherwise
3852 */
3853
3854int
3855xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3856 xmlElementPtr elem) {
3857 int ret = 1;
3858 xmlElementPtr tst;
3859
3860 CHECK_DTD;
3861
3862 if (elem == NULL) return(1);
3863
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003864#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003865#ifdef LIBXML_REGEXP_ENABLED
3866 /* Build the regexp associated to the content model */
3867 ret = xmlValidBuildContentModel(ctxt, elem);
3868#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003869#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003870
Owen Taylor3473f882001-02-23 17:55:21 +00003871 /* No Duplicate Types */
3872 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3873 xmlElementContentPtr cur, next;
3874 const xmlChar *name;
3875
3876 cur = elem->content;
3877 while (cur != NULL) {
3878 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3879 if (cur->c1 == NULL) break;
3880 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3881 name = cur->c1->name;
3882 next = cur->c2;
3883 while (next != NULL) {
3884 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard7b68df92003-08-03 22:58:54 +00003885 if ((xmlStrEqual(next->name, name)) &&
3886 (xmlStrEqual(next->prefix, cur->prefix))) {
3887 if (cur->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003888 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00003889 "Definition of %s has duplicate references of %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003890 elem->name, name, NULL);
Daniel Veillard7b68df92003-08-03 22:58:54 +00003891 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003892 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00003893 "Definition of %s has duplicate references of %s:%s\n",
3894 elem->name, cur->prefix, name);
3895 }
Owen Taylor3473f882001-02-23 17:55:21 +00003896 ret = 0;
3897 }
3898 break;
3899 }
3900 if (next->c1 == NULL) break;
3901 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
Daniel Veillard7b68df92003-08-03 22:58:54 +00003902 if ((xmlStrEqual(next->c1->name, name)) &&
3903 (xmlStrEqual(next->c1->prefix, cur->prefix))) {
3904 if (cur->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003905 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00003906 "Definition of %s has duplicate references to %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003907 elem->name, name, NULL);
Daniel Veillard7b68df92003-08-03 22:58:54 +00003908 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003909 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00003910 "Definition of %s has duplicate references to %s:%s\n",
3911 elem->name, cur->prefix, name);
3912 }
Owen Taylor3473f882001-02-23 17:55:21 +00003913 ret = 0;
3914 }
3915 next = next->c2;
3916 }
3917 }
3918 cur = cur->c2;
3919 }
3920 }
3921
3922 /* VC: Unique Element Type Declaration */
3923 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003924 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003925 ((tst->prefix == elem->prefix) ||
3926 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003927 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003928 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
3929 "Redefinition of element %s\n",
3930 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003931 ret = 0;
3932 }
3933 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003934 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003935 ((tst->prefix == elem->prefix) ||
3936 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003937 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003938 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
3939 "Redefinition of element %s\n",
3940 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003941 ret = 0;
3942 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003943 /* One ID per Element Type
3944 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003945 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3946 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003947 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003948 return(ret);
3949}
3950
3951/**
3952 * xmlValidateOneAttribute:
3953 * @ctxt: the validation context
3954 * @doc: a document instance
3955 * @elem: an element instance
3956 * @attr: an attribute instance
3957 * @value: the attribute value (without entities processing)
3958 *
3959 * Try to validate a single attribute for an element
3960 * basically it does the following checks as described by the
3961 * XML-1.0 recommendation:
3962 * - [ VC: Attribute Value Type ]
3963 * - [ VC: Fixed Attribute Default ]
3964 * - [ VC: Entity Name ]
3965 * - [ VC: Name Token ]
3966 * - [ VC: ID ]
3967 * - [ VC: IDREF ]
3968 * - [ VC: Entity Name ]
3969 * - [ VC: Notation Attributes ]
3970 *
3971 * The ID/IDREF uniqueness and matching are done separately
3972 *
3973 * returns 1 if valid or 0 otherwise
3974 */
3975
3976int
3977xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
Daniel Veillard07cb8222003-09-10 10:51:05 +00003978 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value)
3979{
Owen Taylor3473f882001-02-23 17:55:21 +00003980 xmlAttributePtr attrDecl = NULL;
3981 int val;
3982 int ret = 1;
3983
3984 CHECK_DTD;
3985 if ((elem == NULL) || (elem->name == NULL)) return(0);
3986 if ((attr == NULL) || (attr->name == NULL)) return(0);
3987
3988 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003989 xmlChar fn[50];
3990 xmlChar *fullname;
3991
3992 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3993 if (fullname == NULL)
3994 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003995 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003996 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003997 attr->name, attr->ns->prefix);
3998 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003999 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00004000 attr->name, attr->ns->prefix);
4001 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004002 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004003 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4004 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00004005 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004006 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004007 if ((fullname != fn) && (fullname != elem->name))
4008 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00004009 }
4010 if (attrDecl == NULL) {
4011 if (attr->ns != NULL) {
4012 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4013 attr->name, attr->ns->prefix);
4014 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4015 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4016 attr->name, attr->ns->prefix);
4017 } else {
4018 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4019 elem->name, attr->name);
4020 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4021 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4022 elem->name, attr->name);
4023 }
4024 }
4025
4026
4027 /* Validity Constraint: Attribute Value Type */
4028 if (attrDecl == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004029 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004030 "No declaration for attribute %s of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004031 attr->name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004032 return(0);
4033 }
4034 attr->atype = attrDecl->atype;
4035
4036 val = xmlValidateAttributeValue(attrDecl->atype, value);
4037 if (val == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004038 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004039 "Syntax of value for attribute %s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004040 attr->name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004041 ret = 0;
4042 }
4043
4044 /* Validity constraint: Fixed Attribute Default */
4045 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4046 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004047 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004048 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004049 attr->name, elem->name, attrDecl->defaultValue);
4050 ret = 0;
4051 }
4052 }
4053
4054 /* Validity Constraint: ID uniqueness */
4055 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4056 if (xmlAddID(ctxt, doc, value, attr) == NULL)
4057 ret = 0;
4058 }
4059
4060 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4061 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4062 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
4063 ret = 0;
4064 }
4065
4066 /* Validity Constraint: Notation Attributes */
4067 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4068 xmlEnumerationPtr tree = attrDecl->tree;
4069 xmlNotationPtr nota;
4070
4071 /* First check that the given NOTATION was declared */
4072 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4073 if (nota == NULL)
4074 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4075
4076 if (nota == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004077 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004078 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004079 value, attr->name, elem->name);
4080 ret = 0;
4081 }
4082
4083 /* Second, verify that it's among the list */
4084 while (tree != NULL) {
4085 if (xmlStrEqual(tree->name, value)) break;
4086 tree = tree->next;
4087 }
4088 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004089 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004090"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004091 value, attr->name, elem->name);
4092 ret = 0;
4093 }
4094 }
4095
4096 /* Validity Constraint: Enumeration */
4097 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4098 xmlEnumerationPtr tree = attrDecl->tree;
4099 while (tree != NULL) {
4100 if (xmlStrEqual(tree->name, value)) break;
4101 tree = tree->next;
4102 }
4103 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004104 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004105 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004106 value, attr->name, elem->name);
4107 ret = 0;
4108 }
4109 }
4110
4111 /* Fixed Attribute Default */
4112 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4113 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004114 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004115 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004116 attr->name, elem->name, attrDecl->defaultValue);
4117 ret = 0;
4118 }
4119
4120 /* Extra check for the attribute value */
4121 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
4122 attrDecl->atype, value);
4123
4124 return(ret);
4125}
4126
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004127/**
4128 * xmlValidateOneNamespace:
4129 * @ctxt: the validation context
4130 * @doc: a document instance
4131 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00004132 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004133 * @ns: an namespace declaration instance
4134 * @value: the attribute value (without entities processing)
4135 *
4136 * Try to validate a single namespace declaration for an element
4137 * basically it does the following checks as described by the
4138 * XML-1.0 recommendation:
4139 * - [ VC: Attribute Value Type ]
4140 * - [ VC: Fixed Attribute Default ]
4141 * - [ VC: Entity Name ]
4142 * - [ VC: Name Token ]
4143 * - [ VC: ID ]
4144 * - [ VC: IDREF ]
4145 * - [ VC: Entity Name ]
4146 * - [ VC: Notation Attributes ]
4147 *
4148 * The ID/IDREF uniqueness and matching are done separately
4149 *
4150 * returns 1 if valid or 0 otherwise
4151 */
4152
4153int
4154xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4155xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
4156 /* xmlElementPtr elemDecl; */
4157 xmlAttributePtr attrDecl = NULL;
4158 int val;
4159 int ret = 1;
4160
4161 CHECK_DTD;
4162 if ((elem == NULL) || (elem->name == NULL)) return(0);
4163 if ((ns == NULL) || (ns->href == NULL)) return(0);
4164
4165 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004166 xmlChar fn[50];
4167 xmlChar *fullname;
4168
4169 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
4170 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00004171 xmlVErrMemory(ctxt, "Validating namespace");
Daniel Veillardc00cda82003-04-07 10:22:39 +00004172 return(0);
4173 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004174 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004175 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004176 ns->prefix, BAD_CAST "xmlns");
4177 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004178 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004179 ns->prefix, BAD_CAST "xmlns");
4180 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004181 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004182 BAD_CAST "xmlns");
4183 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004184 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004185 BAD_CAST "xmlns");
4186 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004187 if ((fullname != fn) && (fullname != elem->name))
4188 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004189 }
4190 if (attrDecl == NULL) {
4191 if (ns->prefix != NULL) {
4192 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4193 ns->prefix, BAD_CAST "xmlns");
4194 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4195 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4196 ns->prefix, BAD_CAST "xmlns");
4197 } else {
4198 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4199 elem->name, BAD_CAST "xmlns");
4200 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4201 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4202 elem->name, BAD_CAST "xmlns");
4203 }
4204 }
4205
4206
4207 /* Validity Constraint: Attribute Value Type */
4208 if (attrDecl == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004209 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004210 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004211 "No declaration for attribute xmlns:%s of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004212 ns->prefix, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004213 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004214 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004215 "No declaration for attribute xmlns of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004216 elem->name, NULL, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004217 }
4218 return(0);
4219 }
4220
4221 val = xmlValidateAttributeValue(attrDecl->atype, value);
4222 if (val == 0) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004223 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004224 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004225 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004226 ns->prefix, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004227 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004228 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004229 "Syntax of value for attribute xmlns of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004230 elem->name, NULL, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004231 }
4232 ret = 0;
4233 }
4234
4235 /* Validity constraint: Fixed Attribute Default */
4236 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4237 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004238 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004239 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004240 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4241 ns->prefix, elem->name, attrDecl->defaultValue);
4242 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004243 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004244 "Value for attribute xmlns of %s is different from default \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004245 elem->name, attrDecl->defaultValue, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004246 }
4247 ret = 0;
4248 }
4249 }
4250
4251 /* Validity Constraint: ID uniqueness */
4252 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4253 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4254 ret = 0;
4255 }
4256
4257 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4258 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4259 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4260 ret = 0;
4261 }
4262
4263 /* Validity Constraint: Notation Attributes */
4264 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4265 xmlEnumerationPtr tree = attrDecl->tree;
4266 xmlNotationPtr nota;
4267
4268 /* First check that the given NOTATION was declared */
4269 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4270 if (nota == NULL)
4271 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4272
4273 if (nota == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004274 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004275 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004276 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4277 value, ns->prefix, elem->name);
4278 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004279 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004280 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004281 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004282 }
4283 ret = 0;
4284 }
4285
4286 /* Second, verify that it's among the list */
4287 while (tree != NULL) {
4288 if (xmlStrEqual(tree->name, value)) break;
4289 tree = tree->next;
4290 }
4291 if (tree == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004292 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004293 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004294"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4295 value, ns->prefix, elem->name);
4296 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004297 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004298"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004299 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004300 }
4301 ret = 0;
4302 }
4303 }
4304
4305 /* Validity Constraint: Enumeration */
4306 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4307 xmlEnumerationPtr tree = attrDecl->tree;
4308 while (tree != NULL) {
4309 if (xmlStrEqual(tree->name, value)) break;
4310 tree = tree->next;
4311 }
4312 if (tree == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004313 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004314 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004315"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4316 value, ns->prefix, elem->name);
4317 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004318 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004319"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004320 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004321 }
4322 ret = 0;
4323 }
4324 }
4325
4326 /* Fixed Attribute Default */
4327 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4328 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004329 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004330 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004331 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4332 ns->prefix, elem->name, attrDecl->defaultValue);
4333 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004334 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004335 "Value for attribute xmlns of %s must be \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004336 elem->name, attrDecl->defaultValue, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004337 }
4338 ret = 0;
4339 }
4340
4341 /* Extra check for the attribute value */
4342 if (ns->prefix != NULL) {
4343 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4344 attrDecl->atype, value);
4345 } else {
4346 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4347 attrDecl->atype, value);
4348 }
4349
4350 return(ret);
4351}
4352
Daniel Veillard118aed72002-09-24 14:13:13 +00004353#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004354/**
4355 * xmlValidateSkipIgnorable:
4356 * @ctxt: the validation context
4357 * @child: the child list
4358 *
4359 * Skip ignorable elements w.r.t. the validation process
4360 *
4361 * returns the first element to consider for validation of the content model
4362 */
4363
4364static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004365xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004366 while (child != NULL) {
4367 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004368 /* These things are ignored (skipped) during validation. */
4369 case XML_PI_NODE:
4370 case XML_COMMENT_NODE:
4371 case XML_XINCLUDE_START:
4372 case XML_XINCLUDE_END:
4373 child = child->next;
4374 break;
4375 case XML_TEXT_NODE:
4376 if (xmlIsBlankNode(child))
4377 child = child->next;
4378 else
4379 return(child);
4380 break;
4381 /* keep current node */
4382 default:
4383 return(child);
4384 }
4385 }
4386 return(child);
4387}
4388
4389/**
4390 * xmlValidateElementType:
4391 * @ctxt: the validation context
4392 *
4393 * Try to validate the content model of an element internal function
4394 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004395 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4396 * reference is found and -3 if the validation succeeded but
4397 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004398 */
4399
4400static int
4401xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004402 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004403 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004404
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004405 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004406 if ((NODE == NULL) && (CONT == NULL))
4407 return(1);
4408 if ((NODE == NULL) &&
4409 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4410 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4411 return(1);
4412 }
4413 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004414 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004415 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004416
4417 /*
4418 * We arrive here when more states need to be examined
4419 */
4420cont:
4421
4422 /*
4423 * We just recovered from a rollback generated by a possible
4424 * epsilon transition, go directly to the analysis phase
4425 */
4426 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004427 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004428 DEBUG_VALID_STATE(NODE, CONT)
4429 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004430 goto analyze;
4431 }
4432
4433 DEBUG_VALID_STATE(NODE, CONT)
4434 /*
4435 * we may have to save a backup state here. This is the equivalent
4436 * of handling epsilon transition in NFAs.
4437 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004438 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004439 ((CONT->parent == NULL) ||
4440 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004441 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004442 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004443 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004444 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004445 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4446 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004447 }
4448
4449
4450 /*
4451 * Check first if the content matches
4452 */
4453 switch (CONT->type) {
4454 case XML_ELEMENT_CONTENT_PCDATA:
4455 if (NODE == NULL) {
4456 DEBUG_VALID_MSG("pcdata failed no node");
4457 ret = 0;
4458 break;
4459 }
4460 if (NODE->type == XML_TEXT_NODE) {
4461 DEBUG_VALID_MSG("pcdata found, skip to next");
4462 /*
4463 * go to next element in the content model
4464 * skipping ignorable elems
4465 */
4466 do {
4467 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004468 NODE = xmlValidateSkipIgnorable(NODE);
4469 if ((NODE != NULL) &&
4470 (NODE->type == XML_ENTITY_REF_NODE))
4471 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004472 } while ((NODE != NULL) &&
4473 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004474 (NODE->type != XML_TEXT_NODE) &&
4475 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004476 ret = 1;
4477 break;
4478 } else {
4479 DEBUG_VALID_MSG("pcdata failed");
4480 ret = 0;
4481 break;
4482 }
4483 break;
4484 case XML_ELEMENT_CONTENT_ELEMENT:
4485 if (NODE == NULL) {
4486 DEBUG_VALID_MSG("element failed no node");
4487 ret = 0;
4488 break;
4489 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004490 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4491 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004492 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004493 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4494 ret = (CONT->prefix == NULL);
4495 } else if (CONT->prefix == NULL) {
4496 ret = 0;
4497 } else {
4498 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4499 }
4500 }
4501 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004502 DEBUG_VALID_MSG("element found, skip to next");
4503 /*
4504 * go to next element in the content model
4505 * skipping ignorable elems
4506 */
4507 do {
4508 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004509 NODE = xmlValidateSkipIgnorable(NODE);
4510 if ((NODE != NULL) &&
4511 (NODE->type == XML_ENTITY_REF_NODE))
4512 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004513 } while ((NODE != NULL) &&
4514 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004515 (NODE->type != XML_TEXT_NODE) &&
4516 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004517 } else {
4518 DEBUG_VALID_MSG("element failed");
4519 ret = 0;
4520 break;
4521 }
4522 break;
4523 case XML_ELEMENT_CONTENT_OR:
4524 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004525 * Small optimization.
4526 */
4527 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4528 if ((NODE == NULL) ||
4529 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4530 DEPTH++;
4531 CONT = CONT->c2;
4532 goto cont;
4533 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004534 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4535 ret = (CONT->c1->prefix == NULL);
4536 } else if (CONT->c1->prefix == NULL) {
4537 ret = 0;
4538 } else {
4539 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4540 }
4541 if (ret == 0) {
4542 DEPTH++;
4543 CONT = CONT->c2;
4544 goto cont;
4545 }
Daniel Veillard85349052001-04-20 13:48:21 +00004546 }
4547
4548 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004549 * save the second branch 'or' branch
4550 */
4551 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004552 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4553 OCCURS, ROLLBACK_OR) < 0)
4554 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004555 DEPTH++;
4556 CONT = CONT->c1;
4557 goto cont;
4558 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004559 /*
4560 * Small optimization.
4561 */
4562 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4563 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4564 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4565 if ((NODE == NULL) ||
4566 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4567 DEPTH++;
4568 CONT = CONT->c2;
4569 goto cont;
4570 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004571 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4572 ret = (CONT->c1->prefix == NULL);
4573 } else if (CONT->c1->prefix == NULL) {
4574 ret = 0;
4575 } else {
4576 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4577 }
4578 if (ret == 0) {
4579 DEPTH++;
4580 CONT = CONT->c2;
4581 goto cont;
4582 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004583 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004584 DEPTH++;
4585 CONT = CONT->c1;
4586 goto cont;
4587 }
4588
4589 /*
4590 * At this point handle going up in the tree
4591 */
4592 if (ret == -1) {
4593 DEBUG_VALID_MSG("error found returning");
4594 return(ret);
4595 }
4596analyze:
4597 while (CONT != NULL) {
4598 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004599 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004600 * this level.
4601 */
4602 if (ret == 0) {
4603 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004604 xmlNodePtr cur;
4605
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004606 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004607 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004608 DEBUG_VALID_MSG("Once branch failed, rollback");
4609 if (vstateVPop(ctxt) < 0 ) {
4610 DEBUG_VALID_MSG("exhaustion, failed");
4611 return(0);
4612 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004613 if (cur != ctxt->vstate->node)
4614 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004615 goto cont;
4616 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004617 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004618 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004619 DEBUG_VALID_MSG("Plus branch failed, rollback");
4620 if (vstateVPop(ctxt) < 0 ) {
4621 DEBUG_VALID_MSG("exhaustion, failed");
4622 return(0);
4623 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004624 if (cur != ctxt->vstate->node)
4625 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004626 goto cont;
4627 }
4628 DEBUG_VALID_MSG("Plus branch found");
4629 ret = 1;
4630 break;
4631 case XML_ELEMENT_CONTENT_MULT:
4632#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004633 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004634 DEBUG_VALID_MSG("Mult branch failed");
4635 } else {
4636 DEBUG_VALID_MSG("Mult branch found");
4637 }
4638#endif
4639 ret = 1;
4640 break;
4641 case XML_ELEMENT_CONTENT_OPT:
4642 DEBUG_VALID_MSG("Option branch failed");
4643 ret = 1;
4644 break;
4645 }
4646 } else {
4647 switch (CONT->ocur) {
4648 case XML_ELEMENT_CONTENT_OPT:
4649 DEBUG_VALID_MSG("Option branch succeeded");
4650 ret = 1;
4651 break;
4652 case XML_ELEMENT_CONTENT_ONCE:
4653 DEBUG_VALID_MSG("Once branch succeeded");
4654 ret = 1;
4655 break;
4656 case XML_ELEMENT_CONTENT_PLUS:
4657 if (STATE == ROLLBACK_PARENT) {
4658 DEBUG_VALID_MSG("Plus branch rollback");
4659 ret = 1;
4660 break;
4661 }
4662 if (NODE == NULL) {
4663 DEBUG_VALID_MSG("Plus branch exhausted");
4664 ret = 1;
4665 break;
4666 }
4667 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004668 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004669 goto cont;
4670 case XML_ELEMENT_CONTENT_MULT:
4671 if (STATE == ROLLBACK_PARENT) {
4672 DEBUG_VALID_MSG("Mult branch rollback");
4673 ret = 1;
4674 break;
4675 }
4676 if (NODE == NULL) {
4677 DEBUG_VALID_MSG("Mult branch exhausted");
4678 ret = 1;
4679 break;
4680 }
4681 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004682 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004683 goto cont;
4684 }
4685 }
4686 STATE = 0;
4687
4688 /*
4689 * Then act accordingly at the parent level
4690 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004691 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004692 if (CONT->parent == NULL)
4693 break;
4694
4695 switch (CONT->parent->type) {
4696 case XML_ELEMENT_CONTENT_PCDATA:
4697 DEBUG_VALID_MSG("Error: parent pcdata");
4698 return(-1);
4699 case XML_ELEMENT_CONTENT_ELEMENT:
4700 DEBUG_VALID_MSG("Error: parent element");
4701 return(-1);
4702 case XML_ELEMENT_CONTENT_OR:
4703 if (ret == 1) {
4704 DEBUG_VALID_MSG("Or succeeded");
4705 CONT = CONT->parent;
4706 DEPTH--;
4707 } else {
4708 DEBUG_VALID_MSG("Or failed");
4709 CONT = CONT->parent;
4710 DEPTH--;
4711 }
4712 break;
4713 case XML_ELEMENT_CONTENT_SEQ:
4714 if (ret == 0) {
4715 DEBUG_VALID_MSG("Sequence failed");
4716 CONT = CONT->parent;
4717 DEPTH--;
4718 } else if (CONT == CONT->parent->c1) {
4719 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4720 CONT = CONT->parent->c2;
4721 goto cont;
4722 } else {
4723 DEBUG_VALID_MSG("Sequence succeeded");
4724 CONT = CONT->parent;
4725 DEPTH--;
4726 }
4727 }
4728 }
4729 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004730 xmlNodePtr cur;
4731
4732 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004733 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4734 if (vstateVPop(ctxt) < 0 ) {
4735 DEBUG_VALID_MSG("exhaustion, failed");
4736 return(0);
4737 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004738 if (cur != ctxt->vstate->node)
4739 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004740 goto cont;
4741 }
4742 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004743 xmlNodePtr cur;
4744
4745 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004746 DEBUG_VALID_MSG("Failure, rollback");
4747 if (vstateVPop(ctxt) < 0 ) {
4748 DEBUG_VALID_MSG("exhaustion, failed");
4749 return(0);
4750 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004751 if (cur != ctxt->vstate->node)
4752 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004753 goto cont;
4754 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004755 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004756}
Daniel Veillard23e73572002-09-19 19:56:43 +00004757#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004758
4759/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004760 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004761 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004762 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004763 * @content: An element
4764 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4765 *
4766 * This will dump the list of elements to the buffer
4767 * Intended just for the debug routine
4768 */
4769static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004770xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004771 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004772 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004773
4774 if (node == NULL) return;
4775 if (glob) strcat(buf, "(");
4776 cur = node;
4777 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004778 len = strlen(buf);
4779 if (size - len < 50) {
4780 if ((size - len > 4) && (buf[len - 1] != '.'))
4781 strcat(buf, " ...");
4782 return;
4783 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004784 switch (cur->type) {
4785 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004786 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004787 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004788 if ((size - len > 4) && (buf[len - 1] != '.'))
4789 strcat(buf, " ...");
4790 return;
4791 }
4792 strcat(buf, (char *) cur->ns->prefix);
4793 strcat(buf, ":");
4794 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004795 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004796 if ((size - len > 4) && (buf[len - 1] != '.'))
4797 strcat(buf, " ...");
4798 return;
4799 }
4800 strcat(buf, (char *) cur->name);
4801 if (cur->next != NULL)
4802 strcat(buf, " ");
4803 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004804 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004805 if (xmlIsBlankNode(cur))
4806 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004807 case XML_CDATA_SECTION_NODE:
4808 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004809 strcat(buf, "CDATA");
4810 if (cur->next != NULL)
4811 strcat(buf, " ");
4812 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004813 case XML_ATTRIBUTE_NODE:
4814 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004815#ifdef LIBXML_DOCB_ENABLED
4816 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004817#endif
4818 case XML_HTML_DOCUMENT_NODE:
4819 case XML_DOCUMENT_TYPE_NODE:
4820 case XML_DOCUMENT_FRAG_NODE:
4821 case XML_NOTATION_NODE:
4822 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004823 strcat(buf, "???");
4824 if (cur->next != NULL)
4825 strcat(buf, " ");
4826 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004827 case XML_ENTITY_NODE:
4828 case XML_PI_NODE:
4829 case XML_DTD_NODE:
4830 case XML_COMMENT_NODE:
4831 case XML_ELEMENT_DECL:
4832 case XML_ATTRIBUTE_DECL:
4833 case XML_ENTITY_DECL:
4834 case XML_XINCLUDE_START:
4835 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004836 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004837 }
4838 cur = cur->next;
4839 }
4840 if (glob) strcat(buf, ")");
4841}
4842
4843/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004844 * xmlValidateElementContent:
4845 * @ctxt: the validation context
4846 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004847 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004848 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004849 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004850 *
4851 * Try to validate the content model of an element
4852 *
4853 * returns 1 if valid or 0 if not and -1 in case of error
4854 */
4855
4856static int
4857xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004858 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004859 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004860#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004861 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004862#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004863 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004864 xmlElementContentPtr cont;
4865 const xmlChar *name;
4866
4867 if (elemDecl == NULL)
4868 return(-1);
4869 cont = elemDecl->content;
4870 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004871
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004872#ifdef LIBXML_REGEXP_ENABLED
4873 /* Build the regexp associated to the content model */
4874 if (elemDecl->contModel == NULL)
4875 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4876 if (elemDecl->contModel == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004877 return(-1);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004878 } else {
4879 xmlRegExecCtxtPtr exec;
4880
Daniel Veillardec498e12003-02-05 11:01:50 +00004881 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
4882 return(-1);
4883 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004884 ctxt->nodeMax = 0;
4885 ctxt->nodeNr = 0;
4886 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004887 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4888 if (exec != NULL) {
4889 cur = child;
4890 while (cur != NULL) {
4891 switch (cur->type) {
4892 case XML_ENTITY_REF_NODE:
4893 /*
4894 * Push the current node to be able to roll back
4895 * and process within the entity
4896 */
4897 if ((cur->children != NULL) &&
4898 (cur->children->children != NULL)) {
4899 nodeVPush(ctxt, cur);
4900 cur = cur->children->children;
4901 continue;
4902 }
4903 break;
4904 case XML_TEXT_NODE:
4905 if (xmlIsBlankNode(cur))
4906 break;
4907 ret = 0;
4908 goto fail;
4909 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004910 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004911 ret = 0;
4912 goto fail;
4913 case XML_ELEMENT_NODE:
4914 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004915 xmlChar fn[50];
4916 xmlChar *fullname;
4917
4918 fullname = xmlBuildQName(cur->name,
4919 cur->ns->prefix, fn, 50);
4920 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004921 ret = -1;
4922 goto fail;
4923 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004924 ret = xmlRegExecPushString(exec, fullname, NULL);
4925 if ((fullname != fn) && (fullname != cur->name))
4926 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004927 } else {
4928 ret = xmlRegExecPushString(exec, cur->name, NULL);
4929 }
4930 break;
4931 default:
4932 break;
4933 }
4934 /*
4935 * Switch to next element
4936 */
4937 cur = cur->next;
4938 while (cur == NULL) {
4939 cur = nodeVPop(ctxt);
4940 if (cur == NULL)
4941 break;
4942 cur = cur->next;
4943 }
4944 }
4945 ret = xmlRegExecPushString(exec, NULL, NULL);
4946fail:
4947 xmlRegFreeExecCtxt(exec);
4948 }
4949 }
4950#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004951 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004952 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004953 */
4954 ctxt->vstateMax = 8;
4955 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4956 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4957 if (ctxt->vstateTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00004958 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004959 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004960 }
4961 /*
4962 * The first entry in the stack is reserved to the current state
4963 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004964 ctxt->nodeMax = 0;
4965 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004966 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004967 ctxt->vstate = &ctxt->vstateTab[0];
4968 ctxt->vstateNr = 1;
4969 CONT = cont;
4970 NODE = child;
4971 DEPTH = 0;
4972 OCCURS = 0;
4973 STATE = 0;
4974 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004975 if ((ret == -3) && (warn)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004976 xmlErrValidWarning(ctxt, child, XML_DTD_CONTENT_NOT_DETERMINIST,
4977 "Content model for Element %s is ambiguous\n",
Daniel Veillard0cc72772003-10-13 14:00:21 +00004978 name, NULL, NULL);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004979 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004980 /*
4981 * An entities reference appeared at this level.
4982 * Buid a minimal representation of this node content
4983 * sufficient to run the validation process on it
4984 */
4985 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004986 cur = child;
4987 while (cur != NULL) {
4988 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004989 case XML_ENTITY_REF_NODE:
4990 /*
4991 * Push the current node to be able to roll back
4992 * and process within the entity
4993 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004994 if ((cur->children != NULL) &&
4995 (cur->children->children != NULL)) {
4996 nodeVPush(ctxt, cur);
4997 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004998 continue;
4999 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00005000 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005001 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005002 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005003 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005004 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00005005 case XML_CDATA_SECTION_NODE:
5006 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005007 case XML_ELEMENT_NODE:
5008 /*
5009 * Allocate a new node and minimally fills in
5010 * what's required
5011 */
5012 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
5013 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00005014 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005015 xmlFreeNodeList(repl);
5016 ret = -1;
5017 goto done;
5018 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005019 tmp->type = cur->type;
5020 tmp->name = cur->name;
5021 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005022 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00005023 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005024 if (repl == NULL)
5025 repl = last = tmp;
5026 else {
5027 last->next = tmp;
5028 last = tmp;
5029 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00005030 if (cur->type == XML_CDATA_SECTION_NODE) {
5031 /*
5032 * E59 spaces in CDATA does not match the
5033 * nonterminal S
5034 */
5035 tmp->content = xmlStrdup(BAD_CAST "CDATA");
5036 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005037 break;
5038 default:
5039 break;
5040 }
5041 /*
5042 * Switch to next element
5043 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005044 cur = cur->next;
5045 while (cur == NULL) {
5046 cur = nodeVPop(ctxt);
5047 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005048 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005049 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005050 }
5051 }
5052
5053 /*
5054 * Relaunch the validation
5055 */
5056 ctxt->vstate = &ctxt->vstateTab[0];
5057 ctxt->vstateNr = 1;
5058 CONT = cont;
5059 NODE = repl;
5060 DEPTH = 0;
5061 OCCURS = 0;
5062 STATE = 0;
5063 ret = xmlValidateElementType(ctxt);
5064 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005065#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005066 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005067 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
5068 char expr[5000];
5069 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005070
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005071 expr[0] = 0;
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005072 xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005073 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00005074#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005075 if (repl != NULL)
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005076 xmlSnprintfElements(&list[0], 5000, repl, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005077 else
Daniel Veillard01992e02002-10-09 10:20:30 +00005078#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005079 xmlSnprintfElements(&list[0], 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005080
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005081 if (name != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005082 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5083 "Element %s content does not follow the DTD, expecting %s, got %s\n",
5084 name, BAD_CAST expr, BAD_CAST list);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005085 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005086 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5087 "Element content does not follow the DTD, expecting %s, got %s\n",
5088 BAD_CAST expr, BAD_CAST list, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005089 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005090 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005091 if (name != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005092 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005093 "Element %s content does not follow the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005094 name, NULL, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005095 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005096 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5097 "Element content does not follow the DTD\n",
5098 NULL, NULL, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005099 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005100 }
5101 ret = 0;
5102 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005103 if (ret == -3)
5104 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005105
Daniel Veillard23e73572002-09-19 19:56:43 +00005106#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005107done:
5108 /*
5109 * Deallocate the copy if done, and free up the validation stack
5110 */
5111 while (repl != NULL) {
5112 tmp = repl->next;
5113 xmlFree(repl);
5114 repl = tmp;
5115 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005116 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005117 if (ctxt->vstateTab != NULL) {
5118 xmlFree(ctxt->vstateTab);
5119 ctxt->vstateTab = NULL;
5120 }
Daniel Veillard01992e02002-10-09 10:20:30 +00005121#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005122 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00005123 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005124 if (ctxt->nodeTab != NULL) {
5125 xmlFree(ctxt->nodeTab);
5126 ctxt->nodeTab = NULL;
5127 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005128 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005129
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005130}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005131
Owen Taylor3473f882001-02-23 17:55:21 +00005132/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005133 * xmlValidateCdataElement:
5134 * @ctxt: the validation context
5135 * @doc: a document instance
5136 * @elem: an element instance
5137 *
5138 * Check that an element follows #CDATA
5139 *
5140 * returns 1 if valid or 0 otherwise
5141 */
5142static int
5143xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5144 xmlNodePtr elem) {
5145 int ret = 1;
5146 xmlNodePtr cur, child;
5147
Daniel Veillardceb09b92002-10-04 11:46:37 +00005148 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005149 return(0);
5150
5151 child = elem->children;
5152
5153 cur = child;
5154 while (cur != NULL) {
5155 switch (cur->type) {
5156 case XML_ENTITY_REF_NODE:
5157 /*
5158 * Push the current node to be able to roll back
5159 * and process within the entity
5160 */
5161 if ((cur->children != NULL) &&
5162 (cur->children->children != NULL)) {
5163 nodeVPush(ctxt, cur);
5164 cur = cur->children->children;
5165 continue;
5166 }
5167 break;
5168 case XML_COMMENT_NODE:
5169 case XML_PI_NODE:
5170 case XML_TEXT_NODE:
5171 case XML_CDATA_SECTION_NODE:
5172 break;
5173 default:
5174 ret = 0;
5175 goto done;
5176 }
5177 /*
5178 * Switch to next element
5179 */
5180 cur = cur->next;
5181 while (cur == NULL) {
5182 cur = nodeVPop(ctxt);
5183 if (cur == NULL)
5184 break;
5185 cur = cur->next;
5186 }
5187 }
5188done:
5189 ctxt->nodeMax = 0;
5190 ctxt->nodeNr = 0;
5191 if (ctxt->nodeTab != NULL) {
5192 xmlFree(ctxt->nodeTab);
5193 ctxt->nodeTab = NULL;
5194 }
5195 return(ret);
5196}
5197
5198/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005199 * xmlValidateCheckMixed:
5200 * @ctxt: the validation context
5201 * @cont: the mixed content model
5202 * @qname: the qualified name as appearing in the serialization
5203 *
5204 * Check if the given node is part of the content model.
5205 *
5206 * Returns 1 if yes, 0 if no, -1 in case of error
5207 */
5208static int
5209xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5210 xmlElementContentPtr cont, const xmlChar *qname) {
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005211 const xmlChar *name;
5212 int plen;
5213 name = xmlSplitQName3(qname, &plen);
5214
5215 if (name == NULL) {
5216 while (cont != NULL) {
5217 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5218 if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname)))
5219 return(1);
5220 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5221 (cont->c1 != NULL) &&
5222 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5223 if ((cont->c1->prefix == NULL) &&
5224 (xmlStrEqual(cont->c1->name, qname)))
5225 return(1);
5226 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5227 (cont->c1 == NULL) ||
5228 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005229 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5230 "Internal: MIXED struct corrupted\n",
5231 NULL);
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005232 break;
5233 }
5234 cont = cont->c2;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005235 }
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005236 } else {
5237 while (cont != NULL) {
5238 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5239 if ((cont->prefix != NULL) &&
5240 (xmlStrncmp(cont->prefix, qname, plen) == 0) &&
5241 (xmlStrEqual(cont->name, name)))
5242 return(1);
5243 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5244 (cont->c1 != NULL) &&
5245 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5246 if ((cont->c1->prefix != NULL) &&
5247 (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) &&
5248 (xmlStrEqual(cont->c1->name, name)))
5249 return(1);
5250 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5251 (cont->c1 == NULL) ||
5252 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005253 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
5254 "Internal: MIXED struct corrupted\n",
5255 NULL);
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005256 break;
5257 }
5258 cont = cont->c2;
5259 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005260 }
5261 return(0);
5262}
5263
5264/**
5265 * xmlValidGetElemDecl:
5266 * @ctxt: the validation context
5267 * @doc: a document instance
5268 * @elem: an element instance
5269 * @extsubset: pointer, (out) indicate if the declaration was found
5270 * in the external subset.
5271 *
5272 * Finds a declaration associated to an element in the document.
5273 *
5274 * returns the pointer to the declaration or NULL if not found.
5275 */
5276static xmlElementPtr
5277xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5278 xmlNodePtr elem, int *extsubset) {
5279 xmlElementPtr elemDecl = NULL;
5280 const xmlChar *prefix = NULL;
5281
5282 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5283 if (extsubset != NULL)
5284 *extsubset = 0;
5285
5286 /*
5287 * Fetch the declaration for the qualified name
5288 */
5289 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5290 prefix = elem->ns->prefix;
5291
5292 if (prefix != NULL) {
5293 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5294 elem->name, prefix);
5295 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5296 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5297 elem->name, prefix);
5298 if ((elemDecl != NULL) && (extsubset != NULL))
5299 *extsubset = 1;
5300 }
5301 }
5302
5303 /*
5304 * Fetch the declaration for the non qualified name
5305 * This is "non-strict" validation should be done on the
5306 * full QName but in that case being flexible makes sense.
5307 */
5308 if (elemDecl == NULL) {
5309 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5310 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5311 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5312 if ((elemDecl != NULL) && (extsubset != NULL))
5313 *extsubset = 1;
5314 }
5315 }
5316 if (elemDecl == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005317 xmlErrValidNode(ctxt, elem,
5318 XML_DTD_UNKNOWN_ELEM,
5319 "No declaration for element %s\n",
5320 elem->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005321 }
5322 return(elemDecl);
5323}
5324
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005325#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005326/**
5327 * xmlValidatePushElement:
5328 * @ctxt: the validation context
5329 * @doc: a document instance
5330 * @elem: an element instance
5331 * @qname: the qualified name as appearing in the serialization
5332 *
5333 * Push a new element start on the validation stack.
5334 *
5335 * returns 1 if no validation problem was found or 0 otherwise
5336 */
5337int
5338xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5339 xmlNodePtr elem, const xmlChar *qname) {
5340 int ret = 1;
5341 xmlElementPtr eDecl;
5342 int extsubset = 0;
5343
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005344/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005345 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5346 xmlValidStatePtr state = ctxt->vstate;
5347 xmlElementPtr elemDecl;
5348
5349 /*
5350 * Check the new element agaisnt the content model of the new elem.
5351 */
5352 if (state->elemDecl != NULL) {
5353 elemDecl = state->elemDecl;
5354
5355 switch(elemDecl->etype) {
5356 case XML_ELEMENT_TYPE_UNDEFINED:
5357 ret = 0;
5358 break;
5359 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005360 xmlErrValidNode(ctxt, state->node,
5361 XML_DTD_NOT_EMPTY,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005362 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005363 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005364 ret = 0;
5365 break;
5366 case XML_ELEMENT_TYPE_ANY:
5367 /* I don't think anything is required then */
5368 break;
5369 case XML_ELEMENT_TYPE_MIXED:
5370 /* simple case of declared as #PCDATA */
5371 if ((elemDecl->content != NULL) &&
5372 (elemDecl->content->type ==
5373 XML_ELEMENT_CONTENT_PCDATA)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005374 xmlErrValidNode(ctxt, state->node,
5375 XML_DTD_NOT_PCDATA,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005376 "Element %s was declared #PCDATA but contains non text nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005377 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005378 ret = 0;
5379 } else {
5380 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5381 qname);
5382 if (ret != 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005383 xmlErrValidNode(ctxt, state->node,
5384 XML_DTD_INVALID_CHILD,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005385 "Element %s is not declared in %s list of possible children\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005386 qname, state->node->name, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005387 }
5388 }
5389 break;
5390 case XML_ELEMENT_TYPE_ELEMENT:
5391 /*
5392 * TODO:
5393 * VC: Standalone Document Declaration
5394 * - element types with element content, if white space
5395 * occurs directly within any instance of those types.
5396 */
5397 if (state->exec != NULL) {
5398 ret = xmlRegExecPushString(state->exec, qname, NULL);
5399 if (ret < 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005400 xmlErrValidNode(ctxt, state->node,
5401 XML_DTD_CONTENT_MODEL,
5402 "Element %s content does not follow the DTD, Misplaced %s\n",
5403 state->node->name, qname, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005404 ret = 0;
5405 } else {
5406 ret = 1;
5407 }
5408 }
5409 break;
5410 }
5411 }
5412 }
5413 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5414 vstateVPush(ctxt, eDecl, elem);
5415 return(ret);
5416}
5417
5418/**
5419 * xmlValidatePushCData:
5420 * @ctxt: the validation context
5421 * @data: some character data read
5422 * @len: the lenght of the data
5423 *
5424 * check the CData parsed for validation in the current stack
5425 *
5426 * returns 1 if no validation problem was found or 0 otherwise
5427 */
5428int
5429xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5430 int ret = 1;
5431
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005432/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005433 if (len <= 0)
5434 return(ret);
5435 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5436 xmlValidStatePtr state = ctxt->vstate;
5437 xmlElementPtr elemDecl;
5438
5439 /*
5440 * Check the new element agaisnt the content model of the new elem.
5441 */
5442 if (state->elemDecl != NULL) {
5443 elemDecl = state->elemDecl;
5444
5445 switch(elemDecl->etype) {
5446 case XML_ELEMENT_TYPE_UNDEFINED:
5447 ret = 0;
5448 break;
5449 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005450 xmlErrValidNode(ctxt, state->node,
5451 XML_DTD_NOT_EMPTY,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005452 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005453 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005454 ret = 0;
5455 break;
5456 case XML_ELEMENT_TYPE_ANY:
5457 break;
5458 case XML_ELEMENT_TYPE_MIXED:
5459 break;
5460 case XML_ELEMENT_TYPE_ELEMENT:
5461 if (len > 0) {
5462 int i;
5463
5464 for (i = 0;i < len;i++) {
William M. Brack76e95df2003-10-18 16:20:14 +00005465 if (!IS_BLANK_CH(data[i])) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005466 xmlErrValidNode(ctxt, state->node,
5467 XML_DTD_CONTENT_MODEL,
5468 "Element %s content does not follow the DTD, Text not allowed\n",
5469 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005470 ret = 0;
5471 goto done;
5472 }
5473 }
5474 /*
5475 * TODO:
5476 * VC: Standalone Document Declaration
5477 * element types with element content, if white space
5478 * occurs directly within any instance of those types.
5479 */
5480 }
5481 break;
5482 }
5483 }
5484 }
5485done:
5486 return(ret);
5487}
5488
5489/**
5490 * xmlValidatePopElement:
5491 * @ctxt: the validation context
5492 * @doc: a document instance
5493 * @elem: an element instance
5494 * @qname: the qualified name as appearing in the serialization
5495 *
5496 * Pop the element end from the validation stack.
5497 *
5498 * returns 1 if no validation problem was found or 0 otherwise
5499 */
5500int
5501xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005502 xmlNodePtr elem ATTRIBUTE_UNUSED,
5503 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005504 int ret = 1;
5505
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005506/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005507 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5508 xmlValidStatePtr state = ctxt->vstate;
5509 xmlElementPtr elemDecl;
5510
5511 /*
5512 * Check the new element agaisnt the content model of the new elem.
5513 */
5514 if (state->elemDecl != NULL) {
5515 elemDecl = state->elemDecl;
5516
5517 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5518 if (state->exec != NULL) {
5519 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5520 if (ret == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005521 xmlErrValidNode(ctxt, state->node,
5522 XML_DTD_CONTENT_MODEL,
5523 "Element %s content does not follow the DTD, Expecting more child\n",
5524 state->node->name, NULL,NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005525 } else {
5526 /*
5527 * previous validation errors should not generate
5528 * a new one here
5529 */
5530 ret = 1;
5531 }
5532 }
5533 }
5534 }
5535 vstateVPop(ctxt);
5536 }
5537 return(ret);
5538}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005539#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005540
5541/**
Owen Taylor3473f882001-02-23 17:55:21 +00005542 * xmlValidateOneElement:
5543 * @ctxt: the validation context
5544 * @doc: a document instance
5545 * @elem: an element instance
5546 *
5547 * Try to validate a single element and it's attributes,
5548 * basically it does the following checks as described by the
5549 * XML-1.0 recommendation:
5550 * - [ VC: Element Valid ]
5551 * - [ VC: Required Attribute ]
5552 * Then call xmlValidateOneAttribute() for each attribute present.
5553 *
5554 * The ID/IDREF checkings are done separately
5555 *
5556 * returns 1 if valid or 0 otherwise
5557 */
5558
5559int
5560xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5561 xmlNodePtr elem) {
5562 xmlElementPtr elemDecl = NULL;
5563 xmlElementContentPtr cont;
5564 xmlAttributePtr attr;
5565 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005566 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005567 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005568 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005569
5570 CHECK_DTD;
5571
5572 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005573 switch (elem->type) {
5574 case XML_ATTRIBUTE_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005575 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5576 "Attribute element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005577 return(0);
5578 case XML_TEXT_NODE:
5579 if (elem->children != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005580 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5581 "Text element has children !\n",
5582 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005583 return(0);
5584 }
5585 if (elem->properties != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005586 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5587 "Text element has attribute !\n",
5588 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005589 return(0);
5590 }
5591 if (elem->ns != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005592 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5593 "Text element has namespace !\n",
5594 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005595 return(0);
5596 }
5597 if (elem->nsDef != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005598 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5599 "Text element has namespace !\n",
5600 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005601 return(0);
5602 }
5603 if (elem->content == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005604 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5605 "Text element has no content !\n",
5606 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005607 return(0);
5608 }
5609 return(1);
5610 case XML_XINCLUDE_START:
5611 case XML_XINCLUDE_END:
5612 return(1);
5613 case XML_CDATA_SECTION_NODE:
5614 case XML_ENTITY_REF_NODE:
5615 case XML_PI_NODE:
5616 case XML_COMMENT_NODE:
5617 return(1);
5618 case XML_ENTITY_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005619 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5620 "Entity element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005621 return(0);
5622 case XML_NOTATION_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005623 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5624 "Notation element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005625 return(0);
5626 case XML_DOCUMENT_NODE:
5627 case XML_DOCUMENT_TYPE_NODE:
5628 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005629 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5630 "Document element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005631 return(0);
5632 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005633 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5634 "HTML Document not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005635 return(0);
5636 case XML_ELEMENT_NODE:
5637 break;
5638 default:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005639 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5640 "unknown element type\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005641 return(0);
5642 }
Owen Taylor3473f882001-02-23 17:55:21 +00005643
5644 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005645 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005646 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005647 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5648 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005649 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005650
Daniel Veillardea7751d2002-12-20 00:16:24 +00005651 /*
5652 * If vstateNr is not zero that means continuous validation is
5653 * activated, do not try to check the content model at that level.
5654 */
5655 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005656 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005657 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005658 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005659 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ELEM,
5660 "No declaration for element %s\n",
5661 elem->name, NULL, NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005662 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005663 case XML_ELEMENT_TYPE_EMPTY:
5664 if (elem->children != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005665 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_EMPTY,
Owen Taylor3473f882001-02-23 17:55:21 +00005666 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005667 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005668 ret = 0;
5669 }
5670 break;
5671 case XML_ELEMENT_TYPE_ANY:
5672 /* I don't think anything is required then */
5673 break;
5674 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005675
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005676 /* simple case of declared as #PCDATA */
5677 if ((elemDecl->content != NULL) &&
5678 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5679 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5680 if (!ret) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005681 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_PCDATA,
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005682 "Element %s was declared #PCDATA but contains non text nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005683 elem->name, NULL, NULL);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005684 }
5685 break;
5686 }
Owen Taylor3473f882001-02-23 17:55:21 +00005687 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005688 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005689 while (child != NULL) {
5690 if (child->type == XML_ELEMENT_NODE) {
5691 name = child->name;
5692 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005693 xmlChar fn[50];
5694 xmlChar *fullname;
5695
5696 fullname = xmlBuildQName(child->name, child->ns->prefix,
5697 fn, 50);
5698 if (fullname == NULL)
5699 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005700 cont = elemDecl->content;
5701 while (cont != NULL) {
5702 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005703 if (xmlStrEqual(cont->name, fullname))
5704 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005705 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5706 (cont->c1 != NULL) &&
5707 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005708 if (xmlStrEqual(cont->c1->name, fullname))
5709 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005710 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5711 (cont->c1 == NULL) ||
5712 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005713 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5714 "Internal: MIXED struct corrupted\n",
5715 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005716 break;
5717 }
5718 cont = cont->c2;
5719 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005720 if ((fullname != fn) && (fullname != child->name))
5721 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005722 if (cont != NULL)
5723 goto child_ok;
5724 }
5725 cont = elemDecl->content;
5726 while (cont != NULL) {
5727 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5728 if (xmlStrEqual(cont->name, name)) break;
5729 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5730 (cont->c1 != NULL) &&
5731 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5732 if (xmlStrEqual(cont->c1->name, name)) break;
5733 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5734 (cont->c1 == NULL) ||
5735 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005736 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
5737 "Internal: MIXED struct corrupted\n",
5738 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005739 break;
5740 }
5741 cont = cont->c2;
5742 }
5743 if (cont == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005744 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_CHILD,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005745 "Element %s is not declared in %s list of possible children\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005746 name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005747 ret = 0;
5748 }
5749 }
5750child_ok:
5751 child = child->next;
5752 }
5753 break;
5754 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005755 if ((doc->standalone == 1) && (extsubset == 1)) {
5756 /*
5757 * VC: Standalone Document Declaration
5758 * - element types with element content, if white space
5759 * occurs directly within any instance of those types.
5760 */
5761 child = elem->children;
5762 while (child != NULL) {
5763 if (child->type == XML_TEXT_NODE) {
5764 const xmlChar *content = child->content;
5765
William M. Brack76e95df2003-10-18 16:20:14 +00005766 while (IS_BLANK_CH(*content))
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005767 content++;
5768 if (*content == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005769 xmlErrValidNode(ctxt, elem,
5770 XML_DTD_STANDALONE_WHITE_SPACE,
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005771"standalone: %s declared in the external subset contains white spaces nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005772 elem->name, NULL, NULL);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005773 ret = 0;
5774 break;
5775 }
5776 }
5777 child =child->next;
5778 }
5779 }
Owen Taylor3473f882001-02-23 17:55:21 +00005780 child = elem->children;
5781 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005782 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005783 if (tmp <= 0)
5784 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005785 break;
5786 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005787 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005788
5789 /* [ VC: Required Attribute ] */
5790 attr = elemDecl->attributes;
5791 while (attr != NULL) {
5792 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005793 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005794
Daniel Veillarde4301c82002-02-13 13:32:35 +00005795 if ((attr->prefix == NULL) &&
5796 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5797 xmlNsPtr ns;
5798
5799 ns = elem->nsDef;
5800 while (ns != NULL) {
5801 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005802 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005803 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005804 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005805 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5806 xmlNsPtr ns;
5807
5808 ns = elem->nsDef;
5809 while (ns != NULL) {
5810 if (xmlStrEqual(attr->name, ns->prefix))
5811 goto found;
5812 ns = ns->next;
5813 }
5814 } else {
5815 xmlAttrPtr attrib;
5816
5817 attrib = elem->properties;
5818 while (attrib != NULL) {
5819 if (xmlStrEqual(attrib->name, attr->name)) {
5820 if (attr->prefix != NULL) {
5821 xmlNsPtr nameSpace = attrib->ns;
5822
5823 if (nameSpace == NULL)
5824 nameSpace = elem->ns;
5825 /*
5826 * qualified names handling is problematic, having a
5827 * different prefix should be possible but DTDs don't
5828 * allow to define the URI instead of the prefix :-(
5829 */
5830 if (nameSpace == NULL) {
5831 if (qualified < 0)
5832 qualified = 0;
5833 } else if (!xmlStrEqual(nameSpace->prefix,
5834 attr->prefix)) {
5835 if (qualified < 1)
5836 qualified = 1;
5837 } else
5838 goto found;
5839 } else {
5840 /*
5841 * We should allow applications to define namespaces
5842 * for their application even if the DTD doesn't
5843 * carry one, otherwise, basically we would always
5844 * break.
5845 */
5846 goto found;
5847 }
5848 }
5849 attrib = attrib->next;
5850 }
Owen Taylor3473f882001-02-23 17:55:21 +00005851 }
5852 if (qualified == -1) {
5853 if (attr->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005854 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005855 "Element %s does not carry attribute %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005856 elem->name, attr->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005857 ret = 0;
5858 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005859 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005860 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005861 elem->name, attr->prefix,attr->name);
5862 ret = 0;
5863 }
5864 } else if (qualified == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005865 xmlErrValidWarning(ctxt, elem, XML_DTD_NO_PREFIX,
Owen Taylor3473f882001-02-23 17:55:21 +00005866 "Element %s required attribute %s:%s has no prefix\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005867 elem->name, attr->prefix, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005868 } else if (qualified == 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005869 xmlErrValidWarning(ctxt, elem, XML_DTD_DIFFERENT_PREFIX,
Owen Taylor3473f882001-02-23 17:55:21 +00005870 "Element %s required attribute %s:%s has different prefix\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005871 elem->name, attr->prefix, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005872 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005873 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5874 /*
5875 * Special tests checking #FIXED namespace declarations
5876 * have the right value since this is not done as an
5877 * attribute checking
5878 */
5879 if ((attr->prefix == NULL) &&
5880 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5881 xmlNsPtr ns;
5882
5883 ns = elem->nsDef;
5884 while (ns != NULL) {
5885 if (ns->prefix == NULL) {
5886 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005887 xmlErrValidNode(ctxt, elem,
5888 XML_DTD_ELEM_DEFAULT_NAMESPACE,
Daniel Veillarde4301c82002-02-13 13:32:35 +00005889 "Element %s namespace name for default namespace does not match the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005890 elem->name, NULL, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00005891 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005892 }
5893 goto found;
5894 }
5895 ns = ns->next;
5896 }
5897 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5898 xmlNsPtr ns;
5899
5900 ns = elem->nsDef;
5901 while (ns != NULL) {
5902 if (xmlStrEqual(attr->name, ns->prefix)) {
5903 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005904 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005905 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005906 elem->name, ns->prefix, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00005907 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005908 }
5909 goto found;
5910 }
5911 ns = ns->next;
5912 }
5913 }
Owen Taylor3473f882001-02-23 17:55:21 +00005914 }
5915found:
5916 attr = attr->nexth;
5917 }
5918 return(ret);
5919}
5920
5921/**
5922 * xmlValidateRoot:
5923 * @ctxt: the validation context
5924 * @doc: a document instance
5925 *
5926 * Try to validate a the root element
5927 * basically it does the following check as described by the
5928 * XML-1.0 recommendation:
5929 * - [ VC: Root Element Type ]
5930 * it doesn't try to recurse or apply other check to the element
5931 *
5932 * returns 1 if valid or 0 otherwise
5933 */
5934
5935int
5936xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5937 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00005938 int ret;
5939
Owen Taylor3473f882001-02-23 17:55:21 +00005940 if (doc == NULL) return(0);
5941
5942 root = xmlDocGetRootElement(doc);
5943 if ((root == NULL) || (root->name == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005944 xmlErrValid(ctxt, XML_DTD_NO_ROOT,
5945 "no root element\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005946 return(0);
5947 }
5948
5949 /*
5950 * When doing post validation against a separate DTD, those may
5951 * no internal subset has been generated
5952 */
5953 if ((doc->intSubset != NULL) &&
5954 (doc->intSubset->name != NULL)) {
5955 /*
5956 * Check first the document root against the NQName
5957 */
5958 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5959 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005960 xmlChar fn[50];
5961 xmlChar *fullname;
5962
5963 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
5964 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00005965 xmlVErrMemory(ctxt, NULL);
Daniel Veillardc00cda82003-04-07 10:22:39 +00005966 return(0);
5967 }
5968 ret = xmlStrEqual(doc->intSubset->name, fullname);
5969 if ((fullname != fn) && (fullname != root->name))
5970 xmlFree(fullname);
5971 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00005972 goto name_ok;
5973 }
5974 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5975 (xmlStrEqual(root->name, BAD_CAST "html")))
5976 goto name_ok;
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005977 xmlErrValidNode(ctxt, root, XML_DTD_ROOT_NAME,
5978 "root and DTD name do not match '%s' and '%s'\n",
5979 root->name, doc->intSubset->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005980 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005981 }
5982 }
5983name_ok:
5984 return(1);
5985}
5986
5987
5988/**
5989 * xmlValidateElement:
5990 * @ctxt: the validation context
5991 * @doc: a document instance
5992 * @elem: an element instance
5993 *
5994 * Try to validate the subtree under an element
5995 *
5996 * returns 1 if valid or 0 otherwise
5997 */
5998
5999int
6000xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
6001 xmlNodePtr child;
6002 xmlAttrPtr attr;
6003 xmlChar *value;
6004 int ret = 1;
6005
6006 if (elem == NULL) return(0);
6007
6008 /*
6009 * XInclude elements were added after parsing in the infoset,
6010 * they don't really mean anything validation wise.
6011 */
6012 if ((elem->type == XML_XINCLUDE_START) ||
6013 (elem->type == XML_XINCLUDE_END))
6014 return(1);
6015
6016 CHECK_DTD;
6017
Daniel Veillard10ea86c2001-06-20 13:55:33 +00006018 /*
6019 * Entities references have to be handled separately
6020 */
6021 if (elem->type == XML_ENTITY_REF_NODE) {
6022 return(1);
6023 }
6024
Owen Taylor3473f882001-02-23 17:55:21 +00006025 ret &= xmlValidateOneElement(ctxt, doc, elem);
6026 attr = elem->properties;
6027 while(attr != NULL) {
6028 value = xmlNodeListGetString(doc, attr->children, 0);
6029 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
6030 if (value != NULL)
6031 xmlFree(value);
6032 attr= attr->next;
6033 }
6034 child = elem->children;
6035 while (child != NULL) {
6036 ret &= xmlValidateElement(ctxt, doc, child);
6037 child = child->next;
6038 }
6039
6040 return(ret);
6041}
6042
Daniel Veillard8730c562001-02-26 10:49:57 +00006043/**
6044 * xmlValidateRef:
6045 * @ref: A reference to be validated
6046 * @ctxt: Validation context
6047 * @name: Name of ID we are searching for
6048 *
6049 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006050static void
Daniel Veillard8730c562001-02-26 10:49:57 +00006051xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00006052 const xmlChar *name) {
6053 xmlAttrPtr id;
6054 xmlAttrPtr attr;
6055
6056 if (ref == NULL)
6057 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006058 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00006059 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006060 attr = ref->attr;
6061 if (attr == NULL) {
6062 xmlChar *dup, *str = NULL, *cur, save;
6063
6064 dup = xmlStrdup(name);
6065 if (dup == NULL) {
6066 ctxt->valid = 0;
6067 return;
6068 }
6069 cur = dup;
6070 while (*cur != 0) {
6071 str = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00006072 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006073 save = *cur;
6074 *cur = 0;
6075 id = xmlGetID(ctxt->doc, str);
6076 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006077 xmlErrValidNodeNr(ctxt, NULL, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006078 "attribute %s line %d references an unknown ID \"%s\"\n",
6079 ref->name, ref->lineno, str);
6080 ctxt->valid = 0;
6081 }
6082 if (save == 0)
6083 break;
6084 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00006085 while (IS_BLANK_CH(*cur)) cur++;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006086 }
6087 xmlFree(dup);
6088 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00006089 id = xmlGetID(ctxt->doc, name);
6090 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006091 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006092 "IDREF attribute %s references an unknown ID \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006093 attr->name, name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006094 ctxt->valid = 0;
6095 }
6096 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
6097 xmlChar *dup, *str = NULL, *cur, save;
6098
6099 dup = xmlStrdup(name);
6100 if (dup == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00006101 xmlVErrMemory(ctxt, "IDREFS split");
Owen Taylor3473f882001-02-23 17:55:21 +00006102 ctxt->valid = 0;
6103 return;
6104 }
6105 cur = dup;
6106 while (*cur != 0) {
6107 str = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00006108 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00006109 save = *cur;
6110 *cur = 0;
6111 id = xmlGetID(ctxt->doc, str);
6112 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006113 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006114 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006115 attr->name, str, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006116 ctxt->valid = 0;
6117 }
6118 if (save == 0)
6119 break;
6120 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00006121 while (IS_BLANK_CH(*cur)) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00006122 }
6123 xmlFree(dup);
6124 }
6125}
6126
6127/**
Daniel Veillard8730c562001-02-26 10:49:57 +00006128 * xmlWalkValidateList:
6129 * @data: Contents of current link
6130 * @user: Value supplied by the user
6131 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00006132 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00006133 */
6134static int
6135xmlWalkValidateList(const void *data, const void *user)
6136{
6137 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
6138 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
6139 return 1;
6140}
6141
6142/**
6143 * xmlValidateCheckRefCallback:
6144 * @ref_list: List of references
6145 * @ctxt: Validation context
6146 * @name: Name of ID we are searching for
6147 *
6148 */
6149static void
6150xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
6151 const xmlChar *name) {
6152 xmlValidateMemo memo;
6153
6154 if (ref_list == NULL)
6155 return;
6156 memo.ctxt = ctxt;
6157 memo.name = name;
6158
6159 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
6160
6161}
6162
6163/**
Owen Taylor3473f882001-02-23 17:55:21 +00006164 * xmlValidateDocumentFinal:
6165 * @ctxt: the validation context
6166 * @doc: a document instance
6167 *
6168 * Does the final step for the document validation once all the
6169 * incremental validation steps have been completed
6170 *
6171 * basically it does the following checks described by the XML Rec
6172 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006173 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00006174 *
6175 * returns 1 if valid or 0 otherwise
6176 */
6177
6178int
6179xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6180 xmlRefTablePtr table;
6181
6182 if (doc == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00006183 xmlErrValid(ctxt, XML_DTD_NO_DOC,
6184 "xmlValidateDocumentFinal: doc == NULL\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006185 return(0);
6186 }
6187
6188 /*
6189 * Check all the NOTATION/NOTATIONS attributes
6190 */
6191 /*
6192 * Check all the ENTITY/ENTITIES attributes definition for validity
6193 */
6194 /*
6195 * Check all the IDREF/IDREFS attributes definition for validity
6196 */
6197 table = (xmlRefTablePtr) doc->refs;
6198 ctxt->doc = doc;
6199 ctxt->valid = 1;
6200 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6201 return(ctxt->valid);
6202}
6203
6204/**
6205 * xmlValidateDtd:
6206 * @ctxt: the validation context
6207 * @doc: a document instance
6208 * @dtd: a dtd instance
6209 *
6210 * Try to validate the document against the dtd instance
6211 *
6212 * basically it does check all the definitions in the DtD.
6213 *
6214 * returns 1 if valid or 0 otherwise
6215 */
6216
6217int
6218xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6219 int ret;
6220 xmlDtdPtr oldExt;
6221 xmlNodePtr root;
6222
6223 if (dtd == NULL) return(0);
6224 if (doc == NULL) return(0);
6225 oldExt = doc->extSubset;
6226 doc->extSubset = dtd;
6227 ret = xmlValidateRoot(ctxt, doc);
6228 if (ret == 0) {
6229 doc->extSubset = oldExt;
6230 return(ret);
6231 }
6232 if (doc->ids != NULL) {
6233 xmlFreeIDTable(doc->ids);
6234 doc->ids = NULL;
6235 }
6236 if (doc->refs != NULL) {
6237 xmlFreeRefTable(doc->refs);
6238 doc->refs = NULL;
6239 }
6240 root = xmlDocGetRootElement(doc);
6241 ret = xmlValidateElement(ctxt, doc, root);
6242 ret &= xmlValidateDocumentFinal(ctxt, doc);
6243 doc->extSubset = oldExt;
6244 return(ret);
6245}
6246
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006247static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006248xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6249 const xmlChar *name ATTRIBUTE_UNUSED) {
6250 if (cur == NULL)
6251 return;
6252 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6253 xmlChar *notation = cur->content;
6254
Daniel Veillard878eab02002-02-19 13:46:09 +00006255 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006256 int ret;
6257
6258 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6259 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006260 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006261 }
6262 }
6263 }
6264}
6265
6266static void
Owen Taylor3473f882001-02-23 17:55:21 +00006267xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006268 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006269 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006270 xmlDocPtr doc;
6271 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006272
Owen Taylor3473f882001-02-23 17:55:21 +00006273 if (cur == NULL)
6274 return;
6275 switch (cur->atype) {
6276 case XML_ATTRIBUTE_CDATA:
6277 case XML_ATTRIBUTE_ID:
6278 case XML_ATTRIBUTE_IDREF :
6279 case XML_ATTRIBUTE_IDREFS:
6280 case XML_ATTRIBUTE_NMTOKEN:
6281 case XML_ATTRIBUTE_NMTOKENS:
6282 case XML_ATTRIBUTE_ENUMERATION:
6283 break;
6284 case XML_ATTRIBUTE_ENTITY:
6285 case XML_ATTRIBUTE_ENTITIES:
6286 case XML_ATTRIBUTE_NOTATION:
6287 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006288
6289 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6290 cur->atype, cur->defaultValue);
6291 if ((ret == 0) && (ctxt->valid == 1))
6292 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006293 }
6294 if (cur->tree != NULL) {
6295 xmlEnumerationPtr tree = cur->tree;
6296 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006297 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006298 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006299 if ((ret == 0) && (ctxt->valid == 1))
6300 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006301 tree = tree->next;
6302 }
6303 }
6304 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006305 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6306 doc = cur->doc;
6307 if ((doc == NULL) || (cur->elem == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006308 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
Daniel Veillard878eab02002-02-19 13:46:09 +00006309 "xmlValidateAttributeCallback(%s): internal error\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006310 (const char *) cur->name);
Daniel Veillard878eab02002-02-19 13:46:09 +00006311 return;
6312 }
6313 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6314 if (elem == NULL)
6315 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6316 if (elem == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006317 xmlErrValidNode(ctxt, NULL, XML_DTD_UNKNOWN_ELEM,
Daniel Veillard878eab02002-02-19 13:46:09 +00006318 "attribute %s: could not find decl for element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006319 cur->name, cur->elem, NULL);
Daniel Veillard878eab02002-02-19 13:46:09 +00006320 return;
6321 }
6322 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006323 xmlErrValidNode(ctxt, NULL, XML_DTD_EMPTY_NOTATION,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006324 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006325 cur->name, cur->elem, NULL);
Daniel Veillard878eab02002-02-19 13:46:09 +00006326 ctxt->valid = 0;
6327 }
6328 }
Owen Taylor3473f882001-02-23 17:55:21 +00006329}
6330
6331/**
6332 * xmlValidateDtdFinal:
6333 * @ctxt: the validation context
6334 * @doc: a document instance
6335 *
6336 * Does the final step for the dtds validation once all the
6337 * subsets have been parsed
6338 *
6339 * basically it does the following checks described by the XML Rec
6340 * - check that ENTITY and ENTITIES type attributes default or
6341 * possible values matches one of the defined entities.
6342 * - check that NOTATION type attributes default or
6343 * possible values matches one of the defined notations.
6344 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006345 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006346 */
6347
6348int
6349xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006350 xmlDtdPtr dtd;
6351 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006352 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006353
6354 if (doc == NULL) return(0);
6355 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6356 return(0);
6357 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006358 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006359 dtd = doc->intSubset;
6360 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6361 table = (xmlAttributeTablePtr) dtd->attributes;
6362 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006363 }
6364 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006365 entities = (xmlEntitiesTablePtr) dtd->entities;
6366 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6367 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006368 }
6369 dtd = doc->extSubset;
6370 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6371 table = (xmlAttributeTablePtr) dtd->attributes;
6372 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006373 }
6374 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006375 entities = (xmlEntitiesTablePtr) dtd->entities;
6376 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6377 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006378 }
6379 return(ctxt->valid);
6380}
6381
6382/**
6383 * xmlValidateDocument:
6384 * @ctxt: the validation context
6385 * @doc: a document instance
6386 *
6387 * Try to validate the document instance
6388 *
6389 * basically it does the all the checks described by the XML Rec
6390 * i.e. validates the internal and external subset (if present)
6391 * and validate the document tree.
6392 *
6393 * returns 1 if valid or 0 otherwise
6394 */
6395
6396int
6397xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6398 int ret;
6399 xmlNodePtr root;
6400
Daniel Veillard2fd85422002-10-16 14:32:41 +00006401 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006402 xmlErrValid(ctxt, XML_DTD_NO_DTD,
6403 "no DTD found!\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006404 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006405 }
Owen Taylor3473f882001-02-23 17:55:21 +00006406 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6407 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6408 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6409 doc->intSubset->SystemID);
6410 if (doc->extSubset == NULL) {
6411 if (doc->intSubset->SystemID != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006412 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00006413 "Could not load the external subset \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006414 (const char *) doc->intSubset->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00006415 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006416 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00006417 "Could not load the external subset \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006418 (const char *) doc->intSubset->ExternalID);
Owen Taylor3473f882001-02-23 17:55:21 +00006419 }
6420 return(0);
6421 }
6422 }
6423
6424 if (doc->ids != NULL) {
6425 xmlFreeIDTable(doc->ids);
6426 doc->ids = NULL;
6427 }
6428 if (doc->refs != NULL) {
6429 xmlFreeRefTable(doc->refs);
6430 doc->refs = NULL;
6431 }
6432 ret = xmlValidateDtdFinal(ctxt, doc);
6433 if (!xmlValidateRoot(ctxt, doc)) return(0);
6434
6435 root = xmlDocGetRootElement(doc);
6436 ret &= xmlValidateElement(ctxt, doc, root);
6437 ret &= xmlValidateDocumentFinal(ctxt, doc);
6438 return(ret);
6439}
6440
Owen Taylor3473f882001-02-23 17:55:21 +00006441/************************************************************************
6442 * *
6443 * Routines for dynamic validation editing *
6444 * *
6445 ************************************************************************/
6446
6447/**
6448 * xmlValidGetPotentialChildren:
6449 * @ctree: an element content tree
6450 * @list: an array to store the list of child names
6451 * @len: a pointer to the number of element in the list
6452 * @max: the size of the array
6453 *
6454 * Build/extend a list of potential children allowed by the content tree
6455 *
6456 * returns the number of element in the list, or -1 in case of error.
6457 */
6458
6459int
6460xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6461 int *len, int max) {
6462 int i;
6463
6464 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6465 return(-1);
6466 if (*len >= max) return(*len);
6467
6468 switch (ctree->type) {
6469 case XML_ELEMENT_CONTENT_PCDATA:
6470 for (i = 0; i < *len;i++)
6471 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6472 list[(*len)++] = BAD_CAST "#PCDATA";
6473 break;
6474 case XML_ELEMENT_CONTENT_ELEMENT:
6475 for (i = 0; i < *len;i++)
6476 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6477 list[(*len)++] = ctree->name;
6478 break;
6479 case XML_ELEMENT_CONTENT_SEQ:
6480 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6481 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6482 break;
6483 case XML_ELEMENT_CONTENT_OR:
6484 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6485 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6486 break;
6487 }
6488
6489 return(*len);
6490}
6491
6492/**
6493 * xmlValidGetValidElements:
6494 * @prev: an element to insert after
6495 * @next: an element to insert next
6496 * @list: an array to store the list of child names
6497 * @max: the size of the array
6498 *
6499 * This function returns the list of authorized children to insert
6500 * within an existing tree while respecting the validity constraints
6501 * forced by the Dtd. The insertion point is defined using @prev and
6502 * @next in the following ways:
6503 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6504 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6505 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6506 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6507 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6508 *
6509 * pointers to the element names are inserted at the beginning of the array
6510 * and do not need to be freed.
6511 *
6512 * returns the number of element in the list, or -1 in case of error. If
6513 * the function returns the value @max the caller is invited to grow the
6514 * receiving array and retry.
6515 */
6516
6517int
6518xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6519 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006520 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006521 int nb_valid_elements = 0;
6522 const xmlChar *elements[256];
6523 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006524 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006525
6526 xmlNode *ref_node;
6527 xmlNode *parent;
6528 xmlNode *test_node;
6529
6530 xmlNode *prev_next;
6531 xmlNode *next_prev;
6532 xmlNode *parent_childs;
6533 xmlNode *parent_last;
6534
6535 xmlElement *element_desc;
6536
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006537 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006538
Owen Taylor3473f882001-02-23 17:55:21 +00006539 if (prev == NULL && next == NULL)
6540 return(-1);
6541
6542 if (list == NULL) return(-1);
6543 if (max <= 0) return(-1);
6544
6545 nb_valid_elements = 0;
6546 ref_node = prev ? prev : next;
6547 parent = ref_node->parent;
6548
6549 /*
6550 * Retrieves the parent element declaration
6551 */
6552 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6553 parent->name);
6554 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6555 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6556 parent->name);
6557 if (element_desc == NULL) return(-1);
6558
6559 /*
6560 * Do a backup of the current tree structure
6561 */
6562 prev_next = prev ? prev->next : NULL;
6563 next_prev = next ? next->prev : NULL;
6564 parent_childs = parent->children;
6565 parent_last = parent->last;
6566
6567 /*
6568 * Creates a dummy node and insert it into the tree
6569 */
6570 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6571 test_node->doc = ref_node->doc;
6572 test_node->parent = parent;
6573 test_node->prev = prev;
6574 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006575 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006576
6577 if (prev) prev->next = test_node;
6578 else parent->children = test_node;
6579
6580 if (next) next->prev = test_node;
6581 else parent->last = test_node;
6582
6583 /*
6584 * Insert each potential child node and check if the parent is
6585 * still valid
6586 */
6587 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6588 elements, &nb_elements, 256);
6589
6590 for (i = 0;i < nb_elements;i++) {
6591 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006592 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006593 int j;
6594
6595 for (j = 0; j < nb_valid_elements;j++)
6596 if (xmlStrEqual(elements[i], list[j])) break;
6597 list[nb_valid_elements++] = elements[i];
6598 if (nb_valid_elements >= max) break;
6599 }
6600 }
6601
6602 /*
6603 * Restore the tree structure
6604 */
6605 if (prev) prev->next = prev_next;
6606 if (next) next->prev = next_prev;
6607 parent->children = parent_childs;
6608 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006609
6610 /*
6611 * Free up the dummy node
6612 */
6613 test_node->name = name;
6614 xmlFreeNode(test_node);
6615
Owen Taylor3473f882001-02-23 17:55:21 +00006616 return(nb_valid_elements);
6617}
Daniel Veillard4432df22003-09-28 18:58:27 +00006618#endif /* LIBXML_VALID_ENABLED */
6619