blob: 3c518d64e47a3330945415f8bd54b9c499abc44d [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 Veillard72b9e292003-10-28 15:44:17 +000065 pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000066 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 Veillard72b9e292003-10-28 15:44:17 +000070 pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000071 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
Daniel Veillardf88d8cf2003-12-08 10:25:02 +000084xmlErrValid(xmlValidCtxtPtr ctxt, 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 Veillard72b9e292003-10-28 15:44:17 +000098 pctxt, NULL, XML_FROM_VALID, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000099 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 Veillard72b9e292003-10-28 15:44:17 +0000103 pctxt, NULL, XML_FROM_VALID, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000104 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
Daniel Veillard72b9e292003-10-28 15:44:17 +0000121xmlErrValidNodeNr(xmlValidCtxtPtr ctxt,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000122 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 Veillard72b9e292003-10-28 15:44:17 +0000137 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, 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 Veillard72b9e292003-10-28 15:44:17 +0000171 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, 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 Veillard72b9e292003-10-28 15:44:17 +0000205 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, 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/**
William M. Brack9e660592003-10-20 14:56:06 +00001534 * xmlDumpElementDeclScan:
1535 * @elem: An element table
1536 * @buf: the XML buffer output
1537 *
1538 * This routine is used by the hash scan function. It just reverses
1539 * the arguments.
1540 */
1541static void
1542xmlDumpElementDeclScan(xmlElementPtr elem, xmlBufferPtr buf) {
1543 xmlDumpElementDecl(buf, elem);
1544}
1545
1546/**
Owen Taylor3473f882001-02-23 17:55:21 +00001547 * xmlDumpElementTable:
1548 * @buf: the XML buffer output
1549 * @table: An element table
1550 *
1551 * This will dump the content of the element table as an XML DTD definition
1552 */
1553void
1554xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
William M. Brack9e660592003-10-20 14:56:06 +00001555 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDeclScan, buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001556}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001557#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001558
1559/**
1560 * xmlCreateEnumeration:
1561 * @name: the enumeration name or NULL
1562 *
1563 * create and initialize an enumeration attribute node.
1564 *
1565 * Returns the xmlEnumerationPtr just created or NULL in case
1566 * of error.
1567 */
1568xmlEnumerationPtr
Daniel Veillard2fdbd322003-08-18 12:15:38 +00001569xmlCreateEnumeration(const xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00001570 xmlEnumerationPtr ret;
1571
1572 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1573 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001574 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001575 return(NULL);
1576 }
1577 memset(ret, 0, sizeof(xmlEnumeration));
1578
1579 if (name != NULL)
1580 ret->name = xmlStrdup(name);
1581 return(ret);
1582}
1583
1584/**
1585 * xmlFreeEnumeration:
1586 * @cur: the tree to free.
1587 *
1588 * free an enumeration attribute node (recursive).
1589 */
1590void
1591xmlFreeEnumeration(xmlEnumerationPtr cur) {
1592 if (cur == NULL) return;
1593
1594 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1595
1596 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001597 xmlFree(cur);
1598}
1599
Daniel Veillard652327a2003-09-29 18:02:38 +00001600#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001601/**
1602 * xmlCopyEnumeration:
1603 * @cur: the tree to copy.
1604 *
1605 * Copy an enumeration attribute node (recursive).
1606 *
1607 * Returns the xmlEnumerationPtr just created or NULL in case
1608 * of error.
1609 */
1610xmlEnumerationPtr
1611xmlCopyEnumeration(xmlEnumerationPtr cur) {
1612 xmlEnumerationPtr ret;
1613
1614 if (cur == NULL) return(NULL);
1615 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1616
1617 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1618 else ret->next = NULL;
1619
1620 return(ret);
1621}
Daniel Veillard652327a2003-09-29 18:02:38 +00001622#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001623
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001624#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001625/**
1626 * xmlDumpEnumeration:
1627 * @buf: the XML buffer output
1628 * @enum: An enumeration
1629 *
1630 * This will dump the content of the enumeration
1631 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001632static void
Owen Taylor3473f882001-02-23 17:55:21 +00001633xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1634 if (cur == NULL) return;
1635
1636 xmlBufferWriteCHAR(buf, cur->name);
1637 if (cur->next == NULL)
1638 xmlBufferWriteChar(buf, ")");
1639 else {
1640 xmlBufferWriteChar(buf, " | ");
1641 xmlDumpEnumeration(buf, cur->next);
1642 }
1643}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001644#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001645
1646/**
1647 * xmlCreateAttributeTable:
1648 *
1649 * create and initialize an empty attribute hash table.
1650 *
1651 * Returns the xmlAttributeTablePtr just created or NULL in case
1652 * of error.
1653 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001654static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001655xmlCreateAttributeTable(void) {
1656 return(xmlHashCreate(0));
1657}
1658
Daniel Veillard4432df22003-09-28 18:58:27 +00001659#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001660/**
1661 * xmlScanAttributeDeclCallback:
1662 * @attr: the attribute decl
1663 * @list: the list to update
1664 *
1665 * Callback called by xmlScanAttributeDecl when a new attribute
1666 * has to be entered in the list.
1667 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001668static void
Owen Taylor3473f882001-02-23 17:55:21 +00001669xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001670 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001671 attr->nexth = *list;
1672 *list = attr;
1673}
1674
1675/**
1676 * xmlScanAttributeDecl:
1677 * @dtd: pointer to the DTD
1678 * @elem: the element name
1679 *
1680 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001681 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001682 *
1683 * Returns the pointer to the first attribute decl in the chain,
1684 * possibly NULL.
1685 */
1686xmlAttributePtr
1687xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1688 xmlAttributePtr ret = NULL;
1689 xmlAttributeTablePtr table;
1690
1691 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001692 return(NULL);
1693 }
1694 if (elem == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001695 return(NULL);
1696 }
1697 table = (xmlAttributeTablePtr) dtd->attributes;
1698 if (table == NULL)
1699 return(NULL);
1700
1701 /* WRONG !!! */
1702 xmlHashScan3(table, NULL, NULL, elem,
1703 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1704 return(ret);
1705}
1706
1707/**
1708 * xmlScanIDAttributeDecl:
1709 * @ctxt: the validation context
1710 * @elem: the element name
1711 *
1712 * Verify that the element don't have too many ID attributes
1713 * declared.
1714 *
1715 * Returns the number of ID attributes found.
1716 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001717static int
Owen Taylor3473f882001-02-23 17:55:21 +00001718xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1719 xmlAttributePtr cur;
1720 int ret = 0;
1721
1722 if (elem == NULL) return(0);
1723 cur = elem->attributes;
1724 while (cur != NULL) {
1725 if (cur->atype == XML_ATTRIBUTE_ID) {
1726 ret ++;
1727 if (ret > 1)
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001728 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_MULTIPLE_ID,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001729 "Element %s has too many ID attributes defined : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001730 elem->name, cur->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001731 }
1732 cur = cur->nexth;
1733 }
1734 return(ret);
1735}
Daniel Veillard4432df22003-09-28 18:58:27 +00001736#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001737
1738/**
1739 * xmlFreeAttribute:
1740 * @elem: An attribute
1741 *
1742 * Deallocate the memory used by an attribute definition
1743 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001744static void
Owen Taylor3473f882001-02-23 17:55:21 +00001745xmlFreeAttribute(xmlAttributePtr attr) {
1746 if (attr == NULL) return;
1747 xmlUnlinkNode((xmlNodePtr) attr);
1748 if (attr->tree != NULL)
1749 xmlFreeEnumeration(attr->tree);
1750 if (attr->elem != NULL)
1751 xmlFree((xmlChar *) attr->elem);
1752 if (attr->name != NULL)
1753 xmlFree((xmlChar *) attr->name);
1754 if (attr->defaultValue != NULL)
1755 xmlFree((xmlChar *) attr->defaultValue);
1756 if (attr->prefix != NULL)
1757 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001758 xmlFree(attr);
1759}
1760
1761
1762/**
1763 * xmlAddAttributeDecl:
1764 * @ctxt: the validation context
1765 * @dtd: pointer to the DTD
1766 * @elem: the element name
1767 * @name: the attribute name
1768 * @ns: the attribute namespace prefix
1769 * @type: the attribute type
1770 * @def: the attribute default type
1771 * @defaultValue: the attribute default value
1772 * @tree: if it's an enumeration, the associated list
1773 *
1774 * Register a new attribute declaration
1775 * Note that @tree becomes the ownership of the DTD
1776 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001777 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001778 */
1779xmlAttributePtr
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001780xmlAddAttributeDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1781 xmlDtdPtr dtd, const xmlChar *elem,
Owen Taylor3473f882001-02-23 17:55:21 +00001782 const xmlChar *name, const xmlChar *ns,
1783 xmlAttributeType type, xmlAttributeDefault def,
1784 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1785 xmlAttributePtr ret;
1786 xmlAttributeTablePtr table;
1787 xmlElementPtr elemDef;
1788
1789 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001790 xmlFreeEnumeration(tree);
1791 return(NULL);
1792 }
1793 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001794 xmlFreeEnumeration(tree);
1795 return(NULL);
1796 }
1797 if (elem == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001798 xmlFreeEnumeration(tree);
1799 return(NULL);
1800 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001801
Daniel Veillard4432df22003-09-28 18:58:27 +00001802#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001803 /*
1804 * Check the type and possibly the default value.
1805 */
1806 switch (type) {
1807 case XML_ATTRIBUTE_CDATA:
1808 break;
1809 case XML_ATTRIBUTE_ID:
1810 break;
1811 case XML_ATTRIBUTE_IDREF:
1812 break;
1813 case XML_ATTRIBUTE_IDREFS:
1814 break;
1815 case XML_ATTRIBUTE_ENTITY:
1816 break;
1817 case XML_ATTRIBUTE_ENTITIES:
1818 break;
1819 case XML_ATTRIBUTE_NMTOKEN:
1820 break;
1821 case XML_ATTRIBUTE_NMTOKENS:
1822 break;
1823 case XML_ATTRIBUTE_ENUMERATION:
1824 break;
1825 case XML_ATTRIBUTE_NOTATION:
1826 break;
1827 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001828 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1829 "Internal: ATTRIBUTE struct corrupted invalid type\n",
1830 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001831 xmlFreeEnumeration(tree);
1832 return(NULL);
1833 }
1834 if ((defaultValue != NULL) &&
1835 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001836 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_DEFAULT,
1837 "Attribute %s of %s: invalid default value\n",
1838 elem, name, defaultValue);
Owen Taylor3473f882001-02-23 17:55:21 +00001839 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001840 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001841 }
Daniel Veillard4432df22003-09-28 18:58:27 +00001842#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001843
1844 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001845 * Check first that an attribute defined in the external subset wasn't
1846 * already defined in the internal subset
1847 */
1848 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1849 (dtd->doc->intSubset != NULL) &&
1850 (dtd->doc->intSubset->attributes != NULL)) {
1851 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1852 if (ret != NULL)
1853 return(NULL);
1854 }
1855
1856 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001857 * Create the Attribute table if needed.
1858 */
1859 table = (xmlAttributeTablePtr) dtd->attributes;
1860 if (table == NULL) {
1861 table = xmlCreateAttributeTable();
1862 dtd->attributes = (void *) table;
1863 }
1864 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001865 xmlVErrMemory(ctxt,
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001866 "xmlAddAttributeDecl: Table creation failed!\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001867 return(NULL);
1868 }
1869
1870
1871 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1872 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001873 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001874 return(NULL);
1875 }
1876 memset(ret, 0, sizeof(xmlAttribute));
1877 ret->type = XML_ATTRIBUTE_DECL;
1878
1879 /*
1880 * fill the structure.
1881 */
1882 ret->atype = type;
1883 ret->name = xmlStrdup(name);
1884 ret->prefix = xmlStrdup(ns);
1885 ret->elem = xmlStrdup(elem);
1886 ret->def = def;
1887 ret->tree = tree;
1888 if (defaultValue != NULL)
1889 ret->defaultValue = xmlStrdup(defaultValue);
1890
1891 /*
1892 * Validity Check:
1893 * Search the DTD for previous declarations of the ATTLIST
1894 */
1895 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001896#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001897 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001898 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001899 */
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001900 xmlErrValidWarning(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_REDEFINED,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001901 "Attribute %s of element %s: already defined\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001902 name, elem, NULL);
Daniel Veillard4432df22003-09-28 18:58:27 +00001903#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001904 xmlFreeAttribute(ret);
1905 return(NULL);
1906 }
1907
1908 /*
1909 * Validity Check:
1910 * Multiple ID per element
1911 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001912 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001913 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001914
Daniel Veillard4432df22003-09-28 18:58:27 +00001915#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001916 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001917 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001918 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID,
Owen Taylor3473f882001-02-23 17:55:21 +00001919 "Element %s has too may ID attributes defined : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001920 elem, name, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00001921 ctxt->valid = 0;
1922 }
Daniel Veillard4432df22003-09-28 18:58:27 +00001923#endif /* LIBXML_VALID_ENABLED */
Daniel Veillardc7612992002-02-17 22:47:37 +00001924
Daniel Veillard48da9102001-08-07 01:10:10 +00001925 /*
1926 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001927 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001928 */
1929 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1930 ((ret->prefix != NULL &&
1931 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1932 ret->nexth = elemDef->attributes;
1933 elemDef->attributes = ret;
1934 } else {
1935 xmlAttributePtr tmp = elemDef->attributes;
1936
1937 while ((tmp != NULL) &&
1938 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1939 ((ret->prefix != NULL &&
1940 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1941 if (tmp->nexth == NULL)
1942 break;
1943 tmp = tmp->nexth;
1944 }
1945 if (tmp != NULL) {
1946 ret->nexth = tmp->nexth;
1947 tmp->nexth = ret;
1948 } else {
1949 ret->nexth = elemDef->attributes;
1950 elemDef->attributes = ret;
1951 }
1952 }
Owen Taylor3473f882001-02-23 17:55:21 +00001953 }
1954
1955 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001956 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001957 */
1958 ret->parent = dtd;
1959 ret->doc = dtd->doc;
1960 if (dtd->last == NULL) {
1961 dtd->children = dtd->last = (xmlNodePtr) ret;
1962 } else {
1963 dtd->last->next = (xmlNodePtr) ret;
1964 ret->prev = dtd->last;
1965 dtd->last = (xmlNodePtr) ret;
1966 }
1967 return(ret);
1968}
1969
1970/**
1971 * xmlFreeAttributeTable:
1972 * @table: An attribute table
1973 *
1974 * Deallocate the memory used by an entities hash table.
1975 */
1976void
1977xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1978 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1979}
1980
Daniel Veillard652327a2003-09-29 18:02:38 +00001981#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001982/**
1983 * xmlCopyAttribute:
1984 * @attr: An attribute
1985 *
1986 * Build a copy of an attribute.
1987 *
1988 * Returns the new xmlAttributePtr or NULL in case of error.
1989 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001990static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001991xmlCopyAttribute(xmlAttributePtr attr) {
1992 xmlAttributePtr cur;
1993
1994 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1995 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001996 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001997 return(NULL);
1998 }
1999 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00002000 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00002001 cur->atype = attr->atype;
2002 cur->def = attr->def;
2003 cur->tree = xmlCopyEnumeration(attr->tree);
2004 if (attr->elem != NULL)
2005 cur->elem = xmlStrdup(attr->elem);
2006 if (attr->name != NULL)
2007 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00002008 if (attr->prefix != NULL)
2009 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00002010 if (attr->defaultValue != NULL)
2011 cur->defaultValue = xmlStrdup(attr->defaultValue);
2012 return(cur);
2013}
2014
2015/**
2016 * xmlCopyAttributeTable:
2017 * @table: An attribute table
2018 *
2019 * Build a copy of an attribute table.
2020 *
2021 * Returns the new xmlAttributeTablePtr or NULL in case of error.
2022 */
2023xmlAttributeTablePtr
2024xmlCopyAttributeTable(xmlAttributeTablePtr table) {
2025 return((xmlAttributeTablePtr) xmlHashCopy(table,
2026 (xmlHashCopier) xmlCopyAttribute));
2027}
Daniel Veillard652327a2003-09-29 18:02:38 +00002028#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002029
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002030#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002031/**
2032 * xmlDumpAttributeDecl:
2033 * @buf: the XML buffer output
2034 * @attr: An attribute declaration
2035 *
2036 * This will dump the content of the attribute declaration as an XML
2037 * DTD definition
2038 */
2039void
2040xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
2041 xmlBufferWriteChar(buf, "<!ATTLIST ");
2042 xmlBufferWriteCHAR(buf, attr->elem);
2043 xmlBufferWriteChar(buf, " ");
2044 if (attr->prefix != NULL) {
2045 xmlBufferWriteCHAR(buf, attr->prefix);
2046 xmlBufferWriteChar(buf, ":");
2047 }
2048 xmlBufferWriteCHAR(buf, attr->name);
2049 switch (attr->atype) {
2050 case XML_ATTRIBUTE_CDATA:
2051 xmlBufferWriteChar(buf, " CDATA");
2052 break;
2053 case XML_ATTRIBUTE_ID:
2054 xmlBufferWriteChar(buf, " ID");
2055 break;
2056 case XML_ATTRIBUTE_IDREF:
2057 xmlBufferWriteChar(buf, " IDREF");
2058 break;
2059 case XML_ATTRIBUTE_IDREFS:
2060 xmlBufferWriteChar(buf, " IDREFS");
2061 break;
2062 case XML_ATTRIBUTE_ENTITY:
2063 xmlBufferWriteChar(buf, " ENTITY");
2064 break;
2065 case XML_ATTRIBUTE_ENTITIES:
2066 xmlBufferWriteChar(buf, " ENTITIES");
2067 break;
2068 case XML_ATTRIBUTE_NMTOKEN:
2069 xmlBufferWriteChar(buf, " NMTOKEN");
2070 break;
2071 case XML_ATTRIBUTE_NMTOKENS:
2072 xmlBufferWriteChar(buf, " NMTOKENS");
2073 break;
2074 case XML_ATTRIBUTE_ENUMERATION:
2075 xmlBufferWriteChar(buf, " (");
2076 xmlDumpEnumeration(buf, attr->tree);
2077 break;
2078 case XML_ATTRIBUTE_NOTATION:
2079 xmlBufferWriteChar(buf, " NOTATION (");
2080 xmlDumpEnumeration(buf, attr->tree);
2081 break;
2082 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002083 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2084 "Internal: ATTRIBUTE struct corrupted invalid type\n",
2085 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002086 }
2087 switch (attr->def) {
2088 case XML_ATTRIBUTE_NONE:
2089 break;
2090 case XML_ATTRIBUTE_REQUIRED:
2091 xmlBufferWriteChar(buf, " #REQUIRED");
2092 break;
2093 case XML_ATTRIBUTE_IMPLIED:
2094 xmlBufferWriteChar(buf, " #IMPLIED");
2095 break;
2096 case XML_ATTRIBUTE_FIXED:
2097 xmlBufferWriteChar(buf, " #FIXED");
2098 break;
2099 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002100 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2101 "Internal: ATTRIBUTE struct corrupted invalid def\n",
2102 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002103 }
2104 if (attr->defaultValue != NULL) {
2105 xmlBufferWriteChar(buf, " ");
2106 xmlBufferWriteQuotedString(buf, attr->defaultValue);
2107 }
2108 xmlBufferWriteChar(buf, ">\n");
2109}
2110
2111/**
William M. Brack9e660592003-10-20 14:56:06 +00002112 * xmlDumpAttributeDeclScan:
2113 * @attr: An attribute declaration
2114 * @buf: the XML buffer output
2115 *
2116 * This is used with the hash scan function - just reverses arguments
2117 */
2118static void
2119xmlDumpAttributeDeclScan(xmlAttributePtr attr, xmlBufferPtr buf) {
2120 xmlDumpAttributeDecl(buf, attr);
2121}
2122
2123/**
Owen Taylor3473f882001-02-23 17:55:21 +00002124 * xmlDumpAttributeTable:
2125 * @buf: the XML buffer output
2126 * @table: An attribute table
2127 *
2128 * This will dump the content of the attribute table as an XML DTD definition
2129 */
2130void
2131xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
William M. Brack9e660592003-10-20 14:56:06 +00002132 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDeclScan, buf);
Owen Taylor3473f882001-02-23 17:55:21 +00002133}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002134#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002135
2136/************************************************************************
2137 * *
2138 * NOTATIONs *
2139 * *
2140 ************************************************************************/
2141/**
2142 * xmlCreateNotationTable:
2143 *
2144 * create and initialize an empty notation hash table.
2145 *
2146 * Returns the xmlNotationTablePtr just created or NULL in case
2147 * of error.
2148 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002149static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002150xmlCreateNotationTable(void) {
2151 return(xmlHashCreate(0));
2152}
2153
2154/**
2155 * xmlFreeNotation:
2156 * @not: A notation
2157 *
2158 * Deallocate the memory used by an notation definition
2159 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002160static void
Owen Taylor3473f882001-02-23 17:55:21 +00002161xmlFreeNotation(xmlNotationPtr nota) {
2162 if (nota == NULL) return;
2163 if (nota->name != NULL)
2164 xmlFree((xmlChar *) nota->name);
2165 if (nota->PublicID != NULL)
2166 xmlFree((xmlChar *) nota->PublicID);
2167 if (nota->SystemID != NULL)
2168 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002169 xmlFree(nota);
2170}
2171
2172
2173/**
2174 * xmlAddNotationDecl:
2175 * @dtd: pointer to the DTD
2176 * @ctxt: the validation context
2177 * @name: the entity name
2178 * @PublicID: the public identifier or NULL
2179 * @SystemID: the system identifier or NULL
2180 *
2181 * Register a new notation declaration
2182 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002183 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00002184 */
2185xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002186xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002187 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00002188 const xmlChar *PublicID, const xmlChar *SystemID) {
2189 xmlNotationPtr ret;
2190 xmlNotationTablePtr table;
2191
2192 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002193 return(NULL);
2194 }
2195 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002196 return(NULL);
2197 }
2198 if ((PublicID == NULL) && (SystemID == NULL)) {
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002199 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002200 }
2201
2202 /*
2203 * Create the Notation table if needed.
2204 */
2205 table = (xmlNotationTablePtr) dtd->notations;
2206 if (table == NULL)
2207 dtd->notations = table = xmlCreateNotationTable();
2208 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002209 xmlVErrMemory(ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00002210 "xmlAddNotationDecl: Table creation failed!\n");
2211 return(NULL);
2212 }
2213
2214 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2215 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002216 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002217 return(NULL);
2218 }
2219 memset(ret, 0, sizeof(xmlNotation));
2220
2221 /*
2222 * fill the structure.
2223 */
2224 ret->name = xmlStrdup(name);
2225 if (SystemID != NULL)
2226 ret->SystemID = xmlStrdup(SystemID);
2227 if (PublicID != NULL)
2228 ret->PublicID = xmlStrdup(PublicID);
2229
2230 /*
2231 * Validity Check:
2232 * Check the DTD for previous declarations of the ATTLIST
2233 */
2234 if (xmlHashAddEntry(table, name, ret)) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002235#ifdef LIBXML_VALID_ENABLED
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002236 xmlErrValid(NULL, XML_DTD_NOTATION_REDEFINED,
2237 "xmlAddNotationDecl: %s already defined\n",
2238 (const char *) name);
Daniel Veillard4432df22003-09-28 18:58:27 +00002239#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002240 xmlFreeNotation(ret);
2241 return(NULL);
2242 }
2243 return(ret);
2244}
2245
2246/**
2247 * xmlFreeNotationTable:
2248 * @table: An notation table
2249 *
2250 * Deallocate the memory used by an entities hash table.
2251 */
2252void
2253xmlFreeNotationTable(xmlNotationTablePtr table) {
2254 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2255}
2256
Daniel Veillard652327a2003-09-29 18:02:38 +00002257#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002258/**
2259 * xmlCopyNotation:
2260 * @nota: A notation
2261 *
2262 * Build a copy of a notation.
2263 *
2264 * Returns the new xmlNotationPtr or NULL in case of error.
2265 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002266static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002267xmlCopyNotation(xmlNotationPtr nota) {
2268 xmlNotationPtr cur;
2269
2270 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2271 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002272 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002273 return(NULL);
2274 }
2275 if (nota->name != NULL)
2276 cur->name = xmlStrdup(nota->name);
2277 else
2278 cur->name = NULL;
2279 if (nota->PublicID != NULL)
2280 cur->PublicID = xmlStrdup(nota->PublicID);
2281 else
2282 cur->PublicID = NULL;
2283 if (nota->SystemID != NULL)
2284 cur->SystemID = xmlStrdup(nota->SystemID);
2285 else
2286 cur->SystemID = NULL;
2287 return(cur);
2288}
2289
2290/**
2291 * xmlCopyNotationTable:
2292 * @table: A notation table
2293 *
2294 * Build a copy of a notation table.
2295 *
2296 * Returns the new xmlNotationTablePtr or NULL in case of error.
2297 */
2298xmlNotationTablePtr
2299xmlCopyNotationTable(xmlNotationTablePtr table) {
2300 return((xmlNotationTablePtr) xmlHashCopy(table,
2301 (xmlHashCopier) xmlCopyNotation));
2302}
Daniel Veillard652327a2003-09-29 18:02:38 +00002303#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002304
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002305#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002306/**
2307 * xmlDumpNotationDecl:
2308 * @buf: the XML buffer output
2309 * @nota: A notation declaration
2310 *
2311 * This will dump the content the notation declaration as an XML DTD definition
2312 */
2313void
2314xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2315 xmlBufferWriteChar(buf, "<!NOTATION ");
2316 xmlBufferWriteCHAR(buf, nota->name);
2317 if (nota->PublicID != NULL) {
2318 xmlBufferWriteChar(buf, " PUBLIC ");
2319 xmlBufferWriteQuotedString(buf, nota->PublicID);
2320 if (nota->SystemID != NULL) {
2321 xmlBufferWriteChar(buf, " ");
2322 xmlBufferWriteCHAR(buf, nota->SystemID);
2323 }
2324 } else {
2325 xmlBufferWriteChar(buf, " SYSTEM ");
2326 xmlBufferWriteCHAR(buf, nota->SystemID);
2327 }
2328 xmlBufferWriteChar(buf, " >\n");
2329}
2330
2331/**
William M. Brack9e660592003-10-20 14:56:06 +00002332 * xmlDumpNotationDeclScan:
2333 * @nota: A notation declaration
2334 * @buf: the XML buffer output
2335 *
2336 * This is called with the hash scan function, and just reverses args
2337 */
2338static void
2339xmlDumpNotationDeclScan(xmlNotationPtr nota, xmlBufferPtr buf) {
2340 xmlDumpNotationDecl(buf, nota);
2341}
2342
2343/**
Owen Taylor3473f882001-02-23 17:55:21 +00002344 * xmlDumpNotationTable:
2345 * @buf: the XML buffer output
2346 * @table: A notation table
2347 *
2348 * This will dump the content of the notation table as an XML DTD definition
2349 */
2350void
2351xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
William M. Brack9e660592003-10-20 14:56:06 +00002352 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDeclScan, buf);
Owen Taylor3473f882001-02-23 17:55:21 +00002353}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002354#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002355
2356/************************************************************************
2357 * *
2358 * IDs *
2359 * *
2360 ************************************************************************/
2361/**
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002362 * DICT_FREE:
2363 * @str: a string
2364 *
2365 * Free a string if it is not owned by the "dict" dictionnary in the
2366 * current scope
2367 */
2368#define DICT_FREE(str) \
2369 if ((str) && ((!dict) || \
2370 (xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \
2371 xmlFree((char *)(str));
2372
2373/**
Owen Taylor3473f882001-02-23 17:55:21 +00002374 * xmlCreateIDTable:
2375 *
2376 * create and initialize an empty id hash table.
2377 *
2378 * Returns the xmlIDTablePtr just created or NULL in case
2379 * of error.
2380 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002381static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002382xmlCreateIDTable(void) {
2383 return(xmlHashCreate(0));
2384}
2385
2386/**
2387 * xmlFreeID:
2388 * @not: A id
2389 *
2390 * Deallocate the memory used by an id definition
2391 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002392static void
Owen Taylor3473f882001-02-23 17:55:21 +00002393xmlFreeID(xmlIDPtr id) {
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002394 xmlDictPtr dict = NULL;
2395
Owen Taylor3473f882001-02-23 17:55:21 +00002396 if (id == NULL) return;
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002397
2398 if (id->doc != NULL)
2399 dict = id->doc->dict;
2400
Owen Taylor3473f882001-02-23 17:55:21 +00002401 if (id->value != NULL)
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002402 DICT_FREE(id->value)
Daniel Veillardea7751d2002-12-20 00:16:24 +00002403 if (id->name != NULL)
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002404 DICT_FREE(id->name)
Owen Taylor3473f882001-02-23 17:55:21 +00002405 xmlFree(id);
2406}
2407
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002408
Owen Taylor3473f882001-02-23 17:55:21 +00002409/**
2410 * xmlAddID:
2411 * @ctxt: the validation context
2412 * @doc: pointer to the document
2413 * @value: the value name
2414 * @attr: the attribute holding the ID
2415 *
2416 * Register a new id declaration
2417 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002418 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002419 */
2420xmlIDPtr
2421xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2422 xmlAttrPtr attr) {
2423 xmlIDPtr ret;
2424 xmlIDTablePtr table;
2425
2426 if (doc == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002427 return(NULL);
2428 }
2429 if (value == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002430 return(NULL);
2431 }
2432 if (attr == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002433 return(NULL);
2434 }
2435
2436 /*
2437 * Create the ID table if needed.
2438 */
2439 table = (xmlIDTablePtr) doc->ids;
2440 if (table == NULL)
2441 doc->ids = table = xmlCreateIDTable();
2442 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002443 xmlVErrMemory(ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00002444 "xmlAddID: Table creation failed!\n");
2445 return(NULL);
2446 }
2447
2448 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2449 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002450 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002451 return(NULL);
2452 }
2453
2454 /*
2455 * fill the structure.
2456 */
2457 ret->value = xmlStrdup(value);
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002458 ret->doc = doc;
Daniel Veillardea7751d2002-12-20 00:16:24 +00002459 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2460 /*
2461 * Operating in streaming mode, attr is gonna disapear
2462 */
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002463 if (doc->dict != NULL)
2464 ret->name = xmlDictLookup(doc->dict, attr->name, -1);
2465 else
2466 ret->name = xmlStrdup(attr->name);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002467 ret->attr = NULL;
2468 } else {
2469 ret->attr = attr;
2470 ret->name = NULL;
2471 }
2472 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002473
2474 if (xmlHashAddEntry(table, value, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002475#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002476 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002477 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002478 */
Daniel Veillard76575762002-09-05 14:21:15 +00002479 if (ctxt != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00002480 xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED,
2481 "ID %s already defined\n",
2482 value, NULL, NULL);
Daniel Veillard76575762002-09-05 14:21:15 +00002483 }
Daniel Veillard4432df22003-09-28 18:58:27 +00002484#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002485 xmlFreeID(ret);
2486 return(NULL);
2487 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002488 if (attr != NULL)
2489 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002490 return(ret);
2491}
2492
2493/**
2494 * xmlFreeIDTable:
2495 * @table: An id table
2496 *
2497 * Deallocate the memory used by an ID hash table.
2498 */
2499void
2500xmlFreeIDTable(xmlIDTablePtr table) {
2501 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2502}
2503
2504/**
2505 * xmlIsID:
2506 * @doc: the document
2507 * @elem: the element carrying the attribute
2508 * @attr: the attribute
2509 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002510 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002511 * then this is done if DTD loading has been requested. In the case
2512 * of HTML documents parsed with the HTML parser, then ID detection is
2513 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002514 *
2515 * Returns 0 or 1 depending on the lookup result
2516 */
2517int
2518xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2519 if (doc == NULL) return(0);
2520 if (attr == NULL) return(0);
2521 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2522 return(0);
2523 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
Daniel Veillard9ba8e382003-10-28 21:31:45 +00002524 if (((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2525 (xmlStrEqual(BAD_CAST "name", attr->name))) &&
2526 ((elem != NULL) && (!xmlStrEqual(elem->name, BAD_CAST "input"))))
Owen Taylor3473f882001-02-23 17:55:21 +00002527 return(1);
2528 return(0);
2529 } else {
2530 xmlAttributePtr attrDecl;
2531
2532 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002533 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00002534 xmlChar fn[50];
Daniel Veillard37f961d2002-07-06 17:53:56 +00002535 xmlChar *fullname;
Daniel Veillardc00cda82003-04-07 10:22:39 +00002536
2537 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002538 if (fullname == NULL)
2539 return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002540 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2541 attr->name);
2542 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2543 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2544 attr->name);
Daniel Veillardc00cda82003-04-07 10:22:39 +00002545 if ((fullname != fn) && (fullname != elem->name))
2546 xmlFree(fullname);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002547 } else {
2548 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2549 attr->name);
2550 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2551 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2552 attr->name);
2553 }
Owen Taylor3473f882001-02-23 17:55:21 +00002554
2555 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2556 return(1);
2557 }
2558 return(0);
2559}
2560
2561/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002562 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002563 * @doc: the document
2564 * @attr: the attribute
2565 *
2566 * Remove the given attribute from the ID table maintained internally.
2567 *
2568 * Returns -1 if the lookup failed and 0 otherwise
2569 */
2570int
2571xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
Owen Taylor3473f882001-02-23 17:55:21 +00002572 xmlIDTablePtr table;
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002573 xmlIDPtr id;
Owen Taylor3473f882001-02-23 17:55:21 +00002574 xmlChar *ID;
2575
2576 if (doc == NULL) return(-1);
2577 if (attr == NULL) return(-1);
2578 table = (xmlIDTablePtr) doc->ids;
2579 if (table == NULL)
2580 return(-1);
2581
2582 if (attr == NULL)
2583 return(-1);
2584 ID = xmlNodeListGetString(doc, attr->children, 1);
2585 if (ID == NULL)
2586 return(-1);
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002587 id = xmlHashLookup(table, ID);
2588 if (id == NULL || id->attr != attr) {
Owen Taylor3473f882001-02-23 17:55:21 +00002589 xmlFree(ID);
2590 return(-1);
2591 }
2592 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2593 xmlFree(ID);
2594 return(0);
2595}
2596
2597/**
2598 * xmlGetID:
2599 * @doc: pointer to the document
2600 * @ID: the ID value
2601 *
2602 * Search the attribute declaring the given ID
2603 *
2604 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2605 */
2606xmlAttrPtr
2607xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2608 xmlIDTablePtr table;
2609 xmlIDPtr id;
2610
2611 if (doc == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002612 return(NULL);
2613 }
2614
2615 if (ID == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002616 return(NULL);
2617 }
2618
2619 table = (xmlIDTablePtr) doc->ids;
2620 if (table == NULL)
2621 return(NULL);
2622
2623 id = xmlHashLookup(table, ID);
2624 if (id == NULL)
2625 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002626 if (id->attr == NULL) {
2627 /*
2628 * We are operating on a stream, return a well known reference
2629 * since the attribute node doesn't exist anymore
2630 */
2631 return((xmlAttrPtr) doc);
2632 }
Owen Taylor3473f882001-02-23 17:55:21 +00002633 return(id->attr);
2634}
2635
2636/************************************************************************
2637 * *
2638 * Refs *
2639 * *
2640 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002641typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002642{
2643 xmlListPtr l;
2644 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002645} xmlRemoveMemo;
2646
2647typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2648
2649typedef struct xmlValidateMemo_t
2650{
2651 xmlValidCtxtPtr ctxt;
2652 const xmlChar *name;
2653} xmlValidateMemo;
2654
2655typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002656
2657/**
2658 * xmlCreateRefTable:
2659 *
2660 * create and initialize an empty ref hash table.
2661 *
2662 * Returns the xmlRefTablePtr just created or NULL in case
2663 * of error.
2664 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002665static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002666xmlCreateRefTable(void) {
2667 return(xmlHashCreate(0));
2668}
2669
2670/**
2671 * xmlFreeRef:
2672 * @lk: A list link
2673 *
2674 * Deallocate the memory used by a ref definition
2675 */
2676static void
2677xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002678 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2679 if (ref == NULL) return;
2680 if (ref->value != NULL)
2681 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002682 if (ref->name != NULL)
2683 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002684 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002685}
2686
2687/**
2688 * xmlFreeRefList:
2689 * @list_ref: A list of references.
2690 *
2691 * Deallocate the memory used by a list of references
2692 */
2693static void
2694xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002695 if (list_ref == NULL) return;
2696 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002697}
2698
2699/**
2700 * xmlWalkRemoveRef:
2701 * @data: Contents of current link
2702 * @user: Value supplied by the user
2703 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002704 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002705 */
2706static int
2707xmlWalkRemoveRef(const void *data, const void *user)
2708{
Daniel Veillard37721922001-05-04 15:21:12 +00002709 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2710 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2711 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002712
Daniel Veillard37721922001-05-04 15:21:12 +00002713 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2714 xmlListRemoveFirst(ref_list, (void *)data);
2715 return 0;
2716 }
2717 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002718}
2719
2720/**
2721 * xmlAddRef:
2722 * @ctxt: the validation context
2723 * @doc: pointer to the document
2724 * @value: the value name
2725 * @attr: the attribute holding the Ref
2726 *
2727 * Register a new ref declaration
2728 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002729 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002730 */
2731xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002732xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002733 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002734 xmlRefPtr ret;
2735 xmlRefTablePtr table;
2736 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002737
Daniel Veillard37721922001-05-04 15:21:12 +00002738 if (doc == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002739 return(NULL);
2740 }
2741 if (value == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002742 return(NULL);
2743 }
2744 if (attr == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002745 return(NULL);
2746 }
Owen Taylor3473f882001-02-23 17:55:21 +00002747
Daniel Veillard37721922001-05-04 15:21:12 +00002748 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002749 * Create the Ref table if needed.
2750 */
Daniel Veillard37721922001-05-04 15:21:12 +00002751 table = (xmlRefTablePtr) doc->refs;
2752 if (table == NULL)
2753 doc->refs = table = xmlCreateRefTable();
2754 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002755 xmlVErrMemory(ctxt,
Daniel Veillard37721922001-05-04 15:21:12 +00002756 "xmlAddRef: Table creation failed!\n");
2757 return(NULL);
2758 }
Owen Taylor3473f882001-02-23 17:55:21 +00002759
Daniel Veillard37721922001-05-04 15:21:12 +00002760 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2761 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002762 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard37721922001-05-04 15:21:12 +00002763 return(NULL);
2764 }
Owen Taylor3473f882001-02-23 17:55:21 +00002765
Daniel Veillard37721922001-05-04 15:21:12 +00002766 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002767 * fill the structure.
2768 */
Daniel Veillard37721922001-05-04 15:21:12 +00002769 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002770 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2771 /*
2772 * Operating in streaming mode, attr is gonna disapear
2773 */
2774 ret->name = xmlStrdup(attr->name);
2775 ret->attr = NULL;
2776 } else {
2777 ret->name = NULL;
2778 ret->attr = attr;
2779 }
2780 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002781
Daniel Veillard37721922001-05-04 15:21:12 +00002782 /* To add a reference :-
2783 * References are maintained as a list of references,
2784 * Lookup the entry, if no entry create new nodelist
2785 * Add the owning node to the NodeList
2786 * Return the ref
2787 */
Owen Taylor3473f882001-02-23 17:55:21 +00002788
Daniel Veillard37721922001-05-04 15:21:12 +00002789 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2790 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002791 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2792 "xmlAddRef: Reference list creation failed!\n",
2793 NULL);
Daniel Veillard37721922001-05-04 15:21:12 +00002794 return(NULL);
2795 }
2796 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2797 xmlListDelete(ref_list);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002798 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2799 "xmlAddRef: Reference list insertion failed!\n",
2800 NULL);
Daniel Veillard37721922001-05-04 15:21:12 +00002801 return(NULL);
2802 }
2803 }
2804 xmlListInsert(ref_list, ret);
2805 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002806}
2807
2808/**
2809 * xmlFreeRefTable:
2810 * @table: An ref table
2811 *
2812 * Deallocate the memory used by an Ref hash table.
2813 */
2814void
2815xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002816 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002817}
2818
2819/**
2820 * xmlIsRef:
2821 * @doc: the document
2822 * @elem: the element carrying the attribute
2823 * @attr: the attribute
2824 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002825 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002826 * then this is simple, otherwise we use an heuristic: name Ref (upper
2827 * or lowercase).
2828 *
2829 * Returns 0 or 1 depending on the lookup result
2830 */
2831int
2832xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002833 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2834 return(0);
2835 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2836 /* TODO @@@ */
2837 return(0);
2838 } else {
2839 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002840
Daniel Veillard37721922001-05-04 15:21:12 +00002841 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2842 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2843 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2844 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002845
Daniel Veillard37721922001-05-04 15:21:12 +00002846 if ((attrDecl != NULL) &&
2847 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2848 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2849 return(1);
2850 }
2851 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002852}
2853
2854/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002855 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002856 * @doc: the document
2857 * @attr: the attribute
2858 *
2859 * Remove the given attribute from the Ref table maintained internally.
2860 *
2861 * Returns -1 if the lookup failed and 0 otherwise
2862 */
2863int
2864xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002865 xmlListPtr ref_list;
2866 xmlRefTablePtr table;
2867 xmlChar *ID;
2868 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002869
Daniel Veillard37721922001-05-04 15:21:12 +00002870 if (doc == NULL) return(-1);
2871 if (attr == NULL) return(-1);
2872 table = (xmlRefTablePtr) doc->refs;
2873 if (table == NULL)
2874 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002875
Daniel Veillard37721922001-05-04 15:21:12 +00002876 if (attr == NULL)
2877 return(-1);
2878 ID = xmlNodeListGetString(doc, attr->children, 1);
2879 if (ID == NULL)
2880 return(-1);
2881 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002882
Daniel Veillard37721922001-05-04 15:21:12 +00002883 if(ref_list == NULL) {
2884 xmlFree(ID);
2885 return (-1);
2886 }
2887 /* At this point, ref_list refers to a list of references which
2888 * have the same key as the supplied attr. Our list of references
2889 * is ordered by reference address and we don't have that information
2890 * here to use when removing. We'll have to walk the list and
2891 * check for a matching attribute, when we find one stop the walk
2892 * and remove the entry.
2893 * The list is ordered by reference, so that means we don't have the
2894 * key. Passing the list and the reference to the walker means we
2895 * will have enough data to be able to remove the entry.
2896 */
2897 target.l = ref_list;
2898 target.ap = attr;
2899
2900 /* Remove the supplied attr from our list */
2901 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002902
Daniel Veillard37721922001-05-04 15:21:12 +00002903 /*If the list is empty then remove the list entry in the hash */
2904 if (xmlListEmpty(ref_list))
2905 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2906 xmlFreeRefList);
2907 xmlFree(ID);
2908 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002909}
2910
2911/**
2912 * xmlGetRefs:
2913 * @doc: pointer to the document
2914 * @ID: the ID value
2915 *
2916 * Find the set of references for the supplied ID.
2917 *
2918 * Returns NULL if not found, otherwise node set for the ID.
2919 */
2920xmlListPtr
2921xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002922 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002923
Daniel Veillard37721922001-05-04 15:21:12 +00002924 if (doc == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002925 return(NULL);
2926 }
Owen Taylor3473f882001-02-23 17:55:21 +00002927
Daniel Veillard37721922001-05-04 15:21:12 +00002928 if (ID == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002929 return(NULL);
2930 }
Owen Taylor3473f882001-02-23 17:55:21 +00002931
Daniel Veillard37721922001-05-04 15:21:12 +00002932 table = (xmlRefTablePtr) doc->refs;
2933 if (table == NULL)
2934 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002935
Daniel Veillard37721922001-05-04 15:21:12 +00002936 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002937}
2938
2939/************************************************************************
2940 * *
2941 * Routines for validity checking *
2942 * *
2943 ************************************************************************/
2944
2945/**
2946 * xmlGetDtdElementDesc:
2947 * @dtd: a pointer to the DtD to search
2948 * @name: the element name
2949 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002950 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002951 *
2952 * returns the xmlElementPtr if found or NULL
2953 */
2954
2955xmlElementPtr
2956xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2957 xmlElementTablePtr table;
2958 xmlElementPtr cur;
2959 xmlChar *uqname = NULL, *prefix = NULL;
2960
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00002961 if ((dtd == NULL) || (name == NULL)) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002962 if (dtd->elements == NULL)
2963 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002964 table = (xmlElementTablePtr) dtd->elements;
2965
2966 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002967 if (uqname != NULL)
2968 name = uqname;
2969 cur = xmlHashLookup2(table, name, prefix);
2970 if (prefix != NULL) xmlFree(prefix);
2971 if (uqname != NULL) xmlFree(uqname);
2972 return(cur);
2973}
2974/**
2975 * xmlGetDtdElementDesc2:
2976 * @dtd: a pointer to the DtD to search
2977 * @name: the element name
2978 * @create: create an empty description if not found
2979 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002980 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002981 *
2982 * returns the xmlElementPtr if found or NULL
2983 */
2984
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002985static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002986xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2987 xmlElementTablePtr table;
2988 xmlElementPtr cur;
2989 xmlChar *uqname = NULL, *prefix = NULL;
2990
2991 if (dtd == NULL) return(NULL);
2992 if (dtd->elements == NULL) {
2993 if (!create)
2994 return(NULL);
2995 /*
2996 * Create the Element table if needed.
2997 */
2998 table = (xmlElementTablePtr) dtd->elements;
2999 if (table == NULL) {
3000 table = xmlCreateElementTable();
3001 dtd->elements = (void *) table;
3002 }
3003 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00003004 xmlVErrMemory(NULL, "element table allocation failed");
Daniel Veillarda10efa82001-04-18 13:09:01 +00003005 return(NULL);
3006 }
3007 }
3008 table = (xmlElementTablePtr) dtd->elements;
3009
3010 uqname = xmlSplitQName2(name, &prefix);
3011 if (uqname != NULL)
3012 name = uqname;
3013 cur = xmlHashLookup2(table, name, prefix);
3014 if ((cur == NULL) && (create)) {
3015 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
3016 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00003017 xmlVErrMemory(NULL, "malloc failed");
Daniel Veillarda10efa82001-04-18 13:09:01 +00003018 return(NULL);
3019 }
3020 memset(cur, 0, sizeof(xmlElement));
3021 cur->type = XML_ELEMENT_DECL;
3022
3023 /*
3024 * fill the structure.
3025 */
3026 cur->name = xmlStrdup(name);
3027 cur->prefix = xmlStrdup(prefix);
3028 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
3029
3030 xmlHashAddEntry2(table, name, prefix, cur);
3031 }
3032 if (prefix != NULL) xmlFree(prefix);
3033 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00003034 return(cur);
3035}
3036
3037/**
3038 * xmlGetDtdQElementDesc:
3039 * @dtd: a pointer to the DtD to search
3040 * @name: the element name
3041 * @prefix: the element namespace prefix
3042 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003043 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00003044 *
3045 * returns the xmlElementPtr if found or NULL
3046 */
3047
Daniel Veillard48da9102001-08-07 01:10:10 +00003048xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00003049xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
3050 const xmlChar *prefix) {
3051 xmlElementTablePtr table;
3052
3053 if (dtd == NULL) return(NULL);
3054 if (dtd->elements == NULL) return(NULL);
3055 table = (xmlElementTablePtr) dtd->elements;
3056
3057 return(xmlHashLookup2(table, name, prefix));
3058}
3059
3060/**
3061 * xmlGetDtdAttrDesc:
3062 * @dtd: a pointer to the DtD to search
3063 * @elem: the element name
3064 * @name: the attribute name
3065 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003066 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00003067 * this element.
3068 *
3069 * returns the xmlAttributePtr if found or NULL
3070 */
3071
3072xmlAttributePtr
3073xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
3074 xmlAttributeTablePtr table;
3075 xmlAttributePtr cur;
3076 xmlChar *uqname = NULL, *prefix = NULL;
3077
3078 if (dtd == NULL) return(NULL);
3079 if (dtd->attributes == NULL) return(NULL);
3080
3081 table = (xmlAttributeTablePtr) dtd->attributes;
3082 if (table == NULL)
3083 return(NULL);
3084
3085 uqname = xmlSplitQName2(name, &prefix);
3086
3087 if (uqname != NULL) {
3088 cur = xmlHashLookup3(table, uqname, prefix, elem);
3089 if (prefix != NULL) xmlFree(prefix);
3090 if (uqname != NULL) xmlFree(uqname);
3091 } else
3092 cur = xmlHashLookup3(table, name, NULL, elem);
3093 return(cur);
3094}
3095
3096/**
3097 * xmlGetDtdQAttrDesc:
3098 * @dtd: a pointer to the DtD to search
3099 * @elem: the element name
3100 * @name: the attribute name
3101 * @prefix: the attribute namespace prefix
3102 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003103 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00003104 * this element.
3105 *
3106 * returns the xmlAttributePtr if found or NULL
3107 */
3108
Daniel Veillard48da9102001-08-07 01:10:10 +00003109xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00003110xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
3111 const xmlChar *prefix) {
3112 xmlAttributeTablePtr table;
3113
3114 if (dtd == NULL) return(NULL);
3115 if (dtd->attributes == NULL) return(NULL);
3116 table = (xmlAttributeTablePtr) dtd->attributes;
3117
3118 return(xmlHashLookup3(table, name, prefix, elem));
3119}
3120
3121/**
3122 * xmlGetDtdNotationDesc:
3123 * @dtd: a pointer to the DtD to search
3124 * @name: the notation name
3125 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003126 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00003127 *
3128 * returns the xmlNotationPtr if found or NULL
3129 */
3130
3131xmlNotationPtr
3132xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
3133 xmlNotationTablePtr table;
3134
3135 if (dtd == NULL) return(NULL);
3136 if (dtd->notations == NULL) return(NULL);
3137 table = (xmlNotationTablePtr) dtd->notations;
3138
3139 return(xmlHashLookup(table, name));
3140}
3141
Daniel Veillard4432df22003-09-28 18:58:27 +00003142#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00003143/**
3144 * xmlValidateNotationUse:
3145 * @ctxt: the validation context
3146 * @doc: the document
3147 * @notationName: the notation name to check
3148 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003149 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00003150 * - [ VC: Notation Declared ]
3151 *
3152 * returns 1 if valid or 0 otherwise
3153 */
3154
3155int
3156xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3157 const xmlChar *notationName) {
3158 xmlNotationPtr notaDecl;
3159 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3160
3161 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
3162 if ((notaDecl == NULL) && (doc->extSubset != NULL))
3163 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
3164
Daniel Veillarde637c4a2003-03-30 21:10:09 +00003165 if ((notaDecl == NULL) && (ctxt != NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003166 xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_NOTATION,
3167 "NOTATION %s is not declared\n",
3168 notationName, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003169 return(0);
3170 }
3171 return(1);
3172}
Daniel Veillard4432df22003-09-28 18:58:27 +00003173#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00003174
3175/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00003176 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00003177 * @doc: the document
3178 * @name: the element name
3179 *
3180 * Search in the DtDs whether an element accept Mixed content (or ANY)
3181 * basically if it is supposed to accept text childs
3182 *
3183 * returns 0 if no, 1 if yes, and -1 if no element description is available
3184 */
3185
3186int
3187xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
3188 xmlElementPtr elemDecl;
3189
3190 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3191
3192 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
3193 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3194 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
3195 if (elemDecl == NULL) return(-1);
3196 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003197 case XML_ELEMENT_TYPE_UNDEFINED:
3198 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003199 case XML_ELEMENT_TYPE_ELEMENT:
3200 return(0);
3201 case XML_ELEMENT_TYPE_EMPTY:
3202 /*
3203 * return 1 for EMPTY since we want VC error to pop up
3204 * on <empty> </empty> for example
3205 */
3206 case XML_ELEMENT_TYPE_ANY:
3207 case XML_ELEMENT_TYPE_MIXED:
3208 return(1);
3209 }
3210 return(1);
3211}
3212
Daniel Veillard4432df22003-09-28 18:58:27 +00003213#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00003214/**
3215 * xmlValidateNameValue:
3216 * @value: an Name value
3217 *
3218 * Validate that the given value match Name production
3219 *
3220 * returns 1 if valid or 0 otherwise
3221 */
3222
Daniel Veillard9b731d72002-04-14 12:56:08 +00003223int
Owen Taylor3473f882001-02-23 17:55:21 +00003224xmlValidateNameValue(const xmlChar *value) {
3225 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003226 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003227
3228 if (value == NULL) return(0);
3229 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003230 val = xmlStringCurrentChar(NULL, cur, &len);
3231 cur += len;
3232 if (!IS_LETTER(val) && (val != '_') &&
3233 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003234 return(0);
3235 }
3236
Daniel Veillardd8224e02002-01-13 15:43:22 +00003237 val = xmlStringCurrentChar(NULL, cur, &len);
3238 cur += len;
3239 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3240 (val == '.') || (val == '-') ||
3241 (val == '_') || (val == ':') ||
3242 (IS_COMBINING(val)) ||
3243 (IS_EXTENDER(val))) {
3244 val = xmlStringCurrentChar(NULL, cur, &len);
3245 cur += len;
3246 }
Owen Taylor3473f882001-02-23 17:55:21 +00003247
Daniel Veillardd8224e02002-01-13 15:43:22 +00003248 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003249
3250 return(1);
3251}
3252
3253/**
3254 * xmlValidateNamesValue:
3255 * @value: an Names value
3256 *
3257 * Validate that the given value match Names production
3258 *
3259 * returns 1 if valid or 0 otherwise
3260 */
3261
Daniel Veillard9b731d72002-04-14 12:56:08 +00003262int
Owen Taylor3473f882001-02-23 17:55:21 +00003263xmlValidateNamesValue(const xmlChar *value) {
3264 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003265 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003266
3267 if (value == NULL) return(0);
3268 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003269 val = xmlStringCurrentChar(NULL, cur, &len);
3270 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003271
Daniel Veillardd8224e02002-01-13 15:43:22 +00003272 if (!IS_LETTER(val) && (val != '_') &&
3273 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003274 return(0);
3275 }
3276
Daniel Veillardd8224e02002-01-13 15:43:22 +00003277 val = xmlStringCurrentChar(NULL, cur, &len);
3278 cur += len;
3279 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3280 (val == '.') || (val == '-') ||
3281 (val == '_') || (val == ':') ||
3282 (IS_COMBINING(val)) ||
3283 (IS_EXTENDER(val))) {
3284 val = xmlStringCurrentChar(NULL, cur, &len);
3285 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003286 }
3287
Daniel Veillardd8224e02002-01-13 15:43:22 +00003288 while (IS_BLANK(val)) {
3289 while (IS_BLANK(val)) {
3290 val = xmlStringCurrentChar(NULL, cur, &len);
3291 cur += len;
3292 }
3293
3294 if (!IS_LETTER(val) && (val != '_') &&
3295 (val != ':')) {
3296 return(0);
3297 }
3298 val = xmlStringCurrentChar(NULL, cur, &len);
3299 cur += len;
3300
3301 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3302 (val == '.') || (val == '-') ||
3303 (val == '_') || (val == ':') ||
3304 (IS_COMBINING(val)) ||
3305 (IS_EXTENDER(val))) {
3306 val = xmlStringCurrentChar(NULL, cur, &len);
3307 cur += len;
3308 }
3309 }
3310
3311 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003312
3313 return(1);
3314}
3315
3316/**
3317 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003318 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003319 *
3320 * Validate that the given value match Nmtoken production
3321 *
3322 * [ VC: Name Token ]
3323 *
3324 * returns 1 if valid or 0 otherwise
3325 */
3326
Daniel Veillard9b731d72002-04-14 12:56:08 +00003327int
Owen Taylor3473f882001-02-23 17:55:21 +00003328xmlValidateNmtokenValue(const xmlChar *value) {
3329 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003330 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003331
3332 if (value == NULL) return(0);
3333 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003334 val = xmlStringCurrentChar(NULL, cur, &len);
3335 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003336
Daniel Veillardd8224e02002-01-13 15:43:22 +00003337 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3338 (val != '.') && (val != '-') &&
3339 (val != '_') && (val != ':') &&
3340 (!IS_COMBINING(val)) &&
3341 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003342 return(0);
3343
Daniel Veillardd8224e02002-01-13 15:43:22 +00003344 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3345 (val == '.') || (val == '-') ||
3346 (val == '_') || (val == ':') ||
3347 (IS_COMBINING(val)) ||
3348 (IS_EXTENDER(val))) {
3349 val = xmlStringCurrentChar(NULL, cur, &len);
3350 cur += len;
3351 }
Owen Taylor3473f882001-02-23 17:55:21 +00003352
Daniel Veillardd8224e02002-01-13 15:43:22 +00003353 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003354
3355 return(1);
3356}
3357
3358/**
3359 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003360 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003361 *
3362 * Validate that the given value match Nmtokens production
3363 *
3364 * [ VC: Name Token ]
3365 *
3366 * returns 1 if valid or 0 otherwise
3367 */
3368
Daniel Veillard9b731d72002-04-14 12:56:08 +00003369int
Owen Taylor3473f882001-02-23 17:55:21 +00003370xmlValidateNmtokensValue(const xmlChar *value) {
3371 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003372 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003373
3374 if (value == NULL) return(0);
3375 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003376 val = xmlStringCurrentChar(NULL, cur, &len);
3377 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003378
Daniel Veillardd8224e02002-01-13 15:43:22 +00003379 while (IS_BLANK(val)) {
3380 val = xmlStringCurrentChar(NULL, cur, &len);
3381 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003382 }
3383
Daniel Veillardd8224e02002-01-13 15:43:22 +00003384 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3385 (val != '.') && (val != '-') &&
3386 (val != '_') && (val != ':') &&
3387 (!IS_COMBINING(val)) &&
3388 (!IS_EXTENDER(val)))
3389 return(0);
3390
3391 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3392 (val == '.') || (val == '-') ||
3393 (val == '_') || (val == ':') ||
3394 (IS_COMBINING(val)) ||
3395 (IS_EXTENDER(val))) {
3396 val = xmlStringCurrentChar(NULL, cur, &len);
3397 cur += len;
3398 }
3399
3400 while (IS_BLANK(val)) {
3401 while (IS_BLANK(val)) {
3402 val = xmlStringCurrentChar(NULL, cur, &len);
3403 cur += len;
3404 }
3405 if (val == 0) return(1);
3406
3407 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3408 (val != '.') && (val != '-') &&
3409 (val != '_') && (val != ':') &&
3410 (!IS_COMBINING(val)) &&
3411 (!IS_EXTENDER(val)))
3412 return(0);
3413
3414 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3415 (val == '.') || (val == '-') ||
3416 (val == '_') || (val == ':') ||
3417 (IS_COMBINING(val)) ||
3418 (IS_EXTENDER(val))) {
3419 val = xmlStringCurrentChar(NULL, cur, &len);
3420 cur += len;
3421 }
3422 }
3423
3424 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003425
3426 return(1);
3427}
3428
3429/**
3430 * xmlValidateNotationDecl:
3431 * @ctxt: the validation context
3432 * @doc: a document instance
3433 * @nota: a notation definition
3434 *
3435 * Try to validate a single notation definition
3436 * basically it does the following checks as described by the
3437 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003438 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003439 * But this function get called anyway ...
3440 *
3441 * returns 1 if valid or 0 otherwise
3442 */
3443
3444int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003445xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3446 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003447 int ret = 1;
3448
3449 return(ret);
3450}
3451
3452/**
3453 * xmlValidateAttributeValue:
3454 * @type: an attribute type
3455 * @value: an attribute value
3456 *
3457 * Validate that the given attribute value match the proper production
3458 *
3459 * [ VC: ID ]
3460 * Values of type ID must match the Name production....
3461 *
3462 * [ VC: IDREF ]
3463 * Values of type IDREF must match the Name production, and values
3464 * of type IDREFS must match Names ...
3465 *
3466 * [ VC: Entity Name ]
3467 * Values of type ENTITY must match the Name production, values
3468 * of type ENTITIES must match Names ...
3469 *
3470 * [ VC: Name Token ]
3471 * Values of type NMTOKEN must match the Nmtoken production; values
3472 * of type NMTOKENS must match Nmtokens.
3473 *
3474 * returns 1 if valid or 0 otherwise
3475 */
3476
3477int
3478xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3479 switch (type) {
3480 case XML_ATTRIBUTE_ENTITIES:
3481 case XML_ATTRIBUTE_IDREFS:
3482 return(xmlValidateNamesValue(value));
3483 case XML_ATTRIBUTE_ENTITY:
3484 case XML_ATTRIBUTE_IDREF:
3485 case XML_ATTRIBUTE_ID:
3486 case XML_ATTRIBUTE_NOTATION:
3487 return(xmlValidateNameValue(value));
3488 case XML_ATTRIBUTE_NMTOKENS:
3489 case XML_ATTRIBUTE_ENUMERATION:
3490 return(xmlValidateNmtokensValue(value));
3491 case XML_ATTRIBUTE_NMTOKEN:
3492 return(xmlValidateNmtokenValue(value));
3493 case XML_ATTRIBUTE_CDATA:
3494 break;
3495 }
3496 return(1);
3497}
3498
3499/**
3500 * xmlValidateAttributeValue2:
3501 * @ctxt: the validation context
3502 * @doc: the document
3503 * @name: the attribute name (used for error reporting only)
3504 * @type: the attribute type
3505 * @value: the attribute value
3506 *
3507 * Validate that the given attribute value match a given type.
3508 * This typically cannot be done before having finished parsing
3509 * the subsets.
3510 *
3511 * [ VC: IDREF ]
3512 * Values of type IDREF must match one of the declared IDs
3513 * Values of type IDREFS must match a sequence of the declared IDs
3514 * each Name must match the value of an ID attribute on some element
3515 * in the XML document; i.e. IDREF values must match the value of
3516 * some ID attribute
3517 *
3518 * [ VC: Entity Name ]
3519 * Values of type ENTITY must match one declared entity
3520 * Values of type ENTITIES must match a sequence of declared entities
3521 *
3522 * [ VC: Notation Attributes ]
3523 * all notation names in the declaration must be declared.
3524 *
3525 * returns 1 if valid or 0 otherwise
3526 */
3527
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003528static int
Owen Taylor3473f882001-02-23 17:55:21 +00003529xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3530 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3531 int ret = 1;
3532 switch (type) {
3533 case XML_ATTRIBUTE_IDREFS:
3534 case XML_ATTRIBUTE_IDREF:
3535 case XML_ATTRIBUTE_ID:
3536 case XML_ATTRIBUTE_NMTOKENS:
3537 case XML_ATTRIBUTE_ENUMERATION:
3538 case XML_ATTRIBUTE_NMTOKEN:
3539 case XML_ATTRIBUTE_CDATA:
3540 break;
3541 case XML_ATTRIBUTE_ENTITY: {
3542 xmlEntityPtr ent;
3543
3544 ent = xmlGetDocEntity(doc, value);
Daniel Veillard62998c02003-09-15 12:56:36 +00003545 /* yeah it's a bit messy... */
Daniel Veillard878eab02002-02-19 13:46:09 +00003546 if ((ent == NULL) && (doc->standalone == 1)) {
3547 doc->standalone = 0;
3548 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003549 }
Owen Taylor3473f882001-02-23 17:55:21 +00003550 if (ent == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003551 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3552 XML_DTD_UNKNOWN_ENTITY,
Owen Taylor3473f882001-02-23 17:55:21 +00003553 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003554 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003555 ret = 0;
3556 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003557 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3558 XML_DTD_ENTITY_TYPE,
Owen Taylor3473f882001-02-23 17:55:21 +00003559 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003560 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003561 ret = 0;
3562 }
3563 break;
3564 }
3565 case XML_ATTRIBUTE_ENTITIES: {
3566 xmlChar *dup, *nam = NULL, *cur, save;
3567 xmlEntityPtr ent;
3568
3569 dup = xmlStrdup(value);
3570 if (dup == NULL)
3571 return(0);
3572 cur = dup;
3573 while (*cur != 0) {
3574 nam = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00003575 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00003576 save = *cur;
3577 *cur = 0;
3578 ent = xmlGetDocEntity(doc, nam);
3579 if (ent == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003580 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3581 XML_DTD_UNKNOWN_ENTITY,
Owen Taylor3473f882001-02-23 17:55:21 +00003582 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003583 name, nam, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003584 ret = 0;
3585 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003586 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3587 XML_DTD_ENTITY_TYPE,
Owen Taylor3473f882001-02-23 17:55:21 +00003588 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003589 name, nam, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003590 ret = 0;
3591 }
3592 if (save == 0)
3593 break;
3594 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00003595 while (IS_BLANK_CH(*cur)) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00003596 }
3597 xmlFree(dup);
3598 break;
3599 }
3600 case XML_ATTRIBUTE_NOTATION: {
3601 xmlNotationPtr nota;
3602
3603 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3604 if ((nota == NULL) && (doc->extSubset != NULL))
3605 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3606
3607 if (nota == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003608 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3609 XML_DTD_UNKNOWN_NOTATION,
Owen Taylor3473f882001-02-23 17:55:21 +00003610 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003611 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003612 ret = 0;
3613 }
3614 break;
3615 }
3616 }
3617 return(ret);
3618}
3619
3620/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003621 * xmlValidCtxtNormalizeAttributeValue:
3622 * @ctxt: the validation context
3623 * @doc: the document
3624 * @elem: the parent
3625 * @name: the attribute name
3626 * @value: the attribute value
3627 * @ctxt: the validation context or NULL
3628 *
3629 * Does the validation related extra step of the normalization of attribute
3630 * values:
3631 *
3632 * If the declared value is not CDATA, then the XML processor must further
3633 * process the normalized attribute value by discarding any leading and
3634 * trailing space (#x20) characters, and by replacing sequences of space
3635 * (#x20) characters by single space (#x20) character.
3636 *
3637 * Also check VC: Standalone Document Declaration in P32, and update
3638 * ctxt->valid accordingly
3639 *
3640 * returns a new normalized string if normalization is needed, NULL otherwise
3641 * the caller must free the returned value.
3642 */
3643
3644xmlChar *
3645xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3646 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3647 xmlChar *ret, *dst;
3648 const xmlChar *src;
3649 xmlAttributePtr attrDecl = NULL;
3650 int extsubset = 0;
3651
3652 if (doc == NULL) return(NULL);
3653 if (elem == NULL) return(NULL);
3654 if (name == NULL) return(NULL);
3655 if (value == NULL) return(NULL);
3656
3657 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003658 xmlChar fn[50];
3659 xmlChar *fullname;
3660
3661 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3662 if (fullname == NULL)
3663 return(0);
3664 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003665 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003666 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003667 if (attrDecl != NULL)
3668 extsubset = 1;
3669 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003670 if ((fullname != fn) && (fullname != elem->name))
3671 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003672 }
3673 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3674 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3675 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3676 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3677 if (attrDecl != NULL)
3678 extsubset = 1;
3679 }
3680
3681 if (attrDecl == NULL)
3682 return(NULL);
3683 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3684 return(NULL);
3685
3686 ret = xmlStrdup(value);
3687 if (ret == NULL)
3688 return(NULL);
3689 src = value;
3690 dst = ret;
3691 while (*src == 0x20) src++;
3692 while (*src != 0) {
3693 if (*src == 0x20) {
3694 while (*src == 0x20) src++;
3695 if (*src != 0)
3696 *dst++ = 0x20;
3697 } else {
3698 *dst++ = *src++;
3699 }
3700 }
3701 *dst = 0;
3702 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003703 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE,
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003704"standalone: %s on %s value had to be normalized based on external subset declaration\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003705 name, elem->name, NULL);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003706 ctxt->valid = 0;
3707 }
3708 return(ret);
3709}
3710
3711/**
Owen Taylor3473f882001-02-23 17:55:21 +00003712 * xmlValidNormalizeAttributeValue:
3713 * @doc: the document
3714 * @elem: the parent
3715 * @name: the attribute name
3716 * @value: the attribute value
3717 *
3718 * Does the validation related extra step of the normalization of attribute
3719 * values:
3720 *
3721 * If the declared value is not CDATA, then the XML processor must further
3722 * process the normalized attribute value by discarding any leading and
3723 * trailing space (#x20) characters, and by replacing sequences of space
3724 * (#x20) characters by single space (#x20) character.
3725 *
Daniel Veillard652327a2003-09-29 18:02:38 +00003726 * Returns a new normalized string if normalization is needed, NULL otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00003727 * the caller must free the returned value.
3728 */
3729
3730xmlChar *
3731xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3732 const xmlChar *name, const xmlChar *value) {
3733 xmlChar *ret, *dst;
3734 const xmlChar *src;
3735 xmlAttributePtr attrDecl = NULL;
3736
3737 if (doc == NULL) return(NULL);
3738 if (elem == NULL) return(NULL);
3739 if (name == NULL) return(NULL);
3740 if (value == NULL) return(NULL);
3741
3742 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003743 xmlChar fn[50];
3744 xmlChar *fullname;
3745
3746 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3747 if (fullname == NULL)
3748 return(0);
3749 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003750 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003751 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3752 if ((fullname != fn) && (fullname != elem->name))
3753 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003754 }
3755 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3756 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3757 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3758
3759 if (attrDecl == NULL)
3760 return(NULL);
3761 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3762 return(NULL);
3763
3764 ret = xmlStrdup(value);
3765 if (ret == NULL)
3766 return(NULL);
3767 src = value;
3768 dst = ret;
3769 while (*src == 0x20) src++;
3770 while (*src != 0) {
3771 if (*src == 0x20) {
3772 while (*src == 0x20) src++;
3773 if (*src != 0)
3774 *dst++ = 0x20;
3775 } else {
3776 *dst++ = *src++;
3777 }
3778 }
3779 *dst = 0;
3780 return(ret);
3781}
3782
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003783static void
Owen Taylor3473f882001-02-23 17:55:21 +00003784xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003785 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003786 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3787}
3788
3789/**
3790 * xmlValidateAttributeDecl:
3791 * @ctxt: the validation context
3792 * @doc: a document instance
3793 * @attr: an attribute definition
3794 *
3795 * Try to validate a single attribute definition
3796 * basically it does the following checks as described by the
3797 * XML-1.0 recommendation:
3798 * - [ VC: Attribute Default Legal ]
3799 * - [ VC: Enumeration ]
3800 * - [ VC: ID Attribute Default ]
3801 *
3802 * The ID/IDREF uniqueness and matching are done separately
3803 *
3804 * returns 1 if valid or 0 otherwise
3805 */
3806
3807int
3808xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3809 xmlAttributePtr attr) {
3810 int ret = 1;
3811 int val;
3812 CHECK_DTD;
3813 if(attr == NULL) return(1);
3814
3815 /* Attribute Default Legal */
3816 /* Enumeration */
3817 if (attr->defaultValue != NULL) {
3818 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3819 if (val == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003820 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003821 "Syntax of default value for attribute %s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003822 attr->name, attr->elem, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003823 }
3824 ret &= val;
3825 }
3826
3827 /* ID Attribute Default */
3828 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3829 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3830 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003831 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_FIXED,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003832 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003833 attr->name, attr->elem, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003834 ret = 0;
3835 }
3836
3837 /* One ID per Element Type */
3838 if (attr->atype == XML_ATTRIBUTE_ID) {
3839 int nbId;
3840
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003841 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003842 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3843 attr->elem);
3844 if (elem != NULL) {
3845 nbId = xmlScanIDAttributeDecl(NULL, elem);
3846 } else {
3847 xmlAttributeTablePtr table;
3848
3849 /*
3850 * The attribute may be declared in the internal subset and the
3851 * element in the external subset.
3852 */
3853 nbId = 0;
3854 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3855 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3856 xmlValidateAttributeIdCallback, &nbId);
3857 }
3858 if (nbId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003859
3860 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003861 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3862 attr->elem, nbId, attr->name);
3863 } else if (doc->extSubset != NULL) {
3864 int extId = 0;
3865 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3866 if (elem != NULL) {
3867 extId = xmlScanIDAttributeDecl(NULL, elem);
3868 }
3869 if (extId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003870 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003871 "Element %s has %d ID attribute defined in the external subset : %s\n",
3872 attr->elem, extId, attr->name);
3873 } else if (extId + nbId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003874 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003875"Element %s has ID attributes defined in the internal and external subset : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003876 attr->elem, attr->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003877 }
3878 }
3879 }
3880
3881 /* Validity Constraint: Enumeration */
3882 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3883 xmlEnumerationPtr tree = attr->tree;
3884 while (tree != NULL) {
3885 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3886 tree = tree->next;
3887 }
3888 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003889 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003890"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003891 attr->defaultValue, attr->name, attr->elem);
3892 ret = 0;
3893 }
3894 }
3895
3896 return(ret);
3897}
3898
3899/**
3900 * xmlValidateElementDecl:
3901 * @ctxt: the validation context
3902 * @doc: a document instance
3903 * @elem: an element definition
3904 *
3905 * Try to validate a single element definition
3906 * basically it does the following checks as described by the
3907 * XML-1.0 recommendation:
3908 * - [ VC: One ID per Element Type ]
3909 * - [ VC: No Duplicate Types ]
3910 * - [ VC: Unique Element Type Declaration ]
3911 *
3912 * returns 1 if valid or 0 otherwise
3913 */
3914
3915int
3916xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3917 xmlElementPtr elem) {
3918 int ret = 1;
3919 xmlElementPtr tst;
3920
3921 CHECK_DTD;
3922
3923 if (elem == NULL) return(1);
3924
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003925#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003926#ifdef LIBXML_REGEXP_ENABLED
3927 /* Build the regexp associated to the content model */
3928 ret = xmlValidBuildContentModel(ctxt, elem);
3929#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003930#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003931
Owen Taylor3473f882001-02-23 17:55:21 +00003932 /* No Duplicate Types */
3933 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3934 xmlElementContentPtr cur, next;
3935 const xmlChar *name;
3936
3937 cur = elem->content;
3938 while (cur != NULL) {
3939 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3940 if (cur->c1 == NULL) break;
3941 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3942 name = cur->c1->name;
3943 next = cur->c2;
3944 while (next != NULL) {
3945 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard7b68df92003-08-03 22:58:54 +00003946 if ((xmlStrEqual(next->name, name)) &&
3947 (xmlStrEqual(next->prefix, cur->prefix))) {
3948 if (cur->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003949 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00003950 "Definition of %s has duplicate references of %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003951 elem->name, name, NULL);
Daniel Veillard7b68df92003-08-03 22:58:54 +00003952 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003953 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00003954 "Definition of %s has duplicate references of %s:%s\n",
3955 elem->name, cur->prefix, name);
3956 }
Owen Taylor3473f882001-02-23 17:55:21 +00003957 ret = 0;
3958 }
3959 break;
3960 }
3961 if (next->c1 == NULL) break;
3962 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
Daniel Veillard7b68df92003-08-03 22:58:54 +00003963 if ((xmlStrEqual(next->c1->name, name)) &&
3964 (xmlStrEqual(next->c1->prefix, cur->prefix))) {
3965 if (cur->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003966 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00003967 "Definition of %s has duplicate references to %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003968 elem->name, name, NULL);
Daniel Veillard7b68df92003-08-03 22:58:54 +00003969 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003970 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00003971 "Definition of %s has duplicate references to %s:%s\n",
3972 elem->name, cur->prefix, name);
3973 }
Owen Taylor3473f882001-02-23 17:55:21 +00003974 ret = 0;
3975 }
3976 next = next->c2;
3977 }
3978 }
3979 cur = cur->c2;
3980 }
3981 }
3982
3983 /* VC: Unique Element Type Declaration */
3984 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003985 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003986 ((tst->prefix == elem->prefix) ||
3987 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003988 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003989 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
3990 "Redefinition of element %s\n",
3991 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003992 ret = 0;
3993 }
3994 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003995 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003996 ((tst->prefix == elem->prefix) ||
3997 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003998 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003999 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
4000 "Redefinition of element %s\n",
4001 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004002 ret = 0;
4003 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00004004 /* One ID per Element Type
4005 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00004006 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
4007 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00004008 } */
Owen Taylor3473f882001-02-23 17:55:21 +00004009 return(ret);
4010}
4011
4012/**
4013 * xmlValidateOneAttribute:
4014 * @ctxt: the validation context
4015 * @doc: a document instance
4016 * @elem: an element instance
4017 * @attr: an attribute instance
4018 * @value: the attribute value (without entities processing)
4019 *
4020 * Try to validate a single attribute for an element
4021 * basically it does the following checks as described by the
4022 * XML-1.0 recommendation:
4023 * - [ VC: Attribute Value Type ]
4024 * - [ VC: Fixed Attribute Default ]
4025 * - [ VC: Entity Name ]
4026 * - [ VC: Name Token ]
4027 * - [ VC: ID ]
4028 * - [ VC: IDREF ]
4029 * - [ VC: Entity Name ]
4030 * - [ VC: Notation Attributes ]
4031 *
4032 * The ID/IDREF uniqueness and matching are done separately
4033 *
4034 * returns 1 if valid or 0 otherwise
4035 */
4036
4037int
4038xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
Daniel Veillard07cb8222003-09-10 10:51:05 +00004039 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value)
4040{
Owen Taylor3473f882001-02-23 17:55:21 +00004041 xmlAttributePtr attrDecl = NULL;
4042 int val;
4043 int ret = 1;
4044
4045 CHECK_DTD;
4046 if ((elem == NULL) || (elem->name == NULL)) return(0);
4047 if ((attr == NULL) || (attr->name == NULL)) return(0);
4048
4049 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004050 xmlChar fn[50];
4051 xmlChar *fullname;
4052
4053 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
4054 if (fullname == NULL)
4055 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004056 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004057 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00004058 attr->name, attr->ns->prefix);
4059 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004060 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00004061 attr->name, attr->ns->prefix);
4062 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004063 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004064 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4065 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00004066 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004067 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004068 if ((fullname != fn) && (fullname != elem->name))
4069 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00004070 }
4071 if (attrDecl == NULL) {
4072 if (attr->ns != NULL) {
4073 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4074 attr->name, attr->ns->prefix);
4075 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4076 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4077 attr->name, attr->ns->prefix);
4078 } else {
4079 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4080 elem->name, attr->name);
4081 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4082 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4083 elem->name, attr->name);
4084 }
4085 }
4086
4087
4088 /* Validity Constraint: Attribute Value Type */
4089 if (attrDecl == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004090 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004091 "No declaration for attribute %s of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004092 attr->name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004093 return(0);
4094 }
4095 attr->atype = attrDecl->atype;
4096
4097 val = xmlValidateAttributeValue(attrDecl->atype, value);
4098 if (val == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004099 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004100 "Syntax of value for attribute %s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004101 attr->name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004102 ret = 0;
4103 }
4104
4105 /* Validity constraint: Fixed Attribute Default */
4106 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4107 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004108 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004109 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004110 attr->name, elem->name, attrDecl->defaultValue);
4111 ret = 0;
4112 }
4113 }
4114
4115 /* Validity Constraint: ID uniqueness */
4116 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4117 if (xmlAddID(ctxt, doc, value, attr) == NULL)
4118 ret = 0;
4119 }
4120
4121 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4122 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4123 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
4124 ret = 0;
4125 }
4126
4127 /* Validity Constraint: Notation Attributes */
4128 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4129 xmlEnumerationPtr tree = attrDecl->tree;
4130 xmlNotationPtr nota;
4131
4132 /* First check that the given NOTATION was declared */
4133 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4134 if (nota == NULL)
4135 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4136
4137 if (nota == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004138 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004139 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004140 value, attr->name, elem->name);
4141 ret = 0;
4142 }
4143
4144 /* Second, verify that it's among the list */
4145 while (tree != NULL) {
4146 if (xmlStrEqual(tree->name, value)) break;
4147 tree = tree->next;
4148 }
4149 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004150 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004151"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004152 value, attr->name, elem->name);
4153 ret = 0;
4154 }
4155 }
4156
4157 /* Validity Constraint: Enumeration */
4158 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4159 xmlEnumerationPtr tree = attrDecl->tree;
4160 while (tree != NULL) {
4161 if (xmlStrEqual(tree->name, value)) break;
4162 tree = tree->next;
4163 }
4164 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004165 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004166 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004167 value, attr->name, elem->name);
4168 ret = 0;
4169 }
4170 }
4171
4172 /* Fixed Attribute Default */
4173 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4174 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004175 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004176 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004177 attr->name, elem->name, attrDecl->defaultValue);
4178 ret = 0;
4179 }
4180
4181 /* Extra check for the attribute value */
4182 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
4183 attrDecl->atype, value);
4184
4185 return(ret);
4186}
4187
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004188/**
4189 * xmlValidateOneNamespace:
4190 * @ctxt: the validation context
4191 * @doc: a document instance
4192 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00004193 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004194 * @ns: an namespace declaration instance
4195 * @value: the attribute value (without entities processing)
4196 *
4197 * Try to validate a single namespace declaration for an element
4198 * basically it does the following checks as described by the
4199 * XML-1.0 recommendation:
4200 * - [ VC: Attribute Value Type ]
4201 * - [ VC: Fixed Attribute Default ]
4202 * - [ VC: Entity Name ]
4203 * - [ VC: Name Token ]
4204 * - [ VC: ID ]
4205 * - [ VC: IDREF ]
4206 * - [ VC: Entity Name ]
4207 * - [ VC: Notation Attributes ]
4208 *
4209 * The ID/IDREF uniqueness and matching are done separately
4210 *
4211 * returns 1 if valid or 0 otherwise
4212 */
4213
4214int
4215xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4216xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
4217 /* xmlElementPtr elemDecl; */
4218 xmlAttributePtr attrDecl = NULL;
4219 int val;
4220 int ret = 1;
4221
4222 CHECK_DTD;
4223 if ((elem == NULL) || (elem->name == NULL)) return(0);
4224 if ((ns == NULL) || (ns->href == NULL)) return(0);
4225
4226 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004227 xmlChar fn[50];
4228 xmlChar *fullname;
4229
4230 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
4231 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00004232 xmlVErrMemory(ctxt, "Validating namespace");
Daniel Veillardc00cda82003-04-07 10:22:39 +00004233 return(0);
4234 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004235 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004236 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004237 ns->prefix, BAD_CAST "xmlns");
4238 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004239 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004240 ns->prefix, BAD_CAST "xmlns");
4241 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004242 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004243 BAD_CAST "xmlns");
4244 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004245 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004246 BAD_CAST "xmlns");
4247 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004248 if ((fullname != fn) && (fullname != elem->name))
4249 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004250 }
4251 if (attrDecl == NULL) {
4252 if (ns->prefix != NULL) {
4253 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4254 ns->prefix, BAD_CAST "xmlns");
4255 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4256 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4257 ns->prefix, BAD_CAST "xmlns");
4258 } else {
4259 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4260 elem->name, BAD_CAST "xmlns");
4261 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4262 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4263 elem->name, BAD_CAST "xmlns");
4264 }
4265 }
4266
4267
4268 /* Validity Constraint: Attribute Value Type */
4269 if (attrDecl == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004270 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004271 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004272 "No declaration for attribute xmlns:%s of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004273 ns->prefix, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004274 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004275 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004276 "No declaration for attribute xmlns of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004277 elem->name, NULL, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004278 }
4279 return(0);
4280 }
4281
4282 val = xmlValidateAttributeValue(attrDecl->atype, value);
4283 if (val == 0) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004284 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004285 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004286 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004287 ns->prefix, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004288 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004289 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004290 "Syntax of value for attribute xmlns of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004291 elem->name, NULL, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004292 }
4293 ret = 0;
4294 }
4295
4296 /* Validity constraint: Fixed Attribute Default */
4297 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4298 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004299 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004300 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004301 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4302 ns->prefix, elem->name, attrDecl->defaultValue);
4303 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004304 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004305 "Value for attribute xmlns of %s is different from default \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004306 elem->name, attrDecl->defaultValue, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004307 }
4308 ret = 0;
4309 }
4310 }
4311
4312 /* Validity Constraint: ID uniqueness */
4313 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4314 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4315 ret = 0;
4316 }
4317
4318 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4319 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4320 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4321 ret = 0;
4322 }
4323
4324 /* Validity Constraint: Notation Attributes */
4325 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4326 xmlEnumerationPtr tree = attrDecl->tree;
4327 xmlNotationPtr nota;
4328
4329 /* First check that the given NOTATION was declared */
4330 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4331 if (nota == NULL)
4332 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4333
4334 if (nota == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004335 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004336 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004337 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4338 value, ns->prefix, elem->name);
4339 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004340 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004341 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004342 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004343 }
4344 ret = 0;
4345 }
4346
4347 /* Second, verify that it's among the list */
4348 while (tree != NULL) {
4349 if (xmlStrEqual(tree->name, value)) break;
4350 tree = tree->next;
4351 }
4352 if (tree == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004353 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004354 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004355"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4356 value, ns->prefix, elem->name);
4357 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004358 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004359"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004360 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004361 }
4362 ret = 0;
4363 }
4364 }
4365
4366 /* Validity Constraint: Enumeration */
4367 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4368 xmlEnumerationPtr tree = attrDecl->tree;
4369 while (tree != NULL) {
4370 if (xmlStrEqual(tree->name, value)) break;
4371 tree = tree->next;
4372 }
4373 if (tree == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004374 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004375 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004376"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4377 value, ns->prefix, elem->name);
4378 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004379 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004380"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004381 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004382 }
4383 ret = 0;
4384 }
4385 }
4386
4387 /* Fixed Attribute Default */
4388 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4389 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004390 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004391 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004392 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4393 ns->prefix, elem->name, attrDecl->defaultValue);
4394 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004395 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004396 "Value for attribute xmlns of %s must be \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004397 elem->name, attrDecl->defaultValue, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004398 }
4399 ret = 0;
4400 }
4401
4402 /* Extra check for the attribute value */
4403 if (ns->prefix != NULL) {
4404 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4405 attrDecl->atype, value);
4406 } else {
4407 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4408 attrDecl->atype, value);
4409 }
4410
4411 return(ret);
4412}
4413
Daniel Veillard118aed72002-09-24 14:13:13 +00004414#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004415/**
4416 * xmlValidateSkipIgnorable:
4417 * @ctxt: the validation context
4418 * @child: the child list
4419 *
4420 * Skip ignorable elements w.r.t. the validation process
4421 *
4422 * returns the first element to consider for validation of the content model
4423 */
4424
4425static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004426xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004427 while (child != NULL) {
4428 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004429 /* These things are ignored (skipped) during validation. */
4430 case XML_PI_NODE:
4431 case XML_COMMENT_NODE:
4432 case XML_XINCLUDE_START:
4433 case XML_XINCLUDE_END:
4434 child = child->next;
4435 break;
4436 case XML_TEXT_NODE:
4437 if (xmlIsBlankNode(child))
4438 child = child->next;
4439 else
4440 return(child);
4441 break;
4442 /* keep current node */
4443 default:
4444 return(child);
4445 }
4446 }
4447 return(child);
4448}
4449
4450/**
4451 * xmlValidateElementType:
4452 * @ctxt: the validation context
4453 *
4454 * Try to validate the content model of an element internal function
4455 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004456 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4457 * reference is found and -3 if the validation succeeded but
4458 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004459 */
4460
4461static int
4462xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004463 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004464 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004465
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004466 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004467 if ((NODE == NULL) && (CONT == NULL))
4468 return(1);
4469 if ((NODE == NULL) &&
4470 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4471 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4472 return(1);
4473 }
4474 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004475 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004476 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004477
4478 /*
4479 * We arrive here when more states need to be examined
4480 */
4481cont:
4482
4483 /*
4484 * We just recovered from a rollback generated by a possible
4485 * epsilon transition, go directly to the analysis phase
4486 */
4487 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004488 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004489 DEBUG_VALID_STATE(NODE, CONT)
4490 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004491 goto analyze;
4492 }
4493
4494 DEBUG_VALID_STATE(NODE, CONT)
4495 /*
4496 * we may have to save a backup state here. This is the equivalent
4497 * of handling epsilon transition in NFAs.
4498 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004499 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004500 ((CONT->parent == NULL) ||
4501 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004502 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004503 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004504 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004505 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004506 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4507 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004508 }
4509
4510
4511 /*
4512 * Check first if the content matches
4513 */
4514 switch (CONT->type) {
4515 case XML_ELEMENT_CONTENT_PCDATA:
4516 if (NODE == NULL) {
4517 DEBUG_VALID_MSG("pcdata failed no node");
4518 ret = 0;
4519 break;
4520 }
4521 if (NODE->type == XML_TEXT_NODE) {
4522 DEBUG_VALID_MSG("pcdata found, skip to next");
4523 /*
4524 * go to next element in the content model
4525 * skipping ignorable elems
4526 */
4527 do {
4528 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004529 NODE = xmlValidateSkipIgnorable(NODE);
4530 if ((NODE != NULL) &&
4531 (NODE->type == XML_ENTITY_REF_NODE))
4532 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004533 } while ((NODE != NULL) &&
4534 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004535 (NODE->type != XML_TEXT_NODE) &&
4536 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004537 ret = 1;
4538 break;
4539 } else {
4540 DEBUG_VALID_MSG("pcdata failed");
4541 ret = 0;
4542 break;
4543 }
4544 break;
4545 case XML_ELEMENT_CONTENT_ELEMENT:
4546 if (NODE == NULL) {
4547 DEBUG_VALID_MSG("element failed no node");
4548 ret = 0;
4549 break;
4550 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004551 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4552 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004553 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004554 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4555 ret = (CONT->prefix == NULL);
4556 } else if (CONT->prefix == NULL) {
4557 ret = 0;
4558 } else {
4559 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4560 }
4561 }
4562 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004563 DEBUG_VALID_MSG("element found, skip to next");
4564 /*
4565 * go to next element in the content model
4566 * skipping ignorable elems
4567 */
4568 do {
4569 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004570 NODE = xmlValidateSkipIgnorable(NODE);
4571 if ((NODE != NULL) &&
4572 (NODE->type == XML_ENTITY_REF_NODE))
4573 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004574 } while ((NODE != NULL) &&
4575 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004576 (NODE->type != XML_TEXT_NODE) &&
4577 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004578 } else {
4579 DEBUG_VALID_MSG("element failed");
4580 ret = 0;
4581 break;
4582 }
4583 break;
4584 case XML_ELEMENT_CONTENT_OR:
4585 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004586 * Small optimization.
4587 */
4588 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4589 if ((NODE == NULL) ||
4590 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4591 DEPTH++;
4592 CONT = CONT->c2;
4593 goto cont;
4594 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004595 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4596 ret = (CONT->c1->prefix == NULL);
4597 } else if (CONT->c1->prefix == NULL) {
4598 ret = 0;
4599 } else {
4600 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4601 }
4602 if (ret == 0) {
4603 DEPTH++;
4604 CONT = CONT->c2;
4605 goto cont;
4606 }
Daniel Veillard85349052001-04-20 13:48:21 +00004607 }
4608
4609 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004610 * save the second branch 'or' branch
4611 */
4612 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004613 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4614 OCCURS, ROLLBACK_OR) < 0)
4615 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004616 DEPTH++;
4617 CONT = CONT->c1;
4618 goto cont;
4619 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004620 /*
4621 * Small optimization.
4622 */
4623 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4624 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4625 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4626 if ((NODE == NULL) ||
4627 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4628 DEPTH++;
4629 CONT = CONT->c2;
4630 goto cont;
4631 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004632 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4633 ret = (CONT->c1->prefix == NULL);
4634 } else if (CONT->c1->prefix == NULL) {
4635 ret = 0;
4636 } else {
4637 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4638 }
4639 if (ret == 0) {
4640 DEPTH++;
4641 CONT = CONT->c2;
4642 goto cont;
4643 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004644 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004645 DEPTH++;
4646 CONT = CONT->c1;
4647 goto cont;
4648 }
4649
4650 /*
4651 * At this point handle going up in the tree
4652 */
4653 if (ret == -1) {
4654 DEBUG_VALID_MSG("error found returning");
4655 return(ret);
4656 }
4657analyze:
4658 while (CONT != NULL) {
4659 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004660 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004661 * this level.
4662 */
4663 if (ret == 0) {
4664 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004665 xmlNodePtr cur;
4666
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004667 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004668 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004669 DEBUG_VALID_MSG("Once branch failed, rollback");
4670 if (vstateVPop(ctxt) < 0 ) {
4671 DEBUG_VALID_MSG("exhaustion, failed");
4672 return(0);
4673 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004674 if (cur != ctxt->vstate->node)
4675 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004676 goto cont;
4677 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004678 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004679 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004680 DEBUG_VALID_MSG("Plus branch failed, rollback");
4681 if (vstateVPop(ctxt) < 0 ) {
4682 DEBUG_VALID_MSG("exhaustion, failed");
4683 return(0);
4684 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004685 if (cur != ctxt->vstate->node)
4686 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004687 goto cont;
4688 }
4689 DEBUG_VALID_MSG("Plus branch found");
4690 ret = 1;
4691 break;
4692 case XML_ELEMENT_CONTENT_MULT:
4693#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004694 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004695 DEBUG_VALID_MSG("Mult branch failed");
4696 } else {
4697 DEBUG_VALID_MSG("Mult branch found");
4698 }
4699#endif
4700 ret = 1;
4701 break;
4702 case XML_ELEMENT_CONTENT_OPT:
4703 DEBUG_VALID_MSG("Option branch failed");
4704 ret = 1;
4705 break;
4706 }
4707 } else {
4708 switch (CONT->ocur) {
4709 case XML_ELEMENT_CONTENT_OPT:
4710 DEBUG_VALID_MSG("Option branch succeeded");
4711 ret = 1;
4712 break;
4713 case XML_ELEMENT_CONTENT_ONCE:
4714 DEBUG_VALID_MSG("Once branch succeeded");
4715 ret = 1;
4716 break;
4717 case XML_ELEMENT_CONTENT_PLUS:
4718 if (STATE == ROLLBACK_PARENT) {
4719 DEBUG_VALID_MSG("Plus branch rollback");
4720 ret = 1;
4721 break;
4722 }
4723 if (NODE == NULL) {
4724 DEBUG_VALID_MSG("Plus branch exhausted");
4725 ret = 1;
4726 break;
4727 }
4728 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004729 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004730 goto cont;
4731 case XML_ELEMENT_CONTENT_MULT:
4732 if (STATE == ROLLBACK_PARENT) {
4733 DEBUG_VALID_MSG("Mult branch rollback");
4734 ret = 1;
4735 break;
4736 }
4737 if (NODE == NULL) {
4738 DEBUG_VALID_MSG("Mult branch exhausted");
4739 ret = 1;
4740 break;
4741 }
4742 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004743 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004744 goto cont;
4745 }
4746 }
4747 STATE = 0;
4748
4749 /*
4750 * Then act accordingly at the parent level
4751 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004752 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004753 if (CONT->parent == NULL)
4754 break;
4755
4756 switch (CONT->parent->type) {
4757 case XML_ELEMENT_CONTENT_PCDATA:
4758 DEBUG_VALID_MSG("Error: parent pcdata");
4759 return(-1);
4760 case XML_ELEMENT_CONTENT_ELEMENT:
4761 DEBUG_VALID_MSG("Error: parent element");
4762 return(-1);
4763 case XML_ELEMENT_CONTENT_OR:
4764 if (ret == 1) {
4765 DEBUG_VALID_MSG("Or succeeded");
4766 CONT = CONT->parent;
4767 DEPTH--;
4768 } else {
4769 DEBUG_VALID_MSG("Or failed");
4770 CONT = CONT->parent;
4771 DEPTH--;
4772 }
4773 break;
4774 case XML_ELEMENT_CONTENT_SEQ:
4775 if (ret == 0) {
4776 DEBUG_VALID_MSG("Sequence failed");
4777 CONT = CONT->parent;
4778 DEPTH--;
4779 } else if (CONT == CONT->parent->c1) {
4780 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4781 CONT = CONT->parent->c2;
4782 goto cont;
4783 } else {
4784 DEBUG_VALID_MSG("Sequence succeeded");
4785 CONT = CONT->parent;
4786 DEPTH--;
4787 }
4788 }
4789 }
4790 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004791 xmlNodePtr cur;
4792
4793 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004794 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4795 if (vstateVPop(ctxt) < 0 ) {
4796 DEBUG_VALID_MSG("exhaustion, failed");
4797 return(0);
4798 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004799 if (cur != ctxt->vstate->node)
4800 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004801 goto cont;
4802 }
4803 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004804 xmlNodePtr cur;
4805
4806 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004807 DEBUG_VALID_MSG("Failure, rollback");
4808 if (vstateVPop(ctxt) < 0 ) {
4809 DEBUG_VALID_MSG("exhaustion, failed");
4810 return(0);
4811 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004812 if (cur != ctxt->vstate->node)
4813 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004814 goto cont;
4815 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004816 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004817}
Daniel Veillard23e73572002-09-19 19:56:43 +00004818#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004819
4820/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004821 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004822 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004823 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004824 * @content: An element
4825 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4826 *
4827 * This will dump the list of elements to the buffer
4828 * Intended just for the debug routine
4829 */
4830static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004831xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004832 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004833 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004834
4835 if (node == NULL) return;
4836 if (glob) strcat(buf, "(");
4837 cur = node;
4838 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004839 len = strlen(buf);
4840 if (size - len < 50) {
4841 if ((size - len > 4) && (buf[len - 1] != '.'))
4842 strcat(buf, " ...");
4843 return;
4844 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004845 switch (cur->type) {
4846 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004847 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004848 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004849 if ((size - len > 4) && (buf[len - 1] != '.'))
4850 strcat(buf, " ...");
4851 return;
4852 }
4853 strcat(buf, (char *) cur->ns->prefix);
4854 strcat(buf, ":");
4855 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004856 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004857 if ((size - len > 4) && (buf[len - 1] != '.'))
4858 strcat(buf, " ...");
4859 return;
4860 }
4861 strcat(buf, (char *) cur->name);
4862 if (cur->next != NULL)
4863 strcat(buf, " ");
4864 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004865 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004866 if (xmlIsBlankNode(cur))
4867 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004868 case XML_CDATA_SECTION_NODE:
4869 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004870 strcat(buf, "CDATA");
4871 if (cur->next != NULL)
4872 strcat(buf, " ");
4873 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004874 case XML_ATTRIBUTE_NODE:
4875 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004876#ifdef LIBXML_DOCB_ENABLED
4877 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004878#endif
4879 case XML_HTML_DOCUMENT_NODE:
4880 case XML_DOCUMENT_TYPE_NODE:
4881 case XML_DOCUMENT_FRAG_NODE:
4882 case XML_NOTATION_NODE:
4883 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004884 strcat(buf, "???");
4885 if (cur->next != NULL)
4886 strcat(buf, " ");
4887 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004888 case XML_ENTITY_NODE:
4889 case XML_PI_NODE:
4890 case XML_DTD_NODE:
4891 case XML_COMMENT_NODE:
4892 case XML_ELEMENT_DECL:
4893 case XML_ATTRIBUTE_DECL:
4894 case XML_ENTITY_DECL:
4895 case XML_XINCLUDE_START:
4896 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004897 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004898 }
4899 cur = cur->next;
4900 }
4901 if (glob) strcat(buf, ")");
4902}
4903
4904/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004905 * xmlValidateElementContent:
4906 * @ctxt: the validation context
4907 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004908 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004909 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004910 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004911 *
4912 * Try to validate the content model of an element
4913 *
4914 * returns 1 if valid or 0 if not and -1 in case of error
4915 */
4916
4917static int
4918xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004919 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004920 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004921#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004922 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004923#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004924 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004925 xmlElementContentPtr cont;
4926 const xmlChar *name;
4927
4928 if (elemDecl == NULL)
4929 return(-1);
4930 cont = elemDecl->content;
4931 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004932
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004933#ifdef LIBXML_REGEXP_ENABLED
4934 /* Build the regexp associated to the content model */
4935 if (elemDecl->contModel == NULL)
4936 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4937 if (elemDecl->contModel == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004938 return(-1);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004939 } else {
4940 xmlRegExecCtxtPtr exec;
4941
Daniel Veillardec498e12003-02-05 11:01:50 +00004942 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
4943 return(-1);
4944 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004945 ctxt->nodeMax = 0;
4946 ctxt->nodeNr = 0;
4947 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004948 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4949 if (exec != NULL) {
4950 cur = child;
4951 while (cur != NULL) {
4952 switch (cur->type) {
4953 case XML_ENTITY_REF_NODE:
4954 /*
4955 * Push the current node to be able to roll back
4956 * and process within the entity
4957 */
4958 if ((cur->children != NULL) &&
4959 (cur->children->children != NULL)) {
4960 nodeVPush(ctxt, cur);
4961 cur = cur->children->children;
4962 continue;
4963 }
4964 break;
4965 case XML_TEXT_NODE:
4966 if (xmlIsBlankNode(cur))
4967 break;
4968 ret = 0;
4969 goto fail;
4970 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004971 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004972 ret = 0;
4973 goto fail;
4974 case XML_ELEMENT_NODE:
4975 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004976 xmlChar fn[50];
4977 xmlChar *fullname;
4978
4979 fullname = xmlBuildQName(cur->name,
4980 cur->ns->prefix, fn, 50);
4981 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004982 ret = -1;
4983 goto fail;
4984 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004985 ret = xmlRegExecPushString(exec, fullname, NULL);
4986 if ((fullname != fn) && (fullname != cur->name))
4987 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004988 } else {
4989 ret = xmlRegExecPushString(exec, cur->name, NULL);
4990 }
4991 break;
4992 default:
4993 break;
4994 }
4995 /*
4996 * Switch to next element
4997 */
4998 cur = cur->next;
4999 while (cur == NULL) {
5000 cur = nodeVPop(ctxt);
5001 if (cur == NULL)
5002 break;
5003 cur = cur->next;
5004 }
5005 }
5006 ret = xmlRegExecPushString(exec, NULL, NULL);
5007fail:
5008 xmlRegFreeExecCtxt(exec);
5009 }
5010 }
5011#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005012 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005013 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005014 */
5015 ctxt->vstateMax = 8;
5016 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
5017 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
5018 if (ctxt->vstateTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00005019 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005020 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005021 }
5022 /*
5023 * The first entry in the stack is reserved to the current state
5024 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00005025 ctxt->nodeMax = 0;
5026 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00005027 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005028 ctxt->vstate = &ctxt->vstateTab[0];
5029 ctxt->vstateNr = 1;
5030 CONT = cont;
5031 NODE = child;
5032 DEPTH = 0;
5033 OCCURS = 0;
5034 STATE = 0;
5035 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005036 if ((ret == -3) && (warn)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005037 xmlErrValidWarning(ctxt, child, XML_DTD_CONTENT_NOT_DETERMINIST,
5038 "Content model for Element %s is ambiguous\n",
Daniel Veillard0cc72772003-10-13 14:00:21 +00005039 name, NULL, NULL);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005040 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005041 /*
5042 * An entities reference appeared at this level.
5043 * Buid a minimal representation of this node content
5044 * sufficient to run the validation process on it
5045 */
5046 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005047 cur = child;
5048 while (cur != NULL) {
5049 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005050 case XML_ENTITY_REF_NODE:
5051 /*
5052 * Push the current node to be able to roll back
5053 * and process within the entity
5054 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005055 if ((cur->children != NULL) &&
5056 (cur->children->children != NULL)) {
5057 nodeVPush(ctxt, cur);
5058 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005059 continue;
5060 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00005061 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005062 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005063 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005064 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005065 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00005066 case XML_CDATA_SECTION_NODE:
5067 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005068 case XML_ELEMENT_NODE:
5069 /*
5070 * Allocate a new node and minimally fills in
5071 * what's required
5072 */
5073 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
5074 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00005075 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005076 xmlFreeNodeList(repl);
5077 ret = -1;
5078 goto done;
5079 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005080 tmp->type = cur->type;
5081 tmp->name = cur->name;
5082 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005083 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00005084 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005085 if (repl == NULL)
5086 repl = last = tmp;
5087 else {
5088 last->next = tmp;
5089 last = tmp;
5090 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00005091 if (cur->type == XML_CDATA_SECTION_NODE) {
5092 /*
5093 * E59 spaces in CDATA does not match the
5094 * nonterminal S
5095 */
5096 tmp->content = xmlStrdup(BAD_CAST "CDATA");
5097 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005098 break;
5099 default:
5100 break;
5101 }
5102 /*
5103 * Switch to next element
5104 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005105 cur = cur->next;
5106 while (cur == NULL) {
5107 cur = nodeVPop(ctxt);
5108 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005109 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005110 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005111 }
5112 }
5113
5114 /*
5115 * Relaunch the validation
5116 */
5117 ctxt->vstate = &ctxt->vstateTab[0];
5118 ctxt->vstateNr = 1;
5119 CONT = cont;
5120 NODE = repl;
5121 DEPTH = 0;
5122 OCCURS = 0;
5123 STATE = 0;
5124 ret = xmlValidateElementType(ctxt);
5125 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005126#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005127 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005128 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
5129 char expr[5000];
5130 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005131
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005132 expr[0] = 0;
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005133 xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005134 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00005135#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005136 if (repl != NULL)
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005137 xmlSnprintfElements(&list[0], 5000, repl, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005138 else
Daniel Veillard01992e02002-10-09 10:20:30 +00005139#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005140 xmlSnprintfElements(&list[0], 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005141
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005142 if (name != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005143 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5144 "Element %s content does not follow the DTD, expecting %s, got %s\n",
5145 name, BAD_CAST expr, BAD_CAST list);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005146 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005147 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5148 "Element content does not follow the DTD, expecting %s, got %s\n",
5149 BAD_CAST expr, BAD_CAST list, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005150 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005151 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005152 if (name != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005153 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005154 "Element %s content does not follow the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005155 name, NULL, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005156 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005157 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5158 "Element content does not follow the DTD\n",
5159 NULL, NULL, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005160 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005161 }
5162 ret = 0;
5163 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005164 if (ret == -3)
5165 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005166
Daniel Veillard23e73572002-09-19 19:56:43 +00005167#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005168done:
5169 /*
5170 * Deallocate the copy if done, and free up the validation stack
5171 */
5172 while (repl != NULL) {
5173 tmp = repl->next;
5174 xmlFree(repl);
5175 repl = tmp;
5176 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005177 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005178 if (ctxt->vstateTab != NULL) {
5179 xmlFree(ctxt->vstateTab);
5180 ctxt->vstateTab = NULL;
5181 }
Daniel Veillard01992e02002-10-09 10:20:30 +00005182#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005183 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00005184 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005185 if (ctxt->nodeTab != NULL) {
5186 xmlFree(ctxt->nodeTab);
5187 ctxt->nodeTab = NULL;
5188 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005189 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005190
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005191}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005192
Owen Taylor3473f882001-02-23 17:55:21 +00005193/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005194 * xmlValidateCdataElement:
5195 * @ctxt: the validation context
5196 * @doc: a document instance
5197 * @elem: an element instance
5198 *
5199 * Check that an element follows #CDATA
5200 *
5201 * returns 1 if valid or 0 otherwise
5202 */
5203static int
5204xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5205 xmlNodePtr elem) {
5206 int ret = 1;
5207 xmlNodePtr cur, child;
5208
Daniel Veillardceb09b92002-10-04 11:46:37 +00005209 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005210 return(0);
5211
5212 child = elem->children;
5213
5214 cur = child;
5215 while (cur != NULL) {
5216 switch (cur->type) {
5217 case XML_ENTITY_REF_NODE:
5218 /*
5219 * Push the current node to be able to roll back
5220 * and process within the entity
5221 */
5222 if ((cur->children != NULL) &&
5223 (cur->children->children != NULL)) {
5224 nodeVPush(ctxt, cur);
5225 cur = cur->children->children;
5226 continue;
5227 }
5228 break;
5229 case XML_COMMENT_NODE:
5230 case XML_PI_NODE:
5231 case XML_TEXT_NODE:
5232 case XML_CDATA_SECTION_NODE:
5233 break;
5234 default:
5235 ret = 0;
5236 goto done;
5237 }
5238 /*
5239 * Switch to next element
5240 */
5241 cur = cur->next;
5242 while (cur == NULL) {
5243 cur = nodeVPop(ctxt);
5244 if (cur == NULL)
5245 break;
5246 cur = cur->next;
5247 }
5248 }
5249done:
5250 ctxt->nodeMax = 0;
5251 ctxt->nodeNr = 0;
5252 if (ctxt->nodeTab != NULL) {
5253 xmlFree(ctxt->nodeTab);
5254 ctxt->nodeTab = NULL;
5255 }
5256 return(ret);
5257}
5258
5259/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005260 * xmlValidateCheckMixed:
5261 * @ctxt: the validation context
5262 * @cont: the mixed content model
5263 * @qname: the qualified name as appearing in the serialization
5264 *
5265 * Check if the given node is part of the content model.
5266 *
5267 * Returns 1 if yes, 0 if no, -1 in case of error
5268 */
5269static int
5270xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5271 xmlElementContentPtr cont, const xmlChar *qname) {
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005272 const xmlChar *name;
5273 int plen;
5274 name = xmlSplitQName3(qname, &plen);
5275
5276 if (name == NULL) {
5277 while (cont != NULL) {
5278 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5279 if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname)))
5280 return(1);
5281 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5282 (cont->c1 != NULL) &&
5283 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5284 if ((cont->c1->prefix == NULL) &&
5285 (xmlStrEqual(cont->c1->name, qname)))
5286 return(1);
5287 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5288 (cont->c1 == NULL) ||
5289 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005290 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5291 "Internal: MIXED struct corrupted\n",
5292 NULL);
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005293 break;
5294 }
5295 cont = cont->c2;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005296 }
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005297 } else {
5298 while (cont != NULL) {
5299 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5300 if ((cont->prefix != NULL) &&
5301 (xmlStrncmp(cont->prefix, qname, plen) == 0) &&
5302 (xmlStrEqual(cont->name, name)))
5303 return(1);
5304 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5305 (cont->c1 != NULL) &&
5306 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5307 if ((cont->c1->prefix != NULL) &&
5308 (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) &&
5309 (xmlStrEqual(cont->c1->name, name)))
5310 return(1);
5311 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5312 (cont->c1 == NULL) ||
5313 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005314 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
5315 "Internal: MIXED struct corrupted\n",
5316 NULL);
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005317 break;
5318 }
5319 cont = cont->c2;
5320 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005321 }
5322 return(0);
5323}
5324
5325/**
5326 * xmlValidGetElemDecl:
5327 * @ctxt: the validation context
5328 * @doc: a document instance
5329 * @elem: an element instance
5330 * @extsubset: pointer, (out) indicate if the declaration was found
5331 * in the external subset.
5332 *
5333 * Finds a declaration associated to an element in the document.
5334 *
5335 * returns the pointer to the declaration or NULL if not found.
5336 */
5337static xmlElementPtr
5338xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5339 xmlNodePtr elem, int *extsubset) {
5340 xmlElementPtr elemDecl = NULL;
5341 const xmlChar *prefix = NULL;
5342
5343 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5344 if (extsubset != NULL)
5345 *extsubset = 0;
5346
5347 /*
5348 * Fetch the declaration for the qualified name
5349 */
5350 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5351 prefix = elem->ns->prefix;
5352
5353 if (prefix != NULL) {
5354 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5355 elem->name, prefix);
5356 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5357 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5358 elem->name, prefix);
5359 if ((elemDecl != NULL) && (extsubset != NULL))
5360 *extsubset = 1;
5361 }
5362 }
5363
5364 /*
5365 * Fetch the declaration for the non qualified name
5366 * This is "non-strict" validation should be done on the
5367 * full QName but in that case being flexible makes sense.
5368 */
5369 if (elemDecl == NULL) {
5370 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5371 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5372 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5373 if ((elemDecl != NULL) && (extsubset != NULL))
5374 *extsubset = 1;
5375 }
5376 }
5377 if (elemDecl == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005378 xmlErrValidNode(ctxt, elem,
5379 XML_DTD_UNKNOWN_ELEM,
5380 "No declaration for element %s\n",
5381 elem->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005382 }
5383 return(elemDecl);
5384}
5385
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005386#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005387/**
5388 * xmlValidatePushElement:
5389 * @ctxt: the validation context
5390 * @doc: a document instance
5391 * @elem: an element instance
5392 * @qname: the qualified name as appearing in the serialization
5393 *
5394 * Push a new element start on the validation stack.
5395 *
5396 * returns 1 if no validation problem was found or 0 otherwise
5397 */
5398int
5399xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5400 xmlNodePtr elem, const xmlChar *qname) {
5401 int ret = 1;
5402 xmlElementPtr eDecl;
5403 int extsubset = 0;
5404
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005405/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005406 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5407 xmlValidStatePtr state = ctxt->vstate;
5408 xmlElementPtr elemDecl;
5409
5410 /*
5411 * Check the new element agaisnt the content model of the new elem.
5412 */
5413 if (state->elemDecl != NULL) {
5414 elemDecl = state->elemDecl;
5415
5416 switch(elemDecl->etype) {
5417 case XML_ELEMENT_TYPE_UNDEFINED:
5418 ret = 0;
5419 break;
5420 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005421 xmlErrValidNode(ctxt, state->node,
5422 XML_DTD_NOT_EMPTY,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005423 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005424 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005425 ret = 0;
5426 break;
5427 case XML_ELEMENT_TYPE_ANY:
5428 /* I don't think anything is required then */
5429 break;
5430 case XML_ELEMENT_TYPE_MIXED:
5431 /* simple case of declared as #PCDATA */
5432 if ((elemDecl->content != NULL) &&
5433 (elemDecl->content->type ==
5434 XML_ELEMENT_CONTENT_PCDATA)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005435 xmlErrValidNode(ctxt, state->node,
5436 XML_DTD_NOT_PCDATA,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005437 "Element %s was declared #PCDATA but contains non text nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005438 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005439 ret = 0;
5440 } else {
5441 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5442 qname);
5443 if (ret != 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005444 xmlErrValidNode(ctxt, state->node,
5445 XML_DTD_INVALID_CHILD,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005446 "Element %s is not declared in %s list of possible children\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005447 qname, state->node->name, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005448 }
5449 }
5450 break;
5451 case XML_ELEMENT_TYPE_ELEMENT:
5452 /*
5453 * TODO:
5454 * VC: Standalone Document Declaration
5455 * - element types with element content, if white space
5456 * occurs directly within any instance of those types.
5457 */
5458 if (state->exec != NULL) {
5459 ret = xmlRegExecPushString(state->exec, qname, NULL);
5460 if (ret < 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005461 xmlErrValidNode(ctxt, state->node,
5462 XML_DTD_CONTENT_MODEL,
5463 "Element %s content does not follow the DTD, Misplaced %s\n",
5464 state->node->name, qname, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005465 ret = 0;
5466 } else {
5467 ret = 1;
5468 }
5469 }
5470 break;
5471 }
5472 }
5473 }
5474 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5475 vstateVPush(ctxt, eDecl, elem);
5476 return(ret);
5477}
5478
5479/**
5480 * xmlValidatePushCData:
5481 * @ctxt: the validation context
5482 * @data: some character data read
5483 * @len: the lenght of the data
5484 *
5485 * check the CData parsed for validation in the current stack
5486 *
5487 * returns 1 if no validation problem was found or 0 otherwise
5488 */
5489int
5490xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5491 int ret = 1;
5492
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005493/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005494 if (len <= 0)
5495 return(ret);
5496 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5497 xmlValidStatePtr state = ctxt->vstate;
5498 xmlElementPtr elemDecl;
5499
5500 /*
5501 * Check the new element agaisnt the content model of the new elem.
5502 */
5503 if (state->elemDecl != NULL) {
5504 elemDecl = state->elemDecl;
5505
5506 switch(elemDecl->etype) {
5507 case XML_ELEMENT_TYPE_UNDEFINED:
5508 ret = 0;
5509 break;
5510 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005511 xmlErrValidNode(ctxt, state->node,
5512 XML_DTD_NOT_EMPTY,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005513 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005514 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005515 ret = 0;
5516 break;
5517 case XML_ELEMENT_TYPE_ANY:
5518 break;
5519 case XML_ELEMENT_TYPE_MIXED:
5520 break;
5521 case XML_ELEMENT_TYPE_ELEMENT:
5522 if (len > 0) {
5523 int i;
5524
5525 for (i = 0;i < len;i++) {
William M. Brack76e95df2003-10-18 16:20:14 +00005526 if (!IS_BLANK_CH(data[i])) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005527 xmlErrValidNode(ctxt, state->node,
5528 XML_DTD_CONTENT_MODEL,
5529 "Element %s content does not follow the DTD, Text not allowed\n",
5530 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005531 ret = 0;
5532 goto done;
5533 }
5534 }
5535 /*
5536 * TODO:
5537 * VC: Standalone Document Declaration
5538 * element types with element content, if white space
5539 * occurs directly within any instance of those types.
5540 */
5541 }
5542 break;
5543 }
5544 }
5545 }
5546done:
5547 return(ret);
5548}
5549
5550/**
5551 * xmlValidatePopElement:
5552 * @ctxt: the validation context
5553 * @doc: a document instance
5554 * @elem: an element instance
5555 * @qname: the qualified name as appearing in the serialization
5556 *
5557 * Pop the element end from the validation stack.
5558 *
5559 * returns 1 if no validation problem was found or 0 otherwise
5560 */
5561int
5562xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005563 xmlNodePtr elem ATTRIBUTE_UNUSED,
5564 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005565 int ret = 1;
5566
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005567/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005568 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5569 xmlValidStatePtr state = ctxt->vstate;
5570 xmlElementPtr elemDecl;
5571
5572 /*
5573 * Check the new element agaisnt the content model of the new elem.
5574 */
5575 if (state->elemDecl != NULL) {
5576 elemDecl = state->elemDecl;
5577
5578 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5579 if (state->exec != NULL) {
5580 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5581 if (ret == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005582 xmlErrValidNode(ctxt, state->node,
5583 XML_DTD_CONTENT_MODEL,
5584 "Element %s content does not follow the DTD, Expecting more child\n",
5585 state->node->name, NULL,NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005586 } else {
5587 /*
5588 * previous validation errors should not generate
5589 * a new one here
5590 */
5591 ret = 1;
5592 }
5593 }
5594 }
5595 }
5596 vstateVPop(ctxt);
5597 }
5598 return(ret);
5599}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005600#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005601
5602/**
Owen Taylor3473f882001-02-23 17:55:21 +00005603 * xmlValidateOneElement:
5604 * @ctxt: the validation context
5605 * @doc: a document instance
5606 * @elem: an element instance
5607 *
5608 * Try to validate a single element and it's attributes,
5609 * basically it does the following checks as described by the
5610 * XML-1.0 recommendation:
5611 * - [ VC: Element Valid ]
5612 * - [ VC: Required Attribute ]
5613 * Then call xmlValidateOneAttribute() for each attribute present.
5614 *
5615 * The ID/IDREF checkings are done separately
5616 *
5617 * returns 1 if valid or 0 otherwise
5618 */
5619
5620int
5621xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5622 xmlNodePtr elem) {
5623 xmlElementPtr elemDecl = NULL;
5624 xmlElementContentPtr cont;
5625 xmlAttributePtr attr;
5626 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005627 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005628 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005629 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005630
5631 CHECK_DTD;
5632
5633 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005634 switch (elem->type) {
5635 case XML_ATTRIBUTE_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005636 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5637 "Attribute element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005638 return(0);
5639 case XML_TEXT_NODE:
5640 if (elem->children != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005641 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5642 "Text element has children !\n",
5643 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005644 return(0);
5645 }
5646 if (elem->properties != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005647 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5648 "Text element has attribute !\n",
5649 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005650 return(0);
5651 }
5652 if (elem->ns != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005653 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5654 "Text element has namespace !\n",
5655 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005656 return(0);
5657 }
5658 if (elem->nsDef != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005659 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5660 "Text element has namespace !\n",
5661 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005662 return(0);
5663 }
5664 if (elem->content == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005665 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5666 "Text element has no content !\n",
5667 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005668 return(0);
5669 }
5670 return(1);
5671 case XML_XINCLUDE_START:
5672 case XML_XINCLUDE_END:
5673 return(1);
5674 case XML_CDATA_SECTION_NODE:
5675 case XML_ENTITY_REF_NODE:
5676 case XML_PI_NODE:
5677 case XML_COMMENT_NODE:
5678 return(1);
5679 case XML_ENTITY_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005680 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5681 "Entity element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005682 return(0);
5683 case XML_NOTATION_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005684 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5685 "Notation element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005686 return(0);
5687 case XML_DOCUMENT_NODE:
5688 case XML_DOCUMENT_TYPE_NODE:
5689 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005690 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5691 "Document element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005692 return(0);
5693 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005694 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5695 "HTML Document not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005696 return(0);
5697 case XML_ELEMENT_NODE:
5698 break;
5699 default:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005700 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5701 "unknown element type\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005702 return(0);
5703 }
Owen Taylor3473f882001-02-23 17:55:21 +00005704
5705 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005706 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005707 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005708 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5709 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005710 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005711
Daniel Veillardea7751d2002-12-20 00:16:24 +00005712 /*
5713 * If vstateNr is not zero that means continuous validation is
5714 * activated, do not try to check the content model at that level.
5715 */
5716 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005717 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005718 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005719 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005720 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ELEM,
5721 "No declaration for element %s\n",
5722 elem->name, NULL, NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005723 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005724 case XML_ELEMENT_TYPE_EMPTY:
5725 if (elem->children != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005726 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_EMPTY,
Owen Taylor3473f882001-02-23 17:55:21 +00005727 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005728 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005729 ret = 0;
5730 }
5731 break;
5732 case XML_ELEMENT_TYPE_ANY:
5733 /* I don't think anything is required then */
5734 break;
5735 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005736
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005737 /* simple case of declared as #PCDATA */
5738 if ((elemDecl->content != NULL) &&
5739 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5740 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5741 if (!ret) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005742 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_PCDATA,
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005743 "Element %s was declared #PCDATA but contains non text nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005744 elem->name, NULL, NULL);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005745 }
5746 break;
5747 }
Owen Taylor3473f882001-02-23 17:55:21 +00005748 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005749 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005750 while (child != NULL) {
5751 if (child->type == XML_ELEMENT_NODE) {
5752 name = child->name;
5753 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005754 xmlChar fn[50];
5755 xmlChar *fullname;
5756
5757 fullname = xmlBuildQName(child->name, child->ns->prefix,
5758 fn, 50);
5759 if (fullname == NULL)
5760 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005761 cont = elemDecl->content;
5762 while (cont != NULL) {
5763 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005764 if (xmlStrEqual(cont->name, fullname))
5765 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005766 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5767 (cont->c1 != NULL) &&
5768 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005769 if (xmlStrEqual(cont->c1->name, fullname))
5770 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005771 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5772 (cont->c1 == NULL) ||
5773 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005774 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5775 "Internal: MIXED struct corrupted\n",
5776 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005777 break;
5778 }
5779 cont = cont->c2;
5780 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005781 if ((fullname != fn) && (fullname != child->name))
5782 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005783 if (cont != NULL)
5784 goto child_ok;
5785 }
5786 cont = elemDecl->content;
5787 while (cont != NULL) {
5788 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5789 if (xmlStrEqual(cont->name, name)) break;
5790 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5791 (cont->c1 != NULL) &&
5792 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5793 if (xmlStrEqual(cont->c1->name, name)) break;
5794 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5795 (cont->c1 == NULL) ||
5796 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005797 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
5798 "Internal: MIXED struct corrupted\n",
5799 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005800 break;
5801 }
5802 cont = cont->c2;
5803 }
5804 if (cont == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005805 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_CHILD,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005806 "Element %s is not declared in %s list of possible children\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005807 name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005808 ret = 0;
5809 }
5810 }
5811child_ok:
5812 child = child->next;
5813 }
5814 break;
5815 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005816 if ((doc->standalone == 1) && (extsubset == 1)) {
5817 /*
5818 * VC: Standalone Document Declaration
5819 * - element types with element content, if white space
5820 * occurs directly within any instance of those types.
5821 */
5822 child = elem->children;
5823 while (child != NULL) {
5824 if (child->type == XML_TEXT_NODE) {
5825 const xmlChar *content = child->content;
5826
William M. Brack76e95df2003-10-18 16:20:14 +00005827 while (IS_BLANK_CH(*content))
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005828 content++;
5829 if (*content == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005830 xmlErrValidNode(ctxt, elem,
5831 XML_DTD_STANDALONE_WHITE_SPACE,
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005832"standalone: %s declared in the external subset contains white spaces nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005833 elem->name, NULL, NULL);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005834 ret = 0;
5835 break;
5836 }
5837 }
5838 child =child->next;
5839 }
5840 }
Owen Taylor3473f882001-02-23 17:55:21 +00005841 child = elem->children;
5842 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005843 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005844 if (tmp <= 0)
5845 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005846 break;
5847 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005848 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005849
5850 /* [ VC: Required Attribute ] */
5851 attr = elemDecl->attributes;
5852 while (attr != NULL) {
5853 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005854 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005855
Daniel Veillarde4301c82002-02-13 13:32:35 +00005856 if ((attr->prefix == NULL) &&
5857 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5858 xmlNsPtr ns;
5859
5860 ns = elem->nsDef;
5861 while (ns != NULL) {
5862 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005863 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005864 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005865 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005866 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5867 xmlNsPtr ns;
5868
5869 ns = elem->nsDef;
5870 while (ns != NULL) {
5871 if (xmlStrEqual(attr->name, ns->prefix))
5872 goto found;
5873 ns = ns->next;
5874 }
5875 } else {
5876 xmlAttrPtr attrib;
5877
5878 attrib = elem->properties;
5879 while (attrib != NULL) {
5880 if (xmlStrEqual(attrib->name, attr->name)) {
5881 if (attr->prefix != NULL) {
5882 xmlNsPtr nameSpace = attrib->ns;
5883
5884 if (nameSpace == NULL)
5885 nameSpace = elem->ns;
5886 /*
5887 * qualified names handling is problematic, having a
5888 * different prefix should be possible but DTDs don't
5889 * allow to define the URI instead of the prefix :-(
5890 */
5891 if (nameSpace == NULL) {
5892 if (qualified < 0)
5893 qualified = 0;
5894 } else if (!xmlStrEqual(nameSpace->prefix,
5895 attr->prefix)) {
5896 if (qualified < 1)
5897 qualified = 1;
5898 } else
5899 goto found;
5900 } else {
5901 /*
5902 * We should allow applications to define namespaces
5903 * for their application even if the DTD doesn't
5904 * carry one, otherwise, basically we would always
5905 * break.
5906 */
5907 goto found;
5908 }
5909 }
5910 attrib = attrib->next;
5911 }
Owen Taylor3473f882001-02-23 17:55:21 +00005912 }
5913 if (qualified == -1) {
5914 if (attr->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005915 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005916 "Element %s does not carry attribute %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005917 elem->name, attr->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005918 ret = 0;
5919 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005920 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005921 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005922 elem->name, attr->prefix,attr->name);
5923 ret = 0;
5924 }
5925 } else if (qualified == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005926 xmlErrValidWarning(ctxt, elem, XML_DTD_NO_PREFIX,
Owen Taylor3473f882001-02-23 17:55:21 +00005927 "Element %s required attribute %s:%s has no prefix\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005928 elem->name, attr->prefix, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005929 } else if (qualified == 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005930 xmlErrValidWarning(ctxt, elem, XML_DTD_DIFFERENT_PREFIX,
Owen Taylor3473f882001-02-23 17:55:21 +00005931 "Element %s required attribute %s:%s has different prefix\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005932 elem->name, attr->prefix, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005933 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005934 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5935 /*
5936 * Special tests checking #FIXED namespace declarations
5937 * have the right value since this is not done as an
5938 * attribute checking
5939 */
5940 if ((attr->prefix == NULL) &&
5941 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5942 xmlNsPtr ns;
5943
5944 ns = elem->nsDef;
5945 while (ns != NULL) {
5946 if (ns->prefix == NULL) {
5947 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005948 xmlErrValidNode(ctxt, elem,
5949 XML_DTD_ELEM_DEFAULT_NAMESPACE,
Daniel Veillarde4301c82002-02-13 13:32:35 +00005950 "Element %s namespace name for default namespace does not match the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005951 elem->name, NULL, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00005952 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005953 }
5954 goto found;
5955 }
5956 ns = ns->next;
5957 }
5958 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5959 xmlNsPtr ns;
5960
5961 ns = elem->nsDef;
5962 while (ns != NULL) {
5963 if (xmlStrEqual(attr->name, ns->prefix)) {
5964 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005965 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005966 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005967 elem->name, ns->prefix, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00005968 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005969 }
5970 goto found;
5971 }
5972 ns = ns->next;
5973 }
5974 }
Owen Taylor3473f882001-02-23 17:55:21 +00005975 }
5976found:
5977 attr = attr->nexth;
5978 }
5979 return(ret);
5980}
5981
5982/**
5983 * xmlValidateRoot:
5984 * @ctxt: the validation context
5985 * @doc: a document instance
5986 *
5987 * Try to validate a the root element
5988 * basically it does the following check as described by the
5989 * XML-1.0 recommendation:
5990 * - [ VC: Root Element Type ]
5991 * it doesn't try to recurse or apply other check to the element
5992 *
5993 * returns 1 if valid or 0 otherwise
5994 */
5995
5996int
5997xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5998 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00005999 int ret;
6000
Owen Taylor3473f882001-02-23 17:55:21 +00006001 if (doc == NULL) return(0);
6002
6003 root = xmlDocGetRootElement(doc);
6004 if ((root == NULL) || (root->name == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006005 xmlErrValid(ctxt, XML_DTD_NO_ROOT,
6006 "no root element\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006007 return(0);
6008 }
6009
6010 /*
6011 * When doing post validation against a separate DTD, those may
6012 * no internal subset has been generated
6013 */
6014 if ((doc->intSubset != NULL) &&
6015 (doc->intSubset->name != NULL)) {
6016 /*
6017 * Check first the document root against the NQName
6018 */
6019 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
6020 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00006021 xmlChar fn[50];
6022 xmlChar *fullname;
6023
6024 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
6025 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00006026 xmlVErrMemory(ctxt, NULL);
Daniel Veillardc00cda82003-04-07 10:22:39 +00006027 return(0);
6028 }
6029 ret = xmlStrEqual(doc->intSubset->name, fullname);
6030 if ((fullname != fn) && (fullname != root->name))
6031 xmlFree(fullname);
6032 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00006033 goto name_ok;
6034 }
6035 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
6036 (xmlStrEqual(root->name, BAD_CAST "html")))
6037 goto name_ok;
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006038 xmlErrValidNode(ctxt, root, XML_DTD_ROOT_NAME,
6039 "root and DTD name do not match '%s' and '%s'\n",
6040 root->name, doc->intSubset->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006041 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00006042 }
6043 }
6044name_ok:
6045 return(1);
6046}
6047
6048
6049/**
6050 * xmlValidateElement:
6051 * @ctxt: the validation context
6052 * @doc: a document instance
6053 * @elem: an element instance
6054 *
6055 * Try to validate the subtree under an element
6056 *
6057 * returns 1 if valid or 0 otherwise
6058 */
6059
6060int
6061xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
6062 xmlNodePtr child;
6063 xmlAttrPtr attr;
Daniel Veillarde133dd82003-10-30 10:42:20 +00006064 xmlNsPtr ns;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006065 const xmlChar *value;
Owen Taylor3473f882001-02-23 17:55:21 +00006066 int ret = 1;
6067
6068 if (elem == NULL) return(0);
6069
6070 /*
6071 * XInclude elements were added after parsing in the infoset,
6072 * they don't really mean anything validation wise.
6073 */
6074 if ((elem->type == XML_XINCLUDE_START) ||
6075 (elem->type == XML_XINCLUDE_END))
6076 return(1);
6077
6078 CHECK_DTD;
6079
Daniel Veillard10ea86c2001-06-20 13:55:33 +00006080 /*
6081 * Entities references have to be handled separately
6082 */
6083 if (elem->type == XML_ENTITY_REF_NODE) {
6084 return(1);
6085 }
6086
Owen Taylor3473f882001-02-23 17:55:21 +00006087 ret &= xmlValidateOneElement(ctxt, doc, elem);
6088 attr = elem->properties;
Daniel Veillarde133dd82003-10-30 10:42:20 +00006089 while (attr != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00006090 value = xmlNodeListGetString(doc, attr->children, 0);
6091 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
6092 if (value != NULL)
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006093 xmlFree((char *)value);
Owen Taylor3473f882001-02-23 17:55:21 +00006094 attr= attr->next;
6095 }
Daniel Veillarde133dd82003-10-30 10:42:20 +00006096 ns = elem->nsDef;
6097 while (ns != NULL) {
Daniel Veillarde133dd82003-10-30 10:42:20 +00006098 ret &= xmlValidateOneNamespace(ctxt, doc, elem, ns->prefix,
6099 ns, ns->href);
6100 ns = ns->next;
6101 }
Owen Taylor3473f882001-02-23 17:55:21 +00006102 child = elem->children;
6103 while (child != NULL) {
6104 ret &= xmlValidateElement(ctxt, doc, child);
6105 child = child->next;
6106 }
6107
6108 return(ret);
6109}
6110
Daniel Veillard8730c562001-02-26 10:49:57 +00006111/**
6112 * xmlValidateRef:
6113 * @ref: A reference to be validated
6114 * @ctxt: Validation context
6115 * @name: Name of ID we are searching for
6116 *
6117 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006118static void
Daniel Veillard8730c562001-02-26 10:49:57 +00006119xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00006120 const xmlChar *name) {
6121 xmlAttrPtr id;
6122 xmlAttrPtr attr;
6123
6124 if (ref == NULL)
6125 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006126 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00006127 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006128 attr = ref->attr;
6129 if (attr == NULL) {
6130 xmlChar *dup, *str = NULL, *cur, save;
6131
6132 dup = xmlStrdup(name);
6133 if (dup == NULL) {
6134 ctxt->valid = 0;
6135 return;
6136 }
6137 cur = dup;
6138 while (*cur != 0) {
6139 str = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00006140 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006141 save = *cur;
6142 *cur = 0;
6143 id = xmlGetID(ctxt->doc, str);
6144 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006145 xmlErrValidNodeNr(ctxt, NULL, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006146 "attribute %s line %d references an unknown ID \"%s\"\n",
6147 ref->name, ref->lineno, str);
6148 ctxt->valid = 0;
6149 }
6150 if (save == 0)
6151 break;
6152 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00006153 while (IS_BLANK_CH(*cur)) cur++;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006154 }
6155 xmlFree(dup);
6156 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00006157 id = xmlGetID(ctxt->doc, name);
6158 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006159 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006160 "IDREF attribute %s references an unknown ID \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006161 attr->name, name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006162 ctxt->valid = 0;
6163 }
6164 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
6165 xmlChar *dup, *str = NULL, *cur, save;
6166
6167 dup = xmlStrdup(name);
6168 if (dup == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00006169 xmlVErrMemory(ctxt, "IDREFS split");
Owen Taylor3473f882001-02-23 17:55:21 +00006170 ctxt->valid = 0;
6171 return;
6172 }
6173 cur = dup;
6174 while (*cur != 0) {
6175 str = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00006176 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00006177 save = *cur;
6178 *cur = 0;
6179 id = xmlGetID(ctxt->doc, str);
6180 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006181 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006182 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006183 attr->name, str, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006184 ctxt->valid = 0;
6185 }
6186 if (save == 0)
6187 break;
6188 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00006189 while (IS_BLANK_CH(*cur)) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00006190 }
6191 xmlFree(dup);
6192 }
6193}
6194
6195/**
Daniel Veillard8730c562001-02-26 10:49:57 +00006196 * xmlWalkValidateList:
6197 * @data: Contents of current link
6198 * @user: Value supplied by the user
6199 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00006200 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00006201 */
6202static int
6203xmlWalkValidateList(const void *data, const void *user)
6204{
6205 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
6206 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
6207 return 1;
6208}
6209
6210/**
6211 * xmlValidateCheckRefCallback:
6212 * @ref_list: List of references
6213 * @ctxt: Validation context
6214 * @name: Name of ID we are searching for
6215 *
6216 */
6217static void
6218xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
6219 const xmlChar *name) {
6220 xmlValidateMemo memo;
6221
6222 if (ref_list == NULL)
6223 return;
6224 memo.ctxt = ctxt;
6225 memo.name = name;
6226
6227 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
6228
6229}
6230
6231/**
Owen Taylor3473f882001-02-23 17:55:21 +00006232 * xmlValidateDocumentFinal:
6233 * @ctxt: the validation context
6234 * @doc: a document instance
6235 *
6236 * Does the final step for the document validation once all the
6237 * incremental validation steps have been completed
6238 *
6239 * basically it does the following checks described by the XML Rec
6240 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006241 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00006242 *
6243 * returns 1 if valid or 0 otherwise
6244 */
6245
6246int
6247xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6248 xmlRefTablePtr table;
6249
6250 if (doc == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00006251 xmlErrValid(ctxt, XML_DTD_NO_DOC,
6252 "xmlValidateDocumentFinal: doc == NULL\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006253 return(0);
6254 }
6255
6256 /*
6257 * Check all the NOTATION/NOTATIONS attributes
6258 */
6259 /*
6260 * Check all the ENTITY/ENTITIES attributes definition for validity
6261 */
6262 /*
6263 * Check all the IDREF/IDREFS attributes definition for validity
6264 */
6265 table = (xmlRefTablePtr) doc->refs;
6266 ctxt->doc = doc;
6267 ctxt->valid = 1;
6268 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6269 return(ctxt->valid);
6270}
6271
6272/**
6273 * xmlValidateDtd:
6274 * @ctxt: the validation context
6275 * @doc: a document instance
6276 * @dtd: a dtd instance
6277 *
6278 * Try to validate the document against the dtd instance
6279 *
6280 * basically it does check all the definitions in the DtD.
6281 *
6282 * returns 1 if valid or 0 otherwise
6283 */
6284
6285int
6286xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6287 int ret;
6288 xmlDtdPtr oldExt;
6289 xmlNodePtr root;
6290
6291 if (dtd == NULL) return(0);
6292 if (doc == NULL) return(0);
6293 oldExt = doc->extSubset;
6294 doc->extSubset = dtd;
6295 ret = xmlValidateRoot(ctxt, doc);
6296 if (ret == 0) {
6297 doc->extSubset = oldExt;
6298 return(ret);
6299 }
6300 if (doc->ids != NULL) {
6301 xmlFreeIDTable(doc->ids);
6302 doc->ids = NULL;
6303 }
6304 if (doc->refs != NULL) {
6305 xmlFreeRefTable(doc->refs);
6306 doc->refs = NULL;
6307 }
6308 root = xmlDocGetRootElement(doc);
6309 ret = xmlValidateElement(ctxt, doc, root);
6310 ret &= xmlValidateDocumentFinal(ctxt, doc);
6311 doc->extSubset = oldExt;
6312 return(ret);
6313}
6314
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006315static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006316xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6317 const xmlChar *name ATTRIBUTE_UNUSED) {
6318 if (cur == NULL)
6319 return;
6320 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6321 xmlChar *notation = cur->content;
6322
Daniel Veillard878eab02002-02-19 13:46:09 +00006323 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006324 int ret;
6325
6326 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6327 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006328 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006329 }
6330 }
6331 }
6332}
6333
6334static void
Owen Taylor3473f882001-02-23 17:55:21 +00006335xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006336 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006337 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006338 xmlDocPtr doc;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006339 xmlElementPtr elem = NULL;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006340
Owen Taylor3473f882001-02-23 17:55:21 +00006341 if (cur == NULL)
6342 return;
6343 switch (cur->atype) {
6344 case XML_ATTRIBUTE_CDATA:
6345 case XML_ATTRIBUTE_ID:
6346 case XML_ATTRIBUTE_IDREF :
6347 case XML_ATTRIBUTE_IDREFS:
6348 case XML_ATTRIBUTE_NMTOKEN:
6349 case XML_ATTRIBUTE_NMTOKENS:
6350 case XML_ATTRIBUTE_ENUMERATION:
6351 break;
6352 case XML_ATTRIBUTE_ENTITY:
6353 case XML_ATTRIBUTE_ENTITIES:
6354 case XML_ATTRIBUTE_NOTATION:
6355 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006356
6357 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6358 cur->atype, cur->defaultValue);
6359 if ((ret == 0) && (ctxt->valid == 1))
6360 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006361 }
6362 if (cur->tree != NULL) {
6363 xmlEnumerationPtr tree = cur->tree;
6364 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006365 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006366 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006367 if ((ret == 0) && (ctxt->valid == 1))
6368 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006369 tree = tree->next;
6370 }
6371 }
6372 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006373 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6374 doc = cur->doc;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006375 if (cur->elem == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006376 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
Daniel Veillard878eab02002-02-19 13:46:09 +00006377 "xmlValidateAttributeCallback(%s): internal error\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006378 (const char *) cur->name);
Daniel Veillard878eab02002-02-19 13:46:09 +00006379 return;
6380 }
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006381
6382 if (doc != NULL)
6383 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6384 if ((elem == NULL) && (doc != NULL))
Daniel Veillard878eab02002-02-19 13:46:09 +00006385 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006386 if ((elem == NULL) && (cur->parent != NULL) &&
6387 (cur->parent->type == XML_DTD_NODE))
6388 elem = xmlGetDtdElementDesc((xmlDtdPtr) cur->parent, cur->elem);
Daniel Veillard878eab02002-02-19 13:46:09 +00006389 if (elem == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006390 xmlErrValidNode(ctxt, NULL, XML_DTD_UNKNOWN_ELEM,
Daniel Veillard878eab02002-02-19 13:46:09 +00006391 "attribute %s: could not find decl for element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006392 cur->name, cur->elem, NULL);
Daniel Veillard878eab02002-02-19 13:46:09 +00006393 return;
6394 }
6395 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006396 xmlErrValidNode(ctxt, NULL, XML_DTD_EMPTY_NOTATION,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006397 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006398 cur->name, cur->elem, NULL);
Daniel Veillard878eab02002-02-19 13:46:09 +00006399 ctxt->valid = 0;
6400 }
6401 }
Owen Taylor3473f882001-02-23 17:55:21 +00006402}
6403
6404/**
6405 * xmlValidateDtdFinal:
6406 * @ctxt: the validation context
6407 * @doc: a document instance
6408 *
6409 * Does the final step for the dtds validation once all the
6410 * subsets have been parsed
6411 *
6412 * basically it does the following checks described by the XML Rec
6413 * - check that ENTITY and ENTITIES type attributes default or
6414 * possible values matches one of the defined entities.
6415 * - check that NOTATION type attributes default or
6416 * possible values matches one of the defined notations.
6417 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006418 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006419 */
6420
6421int
6422xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006423 xmlDtdPtr dtd;
6424 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006425 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006426
6427 if (doc == NULL) return(0);
6428 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6429 return(0);
6430 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006431 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006432 dtd = doc->intSubset;
6433 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6434 table = (xmlAttributeTablePtr) dtd->attributes;
6435 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006436 }
6437 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006438 entities = (xmlEntitiesTablePtr) dtd->entities;
6439 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6440 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006441 }
6442 dtd = doc->extSubset;
6443 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6444 table = (xmlAttributeTablePtr) dtd->attributes;
6445 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006446 }
6447 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006448 entities = (xmlEntitiesTablePtr) dtd->entities;
6449 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6450 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006451 }
6452 return(ctxt->valid);
6453}
6454
6455/**
6456 * xmlValidateDocument:
6457 * @ctxt: the validation context
6458 * @doc: a document instance
6459 *
6460 * Try to validate the document instance
6461 *
6462 * basically it does the all the checks described by the XML Rec
6463 * i.e. validates the internal and external subset (if present)
6464 * and validate the document tree.
6465 *
6466 * returns 1 if valid or 0 otherwise
6467 */
6468
6469int
6470xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6471 int ret;
6472 xmlNodePtr root;
6473
Daniel Veillard2fd85422002-10-16 14:32:41 +00006474 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006475 xmlErrValid(ctxt, XML_DTD_NO_DTD,
6476 "no DTD found!\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006477 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006478 }
Owen Taylor3473f882001-02-23 17:55:21 +00006479 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6480 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6481 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6482 doc->intSubset->SystemID);
6483 if (doc->extSubset == NULL) {
6484 if (doc->intSubset->SystemID != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006485 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00006486 "Could not load the external subset \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006487 (const char *) doc->intSubset->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00006488 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006489 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00006490 "Could not load the external subset \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006491 (const char *) doc->intSubset->ExternalID);
Owen Taylor3473f882001-02-23 17:55:21 +00006492 }
6493 return(0);
6494 }
6495 }
6496
6497 if (doc->ids != NULL) {
6498 xmlFreeIDTable(doc->ids);
6499 doc->ids = NULL;
6500 }
6501 if (doc->refs != NULL) {
6502 xmlFreeRefTable(doc->refs);
6503 doc->refs = NULL;
6504 }
6505 ret = xmlValidateDtdFinal(ctxt, doc);
6506 if (!xmlValidateRoot(ctxt, doc)) return(0);
6507
6508 root = xmlDocGetRootElement(doc);
6509 ret &= xmlValidateElement(ctxt, doc, root);
6510 ret &= xmlValidateDocumentFinal(ctxt, doc);
6511 return(ret);
6512}
6513
Owen Taylor3473f882001-02-23 17:55:21 +00006514/************************************************************************
6515 * *
6516 * Routines for dynamic validation editing *
6517 * *
6518 ************************************************************************/
6519
6520/**
6521 * xmlValidGetPotentialChildren:
6522 * @ctree: an element content tree
6523 * @list: an array to store the list of child names
6524 * @len: a pointer to the number of element in the list
6525 * @max: the size of the array
6526 *
6527 * Build/extend a list of potential children allowed by the content tree
6528 *
6529 * returns the number of element in the list, or -1 in case of error.
6530 */
6531
6532int
6533xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6534 int *len, int max) {
6535 int i;
6536
6537 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6538 return(-1);
6539 if (*len >= max) return(*len);
6540
6541 switch (ctree->type) {
6542 case XML_ELEMENT_CONTENT_PCDATA:
6543 for (i = 0; i < *len;i++)
6544 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6545 list[(*len)++] = BAD_CAST "#PCDATA";
6546 break;
6547 case XML_ELEMENT_CONTENT_ELEMENT:
6548 for (i = 0; i < *len;i++)
6549 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6550 list[(*len)++] = ctree->name;
6551 break;
6552 case XML_ELEMENT_CONTENT_SEQ:
6553 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6554 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6555 break;
6556 case XML_ELEMENT_CONTENT_OR:
6557 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6558 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6559 break;
6560 }
6561
6562 return(*len);
6563}
6564
6565/**
6566 * xmlValidGetValidElements:
6567 * @prev: an element to insert after
6568 * @next: an element to insert next
6569 * @list: an array to store the list of child names
6570 * @max: the size of the array
6571 *
6572 * This function returns the list of authorized children to insert
6573 * within an existing tree while respecting the validity constraints
6574 * forced by the Dtd. The insertion point is defined using @prev and
6575 * @next in the following ways:
6576 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6577 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6578 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6579 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6580 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6581 *
6582 * pointers to the element names are inserted at the beginning of the array
6583 * and do not need to be freed.
6584 *
6585 * returns the number of element in the list, or -1 in case of error. If
6586 * the function returns the value @max the caller is invited to grow the
6587 * receiving array and retry.
6588 */
6589
6590int
6591xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6592 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006593 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006594 int nb_valid_elements = 0;
6595 const xmlChar *elements[256];
6596 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006597 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006598
6599 xmlNode *ref_node;
6600 xmlNode *parent;
6601 xmlNode *test_node;
6602
6603 xmlNode *prev_next;
6604 xmlNode *next_prev;
6605 xmlNode *parent_childs;
6606 xmlNode *parent_last;
6607
6608 xmlElement *element_desc;
6609
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006610 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006611
Owen Taylor3473f882001-02-23 17:55:21 +00006612 if (prev == NULL && next == NULL)
6613 return(-1);
6614
6615 if (list == NULL) return(-1);
6616 if (max <= 0) return(-1);
6617
6618 nb_valid_elements = 0;
6619 ref_node = prev ? prev : next;
6620 parent = ref_node->parent;
6621
6622 /*
6623 * Retrieves the parent element declaration
6624 */
6625 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6626 parent->name);
6627 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6628 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6629 parent->name);
6630 if (element_desc == NULL) return(-1);
6631
6632 /*
6633 * Do a backup of the current tree structure
6634 */
6635 prev_next = prev ? prev->next : NULL;
6636 next_prev = next ? next->prev : NULL;
6637 parent_childs = parent->children;
6638 parent_last = parent->last;
6639
6640 /*
6641 * Creates a dummy node and insert it into the tree
6642 */
6643 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6644 test_node->doc = ref_node->doc;
6645 test_node->parent = parent;
6646 test_node->prev = prev;
6647 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006648 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006649
6650 if (prev) prev->next = test_node;
6651 else parent->children = test_node;
6652
6653 if (next) next->prev = test_node;
6654 else parent->last = test_node;
6655
6656 /*
6657 * Insert each potential child node and check if the parent is
6658 * still valid
6659 */
6660 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6661 elements, &nb_elements, 256);
6662
6663 for (i = 0;i < nb_elements;i++) {
6664 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006665 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006666 int j;
6667
6668 for (j = 0; j < nb_valid_elements;j++)
6669 if (xmlStrEqual(elements[i], list[j])) break;
6670 list[nb_valid_elements++] = elements[i];
6671 if (nb_valid_elements >= max) break;
6672 }
6673 }
6674
6675 /*
6676 * Restore the tree structure
6677 */
6678 if (prev) prev->next = prev_next;
6679 if (next) next->prev = next_prev;
6680 parent->children = parent_childs;
6681 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006682
6683 /*
6684 * Free up the dummy node
6685 */
6686 test_node->name = name;
6687 xmlFreeNode(test_node);
6688
Owen Taylor3473f882001-02-23 17:55:21 +00006689 return(nb_valid_elements);
6690}
Daniel Veillard4432df22003-09-28 18:58:27 +00006691#endif /* LIBXML_VALID_ENABLED */
6692