blob: 60ac1279e66dda42c40306619e84ac2ccc4e908d [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>
Daniel Veillard29b17482004-08-16 00:39:03 +000021#include <libxml/uri.h>
Owen Taylor3473f882001-02-23 17:55:21 +000022#include <libxml/valid.h>
23#include <libxml/parser.h>
24#include <libxml/parserInternals.h>
25#include <libxml/xmlerror.h>
26#include <libxml/list.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000027#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000028
Daniel Veillard4432df22003-09-28 18:58:27 +000029static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
30 int create);
Daniel Veillarde62d36c2001-05-15 08:53:16 +000031/* #define DEBUG_VALID_ALGO */
Daniel Veillard5acfd6b2002-09-18 16:29:02 +000032/* #define DEBUG_REGEXP_ALGO */
Daniel Veillarde62d36c2001-05-15 08:53:16 +000033
Daniel Veillarda646cfd2002-09-17 21:50:03 +000034#define TODO \
35 xmlGenericError(xmlGenericErrorContext, \
36 "Unimplemented block at %s:%d\n", \
37 __FILE__, __LINE__);
Owen Taylor3473f882001-02-23 17:55:21 +000038
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000039/************************************************************************
40 * *
41 * Error handling routines *
42 * *
43 ************************************************************************/
44
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000045/**
Daniel Veillardce9457f2003-10-05 21:33:18 +000046 * xmlVErrMemory:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000047 * @ctxt: an XML validation parser context
48 * @extra: extra informations
49 *
50 * Handle an out of memory error
51 */
52static void
Daniel Veillardce9457f2003-10-05 21:33:18 +000053xmlVErrMemory(xmlValidCtxtPtr ctxt, const char *extra)
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000054{
Daniel Veillardbb5abab2003-10-03 22:21:51 +000055 xmlGenericErrorFunc channel = NULL;
56 xmlParserCtxtPtr pctxt = NULL;
57 void *data = NULL;
58
59 if (ctxt != NULL) {
60 channel = ctxt->error;
61 data = ctxt->userData;
Daniel Veillardeff45a92004-10-29 12:10:55 +000062 /* Use the special values to detect if it is part of a parsing
63 context */
64 if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
65 (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
66 pctxt = ctxt->userData;
67 }
Daniel Veillardbb5abab2003-10-03 22:21:51 +000068 }
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000069 if (extra)
Daniel Veillard73000572003-10-11 11:26:42 +000070 __xmlRaiseError(NULL, channel, data,
Daniel Veillard72b9e292003-10-28 15:44:17 +000071 pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000072 XML_ERR_FATAL, NULL, 0, extra, NULL, NULL, 0, 0,
73 "Memory allocation failed : %s\n", extra);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000074 else
Daniel Veillard73000572003-10-11 11:26:42 +000075 __xmlRaiseError(NULL, channel, data,
Daniel Veillard72b9e292003-10-28 15:44:17 +000076 pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000077 XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, 0, 0,
78 "Memory allocation failed\n");
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000079}
80
81/**
82 * xmlErrValid:
83 * @ctxt: an XML validation parser context
Daniel Veillardbb5abab2003-10-03 22:21:51 +000084 * @error: the error number
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000085 * @extra: extra informations
86 *
87 * Handle a validation error
88 */
89static void
Daniel Veillardf88d8cf2003-12-08 10:25:02 +000090xmlErrValid(xmlValidCtxtPtr ctxt, xmlParserErrors error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000091 const char *msg, const char *extra)
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000092{
Daniel Veillardbb5abab2003-10-03 22:21:51 +000093 xmlGenericErrorFunc channel = NULL;
94 xmlParserCtxtPtr pctxt = NULL;
95 void *data = NULL;
96
97 if (ctxt != NULL) {
98 channel = ctxt->error;
99 data = ctxt->userData;
Daniel Veillardeff45a92004-10-29 12:10:55 +0000100 /* Use the special values to detect if it is part of a parsing
101 context */
102 if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
103 (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
104 pctxt = ctxt->userData;
105 }
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000106 }
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000107 if (extra)
Daniel Veillard73000572003-10-11 11:26:42 +0000108 __xmlRaiseError(NULL, channel, data,
Daniel Veillard72b9e292003-10-28 15:44:17 +0000109 pctxt, NULL, XML_FROM_VALID, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000110 XML_ERR_ERROR, NULL, 0, extra, NULL, NULL, 0, 0,
111 msg, extra);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000112 else
Daniel Veillard73000572003-10-11 11:26:42 +0000113 __xmlRaiseError(NULL, channel, data,
Daniel Veillard72b9e292003-10-28 15:44:17 +0000114 pctxt, NULL, XML_FROM_VALID, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000115 XML_ERR_ERROR, NULL, 0, NULL, NULL, NULL, 0, 0,
116 msg);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000117}
118
Daniel Veillardf54cd532004-02-25 11:52:31 +0000119#if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
120/**
121 * xmlErrValidNode:
122 * @ctxt: an XML validation parser context
123 * @node: the node raising the error
124 * @error: the error number
125 * @str1: extra informations
126 * @str2: extra informations
127 * @str3: extra informations
128 *
129 * Handle a validation error, provide contextual informations
130 */
131static void
132xmlErrValidNode(xmlValidCtxtPtr ctxt,
133 xmlNodePtr node, xmlParserErrors error,
134 const char *msg, const xmlChar * str1,
135 const xmlChar * str2, const xmlChar * str3)
136{
137 xmlStructuredErrorFunc schannel = NULL;
138 xmlGenericErrorFunc channel = NULL;
139 xmlParserCtxtPtr pctxt = NULL;
140 void *data = NULL;
141
142 if (ctxt != NULL) {
143 channel = ctxt->error;
144 data = ctxt->userData;
Daniel Veillardeff45a92004-10-29 12:10:55 +0000145 /* Use the special values to detect if it is part of a parsing
146 context */
147 if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
148 (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
149 pctxt = ctxt->userData;
150 }
Daniel Veillardf54cd532004-02-25 11:52:31 +0000151 }
152 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error,
153 XML_ERR_ERROR, NULL, 0,
154 (const char *) str1,
155 (const char *) str1,
156 (const char *) str3, 0, 0, msg, str1, str2, str3);
157}
158#endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */
159
Daniel Veillardd61e8fb2003-10-19 21:59:17 +0000160#ifdef LIBXML_VALID_ENABLED
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000161/**
162 * xmlErrValidNodeNr:
163 * @ctxt: an XML validation parser context
164 * @node: the node raising the error
165 * @error: the error number
166 * @str1: extra informations
167 * @int2: extra informations
168 * @str3: extra informations
169 *
170 * Handle a validation error, provide contextual informations
171 */
172static void
Daniel Veillard72b9e292003-10-28 15:44:17 +0000173xmlErrValidNodeNr(xmlValidCtxtPtr ctxt,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000174 xmlNodePtr node, xmlParserErrors error,
175 const char *msg, const xmlChar * str1,
176 int int2, const xmlChar * str3)
177{
Daniel Veillard659e71e2003-10-10 14:10:40 +0000178 xmlStructuredErrorFunc schannel = NULL;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000179 xmlGenericErrorFunc channel = NULL;
180 xmlParserCtxtPtr pctxt = NULL;
181 void *data = NULL;
182
183 if (ctxt != NULL) {
184 channel = ctxt->error;
185 data = ctxt->userData;
Daniel Veillardeff45a92004-10-29 12:10:55 +0000186 /* Use the special values to detect if it is part of a parsing
187 context */
188 if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
189 (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
190 pctxt = ctxt->userData;
191 }
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000192 }
Daniel Veillard72b9e292003-10-28 15:44:17 +0000193 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000194 XML_ERR_ERROR, NULL, 0,
195 (const char *) str1,
196 (const char *) str3,
197 NULL, int2, 0, msg, str1, int2, str3);
198}
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000199
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000200/**
201 * xmlErrValidWarning:
202 * @ctxt: an XML validation parser context
203 * @node: the node raising the error
204 * @error: the error number
William M. Brackedb65a72004-02-06 07:36:04 +0000205 * @str1: extra information
206 * @str2: extra information
207 * @str3: extra information
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000208 *
William M. Brackedb65a72004-02-06 07:36:04 +0000209 * Handle a validation error, provide contextual information
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000210 */
211static void
William M. Brackedb65a72004-02-06 07:36:04 +0000212xmlErrValidWarning(xmlValidCtxtPtr ctxt,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000213 xmlNodePtr node, xmlParserErrors error,
214 const char *msg, const xmlChar * str1,
215 const xmlChar * str2, const xmlChar * str3)
216{
Daniel Veillard659e71e2003-10-10 14:10:40 +0000217 xmlStructuredErrorFunc schannel = NULL;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000218 xmlGenericErrorFunc channel = NULL;
219 xmlParserCtxtPtr pctxt = NULL;
220 void *data = NULL;
221
222 if (ctxt != NULL) {
William M. Bracke4d526f2004-12-18 00:01:21 +0000223 channel = ctxt->warning;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000224 data = ctxt->userData;
Daniel Veillardeff45a92004-10-29 12:10:55 +0000225 /* Use the special values to detect if it is part of a parsing
226 context */
227 if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
228 (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
229 pctxt = ctxt->userData;
230 }
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000231 }
Daniel Veillard72b9e292003-10-28 15:44:17 +0000232 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000233 XML_ERR_WARNING, NULL, 0,
234 (const char *) str1,
235 (const char *) str1,
236 (const char *) str3, 0, 0, msg, str1, str2, str3);
237}
238
239
Daniel Veillardea7751d2002-12-20 00:16:24 +0000240
241#ifdef LIBXML_REGEXP_ENABLED
242/*
243 * If regexp are enabled we can do continuous validation without the
244 * need of a tree to validate the content model. this is done in each
245 * callbacks.
246 * Each xmlValidState represent the validation state associated to the
247 * set of nodes currently open from the document root to the current element.
248 */
249
250
251typedef struct _xmlValidState {
252 xmlElementPtr elemDecl; /* pointer to the content model */
253 xmlNodePtr node; /* pointer to the current node */
254 xmlRegExecCtxtPtr exec; /* regexp runtime */
255} _xmlValidState;
256
257
258static int
259vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000260 if ((ctxt->vstateMax == 0) || (ctxt->vstateTab == NULL)) {
Daniel Veillardea7751d2002-12-20 00:16:24 +0000261 ctxt->vstateMax = 10;
262 ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax *
263 sizeof(ctxt->vstateTab[0]));
264 if (ctxt->vstateTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000265 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillardea7751d2002-12-20 00:16:24 +0000266 return(-1);
267 }
268 }
269
270 if (ctxt->vstateNr >= ctxt->vstateMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000271 xmlValidState *tmp;
272
273 tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
274 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
275 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000276 xmlVErrMemory(ctxt, "realloc failed");
Daniel Veillardea7751d2002-12-20 00:16:24 +0000277 return(-1);
278 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000279 ctxt->vstateMax *= 2;
280 ctxt->vstateTab = tmp;
Daniel Veillardea7751d2002-12-20 00:16:24 +0000281 }
282 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr];
283 ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl;
284 ctxt->vstateTab[ctxt->vstateNr].node = node;
285 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
286 if (elemDecl->contModel == NULL)
287 xmlValidBuildContentModel(ctxt, elemDecl);
288 if (elemDecl->contModel != NULL) {
289 ctxt->vstateTab[ctxt->vstateNr].exec =
290 xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
291 } else {
292 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000293 xmlErrValidNode(ctxt, (xmlNodePtr) elemDecl,
294 XML_ERR_INTERNAL_ERROR,
295 "Failed to build content model regexp for %s\n",
296 node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +0000297 }
298 }
299 return(ctxt->vstateNr++);
300}
301
302static int
303vstateVPop(xmlValidCtxtPtr ctxt) {
304 xmlElementPtr elemDecl;
305
Daniel Veillard336fc7d2002-12-27 19:37:04 +0000306 if (ctxt->vstateNr < 1) return(-1);
Daniel Veillardea7751d2002-12-20 00:16:24 +0000307 ctxt->vstateNr--;
308 elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
309 ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
310 ctxt->vstateTab[ctxt->vstateNr].node = NULL;
311 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
312 xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
313 }
314 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
315 if (ctxt->vstateNr >= 1)
316 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
317 else
318 ctxt->vstate = NULL;
319 return(ctxt->vstateNr);
320}
321
322#else /* not LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000323/*
Daniel Veillard1c732d22002-11-30 11:22:59 +0000324 * If regexp are not enabled, it uses a home made algorithm less
325 * complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000326 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000327 * only restriction is on the deepness of the tree limited by the
328 * size of the occurs bitfield
329 *
330 * this is the content of a saved state for rollbacks
331 */
332
333#define ROLLBACK_OR 0
334#define ROLLBACK_PARENT 1
335
Daniel Veillardb44025c2001-10-11 22:55:55 +0000336typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000337 xmlElementContentPtr cont; /* pointer to the content model subtree */
338 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000339 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000340 unsigned char depth; /* current depth in the overall tree */
341 unsigned char state; /* ROLLBACK_XXX */
342} _xmlValidState;
343
Daniel Veillardfc57b412002-04-29 15:50:14 +0000344#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000345#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
346#define CONT ctxt->vstate->cont
347#define NODE ctxt->vstate->node
348#define DEPTH ctxt->vstate->depth
349#define OCCURS ctxt->vstate->occurs
350#define STATE ctxt->vstate->state
351
Daniel Veillard5344c602001-12-31 16:37:34 +0000352#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
353#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000354
Daniel Veillard5344c602001-12-31 16:37:34 +0000355#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
356#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000357
358static int
359vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
360 xmlNodePtr node, unsigned char depth, long occurs,
361 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000362 int i = ctxt->vstateNr - 1;
363
Daniel Veillard940492d2002-04-15 10:15:25 +0000364 if (ctxt->vstateNr > MAX_RECURSE) {
365 return(-1);
366 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000367 if (ctxt->vstateTab == NULL) {
368 ctxt->vstateMax = 8;
369 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
370 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
371 if (ctxt->vstateTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000372 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000373 return(-1);
374 }
375 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000376 if (ctxt->vstateNr >= ctxt->vstateMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000377 xmlValidState *tmp;
378
379 tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
380 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
381 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000382 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard940492d2002-04-15 10:15:25 +0000383 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000384 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000385 ctxt->vstateMax *= 2;
386 ctxt->vstateTab = tmp;
Daniel Veillard06803992001-04-22 10:35:56 +0000387 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000388 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000389 /*
390 * Don't push on the stack a state already here
391 */
392 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
393 (ctxt->vstateTab[i].node == node) &&
394 (ctxt->vstateTab[i].depth == depth) &&
395 (ctxt->vstateTab[i].occurs == occurs) &&
396 (ctxt->vstateTab[i].state == state))
397 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000398 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
399 ctxt->vstateTab[ctxt->vstateNr].node = node;
400 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
401 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
402 ctxt->vstateTab[ctxt->vstateNr].state = state;
403 return(ctxt->vstateNr++);
404}
405
406static int
407vstateVPop(xmlValidCtxtPtr ctxt) {
408 if (ctxt->vstateNr <= 1) return(-1);
409 ctxt->vstateNr--;
410 ctxt->vstate = &ctxt->vstateTab[0];
411 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
412 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
413 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
414 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
415 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
416 return(ctxt->vstateNr);
417}
418
Daniel Veillard118aed72002-09-24 14:13:13 +0000419#endif /* LIBXML_REGEXP_ENABLED */
420
Daniel Veillard1c732d22002-11-30 11:22:59 +0000421static int
422nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value)
423{
424 if (ctxt->nodeMax <= 0) {
425 ctxt->nodeMax = 4;
426 ctxt->nodeTab =
427 (xmlNodePtr *) xmlMalloc(ctxt->nodeMax *
428 sizeof(ctxt->nodeTab[0]));
429 if (ctxt->nodeTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000430 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c732d22002-11-30 11:22:59 +0000431 ctxt->nodeMax = 0;
432 return (0);
433 }
434 }
435 if (ctxt->nodeNr >= ctxt->nodeMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000436 xmlNodePtr *tmp;
437 tmp = (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
438 ctxt->nodeMax * 2 * sizeof(ctxt->nodeTab[0]));
439 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000440 xmlVErrMemory(ctxt, "realloc failed");
Daniel Veillard1c732d22002-11-30 11:22:59 +0000441 return (0);
442 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000443 ctxt->nodeMax *= 2;
444 ctxt->nodeTab = tmp;
Daniel Veillard1c732d22002-11-30 11:22:59 +0000445 }
446 ctxt->nodeTab[ctxt->nodeNr] = value;
447 ctxt->node = value;
448 return (ctxt->nodeNr++);
449}
450static xmlNodePtr
451nodeVPop(xmlValidCtxtPtr ctxt)
452{
453 xmlNodePtr ret;
454
455 if (ctxt->nodeNr <= 0)
456 return (0);
457 ctxt->nodeNr--;
458 if (ctxt->nodeNr > 0)
459 ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1];
460 else
461 ctxt->node = NULL;
462 ret = ctxt->nodeTab[ctxt->nodeNr];
463 ctxt->nodeTab[ctxt->nodeNr] = 0;
464 return (ret);
465}
Owen Taylor3473f882001-02-23 17:55:21 +0000466
Owen Taylor3473f882001-02-23 17:55:21 +0000467#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000468static void
469xmlValidPrintNode(xmlNodePtr cur) {
470 if (cur == NULL) {
471 xmlGenericError(xmlGenericErrorContext, "null");
472 return;
473 }
474 switch (cur->type) {
475 case XML_ELEMENT_NODE:
476 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
477 break;
478 case XML_TEXT_NODE:
479 xmlGenericError(xmlGenericErrorContext, "text ");
480 break;
481 case XML_CDATA_SECTION_NODE:
482 xmlGenericError(xmlGenericErrorContext, "cdata ");
483 break;
484 case XML_ENTITY_REF_NODE:
485 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
486 break;
487 case XML_PI_NODE:
488 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
489 break;
490 case XML_COMMENT_NODE:
491 xmlGenericError(xmlGenericErrorContext, "comment ");
492 break;
493 case XML_ATTRIBUTE_NODE:
494 xmlGenericError(xmlGenericErrorContext, "?attr? ");
495 break;
496 case XML_ENTITY_NODE:
497 xmlGenericError(xmlGenericErrorContext, "?ent? ");
498 break;
499 case XML_DOCUMENT_NODE:
500 xmlGenericError(xmlGenericErrorContext, "?doc? ");
501 break;
502 case XML_DOCUMENT_TYPE_NODE:
503 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
504 break;
505 case XML_DOCUMENT_FRAG_NODE:
506 xmlGenericError(xmlGenericErrorContext, "?frag? ");
507 break;
508 case XML_NOTATION_NODE:
509 xmlGenericError(xmlGenericErrorContext, "?nota? ");
510 break;
511 case XML_HTML_DOCUMENT_NODE:
512 xmlGenericError(xmlGenericErrorContext, "?html? ");
513 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000514#ifdef LIBXML_DOCB_ENABLED
515 case XML_DOCB_DOCUMENT_NODE:
516 xmlGenericError(xmlGenericErrorContext, "?docb? ");
517 break;
518#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000519 case XML_DTD_NODE:
520 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
521 break;
522 case XML_ELEMENT_DECL:
523 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
524 break;
525 case XML_ATTRIBUTE_DECL:
526 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
527 break;
528 case XML_ENTITY_DECL:
529 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
530 break;
531 case XML_NAMESPACE_DECL:
532 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
533 break;
534 case XML_XINCLUDE_START:
535 xmlGenericError(xmlGenericErrorContext, "incstart ");
536 break;
537 case XML_XINCLUDE_END:
538 xmlGenericError(xmlGenericErrorContext, "incend ");
539 break;
540 }
541}
542
543static void
544xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000545 if (cur == NULL)
546 xmlGenericError(xmlGenericErrorContext, "null ");
547 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000548 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000549 cur = cur->next;
550 }
551}
552
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000553static void
554xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Daniel Veillard83391282003-03-06 21:37:30 +0000555 char expr[5000];
Owen Taylor3473f882001-02-23 17:55:21 +0000556
557 expr[0] = 0;
558 xmlGenericError(xmlGenericErrorContext, "valid: ");
559 xmlValidPrintNodeList(cur);
560 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000561 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000562 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
563}
564
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000565static void
566xmlValidDebugState(xmlValidStatePtr state) {
567 xmlGenericError(xmlGenericErrorContext, "(");
568 if (state->cont == NULL)
569 xmlGenericError(xmlGenericErrorContext, "null,");
570 else
571 switch (state->cont->type) {
572 case XML_ELEMENT_CONTENT_PCDATA:
573 xmlGenericError(xmlGenericErrorContext, "pcdata,");
574 break;
575 case XML_ELEMENT_CONTENT_ELEMENT:
576 xmlGenericError(xmlGenericErrorContext, "%s,",
577 state->cont->name);
578 break;
579 case XML_ELEMENT_CONTENT_SEQ:
580 xmlGenericError(xmlGenericErrorContext, "seq,");
581 break;
582 case XML_ELEMENT_CONTENT_OR:
583 xmlGenericError(xmlGenericErrorContext, "or,");
584 break;
585 }
586 xmlValidPrintNode(state->node);
587 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
588 state->depth, state->occurs, state->state);
589}
590
591static void
592xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
593 int i, j;
594
595 xmlGenericError(xmlGenericErrorContext, "state: ");
596 xmlValidDebugState(ctxt->vstate);
597 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
598 ctxt->vstateNr - 1);
599 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
600 xmlValidDebugState(&ctxt->vstateTab[j]);
601 xmlGenericError(xmlGenericErrorContext, "\n");
602}
603
604/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000605#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000606 *****/
607
608#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000609#define DEBUG_VALID_MSG(m) \
610 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
611
Owen Taylor3473f882001-02-23 17:55:21 +0000612#else
613#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000614#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000615#endif
616
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000617/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000618
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000619
Owen Taylor3473f882001-02-23 17:55:21 +0000620#define CHECK_DTD \
621 if (doc == NULL) return(0); \
622 else if ((doc->intSubset == NULL) && \
623 (doc->extSubset == NULL)) return(0)
624
Owen Taylor3473f882001-02-23 17:55:21 +0000625xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
626
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000627#ifdef LIBXML_REGEXP_ENABLED
628
629/************************************************************************
630 * *
631 * Content model validation based on the regexps *
632 * *
633 ************************************************************************/
634
635/**
636 * xmlValidBuildAContentModel:
637 * @content: the content model
638 * @ctxt: the schema parser context
639 * @name: the element name whose content is being built
640 *
641 * Generate the automata sequence needed for that type
642 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000643 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000644 */
645static int
646xmlValidBuildAContentModel(xmlElementContentPtr content,
647 xmlValidCtxtPtr ctxt,
648 const xmlChar *name) {
649 if (content == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000650 xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
651 "Found NULL content in content model of %s\n",
652 name, NULL, NULL);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000653 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000654 }
655 switch (content->type) {
656 case XML_ELEMENT_CONTENT_PCDATA:
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000657 xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
658 "Found PCDATA in content model of %s\n",
659 name, NULL, NULL);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000660 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000661 break;
662 case XML_ELEMENT_CONTENT_ELEMENT: {
663 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillardc00cda82003-04-07 10:22:39 +0000664 xmlChar fn[50];
665 xmlChar *fullname;
666
667 fullname = xmlBuildQName(content->name, content->prefix, fn, 50);
668 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000669 xmlVErrMemory(ctxt, "Building content model");
Daniel Veillardc00cda82003-04-07 10:22:39 +0000670 return(0);
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000671 }
672
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000673 switch (content->ocur) {
674 case XML_ELEMENT_CONTENT_ONCE:
675 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000676 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000677 break;
678 case XML_ELEMENT_CONTENT_OPT:
679 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000680 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000681 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
682 break;
683 case XML_ELEMENT_CONTENT_PLUS:
684 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000685 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000686 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000687 ctxt->state, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000688 break;
689 case XML_ELEMENT_CONTENT_MULT:
William M. Brack8b0cbb02004-04-17 13:31:06 +0000690 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
691 ctxt->state, NULL);
692 xmlAutomataNewTransition(ctxt->am,
693 ctxt->state, ctxt->state, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000694 break;
695 }
Daniel Veillardc00cda82003-04-07 10:22:39 +0000696 if ((fullname != fn) && (fullname != content->name))
697 xmlFree(fullname);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000698 break;
699 }
700 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000701 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000702 xmlElementContentOccur ocur;
703
704 /*
705 * Simply iterate over the content
706 */
707 oldstate = ctxt->state;
708 ocur = content->ocur;
Daniel Veillardf4be0182003-02-24 19:54:33 +0000709 if (ocur != XML_ELEMENT_CONTENT_ONCE) {
710 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
711 oldstate = ctxt->state;
712 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000713 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000714 xmlValidBuildAContentModel(content->c1, ctxt, name);
715 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000716 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
717 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
718 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000719 oldend = ctxt->state;
720 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000721 switch (ocur) {
722 case XML_ELEMENT_CONTENT_ONCE:
723 break;
724 case XML_ELEMENT_CONTENT_OPT:
725 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
726 break;
727 case XML_ELEMENT_CONTENT_MULT:
728 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000729 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000730 break;
731 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000732 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000733 break;
734 }
735 break;
736 }
737 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000738 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000739 xmlElementContentOccur ocur;
740
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000741 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000742 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
743 (ocur == XML_ELEMENT_CONTENT_MULT)) {
744 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
745 ctxt->state, NULL);
746 }
747 oldstate = ctxt->state;
748 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000749
750 /*
751 * iterate over the subtypes and remerge the end with an
752 * epsilon transition
753 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000754 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000755 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000756 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000757 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000758 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000759 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
760 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000761 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000762 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000763 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
764 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000765 switch (ocur) {
766 case XML_ELEMENT_CONTENT_ONCE:
767 break;
768 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000769 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000770 break;
771 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000772 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
773 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000774 break;
775 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000776 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000777 break;
778 }
779 break;
780 }
781 default:
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000782 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
783 "ContentModel broken for element %s\n",
784 (const char *) name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000785 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000786 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000787 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000788}
789/**
790 * xmlValidBuildContentModel:
791 * @ctxt: a validation context
792 * @elem: an element declaration node
793 *
794 * (Re)Build the automata associated to the content model of this
795 * element
796 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000797 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000798 */
799int
800xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000801
802 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000803 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000804 if (elem->type != XML_ELEMENT_DECL)
805 return(0);
806 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
807 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000808 /* TODO: should we rebuild in this case ? */
Daniel Veillardec498e12003-02-05 11:01:50 +0000809 if (elem->contModel != NULL) {
810 if (!xmlRegexpIsDeterminist(elem->contModel)) {
811 ctxt->valid = 0;
812 return(0);
813 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000814 return(1);
Daniel Veillardec498e12003-02-05 11:01:50 +0000815 }
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000816
817 ctxt->am = xmlNewAutomata();
818 if (ctxt->am == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000819 xmlErrValidNode(ctxt, (xmlNodePtr) elem,
820 XML_ERR_INTERNAL_ERROR,
821 "Cannot create automata for element %s\n",
822 elem->name, NULL, NULL);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000823 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000824 }
William M. Brack78637da2003-07-31 14:47:38 +0000825 ctxt->state = xmlAutomataGetInitState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000826 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
827 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000828 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000829 if (xmlRegexpIsDeterminist(elem->contModel) != 1) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000830 char expr[5000];
831 expr[0] = 0;
832 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000833 xmlErrValidNode(ctxt, (xmlNodePtr) elem,
834 XML_DTD_CONTENT_NOT_DETERMINIST,
835 "Content model of %s is not determinist: %s\n",
836 elem->name, BAD_CAST expr, NULL);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000837#ifdef DEBUG_REGEXP_ALGO
838 xmlRegexpPrint(stderr, elem->contModel);
839#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000840 ctxt->valid = 0;
Daniel Veillardec498e12003-02-05 11:01:50 +0000841 ctxt->state = NULL;
842 xmlFreeAutomata(ctxt->am);
843 ctxt->am = NULL;
844 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000845 }
846 ctxt->state = NULL;
847 xmlFreeAutomata(ctxt->am);
848 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000849 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000850}
851
852#endif /* LIBXML_REGEXP_ENABLED */
853
Owen Taylor3473f882001-02-23 17:55:21 +0000854/****************************************************************
855 * *
856 * Util functions for data allocation/deallocation *
857 * *
858 ****************************************************************/
859
860/**
Daniel Veillarda37aab82003-06-09 09:10:36 +0000861 * xmlNewValidCtxt:
862 *
863 * Allocate a validation context structure.
864 *
865 * Returns NULL if not, otherwise the new validation context structure
866 */
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000867xmlValidCtxtPtr xmlNewValidCtxt(void) {
Daniel Veillarda37aab82003-06-09 09:10:36 +0000868 xmlValidCtxtPtr ret;
869
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000870 if ((ret = xmlMalloc(sizeof (xmlValidCtxt))) == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000871 xmlVErrMemory(NULL, "malloc failed");
Daniel Veillarda37aab82003-06-09 09:10:36 +0000872 return (NULL);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000873 }
Daniel Veillarda37aab82003-06-09 09:10:36 +0000874
875 (void) memset(ret, 0, sizeof (xmlValidCtxt));
876
877 return (ret);
878}
879
880/**
881 * xmlFreeValidCtxt:
882 * @cur: the validation context to free
883 *
884 * Free a validation context structure.
885 */
886void
887xmlFreeValidCtxt(xmlValidCtxtPtr cur) {
Daniel Veillardc0be74b2004-11-03 19:16:55 +0000888 if (cur->vstateTab != NULL)
889 xmlFree(cur->vstateTab);
890 if (cur->nodeTab != NULL)
891 xmlFree(cur->nodeTab);
Daniel Veillarda37aab82003-06-09 09:10:36 +0000892 xmlFree(cur);
893}
894
Daniel Veillard4432df22003-09-28 18:58:27 +0000895#endif /* LIBXML_VALID_ENABLED */
896
Daniel Veillarda37aab82003-06-09 09:10:36 +0000897/**
Owen Taylor3473f882001-02-23 17:55:21 +0000898 * xmlNewElementContent:
899 * @name: the subelement name or NULL
900 * @type: the type of element content decl
901 *
902 * Allocate an element content structure.
903 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000904 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000905 */
906xmlElementContentPtr
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000907xmlNewElementContent(const xmlChar *name, xmlElementContentType type) {
Owen Taylor3473f882001-02-23 17:55:21 +0000908 xmlElementContentPtr ret;
909
910 switch(type) {
911 case XML_ELEMENT_CONTENT_ELEMENT:
912 if (name == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000913 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
914 "xmlNewElementContent : name == NULL !\n",
915 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000916 }
917 break;
918 case XML_ELEMENT_CONTENT_PCDATA:
919 case XML_ELEMENT_CONTENT_SEQ:
920 case XML_ELEMENT_CONTENT_OR:
921 if (name != NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000922 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
923 "xmlNewElementContent : name != NULL !\n",
924 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000925 }
926 break;
927 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000928 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
929 "Internal: ELEMENT content corrupted invalid type\n",
930 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000931 return(NULL);
932 }
933 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
934 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000935 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +0000936 return(NULL);
937 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000938 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000939 ret->type = type;
940 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000941 if (name != NULL) {
942 xmlChar *prefix = NULL;
943 ret->name = xmlSplitQName2(name, &prefix);
944 if (ret->name == NULL)
945 ret->name = xmlStrdup(name);
946 ret->prefix = prefix;
947 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000948 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000949 ret->prefix = NULL;
950 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000951 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000952 return(ret);
953}
954
955/**
956 * xmlCopyElementContent:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000957 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +0000958 *
959 * Build a copy of an element content description.
960 *
961 * Returns the new xmlElementContentPtr or NULL in case of error.
962 */
963xmlElementContentPtr
964xmlCopyElementContent(xmlElementContentPtr cur) {
965 xmlElementContentPtr ret;
966
967 if (cur == NULL) return(NULL);
968 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
969 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000970 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +0000971 return(NULL);
972 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000973 if (cur->prefix != NULL)
974 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000975 ret->ocur = cur->ocur;
976 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000977 if (ret->c1 != NULL)
978 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000979 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000980 if (ret->c2 != NULL)
981 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000982 return(ret);
983}
984
985/**
986 * xmlFreeElementContent:
987 * @cur: the element content tree to free
988 *
989 * Free an element content structure. This is a recursive call !
990 */
991void
992xmlFreeElementContent(xmlElementContentPtr cur) {
993 if (cur == NULL) return;
994 switch (cur->type) {
995 case XML_ELEMENT_CONTENT_PCDATA:
996 case XML_ELEMENT_CONTENT_ELEMENT:
997 case XML_ELEMENT_CONTENT_SEQ:
998 case XML_ELEMENT_CONTENT_OR:
999 break;
1000 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001001 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
1002 "Internal: ELEMENT content corrupted invalid type\n",
1003 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001004 return;
1005 }
1006 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
1007 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
1008 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001009 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001010 xmlFree(cur);
1011}
1012
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001013#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001014/**
1015 * xmlDumpElementContent:
1016 * @buf: An XML buffer
1017 * @content: An element table
1018 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
1019 *
1020 * This will dump the content of the element table as an XML DTD definition
1021 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001022static void
Owen Taylor3473f882001-02-23 17:55:21 +00001023xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
1024 if (content == NULL) return;
1025
1026 if (glob) xmlBufferWriteChar(buf, "(");
1027 switch (content->type) {
1028 case XML_ELEMENT_CONTENT_PCDATA:
1029 xmlBufferWriteChar(buf, "#PCDATA");
1030 break;
1031 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001032 if (content->prefix != NULL) {
1033 xmlBufferWriteCHAR(buf, content->prefix);
1034 xmlBufferWriteChar(buf, ":");
1035 }
Owen Taylor3473f882001-02-23 17:55:21 +00001036 xmlBufferWriteCHAR(buf, content->name);
1037 break;
1038 case XML_ELEMENT_CONTENT_SEQ:
1039 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1040 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
1041 xmlDumpElementContent(buf, content->c1, 1);
1042 else
1043 xmlDumpElementContent(buf, content->c1, 0);
1044 xmlBufferWriteChar(buf, " , ");
William M. Brack4119d1c2004-06-24 02:24:44 +00001045 if ((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
1046 ((content->c2->type == XML_ELEMENT_CONTENT_SEQ) &&
1047 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)))
Owen Taylor3473f882001-02-23 17:55:21 +00001048 xmlDumpElementContent(buf, content->c2, 1);
1049 else
1050 xmlDumpElementContent(buf, content->c2, 0);
1051 break;
1052 case XML_ELEMENT_CONTENT_OR:
1053 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1054 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
1055 xmlDumpElementContent(buf, content->c1, 1);
1056 else
1057 xmlDumpElementContent(buf, content->c1, 0);
1058 xmlBufferWriteChar(buf, " | ");
William M. Brack4119d1c2004-06-24 02:24:44 +00001059 if ((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
1060 ((content->c2->type == XML_ELEMENT_CONTENT_OR) &&
1061 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)))
Owen Taylor3473f882001-02-23 17:55:21 +00001062 xmlDumpElementContent(buf, content->c2, 1);
1063 else
1064 xmlDumpElementContent(buf, content->c2, 0);
1065 break;
1066 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001067 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
1068 "Internal: ELEMENT content corrupted invalid type\n",
1069 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001070 }
1071 if (glob)
1072 xmlBufferWriteChar(buf, ")");
1073 switch (content->ocur) {
1074 case XML_ELEMENT_CONTENT_ONCE:
1075 break;
1076 case XML_ELEMENT_CONTENT_OPT:
1077 xmlBufferWriteChar(buf, "?");
1078 break;
1079 case XML_ELEMENT_CONTENT_MULT:
1080 xmlBufferWriteChar(buf, "*");
1081 break;
1082 case XML_ELEMENT_CONTENT_PLUS:
1083 xmlBufferWriteChar(buf, "+");
1084 break;
1085 }
1086}
1087
1088/**
1089 * xmlSprintfElementContent:
1090 * @buf: an output buffer
1091 * @content: An element table
1092 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
1093 *
Daniel Veillardd3d06722001-08-15 12:06:36 +00001094 * Deprecated, unsafe, use xmlSnprintfElementContent
1095 */
1096void
1097xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
1098 xmlElementContentPtr content ATTRIBUTE_UNUSED,
1099 int glob ATTRIBUTE_UNUSED) {
1100}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001101#endif /* LIBXML_OUTPUT_ENABLED */
Daniel Veillardd3d06722001-08-15 12:06:36 +00001102
1103/**
1104 * xmlSnprintfElementContent:
1105 * @buf: an output buffer
1106 * @size: the buffer size
1107 * @content: An element table
1108 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
1109 *
Owen Taylor3473f882001-02-23 17:55:21 +00001110 * This will dump the content of the element content definition
1111 * Intended just for the debug routine
1112 */
1113void
Daniel Veillardd3d06722001-08-15 12:06:36 +00001114xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
1115 int len;
1116
Owen Taylor3473f882001-02-23 17:55:21 +00001117 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +00001118 len = strlen(buf);
1119 if (size - len < 50) {
1120 if ((size - len > 4) && (buf[len - 1] != '.'))
1121 strcat(buf, " ...");
1122 return;
1123 }
Owen Taylor3473f882001-02-23 17:55:21 +00001124 if (glob) strcat(buf, "(");
1125 switch (content->type) {
1126 case XML_ELEMENT_CONTENT_PCDATA:
1127 strcat(buf, "#PCDATA");
1128 break;
1129 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001130 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00001131 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001132 strcat(buf, " ...");
1133 return;
1134 }
1135 strcat(buf, (char *) content->prefix);
1136 strcat(buf, ":");
1137 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00001138 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00001139 strcat(buf, " ...");
1140 return;
1141 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001142 if (content->name != NULL)
1143 strcat(buf, (char *) content->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001144 break;
1145 case XML_ELEMENT_CONTENT_SEQ:
1146 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1147 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001148 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001149 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001150 xmlSnprintfElementContent(buf, size, content->c1, 0);
1151 len = strlen(buf);
1152 if (size - len < 50) {
1153 if ((size - len > 4) && (buf[len - 1] != '.'))
1154 strcat(buf, " ...");
1155 return;
1156 }
Owen Taylor3473f882001-02-23 17:55:21 +00001157 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00001158 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
1159 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1160 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001161 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001162 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001163 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001164 break;
1165 case XML_ELEMENT_CONTENT_OR:
1166 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1167 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001168 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001169 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001170 xmlSnprintfElementContent(buf, size, content->c1, 0);
1171 len = strlen(buf);
1172 if (size - len < 50) {
1173 if ((size - len > 4) && (buf[len - 1] != '.'))
1174 strcat(buf, " ...");
1175 return;
1176 }
Owen Taylor3473f882001-02-23 17:55:21 +00001177 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00001178 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
1179 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1180 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001181 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001182 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001183 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001184 break;
1185 }
1186 if (glob)
1187 strcat(buf, ")");
1188 switch (content->ocur) {
1189 case XML_ELEMENT_CONTENT_ONCE:
1190 break;
1191 case XML_ELEMENT_CONTENT_OPT:
1192 strcat(buf, "?");
1193 break;
1194 case XML_ELEMENT_CONTENT_MULT:
1195 strcat(buf, "*");
1196 break;
1197 case XML_ELEMENT_CONTENT_PLUS:
1198 strcat(buf, "+");
1199 break;
1200 }
1201}
1202
1203/****************************************************************
1204 * *
1205 * Registration of DTD declarations *
1206 * *
1207 ****************************************************************/
1208
1209/**
1210 * xmlCreateElementTable:
1211 *
1212 * create and initialize an empty element hash table.
1213 *
1214 * Returns the xmlElementTablePtr just created or NULL in case of error.
1215 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001216static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001217xmlCreateElementTable(void) {
1218 return(xmlHashCreate(0));
1219}
1220
1221/**
1222 * xmlFreeElement:
1223 * @elem: An element
1224 *
1225 * Deallocate the memory used by an element definition
1226 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001227static void
Owen Taylor3473f882001-02-23 17:55:21 +00001228xmlFreeElement(xmlElementPtr elem) {
1229 if (elem == NULL) return;
1230 xmlUnlinkNode((xmlNodePtr) elem);
1231 xmlFreeElementContent(elem->content);
1232 if (elem->name != NULL)
1233 xmlFree((xmlChar *) elem->name);
1234 if (elem->prefix != NULL)
1235 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001236#ifdef LIBXML_REGEXP_ENABLED
1237 if (elem->contModel != NULL)
1238 xmlRegFreeRegexp(elem->contModel);
1239#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001240 xmlFree(elem);
1241}
1242
1243
1244/**
1245 * xmlAddElementDecl:
1246 * @ctxt: the validation context
1247 * @dtd: pointer to the DTD
1248 * @name: the entity name
1249 * @type: the element type
1250 * @content: the element content tree or NULL
1251 *
1252 * Register a new element declaration
1253 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001254 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001255 */
1256xmlElementPtr
William M. Brackedb65a72004-02-06 07:36:04 +00001257xmlAddElementDecl(xmlValidCtxtPtr ctxt,
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001258 xmlDtdPtr dtd, const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001259 xmlElementTypeVal type,
1260 xmlElementContentPtr content) {
1261 xmlElementPtr ret;
1262 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001263 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001264 xmlChar *ns, *uqname;
1265
1266 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001267 return(NULL);
1268 }
1269 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001270 return(NULL);
1271 }
1272 switch (type) {
1273 case XML_ELEMENT_TYPE_EMPTY:
1274 if (content != NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001275 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1276 "xmlAddElementDecl: content != NULL for EMPTY\n",
1277 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001278 return(NULL);
1279 }
1280 break;
1281 case XML_ELEMENT_TYPE_ANY:
1282 if (content != NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001283 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1284 "xmlAddElementDecl: content != NULL for ANY\n",
1285 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001286 return(NULL);
1287 }
1288 break;
1289 case XML_ELEMENT_TYPE_MIXED:
1290 if (content == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001291 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1292 "xmlAddElementDecl: content == NULL for MIXED\n",
1293 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001294 return(NULL);
1295 }
1296 break;
1297 case XML_ELEMENT_TYPE_ELEMENT:
1298 if (content == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001299 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1300 "xmlAddElementDecl: content == NULL for ELEMENT\n",
1301 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001302 return(NULL);
1303 }
1304 break;
1305 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001306 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1307 "Internal: ELEMENT decl corrupted invalid type\n",
1308 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001309 return(NULL);
1310 }
1311
1312 /*
1313 * check if name is a QName
1314 */
1315 uqname = xmlSplitQName2(name, &ns);
1316 if (uqname != NULL)
1317 name = uqname;
1318
1319 /*
1320 * Create the Element table if needed.
1321 */
1322 table = (xmlElementTablePtr) dtd->elements;
1323 if (table == NULL) {
1324 table = xmlCreateElementTable();
1325 dtd->elements = (void *) table;
1326 }
1327 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001328 xmlVErrMemory(ctxt,
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001329 "xmlAddElementDecl: Table creation failed!\n");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001330 if (uqname != NULL)
1331 xmlFree(uqname);
1332 if (ns != NULL)
1333 xmlFree(ns);
Owen Taylor3473f882001-02-23 17:55:21 +00001334 return(NULL);
1335 }
1336
Daniel Veillarda10efa82001-04-18 13:09:01 +00001337 /*
1338 * lookup old attributes inserted on an undefined element in the
1339 * internal subset.
1340 */
1341 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1342 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1343 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1344 oldAttributes = ret->attributes;
1345 ret->attributes = NULL;
1346 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1347 xmlFreeElement(ret);
1348 }
Owen Taylor3473f882001-02-23 17:55:21 +00001349 }
Owen Taylor3473f882001-02-23 17:55:21 +00001350
1351 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001352 * The element may already be present if one of its attribute
1353 * was registered first
1354 */
1355 ret = xmlHashLookup2(table, name, ns);
1356 if (ret != NULL) {
1357 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001358#ifdef LIBXML_VALID_ENABLED
Daniel Veillarda10efa82001-04-18 13:09:01 +00001359 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001360 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001361 */
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001362 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED,
1363 "Redefinition of element %s\n",
1364 name, NULL, NULL);
Daniel Veillard4432df22003-09-28 18:58:27 +00001365#endif /* LIBXML_VALID_ENABLED */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001366 if (uqname != NULL)
1367 xmlFree(uqname);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001368 if (ns != NULL)
1369 xmlFree(ns);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001370 return(NULL);
1371 }
1372 } else {
1373 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1374 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001375 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001376 if (uqname != NULL)
1377 xmlFree(uqname);
1378 if (ns != NULL)
1379 xmlFree(ns);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001380 return(NULL);
1381 }
1382 memset(ret, 0, sizeof(xmlElement));
1383 ret->type = XML_ELEMENT_DECL;
1384
1385 /*
1386 * fill the structure.
1387 */
1388 ret->name = xmlStrdup(name);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001389 if (ret->name == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001390 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001391 if (uqname != NULL)
1392 xmlFree(uqname);
1393 if (ns != NULL)
1394 xmlFree(ns);
1395 xmlFree(ret);
1396 return(NULL);
1397 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00001398 ret->prefix = ns;
1399
1400 /*
1401 * Validity Check:
1402 * Insertion must not fail
1403 */
1404 if (xmlHashAddEntry2(table, name, ns, ret)) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001405#ifdef LIBXML_VALID_ENABLED
Daniel Veillarda10efa82001-04-18 13:09:01 +00001406 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001407 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001408 */
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001409 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED,
1410 "Redefinition of element %s\n",
1411 name, NULL, NULL);
Daniel Veillard4432df22003-09-28 18:58:27 +00001412#endif /* LIBXML_VALID_ENABLED */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001413 xmlFreeElement(ret);
1414 if (uqname != NULL)
1415 xmlFree(uqname);
1416 return(NULL);
1417 }
William M. Brack4e52f2f2003-09-14 18:07:39 +00001418 /*
1419 * For new element, may have attributes from earlier
1420 * definition in internal subset
1421 */
1422 ret->attributes = oldAttributes;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001423 }
1424
1425 /*
1426 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001427 */
1428 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001429 ret->content = xmlCopyElementContent(content);
Owen Taylor3473f882001-02-23 17:55:21 +00001430
1431 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001432 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001433 */
1434 ret->parent = dtd;
1435 ret->doc = dtd->doc;
1436 if (dtd->last == NULL) {
1437 dtd->children = dtd->last = (xmlNodePtr) ret;
1438 } else {
1439 dtd->last->next = (xmlNodePtr) ret;
1440 ret->prev = dtd->last;
1441 dtd->last = (xmlNodePtr) ret;
1442 }
1443 if (uqname != NULL)
1444 xmlFree(uqname);
1445 return(ret);
1446}
1447
1448/**
1449 * xmlFreeElementTable:
1450 * @table: An element table
1451 *
1452 * Deallocate the memory used by an element hash table.
1453 */
1454void
1455xmlFreeElementTable(xmlElementTablePtr table) {
1456 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1457}
1458
Daniel Veillard652327a2003-09-29 18:02:38 +00001459#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001460/**
1461 * xmlCopyElement:
1462 * @elem: An element
1463 *
1464 * Build a copy of an element.
1465 *
1466 * Returns the new xmlElementPtr or NULL in case of error.
1467 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001468static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001469xmlCopyElement(xmlElementPtr elem) {
1470 xmlElementPtr cur;
1471
1472 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1473 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001474 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001475 return(NULL);
1476 }
1477 memset(cur, 0, sizeof(xmlElement));
1478 cur->type = XML_ELEMENT_DECL;
1479 cur->etype = elem->etype;
1480 if (elem->name != NULL)
1481 cur->name = xmlStrdup(elem->name);
1482 else
1483 cur->name = NULL;
1484 if (elem->prefix != NULL)
1485 cur->prefix = xmlStrdup(elem->prefix);
1486 else
1487 cur->prefix = NULL;
1488 cur->content = xmlCopyElementContent(elem->content);
1489 /* TODO : rebuild the attribute list on the copy */
1490 cur->attributes = NULL;
1491 return(cur);
1492}
1493
1494/**
1495 * xmlCopyElementTable:
1496 * @table: An element table
1497 *
1498 * Build a copy of an element table.
1499 *
1500 * Returns the new xmlElementTablePtr or NULL in case of error.
1501 */
1502xmlElementTablePtr
1503xmlCopyElementTable(xmlElementTablePtr table) {
1504 return((xmlElementTablePtr) xmlHashCopy(table,
1505 (xmlHashCopier) xmlCopyElement));
1506}
Daniel Veillard652327a2003-09-29 18:02:38 +00001507#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001508
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001509#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001510/**
1511 * xmlDumpElementDecl:
1512 * @buf: the XML buffer output
1513 * @elem: An element table
1514 *
1515 * This will dump the content of the element declaration as an XML
1516 * DTD definition
1517 */
1518void
1519xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00001520 if ((buf == NULL) || (elem == NULL))
1521 return;
Owen Taylor3473f882001-02-23 17:55:21 +00001522 switch (elem->etype) {
1523 case XML_ELEMENT_TYPE_EMPTY:
1524 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001525 if (elem->prefix != NULL) {
1526 xmlBufferWriteCHAR(buf, elem->prefix);
1527 xmlBufferWriteChar(buf, ":");
1528 }
Owen Taylor3473f882001-02-23 17:55:21 +00001529 xmlBufferWriteCHAR(buf, elem->name);
1530 xmlBufferWriteChar(buf, " EMPTY>\n");
1531 break;
1532 case XML_ELEMENT_TYPE_ANY:
1533 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001534 if (elem->prefix != NULL) {
1535 xmlBufferWriteCHAR(buf, elem->prefix);
1536 xmlBufferWriteChar(buf, ":");
1537 }
Owen Taylor3473f882001-02-23 17:55:21 +00001538 xmlBufferWriteCHAR(buf, elem->name);
1539 xmlBufferWriteChar(buf, " ANY>\n");
1540 break;
1541 case XML_ELEMENT_TYPE_MIXED:
1542 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001543 if (elem->prefix != NULL) {
1544 xmlBufferWriteCHAR(buf, elem->prefix);
1545 xmlBufferWriteChar(buf, ":");
1546 }
Owen Taylor3473f882001-02-23 17:55:21 +00001547 xmlBufferWriteCHAR(buf, elem->name);
1548 xmlBufferWriteChar(buf, " ");
1549 xmlDumpElementContent(buf, elem->content, 1);
1550 xmlBufferWriteChar(buf, ">\n");
1551 break;
1552 case XML_ELEMENT_TYPE_ELEMENT:
1553 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001554 if (elem->prefix != NULL) {
1555 xmlBufferWriteCHAR(buf, elem->prefix);
1556 xmlBufferWriteChar(buf, ":");
1557 }
Owen Taylor3473f882001-02-23 17:55:21 +00001558 xmlBufferWriteCHAR(buf, elem->name);
1559 xmlBufferWriteChar(buf, " ");
1560 xmlDumpElementContent(buf, elem->content, 1);
1561 xmlBufferWriteChar(buf, ">\n");
1562 break;
1563 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001564 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
1565 "Internal: ELEMENT struct corrupted invalid type\n",
1566 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001567 }
1568}
1569
1570/**
William M. Brack9e660592003-10-20 14:56:06 +00001571 * xmlDumpElementDeclScan:
1572 * @elem: An element table
1573 * @buf: the XML buffer output
1574 *
1575 * This routine is used by the hash scan function. It just reverses
1576 * the arguments.
1577 */
1578static void
1579xmlDumpElementDeclScan(xmlElementPtr elem, xmlBufferPtr buf) {
1580 xmlDumpElementDecl(buf, elem);
1581}
1582
1583/**
Owen Taylor3473f882001-02-23 17:55:21 +00001584 * xmlDumpElementTable:
1585 * @buf: the XML buffer output
1586 * @table: An element table
1587 *
1588 * This will dump the content of the element table as an XML DTD definition
1589 */
1590void
1591xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00001592 if ((buf == NULL) || (table == NULL))
1593 return;
William M. Brack9e660592003-10-20 14:56:06 +00001594 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDeclScan, buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001595}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001596#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001597
1598/**
1599 * xmlCreateEnumeration:
1600 * @name: the enumeration name or NULL
1601 *
1602 * create and initialize an enumeration attribute node.
1603 *
1604 * Returns the xmlEnumerationPtr just created or NULL in case
1605 * of error.
1606 */
1607xmlEnumerationPtr
Daniel Veillard2fdbd322003-08-18 12:15:38 +00001608xmlCreateEnumeration(const xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00001609 xmlEnumerationPtr ret;
1610
1611 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1612 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001613 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001614 return(NULL);
1615 }
1616 memset(ret, 0, sizeof(xmlEnumeration));
1617
1618 if (name != NULL)
1619 ret->name = xmlStrdup(name);
1620 return(ret);
1621}
1622
1623/**
1624 * xmlFreeEnumeration:
1625 * @cur: the tree to free.
1626 *
1627 * free an enumeration attribute node (recursive).
1628 */
1629void
1630xmlFreeEnumeration(xmlEnumerationPtr cur) {
1631 if (cur == NULL) return;
1632
1633 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1634
1635 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001636 xmlFree(cur);
1637}
1638
Daniel Veillard652327a2003-09-29 18:02:38 +00001639#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001640/**
1641 * xmlCopyEnumeration:
1642 * @cur: the tree to copy.
1643 *
1644 * Copy an enumeration attribute node (recursive).
1645 *
1646 * Returns the xmlEnumerationPtr just created or NULL in case
1647 * of error.
1648 */
1649xmlEnumerationPtr
1650xmlCopyEnumeration(xmlEnumerationPtr cur) {
1651 xmlEnumerationPtr ret;
1652
1653 if (cur == NULL) return(NULL);
1654 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1655
1656 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1657 else ret->next = NULL;
1658
1659 return(ret);
1660}
Daniel Veillard652327a2003-09-29 18:02:38 +00001661#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001662
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001663#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001664/**
1665 * xmlDumpEnumeration:
1666 * @buf: the XML buffer output
1667 * @enum: An enumeration
1668 *
1669 * This will dump the content of the enumeration
1670 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001671static void
Owen Taylor3473f882001-02-23 17:55:21 +00001672xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00001673 if ((buf == NULL) || (cur == NULL))
1674 return;
Owen Taylor3473f882001-02-23 17:55:21 +00001675
1676 xmlBufferWriteCHAR(buf, cur->name);
1677 if (cur->next == NULL)
1678 xmlBufferWriteChar(buf, ")");
1679 else {
1680 xmlBufferWriteChar(buf, " | ");
1681 xmlDumpEnumeration(buf, cur->next);
1682 }
1683}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001684#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001685
1686/**
1687 * xmlCreateAttributeTable:
1688 *
1689 * create and initialize an empty attribute hash table.
1690 *
1691 * Returns the xmlAttributeTablePtr just created or NULL in case
1692 * of error.
1693 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001694static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001695xmlCreateAttributeTable(void) {
1696 return(xmlHashCreate(0));
1697}
1698
Daniel Veillard4432df22003-09-28 18:58:27 +00001699#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001700/**
1701 * xmlScanAttributeDeclCallback:
1702 * @attr: the attribute decl
1703 * @list: the list to update
1704 *
1705 * Callback called by xmlScanAttributeDecl when a new attribute
1706 * has to be entered in the list.
1707 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001708static void
Owen Taylor3473f882001-02-23 17:55:21 +00001709xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001710 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001711 attr->nexth = *list;
1712 *list = attr;
1713}
1714
1715/**
1716 * xmlScanAttributeDecl:
1717 * @dtd: pointer to the DTD
1718 * @elem: the element name
1719 *
1720 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001721 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001722 *
1723 * Returns the pointer to the first attribute decl in the chain,
1724 * possibly NULL.
1725 */
1726xmlAttributePtr
1727xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1728 xmlAttributePtr ret = NULL;
1729 xmlAttributeTablePtr table;
1730
1731 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001732 return(NULL);
1733 }
1734 if (elem == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001735 return(NULL);
1736 }
1737 table = (xmlAttributeTablePtr) dtd->attributes;
1738 if (table == NULL)
1739 return(NULL);
1740
1741 /* WRONG !!! */
1742 xmlHashScan3(table, NULL, NULL, elem,
1743 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1744 return(ret);
1745}
1746
1747/**
1748 * xmlScanIDAttributeDecl:
1749 * @ctxt: the validation context
1750 * @elem: the element name
1751 *
1752 * Verify that the element don't have too many ID attributes
1753 * declared.
1754 *
1755 * Returns the number of ID attributes found.
1756 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001757static int
Owen Taylor3473f882001-02-23 17:55:21 +00001758xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1759 xmlAttributePtr cur;
1760 int ret = 0;
1761
1762 if (elem == NULL) return(0);
1763 cur = elem->attributes;
1764 while (cur != NULL) {
1765 if (cur->atype == XML_ATTRIBUTE_ID) {
1766 ret ++;
1767 if (ret > 1)
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001768 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_MULTIPLE_ID,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001769 "Element %s has too many ID attributes defined : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001770 elem->name, cur->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001771 }
1772 cur = cur->nexth;
1773 }
1774 return(ret);
1775}
Daniel Veillard4432df22003-09-28 18:58:27 +00001776#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001777
1778/**
1779 * xmlFreeAttribute:
1780 * @elem: An attribute
1781 *
1782 * Deallocate the memory used by an attribute definition
1783 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001784static void
Owen Taylor3473f882001-02-23 17:55:21 +00001785xmlFreeAttribute(xmlAttributePtr attr) {
1786 if (attr == NULL) return;
1787 xmlUnlinkNode((xmlNodePtr) attr);
1788 if (attr->tree != NULL)
1789 xmlFreeEnumeration(attr->tree);
1790 if (attr->elem != NULL)
1791 xmlFree((xmlChar *) attr->elem);
1792 if (attr->name != NULL)
1793 xmlFree((xmlChar *) attr->name);
1794 if (attr->defaultValue != NULL)
1795 xmlFree((xmlChar *) attr->defaultValue);
1796 if (attr->prefix != NULL)
1797 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001798 xmlFree(attr);
1799}
1800
1801
1802/**
1803 * xmlAddAttributeDecl:
1804 * @ctxt: the validation context
1805 * @dtd: pointer to the DTD
1806 * @elem: the element name
1807 * @name: the attribute name
1808 * @ns: the attribute namespace prefix
1809 * @type: the attribute type
1810 * @def: the attribute default type
1811 * @defaultValue: the attribute default value
1812 * @tree: if it's an enumeration, the associated list
1813 *
1814 * Register a new attribute declaration
1815 * Note that @tree becomes the ownership of the DTD
1816 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001817 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001818 */
1819xmlAttributePtr
William M. Brackedb65a72004-02-06 07:36:04 +00001820xmlAddAttributeDecl(xmlValidCtxtPtr ctxt,
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001821 xmlDtdPtr dtd, const xmlChar *elem,
Owen Taylor3473f882001-02-23 17:55:21 +00001822 const xmlChar *name, const xmlChar *ns,
1823 xmlAttributeType type, xmlAttributeDefault def,
1824 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1825 xmlAttributePtr ret;
1826 xmlAttributeTablePtr table;
1827 xmlElementPtr elemDef;
1828
1829 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001830 xmlFreeEnumeration(tree);
1831 return(NULL);
1832 }
1833 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001834 xmlFreeEnumeration(tree);
1835 return(NULL);
1836 }
1837 if (elem == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001838 xmlFreeEnumeration(tree);
1839 return(NULL);
1840 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001841
Daniel Veillard4432df22003-09-28 18:58:27 +00001842#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001843 /*
1844 * Check the type and possibly the default value.
1845 */
1846 switch (type) {
1847 case XML_ATTRIBUTE_CDATA:
1848 break;
1849 case XML_ATTRIBUTE_ID:
1850 break;
1851 case XML_ATTRIBUTE_IDREF:
1852 break;
1853 case XML_ATTRIBUTE_IDREFS:
1854 break;
1855 case XML_ATTRIBUTE_ENTITY:
1856 break;
1857 case XML_ATTRIBUTE_ENTITIES:
1858 break;
1859 case XML_ATTRIBUTE_NMTOKEN:
1860 break;
1861 case XML_ATTRIBUTE_NMTOKENS:
1862 break;
1863 case XML_ATTRIBUTE_ENUMERATION:
1864 break;
1865 case XML_ATTRIBUTE_NOTATION:
1866 break;
1867 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001868 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1869 "Internal: ATTRIBUTE struct corrupted invalid type\n",
1870 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001871 xmlFreeEnumeration(tree);
1872 return(NULL);
1873 }
1874 if ((defaultValue != NULL) &&
1875 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001876 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_DEFAULT,
1877 "Attribute %s of %s: invalid default value\n",
1878 elem, name, defaultValue);
Owen Taylor3473f882001-02-23 17:55:21 +00001879 defaultValue = NULL;
Daniel Veillard42595322004-11-08 10:52:06 +00001880 if (ctxt != NULL)
1881 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001882 }
Daniel Veillard4432df22003-09-28 18:58:27 +00001883#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001884
1885 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001886 * Check first that an attribute defined in the external subset wasn't
1887 * already defined in the internal subset
1888 */
1889 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1890 (dtd->doc->intSubset != NULL) &&
1891 (dtd->doc->intSubset->attributes != NULL)) {
1892 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1893 if (ret != NULL)
1894 return(NULL);
1895 }
1896
1897 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001898 * Create the Attribute table if needed.
1899 */
1900 table = (xmlAttributeTablePtr) dtd->attributes;
1901 if (table == NULL) {
1902 table = xmlCreateAttributeTable();
1903 dtd->attributes = (void *) table;
1904 }
1905 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001906 xmlVErrMemory(ctxt,
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001907 "xmlAddAttributeDecl: Table creation failed!\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001908 return(NULL);
1909 }
1910
1911
1912 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1913 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001914 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001915 return(NULL);
1916 }
1917 memset(ret, 0, sizeof(xmlAttribute));
1918 ret->type = XML_ATTRIBUTE_DECL;
1919
1920 /*
1921 * fill the structure.
1922 */
1923 ret->atype = type;
1924 ret->name = xmlStrdup(name);
1925 ret->prefix = xmlStrdup(ns);
1926 ret->elem = xmlStrdup(elem);
1927 ret->def = def;
1928 ret->tree = tree;
1929 if (defaultValue != NULL)
1930 ret->defaultValue = xmlStrdup(defaultValue);
1931
1932 /*
1933 * Validity Check:
1934 * Search the DTD for previous declarations of the ATTLIST
1935 */
1936 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001937#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001938 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001939 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001940 */
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001941 xmlErrValidWarning(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_REDEFINED,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001942 "Attribute %s of element %s: already defined\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001943 name, elem, NULL);
Daniel Veillard4432df22003-09-28 18:58:27 +00001944#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001945 xmlFreeAttribute(ret);
1946 return(NULL);
1947 }
1948
1949 /*
1950 * Validity Check:
1951 * Multiple ID per element
1952 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001953 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001954 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001955
Daniel Veillard4432df22003-09-28 18:58:27 +00001956#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001957 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001958 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001959 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID,
Owen Taylor3473f882001-02-23 17:55:21 +00001960 "Element %s has too may ID attributes defined : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001961 elem, name, NULL);
Daniel Veillard42595322004-11-08 10:52:06 +00001962 if (ctxt != NULL)
1963 ctxt->valid = 0;
Daniel Veillardc7612992002-02-17 22:47:37 +00001964 }
Daniel Veillard4432df22003-09-28 18:58:27 +00001965#endif /* LIBXML_VALID_ENABLED */
Daniel Veillardc7612992002-02-17 22:47:37 +00001966
Daniel Veillard48da9102001-08-07 01:10:10 +00001967 /*
1968 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001969 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001970 */
1971 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1972 ((ret->prefix != NULL &&
1973 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1974 ret->nexth = elemDef->attributes;
1975 elemDef->attributes = ret;
1976 } else {
1977 xmlAttributePtr tmp = elemDef->attributes;
1978
1979 while ((tmp != NULL) &&
1980 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1981 ((ret->prefix != NULL &&
1982 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1983 if (tmp->nexth == NULL)
1984 break;
1985 tmp = tmp->nexth;
1986 }
1987 if (tmp != NULL) {
1988 ret->nexth = tmp->nexth;
1989 tmp->nexth = ret;
1990 } else {
1991 ret->nexth = elemDef->attributes;
1992 elemDef->attributes = ret;
1993 }
1994 }
Owen Taylor3473f882001-02-23 17:55:21 +00001995 }
1996
1997 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001998 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001999 */
2000 ret->parent = dtd;
2001 ret->doc = dtd->doc;
2002 if (dtd->last == NULL) {
2003 dtd->children = dtd->last = (xmlNodePtr) ret;
2004 } else {
2005 dtd->last->next = (xmlNodePtr) ret;
2006 ret->prev = dtd->last;
2007 dtd->last = (xmlNodePtr) ret;
2008 }
2009 return(ret);
2010}
2011
2012/**
2013 * xmlFreeAttributeTable:
2014 * @table: An attribute table
2015 *
2016 * Deallocate the memory used by an entities hash table.
2017 */
2018void
2019xmlFreeAttributeTable(xmlAttributeTablePtr table) {
2020 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
2021}
2022
Daniel Veillard652327a2003-09-29 18:02:38 +00002023#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002024/**
2025 * xmlCopyAttribute:
2026 * @attr: An attribute
2027 *
2028 * Build a copy of an attribute.
2029 *
2030 * Returns the new xmlAttributePtr or NULL in case of error.
2031 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002032static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002033xmlCopyAttribute(xmlAttributePtr attr) {
2034 xmlAttributePtr cur;
2035
2036 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
2037 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002038 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002039 return(NULL);
2040 }
2041 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00002042 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00002043 cur->atype = attr->atype;
2044 cur->def = attr->def;
2045 cur->tree = xmlCopyEnumeration(attr->tree);
2046 if (attr->elem != NULL)
2047 cur->elem = xmlStrdup(attr->elem);
2048 if (attr->name != NULL)
2049 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00002050 if (attr->prefix != NULL)
2051 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00002052 if (attr->defaultValue != NULL)
2053 cur->defaultValue = xmlStrdup(attr->defaultValue);
2054 return(cur);
2055}
2056
2057/**
2058 * xmlCopyAttributeTable:
2059 * @table: An attribute table
2060 *
2061 * Build a copy of an attribute table.
2062 *
2063 * Returns the new xmlAttributeTablePtr or NULL in case of error.
2064 */
2065xmlAttributeTablePtr
2066xmlCopyAttributeTable(xmlAttributeTablePtr table) {
2067 return((xmlAttributeTablePtr) xmlHashCopy(table,
2068 (xmlHashCopier) xmlCopyAttribute));
2069}
Daniel Veillard652327a2003-09-29 18:02:38 +00002070#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002071
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002072#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002073/**
2074 * xmlDumpAttributeDecl:
2075 * @buf: the XML buffer output
2076 * @attr: An attribute declaration
2077 *
2078 * This will dump the content of the attribute declaration as an XML
2079 * DTD definition
2080 */
2081void
2082xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00002083 if ((buf == NULL) || (attr == NULL))
2084 return;
Owen Taylor3473f882001-02-23 17:55:21 +00002085 xmlBufferWriteChar(buf, "<!ATTLIST ");
2086 xmlBufferWriteCHAR(buf, attr->elem);
2087 xmlBufferWriteChar(buf, " ");
2088 if (attr->prefix != NULL) {
2089 xmlBufferWriteCHAR(buf, attr->prefix);
2090 xmlBufferWriteChar(buf, ":");
2091 }
2092 xmlBufferWriteCHAR(buf, attr->name);
2093 switch (attr->atype) {
2094 case XML_ATTRIBUTE_CDATA:
2095 xmlBufferWriteChar(buf, " CDATA");
2096 break;
2097 case XML_ATTRIBUTE_ID:
2098 xmlBufferWriteChar(buf, " ID");
2099 break;
2100 case XML_ATTRIBUTE_IDREF:
2101 xmlBufferWriteChar(buf, " IDREF");
2102 break;
2103 case XML_ATTRIBUTE_IDREFS:
2104 xmlBufferWriteChar(buf, " IDREFS");
2105 break;
2106 case XML_ATTRIBUTE_ENTITY:
2107 xmlBufferWriteChar(buf, " ENTITY");
2108 break;
2109 case XML_ATTRIBUTE_ENTITIES:
2110 xmlBufferWriteChar(buf, " ENTITIES");
2111 break;
2112 case XML_ATTRIBUTE_NMTOKEN:
2113 xmlBufferWriteChar(buf, " NMTOKEN");
2114 break;
2115 case XML_ATTRIBUTE_NMTOKENS:
2116 xmlBufferWriteChar(buf, " NMTOKENS");
2117 break;
2118 case XML_ATTRIBUTE_ENUMERATION:
2119 xmlBufferWriteChar(buf, " (");
2120 xmlDumpEnumeration(buf, attr->tree);
2121 break;
2122 case XML_ATTRIBUTE_NOTATION:
2123 xmlBufferWriteChar(buf, " NOTATION (");
2124 xmlDumpEnumeration(buf, attr->tree);
2125 break;
2126 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002127 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2128 "Internal: ATTRIBUTE struct corrupted invalid type\n",
2129 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002130 }
2131 switch (attr->def) {
2132 case XML_ATTRIBUTE_NONE:
2133 break;
2134 case XML_ATTRIBUTE_REQUIRED:
2135 xmlBufferWriteChar(buf, " #REQUIRED");
2136 break;
2137 case XML_ATTRIBUTE_IMPLIED:
2138 xmlBufferWriteChar(buf, " #IMPLIED");
2139 break;
2140 case XML_ATTRIBUTE_FIXED:
2141 xmlBufferWriteChar(buf, " #FIXED");
2142 break;
2143 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002144 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2145 "Internal: ATTRIBUTE struct corrupted invalid def\n",
2146 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002147 }
2148 if (attr->defaultValue != NULL) {
2149 xmlBufferWriteChar(buf, " ");
2150 xmlBufferWriteQuotedString(buf, attr->defaultValue);
2151 }
2152 xmlBufferWriteChar(buf, ">\n");
2153}
2154
2155/**
William M. Brack9e660592003-10-20 14:56:06 +00002156 * xmlDumpAttributeDeclScan:
2157 * @attr: An attribute declaration
2158 * @buf: the XML buffer output
2159 *
2160 * This is used with the hash scan function - just reverses arguments
2161 */
2162static void
2163xmlDumpAttributeDeclScan(xmlAttributePtr attr, xmlBufferPtr buf) {
2164 xmlDumpAttributeDecl(buf, attr);
2165}
2166
2167/**
Owen Taylor3473f882001-02-23 17:55:21 +00002168 * xmlDumpAttributeTable:
2169 * @buf: the XML buffer output
2170 * @table: An attribute table
2171 *
2172 * This will dump the content of the attribute table as an XML DTD definition
2173 */
2174void
2175xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00002176 if ((buf == NULL) || (table == NULL))
2177 return;
William M. Brack9e660592003-10-20 14:56:06 +00002178 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDeclScan, buf);
Owen Taylor3473f882001-02-23 17:55:21 +00002179}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002180#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002181
2182/************************************************************************
2183 * *
2184 * NOTATIONs *
2185 * *
2186 ************************************************************************/
2187/**
2188 * xmlCreateNotationTable:
2189 *
2190 * create and initialize an empty notation hash table.
2191 *
2192 * Returns the xmlNotationTablePtr just created or NULL in case
2193 * of error.
2194 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002195static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002196xmlCreateNotationTable(void) {
2197 return(xmlHashCreate(0));
2198}
2199
2200/**
2201 * xmlFreeNotation:
2202 * @not: A notation
2203 *
2204 * Deallocate the memory used by an notation definition
2205 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002206static void
Owen Taylor3473f882001-02-23 17:55:21 +00002207xmlFreeNotation(xmlNotationPtr nota) {
2208 if (nota == NULL) return;
2209 if (nota->name != NULL)
2210 xmlFree((xmlChar *) nota->name);
2211 if (nota->PublicID != NULL)
2212 xmlFree((xmlChar *) nota->PublicID);
2213 if (nota->SystemID != NULL)
2214 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002215 xmlFree(nota);
2216}
2217
2218
2219/**
2220 * xmlAddNotationDecl:
2221 * @dtd: pointer to the DTD
2222 * @ctxt: the validation context
2223 * @name: the entity name
2224 * @PublicID: the public identifier or NULL
2225 * @SystemID: the system identifier or NULL
2226 *
2227 * Register a new notation declaration
2228 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002229 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00002230 */
2231xmlNotationPtr
William M. Brackedb65a72004-02-06 07:36:04 +00002232xmlAddNotationDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002233 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00002234 const xmlChar *PublicID, const xmlChar *SystemID) {
2235 xmlNotationPtr ret;
2236 xmlNotationTablePtr table;
2237
2238 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002239 return(NULL);
2240 }
2241 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002242 return(NULL);
2243 }
2244 if ((PublicID == NULL) && (SystemID == NULL)) {
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002245 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002246 }
2247
2248 /*
2249 * Create the Notation table if needed.
2250 */
2251 table = (xmlNotationTablePtr) dtd->notations;
2252 if (table == NULL)
2253 dtd->notations = table = xmlCreateNotationTable();
2254 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002255 xmlVErrMemory(ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00002256 "xmlAddNotationDecl: Table creation failed!\n");
2257 return(NULL);
2258 }
2259
2260 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2261 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002262 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002263 return(NULL);
2264 }
2265 memset(ret, 0, sizeof(xmlNotation));
2266
2267 /*
2268 * fill the structure.
2269 */
2270 ret->name = xmlStrdup(name);
2271 if (SystemID != NULL)
2272 ret->SystemID = xmlStrdup(SystemID);
2273 if (PublicID != NULL)
2274 ret->PublicID = xmlStrdup(PublicID);
2275
2276 /*
2277 * Validity Check:
2278 * Check the DTD for previous declarations of the ATTLIST
2279 */
2280 if (xmlHashAddEntry(table, name, ret)) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002281#ifdef LIBXML_VALID_ENABLED
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002282 xmlErrValid(NULL, XML_DTD_NOTATION_REDEFINED,
2283 "xmlAddNotationDecl: %s already defined\n",
2284 (const char *) name);
Daniel Veillard4432df22003-09-28 18:58:27 +00002285#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002286 xmlFreeNotation(ret);
2287 return(NULL);
2288 }
2289 return(ret);
2290}
2291
2292/**
2293 * xmlFreeNotationTable:
2294 * @table: An notation table
2295 *
2296 * Deallocate the memory used by an entities hash table.
2297 */
2298void
2299xmlFreeNotationTable(xmlNotationTablePtr table) {
2300 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2301}
2302
Daniel Veillard652327a2003-09-29 18:02:38 +00002303#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002304/**
2305 * xmlCopyNotation:
2306 * @nota: A notation
2307 *
2308 * Build a copy of a notation.
2309 *
2310 * Returns the new xmlNotationPtr or NULL in case of error.
2311 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002312static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002313xmlCopyNotation(xmlNotationPtr nota) {
2314 xmlNotationPtr cur;
2315
2316 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2317 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002318 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002319 return(NULL);
2320 }
2321 if (nota->name != NULL)
2322 cur->name = xmlStrdup(nota->name);
2323 else
2324 cur->name = NULL;
2325 if (nota->PublicID != NULL)
2326 cur->PublicID = xmlStrdup(nota->PublicID);
2327 else
2328 cur->PublicID = NULL;
2329 if (nota->SystemID != NULL)
2330 cur->SystemID = xmlStrdup(nota->SystemID);
2331 else
2332 cur->SystemID = NULL;
2333 return(cur);
2334}
2335
2336/**
2337 * xmlCopyNotationTable:
2338 * @table: A notation table
2339 *
2340 * Build a copy of a notation table.
2341 *
2342 * Returns the new xmlNotationTablePtr or NULL in case of error.
2343 */
2344xmlNotationTablePtr
2345xmlCopyNotationTable(xmlNotationTablePtr table) {
2346 return((xmlNotationTablePtr) xmlHashCopy(table,
2347 (xmlHashCopier) xmlCopyNotation));
2348}
Daniel Veillard652327a2003-09-29 18:02:38 +00002349#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002350
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002351#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002352/**
2353 * xmlDumpNotationDecl:
2354 * @buf: the XML buffer output
2355 * @nota: A notation declaration
2356 *
2357 * This will dump the content the notation declaration as an XML DTD definition
2358 */
2359void
2360xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00002361 if ((buf == NULL) || (nota == NULL))
2362 return;
Owen Taylor3473f882001-02-23 17:55:21 +00002363 xmlBufferWriteChar(buf, "<!NOTATION ");
2364 xmlBufferWriteCHAR(buf, nota->name);
2365 if (nota->PublicID != NULL) {
2366 xmlBufferWriteChar(buf, " PUBLIC ");
2367 xmlBufferWriteQuotedString(buf, nota->PublicID);
2368 if (nota->SystemID != NULL) {
2369 xmlBufferWriteChar(buf, " ");
Daniel Veillard41c4a752004-09-08 20:55:38 +00002370 xmlBufferWriteQuotedString(buf, nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002371 }
2372 } else {
2373 xmlBufferWriteChar(buf, " SYSTEM ");
Daniel Veillard41c4a752004-09-08 20:55:38 +00002374 xmlBufferWriteQuotedString(buf, nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002375 }
2376 xmlBufferWriteChar(buf, " >\n");
2377}
2378
2379/**
William M. Brack9e660592003-10-20 14:56:06 +00002380 * xmlDumpNotationDeclScan:
2381 * @nota: A notation declaration
2382 * @buf: the XML buffer output
2383 *
2384 * This is called with the hash scan function, and just reverses args
2385 */
2386static void
2387xmlDumpNotationDeclScan(xmlNotationPtr nota, xmlBufferPtr buf) {
2388 xmlDumpNotationDecl(buf, nota);
2389}
2390
2391/**
Owen Taylor3473f882001-02-23 17:55:21 +00002392 * xmlDumpNotationTable:
2393 * @buf: the XML buffer output
2394 * @table: A notation table
2395 *
2396 * This will dump the content of the notation table as an XML DTD definition
2397 */
2398void
2399xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00002400 if ((buf == NULL) || (table == NULL))
2401 return;
William M. Brack9e660592003-10-20 14:56:06 +00002402 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDeclScan, buf);
Owen Taylor3473f882001-02-23 17:55:21 +00002403}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002404#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002405
2406/************************************************************************
2407 * *
2408 * IDs *
2409 * *
2410 ************************************************************************/
2411/**
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002412 * DICT_FREE:
2413 * @str: a string
2414 *
2415 * Free a string if it is not owned by the "dict" dictionnary in the
2416 * current scope
2417 */
2418#define DICT_FREE(str) \
2419 if ((str) && ((!dict) || \
2420 (xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \
2421 xmlFree((char *)(str));
2422
2423/**
Owen Taylor3473f882001-02-23 17:55:21 +00002424 * xmlCreateIDTable:
2425 *
2426 * create and initialize an empty id hash table.
2427 *
2428 * Returns the xmlIDTablePtr just created or NULL in case
2429 * of error.
2430 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002431static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002432xmlCreateIDTable(void) {
2433 return(xmlHashCreate(0));
2434}
2435
2436/**
2437 * xmlFreeID:
2438 * @not: A id
2439 *
2440 * Deallocate the memory used by an id definition
2441 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002442static void
Owen Taylor3473f882001-02-23 17:55:21 +00002443xmlFreeID(xmlIDPtr id) {
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002444 xmlDictPtr dict = NULL;
2445
Owen Taylor3473f882001-02-23 17:55:21 +00002446 if (id == NULL) return;
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002447
2448 if (id->doc != NULL)
2449 dict = id->doc->dict;
2450
Owen Taylor3473f882001-02-23 17:55:21 +00002451 if (id->value != NULL)
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002452 DICT_FREE(id->value)
Daniel Veillardea7751d2002-12-20 00:16:24 +00002453 if (id->name != NULL)
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002454 DICT_FREE(id->name)
Owen Taylor3473f882001-02-23 17:55:21 +00002455 xmlFree(id);
2456}
2457
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002458
Owen Taylor3473f882001-02-23 17:55:21 +00002459/**
2460 * xmlAddID:
2461 * @ctxt: the validation context
2462 * @doc: pointer to the document
2463 * @value: the value name
2464 * @attr: the attribute holding the ID
2465 *
2466 * Register a new id declaration
2467 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002468 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002469 */
2470xmlIDPtr
2471xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2472 xmlAttrPtr attr) {
2473 xmlIDPtr ret;
2474 xmlIDTablePtr table;
2475
2476 if (doc == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002477 return(NULL);
2478 }
2479 if (value == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002480 return(NULL);
2481 }
2482 if (attr == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002483 return(NULL);
2484 }
2485
2486 /*
2487 * Create the ID table if needed.
2488 */
2489 table = (xmlIDTablePtr) doc->ids;
2490 if (table == NULL)
2491 doc->ids = table = xmlCreateIDTable();
2492 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002493 xmlVErrMemory(ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00002494 "xmlAddID: Table creation failed!\n");
2495 return(NULL);
2496 }
2497
2498 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2499 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002500 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002501 return(NULL);
2502 }
2503
2504 /*
2505 * fill the structure.
2506 */
2507 ret->value = xmlStrdup(value);
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002508 ret->doc = doc;
Daniel Veillardea7751d2002-12-20 00:16:24 +00002509 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2510 /*
2511 * Operating in streaming mode, attr is gonna disapear
2512 */
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002513 if (doc->dict != NULL)
2514 ret->name = xmlDictLookup(doc->dict, attr->name, -1);
2515 else
2516 ret->name = xmlStrdup(attr->name);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002517 ret->attr = NULL;
2518 } else {
2519 ret->attr = attr;
2520 ret->name = NULL;
2521 }
2522 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002523
2524 if (xmlHashAddEntry(table, value, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002525#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002526 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002527 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002528 */
Daniel Veillardd3669b22004-02-25 12:34:55 +00002529 if ((ctxt != NULL) && (ctxt->error != NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00002530 xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED,
2531 "ID %s already defined\n",
2532 value, NULL, NULL);
Daniel Veillard76575762002-09-05 14:21:15 +00002533 }
Daniel Veillard4432df22003-09-28 18:58:27 +00002534#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002535 xmlFreeID(ret);
2536 return(NULL);
2537 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002538 if (attr != NULL)
2539 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002540 return(ret);
2541}
2542
2543/**
2544 * xmlFreeIDTable:
2545 * @table: An id table
2546 *
2547 * Deallocate the memory used by an ID hash table.
2548 */
2549void
2550xmlFreeIDTable(xmlIDTablePtr table) {
2551 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2552}
2553
2554/**
2555 * xmlIsID:
2556 * @doc: the document
2557 * @elem: the element carrying the attribute
2558 * @attr: the attribute
2559 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002560 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002561 * then this is done if DTD loading has been requested. In the case
2562 * of HTML documents parsed with the HTML parser, then ID detection is
2563 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002564 *
2565 * Returns 0 or 1 depending on the lookup result
2566 */
2567int
2568xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2569 if (doc == NULL) return(0);
2570 if (attr == NULL) return(0);
2571 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2572 return(0);
2573 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
Daniel Veillard9ba8e382003-10-28 21:31:45 +00002574 if (((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2575 (xmlStrEqual(BAD_CAST "name", attr->name))) &&
2576 ((elem != NULL) && (!xmlStrEqual(elem->name, BAD_CAST "input"))))
Owen Taylor3473f882001-02-23 17:55:21 +00002577 return(1);
2578 return(0);
2579 } else {
2580 xmlAttributePtr attrDecl;
2581
2582 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002583 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00002584 xmlChar fn[50];
Daniel Veillard37f961d2002-07-06 17:53:56 +00002585 xmlChar *fullname;
Daniel Veillardc00cda82003-04-07 10:22:39 +00002586
2587 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002588 if (fullname == NULL)
2589 return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002590 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2591 attr->name);
2592 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2593 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2594 attr->name);
Daniel Veillardc00cda82003-04-07 10:22:39 +00002595 if ((fullname != fn) && (fullname != elem->name))
2596 xmlFree(fullname);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002597 } else {
2598 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2599 attr->name);
2600 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2601 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2602 attr->name);
2603 }
Owen Taylor3473f882001-02-23 17:55:21 +00002604
2605 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2606 return(1);
2607 }
2608 return(0);
2609}
2610
2611/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002612 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002613 * @doc: the document
2614 * @attr: the attribute
2615 *
2616 * Remove the given attribute from the ID table maintained internally.
2617 *
2618 * Returns -1 if the lookup failed and 0 otherwise
2619 */
2620int
2621xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
Owen Taylor3473f882001-02-23 17:55:21 +00002622 xmlIDTablePtr table;
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002623 xmlIDPtr id;
Owen Taylor3473f882001-02-23 17:55:21 +00002624 xmlChar *ID;
2625
2626 if (doc == NULL) return(-1);
2627 if (attr == NULL) return(-1);
2628 table = (xmlIDTablePtr) doc->ids;
2629 if (table == NULL)
2630 return(-1);
2631
2632 if (attr == NULL)
2633 return(-1);
2634 ID = xmlNodeListGetString(doc, attr->children, 1);
2635 if (ID == NULL)
2636 return(-1);
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002637 id = xmlHashLookup(table, ID);
2638 if (id == NULL || id->attr != attr) {
Owen Taylor3473f882001-02-23 17:55:21 +00002639 xmlFree(ID);
2640 return(-1);
2641 }
Daniel Veillard91b955c2004-12-10 10:26:42 +00002642 xmlHashRemoveEntry(table, ID, (xmlHashDeallocator) xmlFreeID);
Owen Taylor3473f882001-02-23 17:55:21 +00002643 xmlFree(ID);
2644 return(0);
2645}
2646
2647/**
2648 * xmlGetID:
2649 * @doc: pointer to the document
2650 * @ID: the ID value
2651 *
2652 * Search the attribute declaring the given ID
2653 *
2654 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2655 */
2656xmlAttrPtr
2657xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2658 xmlIDTablePtr table;
2659 xmlIDPtr id;
2660
2661 if (doc == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002662 return(NULL);
2663 }
2664
2665 if (ID == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002666 return(NULL);
2667 }
2668
2669 table = (xmlIDTablePtr) doc->ids;
2670 if (table == NULL)
2671 return(NULL);
2672
2673 id = xmlHashLookup(table, ID);
2674 if (id == NULL)
2675 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002676 if (id->attr == NULL) {
2677 /*
2678 * We are operating on a stream, return a well known reference
2679 * since the attribute node doesn't exist anymore
2680 */
2681 return((xmlAttrPtr) doc);
2682 }
Owen Taylor3473f882001-02-23 17:55:21 +00002683 return(id->attr);
2684}
2685
2686/************************************************************************
2687 * *
2688 * Refs *
2689 * *
2690 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002691typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002692{
2693 xmlListPtr l;
2694 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002695} xmlRemoveMemo;
2696
2697typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2698
2699typedef struct xmlValidateMemo_t
2700{
2701 xmlValidCtxtPtr ctxt;
2702 const xmlChar *name;
2703} xmlValidateMemo;
2704
2705typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002706
2707/**
2708 * xmlCreateRefTable:
2709 *
2710 * create and initialize an empty ref hash table.
2711 *
2712 * Returns the xmlRefTablePtr just created or NULL in case
2713 * of error.
2714 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002715static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002716xmlCreateRefTable(void) {
2717 return(xmlHashCreate(0));
2718}
2719
2720/**
2721 * xmlFreeRef:
2722 * @lk: A list link
2723 *
2724 * Deallocate the memory used by a ref definition
2725 */
2726static void
2727xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002728 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2729 if (ref == NULL) return;
2730 if (ref->value != NULL)
2731 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002732 if (ref->name != NULL)
2733 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002734 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002735}
2736
2737/**
2738 * xmlFreeRefList:
2739 * @list_ref: A list of references.
2740 *
2741 * Deallocate the memory used by a list of references
2742 */
2743static void
2744xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002745 if (list_ref == NULL) return;
2746 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002747}
2748
2749/**
2750 * xmlWalkRemoveRef:
2751 * @data: Contents of current link
2752 * @user: Value supplied by the user
2753 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002754 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002755 */
2756static int
2757xmlWalkRemoveRef(const void *data, const void *user)
2758{
Daniel Veillard37721922001-05-04 15:21:12 +00002759 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2760 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2761 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002762
Daniel Veillard37721922001-05-04 15:21:12 +00002763 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2764 xmlListRemoveFirst(ref_list, (void *)data);
2765 return 0;
2766 }
2767 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002768}
2769
2770/**
Daniel Veillard770075b2004-02-25 10:44:30 +00002771 * xmlDummyCompare
2772 * @data0: Value supplied by the user
2773 * @data1: Value supplied by the user
2774 *
2775 * Do nothing, return 0. Used to create unordered lists.
2776 */
2777static int
Daniel Veillardf54cd532004-02-25 11:52:31 +00002778xmlDummyCompare(const void *data0 ATTRIBUTE_UNUSED,
2779 const void *data1 ATTRIBUTE_UNUSED)
Daniel Veillard770075b2004-02-25 10:44:30 +00002780{
2781 return (0);
2782}
2783
2784/**
Owen Taylor3473f882001-02-23 17:55:21 +00002785 * xmlAddRef:
2786 * @ctxt: the validation context
2787 * @doc: pointer to the document
2788 * @value: the value name
2789 * @attr: the attribute holding the Ref
2790 *
2791 * Register a new ref declaration
2792 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002793 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002794 */
2795xmlRefPtr
William M. Brackedb65a72004-02-06 07:36:04 +00002796xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002797 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002798 xmlRefPtr ret;
2799 xmlRefTablePtr table;
2800 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002801
Daniel Veillard37721922001-05-04 15:21:12 +00002802 if (doc == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002803 return(NULL);
2804 }
2805 if (value == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002806 return(NULL);
2807 }
2808 if (attr == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002809 return(NULL);
2810 }
Owen Taylor3473f882001-02-23 17:55:21 +00002811
Daniel Veillard37721922001-05-04 15:21:12 +00002812 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002813 * Create the Ref table if needed.
2814 */
Daniel Veillard37721922001-05-04 15:21:12 +00002815 table = (xmlRefTablePtr) doc->refs;
2816 if (table == NULL)
2817 doc->refs = table = xmlCreateRefTable();
2818 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002819 xmlVErrMemory(ctxt,
Daniel Veillard37721922001-05-04 15:21:12 +00002820 "xmlAddRef: Table creation failed!\n");
2821 return(NULL);
2822 }
Owen Taylor3473f882001-02-23 17:55:21 +00002823
Daniel Veillard37721922001-05-04 15:21:12 +00002824 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2825 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002826 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard37721922001-05-04 15:21:12 +00002827 return(NULL);
2828 }
Owen Taylor3473f882001-02-23 17:55:21 +00002829
Daniel Veillard37721922001-05-04 15:21:12 +00002830 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002831 * fill the structure.
2832 */
Daniel Veillard37721922001-05-04 15:21:12 +00002833 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002834 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2835 /*
2836 * Operating in streaming mode, attr is gonna disapear
2837 */
2838 ret->name = xmlStrdup(attr->name);
2839 ret->attr = NULL;
2840 } else {
2841 ret->name = NULL;
2842 ret->attr = attr;
2843 }
2844 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002845
Daniel Veillard37721922001-05-04 15:21:12 +00002846 /* To add a reference :-
2847 * References are maintained as a list of references,
2848 * Lookup the entry, if no entry create new nodelist
2849 * Add the owning node to the NodeList
2850 * Return the ref
2851 */
Owen Taylor3473f882001-02-23 17:55:21 +00002852
Daniel Veillard37721922001-05-04 15:21:12 +00002853 if (NULL == (ref_list = xmlHashLookup(table, value))) {
Daniel Veillard770075b2004-02-25 10:44:30 +00002854 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, xmlDummyCompare))) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002855 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2856 "xmlAddRef: Reference list creation failed!\n",
2857 NULL);
Daniel Veillard37721922001-05-04 15:21:12 +00002858 return(NULL);
2859 }
2860 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2861 xmlListDelete(ref_list);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002862 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2863 "xmlAddRef: Reference list insertion failed!\n",
2864 NULL);
Daniel Veillard37721922001-05-04 15:21:12 +00002865 return(NULL);
2866 }
2867 }
Daniel Veillard965983a2004-02-17 16:30:24 +00002868/* xmlListInsert(ref_list, ret); */
2869 xmlListAppend(ref_list, ret);
Daniel Veillard37721922001-05-04 15:21:12 +00002870 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002871}
2872
2873/**
2874 * xmlFreeRefTable:
2875 * @table: An ref table
2876 *
2877 * Deallocate the memory used by an Ref hash table.
2878 */
2879void
2880xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002881 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002882}
2883
2884/**
2885 * xmlIsRef:
2886 * @doc: the document
2887 * @elem: the element carrying the attribute
2888 * @attr: the attribute
2889 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002890 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002891 * then this is simple, otherwise we use an heuristic: name Ref (upper
2892 * or lowercase).
2893 *
2894 * Returns 0 or 1 depending on the lookup result
2895 */
2896int
2897xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillardce244ad2004-11-05 10:03:46 +00002898 if (attr == NULL)
2899 return(0);
2900 if (doc == NULL) {
2901 doc = attr->doc;
2902 if (doc == NULL) return(0);
2903 }
2904
Daniel Veillard37721922001-05-04 15:21:12 +00002905 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2906 return(0);
2907 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2908 /* TODO @@@ */
2909 return(0);
2910 } else {
2911 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002912
Daniel Veillardce244ad2004-11-05 10:03:46 +00002913 if (elem == NULL) return(0);
Daniel Veillard37721922001-05-04 15:21:12 +00002914 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2915 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2916 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2917 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002918
Daniel Veillard37721922001-05-04 15:21:12 +00002919 if ((attrDecl != NULL) &&
2920 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2921 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2922 return(1);
2923 }
2924 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002925}
2926
2927/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002928 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002929 * @doc: the document
2930 * @attr: the attribute
2931 *
2932 * Remove the given attribute from the Ref table maintained internally.
2933 *
2934 * Returns -1 if the lookup failed and 0 otherwise
2935 */
2936int
2937xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002938 xmlListPtr ref_list;
2939 xmlRefTablePtr table;
2940 xmlChar *ID;
2941 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002942
Daniel Veillard37721922001-05-04 15:21:12 +00002943 if (doc == NULL) return(-1);
2944 if (attr == NULL) return(-1);
2945 table = (xmlRefTablePtr) doc->refs;
2946 if (table == NULL)
2947 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002948
Daniel Veillard37721922001-05-04 15:21:12 +00002949 if (attr == NULL)
2950 return(-1);
2951 ID = xmlNodeListGetString(doc, attr->children, 1);
2952 if (ID == NULL)
2953 return(-1);
2954 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002955
Daniel Veillard37721922001-05-04 15:21:12 +00002956 if(ref_list == NULL) {
2957 xmlFree(ID);
2958 return (-1);
2959 }
2960 /* At this point, ref_list refers to a list of references which
2961 * have the same key as the supplied attr. Our list of references
2962 * is ordered by reference address and we don't have that information
2963 * here to use when removing. We'll have to walk the list and
2964 * check for a matching attribute, when we find one stop the walk
2965 * and remove the entry.
2966 * The list is ordered by reference, so that means we don't have the
2967 * key. Passing the list and the reference to the walker means we
2968 * will have enough data to be able to remove the entry.
2969 */
2970 target.l = ref_list;
2971 target.ap = attr;
2972
2973 /* Remove the supplied attr from our list */
2974 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002975
Daniel Veillard37721922001-05-04 15:21:12 +00002976 /*If the list is empty then remove the list entry in the hash */
2977 if (xmlListEmpty(ref_list))
2978 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2979 xmlFreeRefList);
2980 xmlFree(ID);
2981 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002982}
2983
2984/**
2985 * xmlGetRefs:
2986 * @doc: pointer to the document
2987 * @ID: the ID value
2988 *
2989 * Find the set of references for the supplied ID.
2990 *
2991 * Returns NULL if not found, otherwise node set for the ID.
2992 */
2993xmlListPtr
2994xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002995 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002996
Daniel Veillard37721922001-05-04 15:21:12 +00002997 if (doc == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002998 return(NULL);
2999 }
Owen Taylor3473f882001-02-23 17:55:21 +00003000
Daniel Veillard37721922001-05-04 15:21:12 +00003001 if (ID == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00003002 return(NULL);
3003 }
Owen Taylor3473f882001-02-23 17:55:21 +00003004
Daniel Veillard37721922001-05-04 15:21:12 +00003005 table = (xmlRefTablePtr) doc->refs;
3006 if (table == NULL)
3007 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003008
Daniel Veillard37721922001-05-04 15:21:12 +00003009 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00003010}
3011
3012/************************************************************************
3013 * *
3014 * Routines for validity checking *
3015 * *
3016 ************************************************************************/
3017
3018/**
3019 * xmlGetDtdElementDesc:
3020 * @dtd: a pointer to the DtD to search
3021 * @name: the element name
3022 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003023 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00003024 *
3025 * returns the xmlElementPtr if found or NULL
3026 */
3027
3028xmlElementPtr
3029xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
3030 xmlElementTablePtr table;
3031 xmlElementPtr cur;
3032 xmlChar *uqname = NULL, *prefix = NULL;
3033
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00003034 if ((dtd == NULL) || (name == NULL)) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003035 if (dtd->elements == NULL)
3036 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003037 table = (xmlElementTablePtr) dtd->elements;
3038
3039 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003040 if (uqname != NULL)
3041 name = uqname;
3042 cur = xmlHashLookup2(table, name, prefix);
3043 if (prefix != NULL) xmlFree(prefix);
3044 if (uqname != NULL) xmlFree(uqname);
3045 return(cur);
3046}
3047/**
3048 * xmlGetDtdElementDesc2:
3049 * @dtd: a pointer to the DtD to search
3050 * @name: the element name
3051 * @create: create an empty description if not found
3052 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003053 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00003054 *
3055 * returns the xmlElementPtr if found or NULL
3056 */
3057
Daniel Veillard86fd5a72001-12-13 14:55:21 +00003058static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00003059xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
3060 xmlElementTablePtr table;
3061 xmlElementPtr cur;
3062 xmlChar *uqname = NULL, *prefix = NULL;
3063
3064 if (dtd == NULL) return(NULL);
3065 if (dtd->elements == NULL) {
3066 if (!create)
3067 return(NULL);
3068 /*
3069 * Create the Element table if needed.
3070 */
3071 table = (xmlElementTablePtr) dtd->elements;
3072 if (table == NULL) {
3073 table = xmlCreateElementTable();
3074 dtd->elements = (void *) table;
3075 }
3076 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00003077 xmlVErrMemory(NULL, "element table allocation failed");
Daniel Veillarda10efa82001-04-18 13:09:01 +00003078 return(NULL);
3079 }
3080 }
3081 table = (xmlElementTablePtr) dtd->elements;
3082
3083 uqname = xmlSplitQName2(name, &prefix);
3084 if (uqname != NULL)
3085 name = uqname;
3086 cur = xmlHashLookup2(table, name, prefix);
3087 if ((cur == NULL) && (create)) {
3088 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
3089 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00003090 xmlVErrMemory(NULL, "malloc failed");
Daniel Veillarda10efa82001-04-18 13:09:01 +00003091 return(NULL);
3092 }
3093 memset(cur, 0, sizeof(xmlElement));
3094 cur->type = XML_ELEMENT_DECL;
3095
3096 /*
3097 * fill the structure.
3098 */
3099 cur->name = xmlStrdup(name);
3100 cur->prefix = xmlStrdup(prefix);
3101 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
3102
3103 xmlHashAddEntry2(table, name, prefix, cur);
3104 }
3105 if (prefix != NULL) xmlFree(prefix);
3106 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00003107 return(cur);
3108}
3109
3110/**
3111 * xmlGetDtdQElementDesc:
3112 * @dtd: a pointer to the DtD to search
3113 * @name: the element name
3114 * @prefix: the element namespace prefix
3115 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003116 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00003117 *
3118 * returns the xmlElementPtr if found or NULL
3119 */
3120
Daniel Veillard48da9102001-08-07 01:10:10 +00003121xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00003122xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
3123 const xmlChar *prefix) {
3124 xmlElementTablePtr table;
3125
3126 if (dtd == NULL) return(NULL);
3127 if (dtd->elements == NULL) return(NULL);
3128 table = (xmlElementTablePtr) dtd->elements;
3129
3130 return(xmlHashLookup2(table, name, prefix));
3131}
3132
3133/**
3134 * xmlGetDtdAttrDesc:
3135 * @dtd: a pointer to the DtD to search
3136 * @elem: the element name
3137 * @name: the attribute name
3138 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003139 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00003140 * this element.
3141 *
3142 * returns the xmlAttributePtr if found or NULL
3143 */
3144
3145xmlAttributePtr
3146xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
3147 xmlAttributeTablePtr table;
3148 xmlAttributePtr cur;
3149 xmlChar *uqname = NULL, *prefix = NULL;
3150
3151 if (dtd == NULL) return(NULL);
3152 if (dtd->attributes == NULL) return(NULL);
3153
3154 table = (xmlAttributeTablePtr) dtd->attributes;
3155 if (table == NULL)
3156 return(NULL);
3157
3158 uqname = xmlSplitQName2(name, &prefix);
3159
3160 if (uqname != NULL) {
3161 cur = xmlHashLookup3(table, uqname, prefix, elem);
3162 if (prefix != NULL) xmlFree(prefix);
3163 if (uqname != NULL) xmlFree(uqname);
3164 } else
3165 cur = xmlHashLookup3(table, name, NULL, elem);
3166 return(cur);
3167}
3168
3169/**
3170 * xmlGetDtdQAttrDesc:
3171 * @dtd: a pointer to the DtD to search
3172 * @elem: the element name
3173 * @name: the attribute name
3174 * @prefix: the attribute namespace prefix
3175 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003176 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00003177 * this element.
3178 *
3179 * returns the xmlAttributePtr if found or NULL
3180 */
3181
Daniel Veillard48da9102001-08-07 01:10:10 +00003182xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00003183xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
3184 const xmlChar *prefix) {
3185 xmlAttributeTablePtr table;
3186
3187 if (dtd == NULL) return(NULL);
3188 if (dtd->attributes == NULL) return(NULL);
3189 table = (xmlAttributeTablePtr) dtd->attributes;
3190
3191 return(xmlHashLookup3(table, name, prefix, elem));
3192}
3193
3194/**
3195 * xmlGetDtdNotationDesc:
3196 * @dtd: a pointer to the DtD to search
3197 * @name: the notation name
3198 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003199 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00003200 *
3201 * returns the xmlNotationPtr if found or NULL
3202 */
3203
3204xmlNotationPtr
3205xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
3206 xmlNotationTablePtr table;
3207
3208 if (dtd == NULL) return(NULL);
3209 if (dtd->notations == NULL) return(NULL);
3210 table = (xmlNotationTablePtr) dtd->notations;
3211
3212 return(xmlHashLookup(table, name));
3213}
3214
Daniel Veillardf54cd532004-02-25 11:52:31 +00003215#if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
Owen Taylor3473f882001-02-23 17:55:21 +00003216/**
3217 * xmlValidateNotationUse:
3218 * @ctxt: the validation context
3219 * @doc: the document
3220 * @notationName: the notation name to check
3221 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003222 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00003223 * - [ VC: Notation Declared ]
3224 *
3225 * returns 1 if valid or 0 otherwise
3226 */
3227
3228int
3229xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3230 const xmlChar *notationName) {
3231 xmlNotationPtr notaDecl;
3232 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3233
3234 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
3235 if ((notaDecl == NULL) && (doc->extSubset != NULL))
3236 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
3237
Daniel Veillarde637c4a2003-03-30 21:10:09 +00003238 if ((notaDecl == NULL) && (ctxt != NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003239 xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_NOTATION,
3240 "NOTATION %s is not declared\n",
3241 notationName, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003242 return(0);
3243 }
3244 return(1);
3245}
Daniel Veillardf54cd532004-02-25 11:52:31 +00003246#endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00003247
3248/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00003249 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00003250 * @doc: the document
3251 * @name: the element name
3252 *
3253 * Search in the DtDs whether an element accept Mixed content (or ANY)
3254 * basically if it is supposed to accept text childs
3255 *
3256 * returns 0 if no, 1 if yes, and -1 if no element description is available
3257 */
3258
3259int
3260xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
3261 xmlElementPtr elemDecl;
3262
3263 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3264
3265 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
3266 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3267 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
3268 if (elemDecl == NULL) return(-1);
3269 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003270 case XML_ELEMENT_TYPE_UNDEFINED:
3271 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003272 case XML_ELEMENT_TYPE_ELEMENT:
3273 return(0);
3274 case XML_ELEMENT_TYPE_EMPTY:
3275 /*
3276 * return 1 for EMPTY since we want VC error to pop up
3277 * on <empty> </empty> for example
3278 */
3279 case XML_ELEMENT_TYPE_ANY:
3280 case XML_ELEMENT_TYPE_MIXED:
3281 return(1);
3282 }
3283 return(1);
3284}
3285
Daniel Veillard4432df22003-09-28 18:58:27 +00003286#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00003287/**
3288 * xmlValidateNameValue:
3289 * @value: an Name value
3290 *
3291 * Validate that the given value match Name production
3292 *
3293 * returns 1 if valid or 0 otherwise
3294 */
3295
Daniel Veillard9b731d72002-04-14 12:56:08 +00003296int
Owen Taylor3473f882001-02-23 17:55:21 +00003297xmlValidateNameValue(const xmlChar *value) {
3298 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003299 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003300
3301 if (value == NULL) return(0);
3302 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003303 val = xmlStringCurrentChar(NULL, cur, &len);
3304 cur += len;
3305 if (!IS_LETTER(val) && (val != '_') &&
3306 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003307 return(0);
3308 }
3309
Daniel Veillardd8224e02002-01-13 15:43:22 +00003310 val = xmlStringCurrentChar(NULL, cur, &len);
3311 cur += len;
3312 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3313 (val == '.') || (val == '-') ||
3314 (val == '_') || (val == ':') ||
3315 (IS_COMBINING(val)) ||
3316 (IS_EXTENDER(val))) {
3317 val = xmlStringCurrentChar(NULL, cur, &len);
3318 cur += len;
3319 }
Owen Taylor3473f882001-02-23 17:55:21 +00003320
Daniel Veillardd8224e02002-01-13 15:43:22 +00003321 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003322
3323 return(1);
3324}
3325
3326/**
3327 * xmlValidateNamesValue:
3328 * @value: an Names value
3329 *
3330 * Validate that the given value match Names production
3331 *
3332 * returns 1 if valid or 0 otherwise
3333 */
3334
Daniel Veillard9b731d72002-04-14 12:56:08 +00003335int
Owen Taylor3473f882001-02-23 17:55:21 +00003336xmlValidateNamesValue(const xmlChar *value) {
3337 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003338 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003339
3340 if (value == NULL) return(0);
3341 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003342 val = xmlStringCurrentChar(NULL, cur, &len);
3343 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003344
Daniel Veillardd8224e02002-01-13 15:43:22 +00003345 if (!IS_LETTER(val) && (val != '_') &&
3346 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003347 return(0);
3348 }
3349
Daniel Veillardd8224e02002-01-13 15:43:22 +00003350 val = xmlStringCurrentChar(NULL, cur, &len);
3351 cur += len;
3352 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3353 (val == '.') || (val == '-') ||
3354 (val == '_') || (val == ':') ||
3355 (IS_COMBINING(val)) ||
3356 (IS_EXTENDER(val))) {
3357 val = xmlStringCurrentChar(NULL, cur, &len);
3358 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003359 }
3360
Daniel Veillard807b4de2004-09-26 14:42:56 +00003361 /* Should not test IS_BLANK(val) here -- see erratum E20*/
3362 while (val == 0x20) {
3363 while (val == 0x20) {
Daniel Veillardd8224e02002-01-13 15:43:22 +00003364 val = xmlStringCurrentChar(NULL, cur, &len);
3365 cur += len;
3366 }
3367
3368 if (!IS_LETTER(val) && (val != '_') &&
3369 (val != ':')) {
3370 return(0);
3371 }
3372 val = xmlStringCurrentChar(NULL, cur, &len);
3373 cur += len;
3374
3375 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3376 (val == '.') || (val == '-') ||
3377 (val == '_') || (val == ':') ||
3378 (IS_COMBINING(val)) ||
3379 (IS_EXTENDER(val))) {
3380 val = xmlStringCurrentChar(NULL, cur, &len);
3381 cur += len;
3382 }
3383 }
3384
3385 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003386
3387 return(1);
3388}
3389
3390/**
3391 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003392 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003393 *
3394 * Validate that the given value match Nmtoken production
3395 *
3396 * [ VC: Name Token ]
3397 *
3398 * returns 1 if valid or 0 otherwise
3399 */
3400
Daniel Veillard9b731d72002-04-14 12:56:08 +00003401int
Owen Taylor3473f882001-02-23 17:55:21 +00003402xmlValidateNmtokenValue(const xmlChar *value) {
3403 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003404 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003405
3406 if (value == NULL) return(0);
3407 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003408 val = xmlStringCurrentChar(NULL, cur, &len);
3409 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003410
Daniel Veillardd8224e02002-01-13 15:43:22 +00003411 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3412 (val != '.') && (val != '-') &&
3413 (val != '_') && (val != ':') &&
3414 (!IS_COMBINING(val)) &&
3415 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003416 return(0);
3417
Daniel Veillardd8224e02002-01-13 15:43:22 +00003418 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3419 (val == '.') || (val == '-') ||
3420 (val == '_') || (val == ':') ||
3421 (IS_COMBINING(val)) ||
3422 (IS_EXTENDER(val))) {
3423 val = xmlStringCurrentChar(NULL, cur, &len);
3424 cur += len;
3425 }
Owen Taylor3473f882001-02-23 17:55:21 +00003426
Daniel Veillardd8224e02002-01-13 15:43:22 +00003427 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003428
3429 return(1);
3430}
3431
3432/**
3433 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003434 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003435 *
3436 * Validate that the given value match Nmtokens production
3437 *
3438 * [ VC: Name Token ]
3439 *
3440 * returns 1 if valid or 0 otherwise
3441 */
3442
Daniel Veillard9b731d72002-04-14 12:56:08 +00003443int
Owen Taylor3473f882001-02-23 17:55:21 +00003444xmlValidateNmtokensValue(const xmlChar *value) {
3445 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003446 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003447
3448 if (value == NULL) return(0);
3449 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003450 val = xmlStringCurrentChar(NULL, cur, &len);
3451 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003452
Daniel Veillardd8224e02002-01-13 15:43:22 +00003453 while (IS_BLANK(val)) {
3454 val = xmlStringCurrentChar(NULL, cur, &len);
3455 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003456 }
3457
Daniel Veillardd8224e02002-01-13 15:43:22 +00003458 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3459 (val != '.') && (val != '-') &&
3460 (val != '_') && (val != ':') &&
3461 (!IS_COMBINING(val)) &&
3462 (!IS_EXTENDER(val)))
3463 return(0);
3464
3465 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3466 (val == '.') || (val == '-') ||
3467 (val == '_') || (val == ':') ||
3468 (IS_COMBINING(val)) ||
3469 (IS_EXTENDER(val))) {
3470 val = xmlStringCurrentChar(NULL, cur, &len);
3471 cur += len;
3472 }
3473
Daniel Veillard807b4de2004-09-26 14:42:56 +00003474 /* Should not test IS_BLANK(val) here -- see erratum E20*/
3475 while (val == 0x20) {
3476 while (val == 0x20) {
Daniel Veillardd8224e02002-01-13 15:43:22 +00003477 val = xmlStringCurrentChar(NULL, cur, &len);
3478 cur += len;
3479 }
3480 if (val == 0) return(1);
3481
3482 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3483 (val != '.') && (val != '-') &&
3484 (val != '_') && (val != ':') &&
3485 (!IS_COMBINING(val)) &&
3486 (!IS_EXTENDER(val)))
3487 return(0);
3488
3489 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3490 (val == '.') || (val == '-') ||
3491 (val == '_') || (val == ':') ||
3492 (IS_COMBINING(val)) ||
3493 (IS_EXTENDER(val))) {
3494 val = xmlStringCurrentChar(NULL, cur, &len);
3495 cur += len;
3496 }
3497 }
3498
3499 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003500
3501 return(1);
3502}
3503
3504/**
3505 * xmlValidateNotationDecl:
3506 * @ctxt: the validation context
3507 * @doc: a document instance
3508 * @nota: a notation definition
3509 *
3510 * Try to validate a single notation definition
3511 * basically it does the following checks as described by the
3512 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003513 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003514 * But this function get called anyway ...
3515 *
3516 * returns 1 if valid or 0 otherwise
3517 */
3518
3519int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003520xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3521 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003522 int ret = 1;
3523
3524 return(ret);
3525}
3526
3527/**
3528 * xmlValidateAttributeValue:
3529 * @type: an attribute type
3530 * @value: an attribute value
3531 *
3532 * Validate that the given attribute value match the proper production
3533 *
3534 * [ VC: ID ]
3535 * Values of type ID must match the Name production....
3536 *
3537 * [ VC: IDREF ]
3538 * Values of type IDREF must match the Name production, and values
3539 * of type IDREFS must match Names ...
3540 *
3541 * [ VC: Entity Name ]
3542 * Values of type ENTITY must match the Name production, values
3543 * of type ENTITIES must match Names ...
3544 *
3545 * [ VC: Name Token ]
3546 * Values of type NMTOKEN must match the Nmtoken production; values
3547 * of type NMTOKENS must match Nmtokens.
3548 *
3549 * returns 1 if valid or 0 otherwise
3550 */
3551
3552int
3553xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3554 switch (type) {
3555 case XML_ATTRIBUTE_ENTITIES:
3556 case XML_ATTRIBUTE_IDREFS:
3557 return(xmlValidateNamesValue(value));
3558 case XML_ATTRIBUTE_ENTITY:
3559 case XML_ATTRIBUTE_IDREF:
3560 case XML_ATTRIBUTE_ID:
3561 case XML_ATTRIBUTE_NOTATION:
3562 return(xmlValidateNameValue(value));
3563 case XML_ATTRIBUTE_NMTOKENS:
3564 case XML_ATTRIBUTE_ENUMERATION:
3565 return(xmlValidateNmtokensValue(value));
3566 case XML_ATTRIBUTE_NMTOKEN:
3567 return(xmlValidateNmtokenValue(value));
3568 case XML_ATTRIBUTE_CDATA:
3569 break;
3570 }
3571 return(1);
3572}
3573
3574/**
3575 * xmlValidateAttributeValue2:
3576 * @ctxt: the validation context
3577 * @doc: the document
3578 * @name: the attribute name (used for error reporting only)
3579 * @type: the attribute type
3580 * @value: the attribute value
3581 *
3582 * Validate that the given attribute value match a given type.
3583 * This typically cannot be done before having finished parsing
3584 * the subsets.
3585 *
3586 * [ VC: IDREF ]
3587 * Values of type IDREF must match one of the declared IDs
3588 * Values of type IDREFS must match a sequence of the declared IDs
3589 * each Name must match the value of an ID attribute on some element
3590 * in the XML document; i.e. IDREF values must match the value of
3591 * some ID attribute
3592 *
3593 * [ VC: Entity Name ]
3594 * Values of type ENTITY must match one declared entity
3595 * Values of type ENTITIES must match a sequence of declared entities
3596 *
3597 * [ VC: Notation Attributes ]
3598 * all notation names in the declaration must be declared.
3599 *
3600 * returns 1 if valid or 0 otherwise
3601 */
3602
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003603static int
Owen Taylor3473f882001-02-23 17:55:21 +00003604xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3605 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3606 int ret = 1;
3607 switch (type) {
3608 case XML_ATTRIBUTE_IDREFS:
3609 case XML_ATTRIBUTE_IDREF:
3610 case XML_ATTRIBUTE_ID:
3611 case XML_ATTRIBUTE_NMTOKENS:
3612 case XML_ATTRIBUTE_ENUMERATION:
3613 case XML_ATTRIBUTE_NMTOKEN:
3614 case XML_ATTRIBUTE_CDATA:
3615 break;
3616 case XML_ATTRIBUTE_ENTITY: {
3617 xmlEntityPtr ent;
3618
3619 ent = xmlGetDocEntity(doc, value);
Daniel Veillard62998c02003-09-15 12:56:36 +00003620 /* yeah it's a bit messy... */
Daniel Veillard878eab02002-02-19 13:46:09 +00003621 if ((ent == NULL) && (doc->standalone == 1)) {
3622 doc->standalone = 0;
3623 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003624 }
Owen Taylor3473f882001-02-23 17:55:21 +00003625 if (ent == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003626 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3627 XML_DTD_UNKNOWN_ENTITY,
Owen Taylor3473f882001-02-23 17:55:21 +00003628 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003629 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003630 ret = 0;
3631 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003632 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3633 XML_DTD_ENTITY_TYPE,
Owen Taylor3473f882001-02-23 17:55:21 +00003634 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003635 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003636 ret = 0;
3637 }
3638 break;
3639 }
3640 case XML_ATTRIBUTE_ENTITIES: {
3641 xmlChar *dup, *nam = NULL, *cur, save;
3642 xmlEntityPtr ent;
3643
3644 dup = xmlStrdup(value);
3645 if (dup == NULL)
3646 return(0);
3647 cur = dup;
3648 while (*cur != 0) {
3649 nam = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00003650 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00003651 save = *cur;
3652 *cur = 0;
3653 ent = xmlGetDocEntity(doc, nam);
3654 if (ent == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003655 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3656 XML_DTD_UNKNOWN_ENTITY,
Owen Taylor3473f882001-02-23 17:55:21 +00003657 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003658 name, nam, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003659 ret = 0;
3660 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003661 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3662 XML_DTD_ENTITY_TYPE,
Owen Taylor3473f882001-02-23 17:55:21 +00003663 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003664 name, nam, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003665 ret = 0;
3666 }
3667 if (save == 0)
3668 break;
3669 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00003670 while (IS_BLANK_CH(*cur)) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00003671 }
3672 xmlFree(dup);
3673 break;
3674 }
3675 case XML_ATTRIBUTE_NOTATION: {
3676 xmlNotationPtr nota;
3677
3678 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3679 if ((nota == NULL) && (doc->extSubset != NULL))
3680 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3681
3682 if (nota == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003683 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3684 XML_DTD_UNKNOWN_NOTATION,
Owen Taylor3473f882001-02-23 17:55:21 +00003685 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003686 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003687 ret = 0;
3688 }
3689 break;
3690 }
3691 }
3692 return(ret);
3693}
3694
3695/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003696 * xmlValidCtxtNormalizeAttributeValue:
3697 * @ctxt: the validation context
3698 * @doc: the document
3699 * @elem: the parent
3700 * @name: the attribute name
3701 * @value: the attribute value
3702 * @ctxt: the validation context or NULL
3703 *
3704 * Does the validation related extra step of the normalization of attribute
3705 * values:
3706 *
3707 * If the declared value is not CDATA, then the XML processor must further
3708 * process the normalized attribute value by discarding any leading and
3709 * trailing space (#x20) characters, and by replacing sequences of space
3710 * (#x20) characters by single space (#x20) character.
3711 *
3712 * Also check VC: Standalone Document Declaration in P32, and update
3713 * ctxt->valid accordingly
3714 *
3715 * returns a new normalized string if normalization is needed, NULL otherwise
3716 * the caller must free the returned value.
3717 */
3718
3719xmlChar *
3720xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3721 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3722 xmlChar *ret, *dst;
3723 const xmlChar *src;
3724 xmlAttributePtr attrDecl = NULL;
3725 int extsubset = 0;
3726
3727 if (doc == NULL) return(NULL);
3728 if (elem == NULL) return(NULL);
3729 if (name == NULL) return(NULL);
3730 if (value == NULL) return(NULL);
3731
3732 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003733 xmlChar fn[50];
3734 xmlChar *fullname;
3735
3736 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3737 if (fullname == NULL)
3738 return(0);
3739 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003740 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003741 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003742 if (attrDecl != NULL)
3743 extsubset = 1;
3744 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003745 if ((fullname != fn) && (fullname != elem->name))
3746 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003747 }
3748 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3749 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3750 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3751 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3752 if (attrDecl != NULL)
3753 extsubset = 1;
3754 }
3755
3756 if (attrDecl == NULL)
3757 return(NULL);
3758 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3759 return(NULL);
3760
3761 ret = xmlStrdup(value);
3762 if (ret == NULL)
3763 return(NULL);
3764 src = value;
3765 dst = ret;
3766 while (*src == 0x20) src++;
3767 while (*src != 0) {
3768 if (*src == 0x20) {
3769 while (*src == 0x20) src++;
3770 if (*src != 0)
3771 *dst++ = 0x20;
3772 } else {
3773 *dst++ = *src++;
3774 }
3775 }
3776 *dst = 0;
3777 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003778 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE,
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003779"standalone: %s on %s value had to be normalized based on external subset declaration\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003780 name, elem->name, NULL);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003781 ctxt->valid = 0;
3782 }
3783 return(ret);
3784}
3785
3786/**
Owen Taylor3473f882001-02-23 17:55:21 +00003787 * xmlValidNormalizeAttributeValue:
3788 * @doc: the document
3789 * @elem: the parent
3790 * @name: the attribute name
3791 * @value: the attribute value
3792 *
3793 * Does the validation related extra step of the normalization of attribute
3794 * values:
3795 *
3796 * If the declared value is not CDATA, then the XML processor must further
3797 * process the normalized attribute value by discarding any leading and
3798 * trailing space (#x20) characters, and by replacing sequences of space
3799 * (#x20) characters by single space (#x20) character.
3800 *
Daniel Veillard652327a2003-09-29 18:02:38 +00003801 * Returns a new normalized string if normalization is needed, NULL otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00003802 * the caller must free the returned value.
3803 */
3804
3805xmlChar *
3806xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3807 const xmlChar *name, const xmlChar *value) {
3808 xmlChar *ret, *dst;
3809 const xmlChar *src;
3810 xmlAttributePtr attrDecl = NULL;
3811
3812 if (doc == NULL) return(NULL);
3813 if (elem == NULL) return(NULL);
3814 if (name == NULL) return(NULL);
3815 if (value == NULL) return(NULL);
3816
3817 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003818 xmlChar fn[50];
3819 xmlChar *fullname;
3820
3821 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3822 if (fullname == NULL)
3823 return(0);
3824 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003825 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003826 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3827 if ((fullname != fn) && (fullname != elem->name))
3828 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003829 }
3830 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3831 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3832 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3833
3834 if (attrDecl == NULL)
3835 return(NULL);
3836 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3837 return(NULL);
3838
3839 ret = xmlStrdup(value);
3840 if (ret == NULL)
3841 return(NULL);
3842 src = value;
3843 dst = ret;
3844 while (*src == 0x20) src++;
3845 while (*src != 0) {
3846 if (*src == 0x20) {
3847 while (*src == 0x20) src++;
3848 if (*src != 0)
3849 *dst++ = 0x20;
3850 } else {
3851 *dst++ = *src++;
3852 }
3853 }
3854 *dst = 0;
3855 return(ret);
3856}
3857
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003858static void
Owen Taylor3473f882001-02-23 17:55:21 +00003859xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003860 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003861 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3862}
3863
3864/**
3865 * xmlValidateAttributeDecl:
3866 * @ctxt: the validation context
3867 * @doc: a document instance
3868 * @attr: an attribute definition
3869 *
3870 * Try to validate a single attribute definition
3871 * basically it does the following checks as described by the
3872 * XML-1.0 recommendation:
3873 * - [ VC: Attribute Default Legal ]
3874 * - [ VC: Enumeration ]
3875 * - [ VC: ID Attribute Default ]
3876 *
3877 * The ID/IDREF uniqueness and matching are done separately
3878 *
3879 * returns 1 if valid or 0 otherwise
3880 */
3881
3882int
3883xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3884 xmlAttributePtr attr) {
3885 int ret = 1;
3886 int val;
3887 CHECK_DTD;
3888 if(attr == NULL) return(1);
3889
3890 /* Attribute Default Legal */
3891 /* Enumeration */
3892 if (attr->defaultValue != NULL) {
3893 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3894 if (val == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003895 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003896 "Syntax of default value for attribute %s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003897 attr->name, attr->elem, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003898 }
3899 ret &= val;
3900 }
3901
3902 /* ID Attribute Default */
3903 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3904 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3905 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003906 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_FIXED,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003907 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003908 attr->name, attr->elem, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003909 ret = 0;
3910 }
3911
3912 /* One ID per Element Type */
3913 if (attr->atype == XML_ATTRIBUTE_ID) {
3914 int nbId;
3915
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003916 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003917 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3918 attr->elem);
3919 if (elem != NULL) {
3920 nbId = xmlScanIDAttributeDecl(NULL, elem);
3921 } else {
3922 xmlAttributeTablePtr table;
3923
3924 /*
3925 * The attribute may be declared in the internal subset and the
3926 * element in the external subset.
3927 */
3928 nbId = 0;
3929 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3930 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3931 xmlValidateAttributeIdCallback, &nbId);
3932 }
3933 if (nbId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003934
3935 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003936 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3937 attr->elem, nbId, attr->name);
3938 } else if (doc->extSubset != NULL) {
3939 int extId = 0;
3940 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3941 if (elem != NULL) {
3942 extId = xmlScanIDAttributeDecl(NULL, elem);
3943 }
3944 if (extId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003945 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003946 "Element %s has %d ID attribute defined in the external subset : %s\n",
3947 attr->elem, extId, attr->name);
3948 } else if (extId + nbId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003949 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003950"Element %s has ID attributes defined in the internal and external subset : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003951 attr->elem, attr->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003952 }
3953 }
3954 }
3955
3956 /* Validity Constraint: Enumeration */
3957 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3958 xmlEnumerationPtr tree = attr->tree;
3959 while (tree != NULL) {
3960 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3961 tree = tree->next;
3962 }
3963 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003964 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003965"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003966 attr->defaultValue, attr->name, attr->elem);
3967 ret = 0;
3968 }
3969 }
3970
3971 return(ret);
3972}
3973
3974/**
3975 * xmlValidateElementDecl:
3976 * @ctxt: the validation context
3977 * @doc: a document instance
3978 * @elem: an element definition
3979 *
3980 * Try to validate a single element definition
3981 * basically it does the following checks as described by the
3982 * XML-1.0 recommendation:
3983 * - [ VC: One ID per Element Type ]
3984 * - [ VC: No Duplicate Types ]
3985 * - [ VC: Unique Element Type Declaration ]
3986 *
3987 * returns 1 if valid or 0 otherwise
3988 */
3989
3990int
3991xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3992 xmlElementPtr elem) {
3993 int ret = 1;
3994 xmlElementPtr tst;
3995
3996 CHECK_DTD;
3997
3998 if (elem == NULL) return(1);
3999
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004000#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00004001#ifdef LIBXML_REGEXP_ENABLED
4002 /* Build the regexp associated to the content model */
4003 ret = xmlValidBuildContentModel(ctxt, elem);
4004#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004005#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00004006
Owen Taylor3473f882001-02-23 17:55:21 +00004007 /* No Duplicate Types */
4008 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
4009 xmlElementContentPtr cur, next;
4010 const xmlChar *name;
4011
4012 cur = elem->content;
4013 while (cur != NULL) {
4014 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
4015 if (cur->c1 == NULL) break;
4016 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4017 name = cur->c1->name;
4018 next = cur->c2;
4019 while (next != NULL) {
4020 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard7b68df92003-08-03 22:58:54 +00004021 if ((xmlStrEqual(next->name, name)) &&
4022 (xmlStrEqual(next->prefix, cur->prefix))) {
4023 if (cur->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004024 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00004025 "Definition of %s has duplicate references of %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004026 elem->name, name, NULL);
Daniel Veillard7b68df92003-08-03 22:58:54 +00004027 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004028 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00004029 "Definition of %s has duplicate references of %s:%s\n",
4030 elem->name, cur->prefix, name);
4031 }
Owen Taylor3473f882001-02-23 17:55:21 +00004032 ret = 0;
4033 }
4034 break;
4035 }
4036 if (next->c1 == NULL) break;
4037 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
Daniel Veillard7b68df92003-08-03 22:58:54 +00004038 if ((xmlStrEqual(next->c1->name, name)) &&
4039 (xmlStrEqual(next->c1->prefix, cur->prefix))) {
4040 if (cur->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004041 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00004042 "Definition of %s has duplicate references to %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004043 elem->name, name, NULL);
Daniel Veillard7b68df92003-08-03 22:58:54 +00004044 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004045 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00004046 "Definition of %s has duplicate references to %s:%s\n",
4047 elem->name, cur->prefix, name);
4048 }
Owen Taylor3473f882001-02-23 17:55:21 +00004049 ret = 0;
4050 }
4051 next = next->c2;
4052 }
4053 }
4054 cur = cur->c2;
4055 }
4056 }
4057
4058 /* VC: Unique Element Type Declaration */
4059 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004060 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004061 ((tst->prefix == elem->prefix) ||
4062 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00004063 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004064 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
4065 "Redefinition of element %s\n",
4066 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004067 ret = 0;
4068 }
4069 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004070 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004071 ((tst->prefix == elem->prefix) ||
4072 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00004073 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004074 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
4075 "Redefinition of element %s\n",
4076 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004077 ret = 0;
4078 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00004079 /* One ID per Element Type
4080 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00004081 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
4082 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00004083 } */
Owen Taylor3473f882001-02-23 17:55:21 +00004084 return(ret);
4085}
4086
4087/**
4088 * xmlValidateOneAttribute:
4089 * @ctxt: the validation context
4090 * @doc: a document instance
4091 * @elem: an element instance
4092 * @attr: an attribute instance
4093 * @value: the attribute value (without entities processing)
4094 *
4095 * Try to validate a single attribute for an element
4096 * basically it does the following checks as described by the
4097 * XML-1.0 recommendation:
4098 * - [ VC: Attribute Value Type ]
4099 * - [ VC: Fixed Attribute Default ]
4100 * - [ VC: Entity Name ]
4101 * - [ VC: Name Token ]
4102 * - [ VC: ID ]
4103 * - [ VC: IDREF ]
4104 * - [ VC: Entity Name ]
4105 * - [ VC: Notation Attributes ]
4106 *
4107 * The ID/IDREF uniqueness and matching are done separately
4108 *
4109 * returns 1 if valid or 0 otherwise
4110 */
4111
4112int
4113xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
Daniel Veillard07cb8222003-09-10 10:51:05 +00004114 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value)
4115{
Owen Taylor3473f882001-02-23 17:55:21 +00004116 xmlAttributePtr attrDecl = NULL;
4117 int val;
4118 int ret = 1;
4119
4120 CHECK_DTD;
4121 if ((elem == NULL) || (elem->name == NULL)) return(0);
4122 if ((attr == NULL) || (attr->name == NULL)) return(0);
4123
4124 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004125 xmlChar fn[50];
4126 xmlChar *fullname;
4127
4128 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
4129 if (fullname == NULL)
4130 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004131 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004132 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00004133 attr->name, attr->ns->prefix);
4134 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004135 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00004136 attr->name, attr->ns->prefix);
4137 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004138 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004139 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4140 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00004141 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004142 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004143 if ((fullname != fn) && (fullname != elem->name))
4144 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00004145 }
4146 if (attrDecl == NULL) {
4147 if (attr->ns != NULL) {
4148 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4149 attr->name, attr->ns->prefix);
4150 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4151 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4152 attr->name, attr->ns->prefix);
4153 } else {
4154 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4155 elem->name, attr->name);
4156 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4157 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4158 elem->name, attr->name);
4159 }
4160 }
4161
4162
4163 /* Validity Constraint: Attribute Value Type */
4164 if (attrDecl == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004165 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004166 "No declaration for attribute %s of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004167 attr->name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004168 return(0);
4169 }
4170 attr->atype = attrDecl->atype;
4171
4172 val = xmlValidateAttributeValue(attrDecl->atype, value);
4173 if (val == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004174 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004175 "Syntax of value for attribute %s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004176 attr->name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004177 ret = 0;
4178 }
4179
4180 /* Validity constraint: Fixed Attribute Default */
4181 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4182 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004183 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004184 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004185 attr->name, elem->name, attrDecl->defaultValue);
4186 ret = 0;
4187 }
4188 }
4189
4190 /* Validity Constraint: ID uniqueness */
4191 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4192 if (xmlAddID(ctxt, doc, value, attr) == NULL)
4193 ret = 0;
4194 }
4195
4196 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4197 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4198 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
4199 ret = 0;
4200 }
4201
4202 /* Validity Constraint: Notation Attributes */
4203 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4204 xmlEnumerationPtr tree = attrDecl->tree;
4205 xmlNotationPtr nota;
4206
4207 /* First check that the given NOTATION was declared */
4208 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4209 if (nota == NULL)
4210 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4211
4212 if (nota == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004213 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004214 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004215 value, attr->name, elem->name);
4216 ret = 0;
4217 }
4218
4219 /* Second, verify that it's among the list */
4220 while (tree != NULL) {
4221 if (xmlStrEqual(tree->name, value)) break;
4222 tree = tree->next;
4223 }
4224 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004225 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004226"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004227 value, attr->name, elem->name);
4228 ret = 0;
4229 }
4230 }
4231
4232 /* Validity Constraint: Enumeration */
4233 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4234 xmlEnumerationPtr tree = attrDecl->tree;
4235 while (tree != NULL) {
4236 if (xmlStrEqual(tree->name, value)) break;
4237 tree = tree->next;
4238 }
4239 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004240 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004241 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004242 value, attr->name, elem->name);
4243 ret = 0;
4244 }
4245 }
4246
4247 /* Fixed Attribute Default */
4248 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4249 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004250 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004251 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004252 attr->name, elem->name, attrDecl->defaultValue);
4253 ret = 0;
4254 }
4255
4256 /* Extra check for the attribute value */
4257 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
4258 attrDecl->atype, value);
4259
4260 return(ret);
4261}
4262
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004263/**
4264 * xmlValidateOneNamespace:
4265 * @ctxt: the validation context
4266 * @doc: a document instance
4267 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00004268 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004269 * @ns: an namespace declaration instance
4270 * @value: the attribute value (without entities processing)
4271 *
4272 * Try to validate a single namespace declaration for an element
4273 * basically it does the following checks as described by the
4274 * XML-1.0 recommendation:
4275 * - [ VC: Attribute Value Type ]
4276 * - [ VC: Fixed Attribute Default ]
4277 * - [ VC: Entity Name ]
4278 * - [ VC: Name Token ]
4279 * - [ VC: ID ]
4280 * - [ VC: IDREF ]
4281 * - [ VC: Entity Name ]
4282 * - [ VC: Notation Attributes ]
4283 *
4284 * The ID/IDREF uniqueness and matching are done separately
4285 *
4286 * returns 1 if valid or 0 otherwise
4287 */
4288
4289int
4290xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4291xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
4292 /* xmlElementPtr elemDecl; */
4293 xmlAttributePtr attrDecl = NULL;
4294 int val;
4295 int ret = 1;
4296
4297 CHECK_DTD;
4298 if ((elem == NULL) || (elem->name == NULL)) return(0);
4299 if ((ns == NULL) || (ns->href == NULL)) return(0);
4300
4301 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004302 xmlChar fn[50];
4303 xmlChar *fullname;
4304
4305 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
4306 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00004307 xmlVErrMemory(ctxt, "Validating namespace");
Daniel Veillardc00cda82003-04-07 10:22:39 +00004308 return(0);
4309 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004310 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004311 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004312 ns->prefix, BAD_CAST "xmlns");
4313 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004314 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004315 ns->prefix, BAD_CAST "xmlns");
4316 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004317 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004318 BAD_CAST "xmlns");
4319 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004320 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004321 BAD_CAST "xmlns");
4322 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004323 if ((fullname != fn) && (fullname != elem->name))
4324 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004325 }
4326 if (attrDecl == NULL) {
4327 if (ns->prefix != NULL) {
4328 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4329 ns->prefix, BAD_CAST "xmlns");
4330 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4331 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4332 ns->prefix, BAD_CAST "xmlns");
4333 } else {
4334 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4335 elem->name, BAD_CAST "xmlns");
4336 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4337 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4338 elem->name, BAD_CAST "xmlns");
4339 }
4340 }
4341
4342
4343 /* Validity Constraint: Attribute Value Type */
4344 if (attrDecl == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004345 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004346 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004347 "No declaration for attribute xmlns:%s of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004348 ns->prefix, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004349 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004350 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004351 "No declaration for attribute xmlns of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004352 elem->name, NULL, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004353 }
4354 return(0);
4355 }
4356
4357 val = xmlValidateAttributeValue(attrDecl->atype, value);
4358 if (val == 0) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004359 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004360 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004361 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004362 ns->prefix, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004363 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004364 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004365 "Syntax of value for attribute xmlns of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004366 elem->name, NULL, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004367 }
4368 ret = 0;
4369 }
4370
4371 /* Validity constraint: Fixed Attribute Default */
4372 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4373 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
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_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004376 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4377 ns->prefix, elem->name, attrDecl->defaultValue);
4378 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004379 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004380 "Value for attribute xmlns of %s is different from default \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004381 elem->name, attrDecl->defaultValue, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004382 }
4383 ret = 0;
4384 }
4385 }
4386
4387 /* Validity Constraint: ID uniqueness */
4388 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4389 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4390 ret = 0;
4391 }
4392
4393 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4394 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4395 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4396 ret = 0;
4397 }
4398
4399 /* Validity Constraint: Notation Attributes */
4400 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4401 xmlEnumerationPtr tree = attrDecl->tree;
4402 xmlNotationPtr nota;
4403
4404 /* First check that the given NOTATION was declared */
4405 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4406 if (nota == NULL)
4407 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4408
4409 if (nota == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004410 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004411 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004412 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4413 value, ns->prefix, elem->name);
4414 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004415 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004416 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004417 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004418 }
4419 ret = 0;
4420 }
4421
4422 /* Second, verify that it's among the list */
4423 while (tree != NULL) {
4424 if (xmlStrEqual(tree->name, value)) break;
4425 tree = tree->next;
4426 }
4427 if (tree == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004428 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004429 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004430"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4431 value, ns->prefix, elem->name);
4432 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004433 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004434"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004435 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004436 }
4437 ret = 0;
4438 }
4439 }
4440
4441 /* Validity Constraint: Enumeration */
4442 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4443 xmlEnumerationPtr tree = attrDecl->tree;
4444 while (tree != NULL) {
4445 if (xmlStrEqual(tree->name, value)) break;
4446 tree = tree->next;
4447 }
4448 if (tree == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004449 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004450 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004451"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4452 value, ns->prefix, elem->name);
4453 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004454 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004455"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004456 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004457 }
4458 ret = 0;
4459 }
4460 }
4461
4462 /* Fixed Attribute Default */
4463 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4464 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004465 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004466 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004467 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4468 ns->prefix, elem->name, attrDecl->defaultValue);
4469 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004470 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004471 "Value for attribute xmlns of %s must be \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004472 elem->name, attrDecl->defaultValue, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004473 }
4474 ret = 0;
4475 }
4476
4477 /* Extra check for the attribute value */
4478 if (ns->prefix != NULL) {
4479 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4480 attrDecl->atype, value);
4481 } else {
4482 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4483 attrDecl->atype, value);
4484 }
4485
4486 return(ret);
4487}
4488
Daniel Veillard118aed72002-09-24 14:13:13 +00004489#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004490/**
4491 * xmlValidateSkipIgnorable:
4492 * @ctxt: the validation context
4493 * @child: the child list
4494 *
4495 * Skip ignorable elements w.r.t. the validation process
4496 *
4497 * returns the first element to consider for validation of the content model
4498 */
4499
4500static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004501xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004502 while (child != NULL) {
4503 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004504 /* These things are ignored (skipped) during validation. */
4505 case XML_PI_NODE:
4506 case XML_COMMENT_NODE:
4507 case XML_XINCLUDE_START:
4508 case XML_XINCLUDE_END:
4509 child = child->next;
4510 break;
4511 case XML_TEXT_NODE:
4512 if (xmlIsBlankNode(child))
4513 child = child->next;
4514 else
4515 return(child);
4516 break;
4517 /* keep current node */
4518 default:
4519 return(child);
4520 }
4521 }
4522 return(child);
4523}
4524
4525/**
4526 * xmlValidateElementType:
4527 * @ctxt: the validation context
4528 *
4529 * Try to validate the content model of an element internal function
4530 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004531 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4532 * reference is found and -3 if the validation succeeded but
4533 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004534 */
4535
4536static int
4537xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004538 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004539 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004540
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004541 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004542 if ((NODE == NULL) && (CONT == NULL))
4543 return(1);
4544 if ((NODE == NULL) &&
4545 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4546 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4547 return(1);
4548 }
4549 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004550 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004551 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004552
4553 /*
4554 * We arrive here when more states need to be examined
4555 */
4556cont:
4557
4558 /*
4559 * We just recovered from a rollback generated by a possible
4560 * epsilon transition, go directly to the analysis phase
4561 */
4562 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004563 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004564 DEBUG_VALID_STATE(NODE, CONT)
4565 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004566 goto analyze;
4567 }
4568
4569 DEBUG_VALID_STATE(NODE, CONT)
4570 /*
4571 * we may have to save a backup state here. This is the equivalent
4572 * of handling epsilon transition in NFAs.
4573 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004574 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004575 ((CONT->parent == NULL) ||
4576 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004577 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004578 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004579 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004580 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004581 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4582 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004583 }
4584
4585
4586 /*
4587 * Check first if the content matches
4588 */
4589 switch (CONT->type) {
4590 case XML_ELEMENT_CONTENT_PCDATA:
4591 if (NODE == NULL) {
4592 DEBUG_VALID_MSG("pcdata failed no node");
4593 ret = 0;
4594 break;
4595 }
4596 if (NODE->type == XML_TEXT_NODE) {
4597 DEBUG_VALID_MSG("pcdata found, skip to next");
4598 /*
4599 * go to next element in the content model
4600 * skipping ignorable elems
4601 */
4602 do {
4603 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004604 NODE = xmlValidateSkipIgnorable(NODE);
4605 if ((NODE != NULL) &&
4606 (NODE->type == XML_ENTITY_REF_NODE))
4607 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004608 } while ((NODE != NULL) &&
4609 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004610 (NODE->type != XML_TEXT_NODE) &&
4611 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004612 ret = 1;
4613 break;
4614 } else {
4615 DEBUG_VALID_MSG("pcdata failed");
4616 ret = 0;
4617 break;
4618 }
4619 break;
4620 case XML_ELEMENT_CONTENT_ELEMENT:
4621 if (NODE == NULL) {
4622 DEBUG_VALID_MSG("element failed no node");
4623 ret = 0;
4624 break;
4625 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004626 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4627 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004628 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004629 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4630 ret = (CONT->prefix == NULL);
4631 } else if (CONT->prefix == NULL) {
4632 ret = 0;
4633 } else {
4634 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4635 }
4636 }
4637 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004638 DEBUG_VALID_MSG("element found, skip to next");
4639 /*
4640 * go to next element in the content model
4641 * skipping ignorable elems
4642 */
4643 do {
4644 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004645 NODE = xmlValidateSkipIgnorable(NODE);
4646 if ((NODE != NULL) &&
4647 (NODE->type == XML_ENTITY_REF_NODE))
4648 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004649 } while ((NODE != NULL) &&
4650 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004651 (NODE->type != XML_TEXT_NODE) &&
4652 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004653 } else {
4654 DEBUG_VALID_MSG("element failed");
4655 ret = 0;
4656 break;
4657 }
4658 break;
4659 case XML_ELEMENT_CONTENT_OR:
4660 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004661 * Small optimization.
4662 */
4663 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4664 if ((NODE == NULL) ||
4665 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4666 DEPTH++;
4667 CONT = CONT->c2;
4668 goto cont;
4669 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004670 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4671 ret = (CONT->c1->prefix == NULL);
4672 } else if (CONT->c1->prefix == NULL) {
4673 ret = 0;
4674 } else {
4675 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4676 }
4677 if (ret == 0) {
4678 DEPTH++;
4679 CONT = CONT->c2;
4680 goto cont;
4681 }
Daniel Veillard85349052001-04-20 13:48:21 +00004682 }
4683
4684 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004685 * save the second branch 'or' branch
4686 */
4687 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004688 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4689 OCCURS, ROLLBACK_OR) < 0)
4690 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004691 DEPTH++;
4692 CONT = CONT->c1;
4693 goto cont;
4694 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004695 /*
4696 * Small optimization.
4697 */
4698 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4699 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4700 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4701 if ((NODE == NULL) ||
4702 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4703 DEPTH++;
4704 CONT = CONT->c2;
4705 goto cont;
4706 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004707 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4708 ret = (CONT->c1->prefix == NULL);
4709 } else if (CONT->c1->prefix == NULL) {
4710 ret = 0;
4711 } else {
4712 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4713 }
4714 if (ret == 0) {
4715 DEPTH++;
4716 CONT = CONT->c2;
4717 goto cont;
4718 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004719 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004720 DEPTH++;
4721 CONT = CONT->c1;
4722 goto cont;
4723 }
4724
4725 /*
4726 * At this point handle going up in the tree
4727 */
4728 if (ret == -1) {
4729 DEBUG_VALID_MSG("error found returning");
4730 return(ret);
4731 }
4732analyze:
4733 while (CONT != NULL) {
4734 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004735 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004736 * this level.
4737 */
4738 if (ret == 0) {
4739 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004740 xmlNodePtr cur;
4741
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004742 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004743 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004744 DEBUG_VALID_MSG("Once branch failed, rollback");
4745 if (vstateVPop(ctxt) < 0 ) {
4746 DEBUG_VALID_MSG("exhaustion, failed");
4747 return(0);
4748 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004749 if (cur != ctxt->vstate->node)
4750 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004751 goto cont;
4752 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004753 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004754 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004755 DEBUG_VALID_MSG("Plus branch failed, rollback");
4756 if (vstateVPop(ctxt) < 0 ) {
4757 DEBUG_VALID_MSG("exhaustion, failed");
4758 return(0);
4759 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004760 if (cur != ctxt->vstate->node)
4761 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004762 goto cont;
4763 }
4764 DEBUG_VALID_MSG("Plus branch found");
4765 ret = 1;
4766 break;
4767 case XML_ELEMENT_CONTENT_MULT:
4768#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004769 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004770 DEBUG_VALID_MSG("Mult branch failed");
4771 } else {
4772 DEBUG_VALID_MSG("Mult branch found");
4773 }
4774#endif
4775 ret = 1;
4776 break;
4777 case XML_ELEMENT_CONTENT_OPT:
4778 DEBUG_VALID_MSG("Option branch failed");
4779 ret = 1;
4780 break;
4781 }
4782 } else {
4783 switch (CONT->ocur) {
4784 case XML_ELEMENT_CONTENT_OPT:
4785 DEBUG_VALID_MSG("Option branch succeeded");
4786 ret = 1;
4787 break;
4788 case XML_ELEMENT_CONTENT_ONCE:
4789 DEBUG_VALID_MSG("Once branch succeeded");
4790 ret = 1;
4791 break;
4792 case XML_ELEMENT_CONTENT_PLUS:
4793 if (STATE == ROLLBACK_PARENT) {
4794 DEBUG_VALID_MSG("Plus branch rollback");
4795 ret = 1;
4796 break;
4797 }
4798 if (NODE == NULL) {
4799 DEBUG_VALID_MSG("Plus branch exhausted");
4800 ret = 1;
4801 break;
4802 }
4803 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004804 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004805 goto cont;
4806 case XML_ELEMENT_CONTENT_MULT:
4807 if (STATE == ROLLBACK_PARENT) {
4808 DEBUG_VALID_MSG("Mult branch rollback");
4809 ret = 1;
4810 break;
4811 }
4812 if (NODE == NULL) {
4813 DEBUG_VALID_MSG("Mult branch exhausted");
4814 ret = 1;
4815 break;
4816 }
4817 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004818 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004819 goto cont;
4820 }
4821 }
4822 STATE = 0;
4823
4824 /*
4825 * Then act accordingly at the parent level
4826 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004827 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004828 if (CONT->parent == NULL)
4829 break;
4830
4831 switch (CONT->parent->type) {
4832 case XML_ELEMENT_CONTENT_PCDATA:
4833 DEBUG_VALID_MSG("Error: parent pcdata");
4834 return(-1);
4835 case XML_ELEMENT_CONTENT_ELEMENT:
4836 DEBUG_VALID_MSG("Error: parent element");
4837 return(-1);
4838 case XML_ELEMENT_CONTENT_OR:
4839 if (ret == 1) {
4840 DEBUG_VALID_MSG("Or succeeded");
4841 CONT = CONT->parent;
4842 DEPTH--;
4843 } else {
4844 DEBUG_VALID_MSG("Or failed");
4845 CONT = CONT->parent;
4846 DEPTH--;
4847 }
4848 break;
4849 case XML_ELEMENT_CONTENT_SEQ:
4850 if (ret == 0) {
4851 DEBUG_VALID_MSG("Sequence failed");
4852 CONT = CONT->parent;
4853 DEPTH--;
4854 } else if (CONT == CONT->parent->c1) {
4855 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4856 CONT = CONT->parent->c2;
4857 goto cont;
4858 } else {
4859 DEBUG_VALID_MSG("Sequence succeeded");
4860 CONT = CONT->parent;
4861 DEPTH--;
4862 }
4863 }
4864 }
4865 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004866 xmlNodePtr cur;
4867
4868 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004869 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4870 if (vstateVPop(ctxt) < 0 ) {
4871 DEBUG_VALID_MSG("exhaustion, failed");
4872 return(0);
4873 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004874 if (cur != ctxt->vstate->node)
4875 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004876 goto cont;
4877 }
4878 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004879 xmlNodePtr cur;
4880
4881 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004882 DEBUG_VALID_MSG("Failure, rollback");
4883 if (vstateVPop(ctxt) < 0 ) {
4884 DEBUG_VALID_MSG("exhaustion, failed");
4885 return(0);
4886 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004887 if (cur != ctxt->vstate->node)
4888 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004889 goto cont;
4890 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004891 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004892}
Daniel Veillard23e73572002-09-19 19:56:43 +00004893#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004894
4895/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004896 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004897 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004898 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004899 * @content: An element
4900 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4901 *
4902 * This will dump the list of elements to the buffer
4903 * Intended just for the debug routine
4904 */
4905static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004906xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004907 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004908 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004909
4910 if (node == NULL) return;
4911 if (glob) strcat(buf, "(");
4912 cur = node;
4913 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004914 len = strlen(buf);
4915 if (size - len < 50) {
4916 if ((size - len > 4) && (buf[len - 1] != '.'))
4917 strcat(buf, " ...");
4918 return;
4919 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004920 switch (cur->type) {
4921 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004922 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004923 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004924 if ((size - len > 4) && (buf[len - 1] != '.'))
4925 strcat(buf, " ...");
4926 return;
4927 }
4928 strcat(buf, (char *) cur->ns->prefix);
4929 strcat(buf, ":");
4930 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004931 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004932 if ((size - len > 4) && (buf[len - 1] != '.'))
4933 strcat(buf, " ...");
4934 return;
4935 }
4936 strcat(buf, (char *) cur->name);
4937 if (cur->next != NULL)
4938 strcat(buf, " ");
4939 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004940 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004941 if (xmlIsBlankNode(cur))
4942 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004943 case XML_CDATA_SECTION_NODE:
4944 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004945 strcat(buf, "CDATA");
4946 if (cur->next != NULL)
4947 strcat(buf, " ");
4948 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004949 case XML_ATTRIBUTE_NODE:
4950 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004951#ifdef LIBXML_DOCB_ENABLED
4952 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004953#endif
4954 case XML_HTML_DOCUMENT_NODE:
4955 case XML_DOCUMENT_TYPE_NODE:
4956 case XML_DOCUMENT_FRAG_NODE:
4957 case XML_NOTATION_NODE:
4958 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004959 strcat(buf, "???");
4960 if (cur->next != NULL)
4961 strcat(buf, " ");
4962 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004963 case XML_ENTITY_NODE:
4964 case XML_PI_NODE:
4965 case XML_DTD_NODE:
4966 case XML_COMMENT_NODE:
4967 case XML_ELEMENT_DECL:
4968 case XML_ATTRIBUTE_DECL:
4969 case XML_ENTITY_DECL:
4970 case XML_XINCLUDE_START:
4971 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004972 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004973 }
4974 cur = cur->next;
4975 }
4976 if (glob) strcat(buf, ")");
4977}
4978
4979/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004980 * xmlValidateElementContent:
4981 * @ctxt: the validation context
4982 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004983 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004984 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004985 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004986 *
4987 * Try to validate the content model of an element
4988 *
4989 * returns 1 if valid or 0 if not and -1 in case of error
4990 */
4991
4992static int
4993xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004994 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004995 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004996#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004997 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004998#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004999 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005000 xmlElementContentPtr cont;
5001 const xmlChar *name;
5002
5003 if (elemDecl == NULL)
5004 return(-1);
5005 cont = elemDecl->content;
5006 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005007
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005008#ifdef LIBXML_REGEXP_ENABLED
5009 /* Build the regexp associated to the content model */
5010 if (elemDecl->contModel == NULL)
5011 ret = xmlValidBuildContentModel(ctxt, elemDecl);
5012 if (elemDecl->contModel == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005013 return(-1);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005014 } else {
5015 xmlRegExecCtxtPtr exec;
5016
Daniel Veillardec498e12003-02-05 11:01:50 +00005017 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
5018 return(-1);
5019 }
Daniel Veillard01992e02002-10-09 10:20:30 +00005020 ctxt->nodeMax = 0;
5021 ctxt->nodeNr = 0;
5022 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005023 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
5024 if (exec != NULL) {
5025 cur = child;
5026 while (cur != NULL) {
5027 switch (cur->type) {
5028 case XML_ENTITY_REF_NODE:
5029 /*
5030 * Push the current node to be able to roll back
5031 * and process within the entity
5032 */
5033 if ((cur->children != NULL) &&
5034 (cur->children->children != NULL)) {
5035 nodeVPush(ctxt, cur);
5036 cur = cur->children->children;
5037 continue;
5038 }
5039 break;
5040 case XML_TEXT_NODE:
5041 if (xmlIsBlankNode(cur))
5042 break;
5043 ret = 0;
5044 goto fail;
5045 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00005046 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005047 ret = 0;
5048 goto fail;
5049 case XML_ELEMENT_NODE:
5050 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005051 xmlChar fn[50];
5052 xmlChar *fullname;
5053
5054 fullname = xmlBuildQName(cur->name,
5055 cur->ns->prefix, fn, 50);
5056 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005057 ret = -1;
5058 goto fail;
5059 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005060 ret = xmlRegExecPushString(exec, fullname, NULL);
5061 if ((fullname != fn) && (fullname != cur->name))
5062 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005063 } else {
5064 ret = xmlRegExecPushString(exec, cur->name, NULL);
5065 }
5066 break;
5067 default:
5068 break;
5069 }
5070 /*
5071 * Switch to next element
5072 */
5073 cur = cur->next;
5074 while (cur == NULL) {
5075 cur = nodeVPop(ctxt);
5076 if (cur == NULL)
5077 break;
5078 cur = cur->next;
5079 }
5080 }
5081 ret = xmlRegExecPushString(exec, NULL, NULL);
5082fail:
5083 xmlRegFreeExecCtxt(exec);
5084 }
5085 }
5086#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005087 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005088 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005089 */
5090 ctxt->vstateMax = 8;
5091 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
5092 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
5093 if (ctxt->vstateTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00005094 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005095 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005096 }
5097 /*
5098 * The first entry in the stack is reserved to the current state
5099 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00005100 ctxt->nodeMax = 0;
5101 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00005102 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005103 ctxt->vstate = &ctxt->vstateTab[0];
5104 ctxt->vstateNr = 1;
5105 CONT = cont;
5106 NODE = child;
5107 DEPTH = 0;
5108 OCCURS = 0;
5109 STATE = 0;
5110 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005111 if ((ret == -3) && (warn)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005112 xmlErrValidWarning(ctxt, child, XML_DTD_CONTENT_NOT_DETERMINIST,
5113 "Content model for Element %s is ambiguous\n",
Daniel Veillard0cc72772003-10-13 14:00:21 +00005114 name, NULL, NULL);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005115 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005116 /*
5117 * An entities reference appeared at this level.
5118 * Buid a minimal representation of this node content
5119 * sufficient to run the validation process on it
5120 */
5121 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005122 cur = child;
5123 while (cur != NULL) {
5124 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005125 case XML_ENTITY_REF_NODE:
5126 /*
5127 * Push the current node to be able to roll back
5128 * and process within the entity
5129 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005130 if ((cur->children != NULL) &&
5131 (cur->children->children != NULL)) {
5132 nodeVPush(ctxt, cur);
5133 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005134 continue;
5135 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00005136 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005137 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005138 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005139 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005140 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00005141 case XML_CDATA_SECTION_NODE:
5142 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005143 case XML_ELEMENT_NODE:
5144 /*
5145 * Allocate a new node and minimally fills in
5146 * what's required
5147 */
5148 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
5149 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00005150 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005151 xmlFreeNodeList(repl);
5152 ret = -1;
5153 goto done;
5154 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005155 tmp->type = cur->type;
5156 tmp->name = cur->name;
5157 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005158 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00005159 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005160 if (repl == NULL)
5161 repl = last = tmp;
5162 else {
5163 last->next = tmp;
5164 last = tmp;
5165 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00005166 if (cur->type == XML_CDATA_SECTION_NODE) {
5167 /*
5168 * E59 spaces in CDATA does not match the
5169 * nonterminal S
5170 */
5171 tmp->content = xmlStrdup(BAD_CAST "CDATA");
5172 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005173 break;
5174 default:
5175 break;
5176 }
5177 /*
5178 * Switch to next element
5179 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005180 cur = cur->next;
5181 while (cur == NULL) {
5182 cur = nodeVPop(ctxt);
5183 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005184 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005185 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005186 }
5187 }
5188
5189 /*
5190 * Relaunch the validation
5191 */
5192 ctxt->vstate = &ctxt->vstateTab[0];
5193 ctxt->vstateNr = 1;
5194 CONT = cont;
5195 NODE = repl;
5196 DEPTH = 0;
5197 OCCURS = 0;
5198 STATE = 0;
5199 ret = xmlValidateElementType(ctxt);
5200 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005201#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005202 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005203 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
5204 char expr[5000];
5205 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005206
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005207 expr[0] = 0;
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005208 xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005209 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00005210#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005211 if (repl != NULL)
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005212 xmlSnprintfElements(&list[0], 5000, repl, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005213 else
Daniel Veillard01992e02002-10-09 10:20:30 +00005214#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005215 xmlSnprintfElements(&list[0], 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005216
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005217 if (name != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005218 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5219 "Element %s content does not follow the DTD, expecting %s, got %s\n",
5220 name, BAD_CAST expr, BAD_CAST list);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005221 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005222 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5223 "Element content does not follow the DTD, expecting %s, got %s\n",
5224 BAD_CAST expr, BAD_CAST list, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005225 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005226 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005227 if (name != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005228 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005229 "Element %s content does not follow the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005230 name, NULL, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005231 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005232 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5233 "Element content does not follow the DTD\n",
5234 NULL, NULL, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005235 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005236 }
5237 ret = 0;
5238 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005239 if (ret == -3)
5240 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005241
Daniel Veillard23e73572002-09-19 19:56:43 +00005242#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005243done:
5244 /*
5245 * Deallocate the copy if done, and free up the validation stack
5246 */
5247 while (repl != NULL) {
5248 tmp = repl->next;
5249 xmlFree(repl);
5250 repl = tmp;
5251 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005252 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005253 if (ctxt->vstateTab != NULL) {
5254 xmlFree(ctxt->vstateTab);
5255 ctxt->vstateTab = NULL;
5256 }
Daniel Veillard01992e02002-10-09 10:20:30 +00005257#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005258 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00005259 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005260 if (ctxt->nodeTab != NULL) {
5261 xmlFree(ctxt->nodeTab);
5262 ctxt->nodeTab = NULL;
5263 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005264 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005265
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005266}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005267
Owen Taylor3473f882001-02-23 17:55:21 +00005268/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005269 * xmlValidateCdataElement:
5270 * @ctxt: the validation context
5271 * @doc: a document instance
5272 * @elem: an element instance
5273 *
5274 * Check that an element follows #CDATA
5275 *
5276 * returns 1 if valid or 0 otherwise
5277 */
5278static int
5279xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5280 xmlNodePtr elem) {
5281 int ret = 1;
5282 xmlNodePtr cur, child;
5283
Daniel Veillardceb09b92002-10-04 11:46:37 +00005284 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005285 return(0);
5286
5287 child = elem->children;
5288
5289 cur = child;
5290 while (cur != NULL) {
5291 switch (cur->type) {
5292 case XML_ENTITY_REF_NODE:
5293 /*
5294 * Push the current node to be able to roll back
5295 * and process within the entity
5296 */
5297 if ((cur->children != NULL) &&
5298 (cur->children->children != NULL)) {
5299 nodeVPush(ctxt, cur);
5300 cur = cur->children->children;
5301 continue;
5302 }
5303 break;
5304 case XML_COMMENT_NODE:
5305 case XML_PI_NODE:
5306 case XML_TEXT_NODE:
5307 case XML_CDATA_SECTION_NODE:
5308 break;
5309 default:
5310 ret = 0;
5311 goto done;
5312 }
5313 /*
5314 * Switch to next element
5315 */
5316 cur = cur->next;
5317 while (cur == NULL) {
5318 cur = nodeVPop(ctxt);
5319 if (cur == NULL)
5320 break;
5321 cur = cur->next;
5322 }
5323 }
5324done:
5325 ctxt->nodeMax = 0;
5326 ctxt->nodeNr = 0;
5327 if (ctxt->nodeTab != NULL) {
5328 xmlFree(ctxt->nodeTab);
5329 ctxt->nodeTab = NULL;
5330 }
5331 return(ret);
5332}
5333
5334/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005335 * xmlValidateCheckMixed:
5336 * @ctxt: the validation context
5337 * @cont: the mixed content model
5338 * @qname: the qualified name as appearing in the serialization
5339 *
5340 * Check if the given node is part of the content model.
5341 *
5342 * Returns 1 if yes, 0 if no, -1 in case of error
5343 */
5344static int
William M. Brackedb65a72004-02-06 07:36:04 +00005345xmlValidateCheckMixed(xmlValidCtxtPtr ctxt,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005346 xmlElementContentPtr cont, const xmlChar *qname) {
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005347 const xmlChar *name;
5348 int plen;
5349 name = xmlSplitQName3(qname, &plen);
5350
5351 if (name == NULL) {
5352 while (cont != NULL) {
5353 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5354 if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname)))
5355 return(1);
5356 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5357 (cont->c1 != NULL) &&
5358 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5359 if ((cont->c1->prefix == NULL) &&
5360 (xmlStrEqual(cont->c1->name, qname)))
5361 return(1);
5362 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5363 (cont->c1 == NULL) ||
5364 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005365 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5366 "Internal: MIXED struct corrupted\n",
5367 NULL);
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005368 break;
5369 }
5370 cont = cont->c2;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005371 }
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005372 } else {
5373 while (cont != NULL) {
5374 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5375 if ((cont->prefix != NULL) &&
5376 (xmlStrncmp(cont->prefix, qname, plen) == 0) &&
5377 (xmlStrEqual(cont->name, name)))
5378 return(1);
5379 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5380 (cont->c1 != NULL) &&
5381 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5382 if ((cont->c1->prefix != NULL) &&
5383 (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) &&
5384 (xmlStrEqual(cont->c1->name, name)))
5385 return(1);
5386 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5387 (cont->c1 == NULL) ||
5388 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005389 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
5390 "Internal: MIXED struct corrupted\n",
5391 NULL);
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005392 break;
5393 }
5394 cont = cont->c2;
5395 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005396 }
5397 return(0);
5398}
5399
5400/**
5401 * xmlValidGetElemDecl:
5402 * @ctxt: the validation context
5403 * @doc: a document instance
5404 * @elem: an element instance
5405 * @extsubset: pointer, (out) indicate if the declaration was found
5406 * in the external subset.
5407 *
5408 * Finds a declaration associated to an element in the document.
5409 *
5410 * returns the pointer to the declaration or NULL if not found.
5411 */
5412static xmlElementPtr
5413xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5414 xmlNodePtr elem, int *extsubset) {
5415 xmlElementPtr elemDecl = NULL;
5416 const xmlChar *prefix = NULL;
5417
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005418 if ((ctxt == NULL) || (doc == NULL) ||
5419 (elem == NULL) || (elem->name == NULL))
5420 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005421 if (extsubset != NULL)
5422 *extsubset = 0;
5423
5424 /*
5425 * Fetch the declaration for the qualified name
5426 */
5427 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5428 prefix = elem->ns->prefix;
5429
5430 if (prefix != NULL) {
5431 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5432 elem->name, prefix);
5433 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5434 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5435 elem->name, prefix);
5436 if ((elemDecl != NULL) && (extsubset != NULL))
5437 *extsubset = 1;
5438 }
5439 }
5440
5441 /*
5442 * Fetch the declaration for the non qualified name
5443 * This is "non-strict" validation should be done on the
5444 * full QName but in that case being flexible makes sense.
5445 */
5446 if (elemDecl == NULL) {
5447 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5448 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5449 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5450 if ((elemDecl != NULL) && (extsubset != NULL))
5451 *extsubset = 1;
5452 }
5453 }
5454 if (elemDecl == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005455 xmlErrValidNode(ctxt, elem,
5456 XML_DTD_UNKNOWN_ELEM,
5457 "No declaration for element %s\n",
5458 elem->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005459 }
5460 return(elemDecl);
5461}
5462
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005463#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005464/**
5465 * xmlValidatePushElement:
5466 * @ctxt: the validation context
5467 * @doc: a document instance
5468 * @elem: an element instance
5469 * @qname: the qualified name as appearing in the serialization
5470 *
5471 * Push a new element start on the validation stack.
5472 *
5473 * returns 1 if no validation problem was found or 0 otherwise
5474 */
5475int
5476xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5477 xmlNodePtr elem, const xmlChar *qname) {
5478 int ret = 1;
5479 xmlElementPtr eDecl;
5480 int extsubset = 0;
5481
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005482 if (ctxt == NULL)
5483 return(0);
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005484/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005485 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5486 xmlValidStatePtr state = ctxt->vstate;
5487 xmlElementPtr elemDecl;
5488
5489 /*
5490 * Check the new element agaisnt the content model of the new elem.
5491 */
5492 if (state->elemDecl != NULL) {
5493 elemDecl = state->elemDecl;
5494
5495 switch(elemDecl->etype) {
5496 case XML_ELEMENT_TYPE_UNDEFINED:
5497 ret = 0;
5498 break;
5499 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005500 xmlErrValidNode(ctxt, state->node,
5501 XML_DTD_NOT_EMPTY,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005502 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005503 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005504 ret = 0;
5505 break;
5506 case XML_ELEMENT_TYPE_ANY:
5507 /* I don't think anything is required then */
5508 break;
5509 case XML_ELEMENT_TYPE_MIXED:
5510 /* simple case of declared as #PCDATA */
5511 if ((elemDecl->content != NULL) &&
5512 (elemDecl->content->type ==
5513 XML_ELEMENT_CONTENT_PCDATA)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005514 xmlErrValidNode(ctxt, state->node,
5515 XML_DTD_NOT_PCDATA,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005516 "Element %s was declared #PCDATA but contains non text nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005517 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005518 ret = 0;
5519 } else {
5520 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5521 qname);
5522 if (ret != 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005523 xmlErrValidNode(ctxt, state->node,
5524 XML_DTD_INVALID_CHILD,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005525 "Element %s is not declared in %s list of possible children\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005526 qname, state->node->name, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005527 }
5528 }
5529 break;
5530 case XML_ELEMENT_TYPE_ELEMENT:
5531 /*
5532 * TODO:
5533 * VC: Standalone Document Declaration
5534 * - element types with element content, if white space
5535 * occurs directly within any instance of those types.
5536 */
5537 if (state->exec != NULL) {
5538 ret = xmlRegExecPushString(state->exec, qname, NULL);
5539 if (ret < 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005540 xmlErrValidNode(ctxt, state->node,
5541 XML_DTD_CONTENT_MODEL,
5542 "Element %s content does not follow the DTD, Misplaced %s\n",
5543 state->node->name, qname, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005544 ret = 0;
5545 } else {
5546 ret = 1;
5547 }
5548 }
5549 break;
5550 }
5551 }
5552 }
5553 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5554 vstateVPush(ctxt, eDecl, elem);
5555 return(ret);
5556}
5557
5558/**
5559 * xmlValidatePushCData:
5560 * @ctxt: the validation context
5561 * @data: some character data read
5562 * @len: the lenght of the data
5563 *
5564 * check the CData parsed for validation in the current stack
5565 *
5566 * returns 1 if no validation problem was found or 0 otherwise
5567 */
5568int
5569xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5570 int ret = 1;
5571
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005572/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005573 if (ctxt == NULL)
5574 return(0);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005575 if (len <= 0)
5576 return(ret);
5577 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5578 xmlValidStatePtr state = ctxt->vstate;
5579 xmlElementPtr elemDecl;
5580
5581 /*
5582 * Check the new element agaisnt the content model of the new elem.
5583 */
5584 if (state->elemDecl != NULL) {
5585 elemDecl = state->elemDecl;
5586
5587 switch(elemDecl->etype) {
5588 case XML_ELEMENT_TYPE_UNDEFINED:
5589 ret = 0;
5590 break;
5591 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005592 xmlErrValidNode(ctxt, state->node,
5593 XML_DTD_NOT_EMPTY,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005594 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005595 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005596 ret = 0;
5597 break;
5598 case XML_ELEMENT_TYPE_ANY:
5599 break;
5600 case XML_ELEMENT_TYPE_MIXED:
5601 break;
5602 case XML_ELEMENT_TYPE_ELEMENT:
5603 if (len > 0) {
5604 int i;
5605
5606 for (i = 0;i < len;i++) {
William M. Brack76e95df2003-10-18 16:20:14 +00005607 if (!IS_BLANK_CH(data[i])) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005608 xmlErrValidNode(ctxt, state->node,
5609 XML_DTD_CONTENT_MODEL,
5610 "Element %s content does not follow the DTD, Text not allowed\n",
5611 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005612 ret = 0;
5613 goto done;
5614 }
5615 }
5616 /*
5617 * TODO:
5618 * VC: Standalone Document Declaration
5619 * element types with element content, if white space
5620 * occurs directly within any instance of those types.
5621 */
5622 }
5623 break;
5624 }
5625 }
5626 }
5627done:
5628 return(ret);
5629}
5630
5631/**
5632 * xmlValidatePopElement:
5633 * @ctxt: the validation context
5634 * @doc: a document instance
5635 * @elem: an element instance
5636 * @qname: the qualified name as appearing in the serialization
5637 *
5638 * Pop the element end from the validation stack.
5639 *
5640 * returns 1 if no validation problem was found or 0 otherwise
5641 */
5642int
5643xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005644 xmlNodePtr elem ATTRIBUTE_UNUSED,
5645 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005646 int ret = 1;
5647
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005648 if (ctxt == NULL)
5649 return(0);
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005650/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005651 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5652 xmlValidStatePtr state = ctxt->vstate;
5653 xmlElementPtr elemDecl;
5654
5655 /*
5656 * Check the new element agaisnt the content model of the new elem.
5657 */
5658 if (state->elemDecl != NULL) {
5659 elemDecl = state->elemDecl;
5660
5661 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5662 if (state->exec != NULL) {
5663 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5664 if (ret == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005665 xmlErrValidNode(ctxt, state->node,
5666 XML_DTD_CONTENT_MODEL,
5667 "Element %s content does not follow the DTD, Expecting more child\n",
5668 state->node->name, NULL,NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005669 } else {
5670 /*
5671 * previous validation errors should not generate
5672 * a new one here
5673 */
5674 ret = 1;
5675 }
5676 }
5677 }
5678 }
5679 vstateVPop(ctxt);
5680 }
5681 return(ret);
5682}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005683#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005684
5685/**
Owen Taylor3473f882001-02-23 17:55:21 +00005686 * xmlValidateOneElement:
5687 * @ctxt: the validation context
5688 * @doc: a document instance
5689 * @elem: an element instance
5690 *
5691 * Try to validate a single element and it's attributes,
5692 * basically it does the following checks as described by the
5693 * XML-1.0 recommendation:
5694 * - [ VC: Element Valid ]
5695 * - [ VC: Required Attribute ]
5696 * Then call xmlValidateOneAttribute() for each attribute present.
5697 *
5698 * The ID/IDREF checkings are done separately
5699 *
5700 * returns 1 if valid or 0 otherwise
5701 */
5702
5703int
5704xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5705 xmlNodePtr elem) {
5706 xmlElementPtr elemDecl = NULL;
5707 xmlElementContentPtr cont;
5708 xmlAttributePtr attr;
5709 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005710 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005711 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005712 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005713
5714 CHECK_DTD;
5715
5716 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005717 switch (elem->type) {
5718 case XML_ATTRIBUTE_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005719 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5720 "Attribute element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005721 return(0);
5722 case XML_TEXT_NODE:
5723 if (elem->children != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005724 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5725 "Text element has children !\n",
5726 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005727 return(0);
5728 }
5729 if (elem->properties != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005730 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5731 "Text element has attribute !\n",
5732 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005733 return(0);
5734 }
5735 if (elem->ns != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005736 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5737 "Text element has namespace !\n",
5738 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005739 return(0);
5740 }
5741 if (elem->nsDef != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005742 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5743 "Text element has namespace !\n",
5744 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005745 return(0);
5746 }
5747 if (elem->content == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005748 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5749 "Text element has no content !\n",
5750 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005751 return(0);
5752 }
5753 return(1);
5754 case XML_XINCLUDE_START:
5755 case XML_XINCLUDE_END:
5756 return(1);
5757 case XML_CDATA_SECTION_NODE:
5758 case XML_ENTITY_REF_NODE:
5759 case XML_PI_NODE:
5760 case XML_COMMENT_NODE:
5761 return(1);
5762 case XML_ENTITY_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005763 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5764 "Entity element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005765 return(0);
5766 case XML_NOTATION_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005767 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5768 "Notation element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005769 return(0);
5770 case XML_DOCUMENT_NODE:
5771 case XML_DOCUMENT_TYPE_NODE:
5772 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005773 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5774 "Document element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005775 return(0);
5776 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005777 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5778 "HTML Document not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005779 return(0);
5780 case XML_ELEMENT_NODE:
5781 break;
5782 default:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005783 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5784 "unknown element type\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005785 return(0);
5786 }
Owen Taylor3473f882001-02-23 17:55:21 +00005787
5788 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005789 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005790 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005791 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5792 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005793 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005794
Daniel Veillardea7751d2002-12-20 00:16:24 +00005795 /*
5796 * If vstateNr is not zero that means continuous validation is
5797 * activated, do not try to check the content model at that level.
5798 */
5799 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005800 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005801 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005802 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005803 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ELEM,
5804 "No declaration for element %s\n",
5805 elem->name, NULL, NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005806 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005807 case XML_ELEMENT_TYPE_EMPTY:
5808 if (elem->children != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005809 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_EMPTY,
Owen Taylor3473f882001-02-23 17:55:21 +00005810 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005811 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005812 ret = 0;
5813 }
5814 break;
5815 case XML_ELEMENT_TYPE_ANY:
5816 /* I don't think anything is required then */
5817 break;
5818 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005819
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005820 /* simple case of declared as #PCDATA */
5821 if ((elemDecl->content != NULL) &&
5822 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5823 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5824 if (!ret) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005825 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_PCDATA,
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005826 "Element %s was declared #PCDATA but contains non text nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005827 elem->name, NULL, NULL);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005828 }
5829 break;
5830 }
Owen Taylor3473f882001-02-23 17:55:21 +00005831 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005832 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005833 while (child != NULL) {
5834 if (child->type == XML_ELEMENT_NODE) {
5835 name = child->name;
5836 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005837 xmlChar fn[50];
5838 xmlChar *fullname;
5839
5840 fullname = xmlBuildQName(child->name, child->ns->prefix,
5841 fn, 50);
5842 if (fullname == NULL)
5843 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005844 cont = elemDecl->content;
5845 while (cont != NULL) {
5846 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005847 if (xmlStrEqual(cont->name, fullname))
5848 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005849 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5850 (cont->c1 != NULL) &&
5851 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005852 if (xmlStrEqual(cont->c1->name, fullname))
5853 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005854 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5855 (cont->c1 == NULL) ||
5856 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005857 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5858 "Internal: MIXED struct corrupted\n",
5859 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005860 break;
5861 }
5862 cont = cont->c2;
5863 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005864 if ((fullname != fn) && (fullname != child->name))
5865 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005866 if (cont != NULL)
5867 goto child_ok;
5868 }
5869 cont = elemDecl->content;
5870 while (cont != NULL) {
5871 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5872 if (xmlStrEqual(cont->name, name)) break;
5873 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5874 (cont->c1 != NULL) &&
5875 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5876 if (xmlStrEqual(cont->c1->name, name)) break;
5877 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5878 (cont->c1 == NULL) ||
5879 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005880 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
5881 "Internal: MIXED struct corrupted\n",
5882 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005883 break;
5884 }
5885 cont = cont->c2;
5886 }
5887 if (cont == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005888 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_CHILD,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005889 "Element %s is not declared in %s list of possible children\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005890 name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005891 ret = 0;
5892 }
5893 }
5894child_ok:
5895 child = child->next;
5896 }
5897 break;
5898 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005899 if ((doc->standalone == 1) && (extsubset == 1)) {
5900 /*
5901 * VC: Standalone Document Declaration
5902 * - element types with element content, if white space
5903 * occurs directly within any instance of those types.
5904 */
5905 child = elem->children;
5906 while (child != NULL) {
5907 if (child->type == XML_TEXT_NODE) {
5908 const xmlChar *content = child->content;
5909
William M. Brack76e95df2003-10-18 16:20:14 +00005910 while (IS_BLANK_CH(*content))
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005911 content++;
5912 if (*content == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005913 xmlErrValidNode(ctxt, elem,
5914 XML_DTD_STANDALONE_WHITE_SPACE,
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005915"standalone: %s declared in the external subset contains white spaces nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005916 elem->name, NULL, NULL);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005917 ret = 0;
5918 break;
5919 }
5920 }
5921 child =child->next;
5922 }
5923 }
Owen Taylor3473f882001-02-23 17:55:21 +00005924 child = elem->children;
5925 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005926 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005927 if (tmp <= 0)
5928 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005929 break;
5930 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005931 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005932
5933 /* [ VC: Required Attribute ] */
5934 attr = elemDecl->attributes;
5935 while (attr != NULL) {
5936 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005937 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005938
Daniel Veillarde4301c82002-02-13 13:32:35 +00005939 if ((attr->prefix == NULL) &&
5940 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5941 xmlNsPtr ns;
5942
5943 ns = elem->nsDef;
5944 while (ns != NULL) {
5945 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005946 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005947 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005948 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005949 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5950 xmlNsPtr ns;
5951
5952 ns = elem->nsDef;
5953 while (ns != NULL) {
5954 if (xmlStrEqual(attr->name, ns->prefix))
5955 goto found;
5956 ns = ns->next;
5957 }
5958 } else {
5959 xmlAttrPtr attrib;
5960
5961 attrib = elem->properties;
5962 while (attrib != NULL) {
5963 if (xmlStrEqual(attrib->name, attr->name)) {
5964 if (attr->prefix != NULL) {
5965 xmlNsPtr nameSpace = attrib->ns;
5966
5967 if (nameSpace == NULL)
5968 nameSpace = elem->ns;
5969 /*
5970 * qualified names handling is problematic, having a
5971 * different prefix should be possible but DTDs don't
5972 * allow to define the URI instead of the prefix :-(
5973 */
5974 if (nameSpace == NULL) {
5975 if (qualified < 0)
5976 qualified = 0;
5977 } else if (!xmlStrEqual(nameSpace->prefix,
5978 attr->prefix)) {
5979 if (qualified < 1)
5980 qualified = 1;
5981 } else
5982 goto found;
5983 } else {
5984 /*
5985 * We should allow applications to define namespaces
5986 * for their application even if the DTD doesn't
5987 * carry one, otherwise, basically we would always
5988 * break.
5989 */
5990 goto found;
5991 }
5992 }
5993 attrib = attrib->next;
5994 }
Owen Taylor3473f882001-02-23 17:55:21 +00005995 }
5996 if (qualified == -1) {
5997 if (attr->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005998 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005999 "Element %s does not carry attribute %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006000 elem->name, attr->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006001 ret = 0;
6002 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006003 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006004 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00006005 elem->name, attr->prefix,attr->name);
6006 ret = 0;
6007 }
6008 } else if (qualified == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006009 xmlErrValidWarning(ctxt, elem, XML_DTD_NO_PREFIX,
Owen Taylor3473f882001-02-23 17:55:21 +00006010 "Element %s required attribute %s:%s has no prefix\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006011 elem->name, attr->prefix, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00006012 } else if (qualified == 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006013 xmlErrValidWarning(ctxt, elem, XML_DTD_DIFFERENT_PREFIX,
Owen Taylor3473f882001-02-23 17:55:21 +00006014 "Element %s required attribute %s:%s has different prefix\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006015 elem->name, attr->prefix, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00006016 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00006017 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
6018 /*
6019 * Special tests checking #FIXED namespace declarations
6020 * have the right value since this is not done as an
6021 * attribute checking
6022 */
6023 if ((attr->prefix == NULL) &&
6024 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
6025 xmlNsPtr ns;
6026
6027 ns = elem->nsDef;
6028 while (ns != NULL) {
6029 if (ns->prefix == NULL) {
6030 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006031 xmlErrValidNode(ctxt, elem,
6032 XML_DTD_ELEM_DEFAULT_NAMESPACE,
Daniel Veillarde4301c82002-02-13 13:32:35 +00006033 "Element %s namespace name for default namespace does not match the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006034 elem->name, NULL, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00006035 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00006036 }
6037 goto found;
6038 }
6039 ns = ns->next;
6040 }
6041 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
6042 xmlNsPtr ns;
6043
6044 ns = elem->nsDef;
6045 while (ns != NULL) {
6046 if (xmlStrEqual(attr->name, ns->prefix)) {
6047 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006048 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006049 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006050 elem->name, ns->prefix, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00006051 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00006052 }
6053 goto found;
6054 }
6055 ns = ns->next;
6056 }
6057 }
Owen Taylor3473f882001-02-23 17:55:21 +00006058 }
6059found:
6060 attr = attr->nexth;
6061 }
6062 return(ret);
6063}
6064
6065/**
6066 * xmlValidateRoot:
6067 * @ctxt: the validation context
6068 * @doc: a document instance
6069 *
6070 * Try to validate a the root element
6071 * basically it does the following check as described by the
6072 * XML-1.0 recommendation:
6073 * - [ VC: Root Element Type ]
6074 * it doesn't try to recurse or apply other check to the element
6075 *
6076 * returns 1 if valid or 0 otherwise
6077 */
6078
6079int
6080xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6081 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00006082 int ret;
6083
Owen Taylor3473f882001-02-23 17:55:21 +00006084 if (doc == NULL) return(0);
6085
6086 root = xmlDocGetRootElement(doc);
6087 if ((root == NULL) || (root->name == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006088 xmlErrValid(ctxt, XML_DTD_NO_ROOT,
6089 "no root element\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006090 return(0);
6091 }
6092
6093 /*
6094 * When doing post validation against a separate DTD, those may
6095 * no internal subset has been generated
6096 */
6097 if ((doc->intSubset != NULL) &&
6098 (doc->intSubset->name != NULL)) {
6099 /*
6100 * Check first the document root against the NQName
6101 */
6102 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
6103 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00006104 xmlChar fn[50];
6105 xmlChar *fullname;
6106
6107 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
6108 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00006109 xmlVErrMemory(ctxt, NULL);
Daniel Veillardc00cda82003-04-07 10:22:39 +00006110 return(0);
6111 }
6112 ret = xmlStrEqual(doc->intSubset->name, fullname);
6113 if ((fullname != fn) && (fullname != root->name))
6114 xmlFree(fullname);
6115 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00006116 goto name_ok;
6117 }
6118 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
6119 (xmlStrEqual(root->name, BAD_CAST "html")))
6120 goto name_ok;
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006121 xmlErrValidNode(ctxt, root, XML_DTD_ROOT_NAME,
6122 "root and DTD name do not match '%s' and '%s'\n",
6123 root->name, doc->intSubset->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006124 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00006125 }
6126 }
6127name_ok:
6128 return(1);
6129}
6130
6131
6132/**
6133 * xmlValidateElement:
6134 * @ctxt: the validation context
6135 * @doc: a document instance
6136 * @elem: an element instance
6137 *
6138 * Try to validate the subtree under an element
6139 *
6140 * returns 1 if valid or 0 otherwise
6141 */
6142
6143int
6144xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
6145 xmlNodePtr child;
6146 xmlAttrPtr attr;
Daniel Veillarde133dd82003-10-30 10:42:20 +00006147 xmlNsPtr ns;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006148 const xmlChar *value;
Owen Taylor3473f882001-02-23 17:55:21 +00006149 int ret = 1;
6150
6151 if (elem == NULL) return(0);
6152
6153 /*
6154 * XInclude elements were added after parsing in the infoset,
6155 * they don't really mean anything validation wise.
6156 */
6157 if ((elem->type == XML_XINCLUDE_START) ||
6158 (elem->type == XML_XINCLUDE_END))
6159 return(1);
6160
6161 CHECK_DTD;
6162
Daniel Veillard10ea86c2001-06-20 13:55:33 +00006163 /*
6164 * Entities references have to be handled separately
6165 */
6166 if (elem->type == XML_ENTITY_REF_NODE) {
6167 return(1);
6168 }
6169
Owen Taylor3473f882001-02-23 17:55:21 +00006170 ret &= xmlValidateOneElement(ctxt, doc, elem);
6171 attr = elem->properties;
Daniel Veillarde133dd82003-10-30 10:42:20 +00006172 while (attr != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00006173 value = xmlNodeListGetString(doc, attr->children, 0);
6174 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
6175 if (value != NULL)
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006176 xmlFree((char *)value);
Owen Taylor3473f882001-02-23 17:55:21 +00006177 attr= attr->next;
6178 }
Daniel Veillarde133dd82003-10-30 10:42:20 +00006179 ns = elem->nsDef;
6180 while (ns != NULL) {
Daniel Veillard1f5c9892003-12-29 17:09:55 +00006181 if (elem->ns == NULL)
6182 ret &= xmlValidateOneNamespace(ctxt, doc, elem, NULL,
6183 ns, ns->href);
6184 else
6185 ret &= xmlValidateOneNamespace(ctxt, doc, elem, elem->ns->prefix,
6186 ns, ns->href);
Daniel Veillarde133dd82003-10-30 10:42:20 +00006187 ns = ns->next;
6188 }
Owen Taylor3473f882001-02-23 17:55:21 +00006189 child = elem->children;
6190 while (child != NULL) {
6191 ret &= xmlValidateElement(ctxt, doc, child);
6192 child = child->next;
6193 }
6194
6195 return(ret);
6196}
6197
Daniel Veillard8730c562001-02-26 10:49:57 +00006198/**
6199 * xmlValidateRef:
6200 * @ref: A reference to be validated
6201 * @ctxt: Validation context
6202 * @name: Name of ID we are searching for
6203 *
6204 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006205static void
Daniel Veillard8730c562001-02-26 10:49:57 +00006206xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00006207 const xmlChar *name) {
6208 xmlAttrPtr id;
6209 xmlAttrPtr attr;
6210
6211 if (ref == NULL)
6212 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006213 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00006214 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006215 attr = ref->attr;
6216 if (attr == NULL) {
6217 xmlChar *dup, *str = NULL, *cur, save;
6218
6219 dup = xmlStrdup(name);
6220 if (dup == NULL) {
6221 ctxt->valid = 0;
6222 return;
6223 }
6224 cur = dup;
6225 while (*cur != 0) {
6226 str = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00006227 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006228 save = *cur;
6229 *cur = 0;
6230 id = xmlGetID(ctxt->doc, str);
6231 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006232 xmlErrValidNodeNr(ctxt, NULL, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006233 "attribute %s line %d references an unknown ID \"%s\"\n",
6234 ref->name, ref->lineno, str);
6235 ctxt->valid = 0;
6236 }
6237 if (save == 0)
6238 break;
6239 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00006240 while (IS_BLANK_CH(*cur)) cur++;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006241 }
6242 xmlFree(dup);
6243 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00006244 id = xmlGetID(ctxt->doc, name);
6245 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006246 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006247 "IDREF attribute %s references an unknown ID \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006248 attr->name, name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006249 ctxt->valid = 0;
6250 }
6251 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
6252 xmlChar *dup, *str = NULL, *cur, save;
6253
6254 dup = xmlStrdup(name);
6255 if (dup == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00006256 xmlVErrMemory(ctxt, "IDREFS split");
Owen Taylor3473f882001-02-23 17:55:21 +00006257 ctxt->valid = 0;
6258 return;
6259 }
6260 cur = dup;
6261 while (*cur != 0) {
6262 str = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00006263 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00006264 save = *cur;
6265 *cur = 0;
6266 id = xmlGetID(ctxt->doc, str);
6267 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006268 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006269 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006270 attr->name, str, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006271 ctxt->valid = 0;
6272 }
6273 if (save == 0)
6274 break;
6275 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00006276 while (IS_BLANK_CH(*cur)) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00006277 }
6278 xmlFree(dup);
6279 }
6280}
6281
6282/**
Daniel Veillard8730c562001-02-26 10:49:57 +00006283 * xmlWalkValidateList:
6284 * @data: Contents of current link
6285 * @user: Value supplied by the user
6286 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00006287 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00006288 */
6289static int
6290xmlWalkValidateList(const void *data, const void *user)
6291{
6292 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
6293 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
6294 return 1;
6295}
6296
6297/**
6298 * xmlValidateCheckRefCallback:
6299 * @ref_list: List of references
6300 * @ctxt: Validation context
6301 * @name: Name of ID we are searching for
6302 *
6303 */
6304static void
6305xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
6306 const xmlChar *name) {
6307 xmlValidateMemo memo;
6308
6309 if (ref_list == NULL)
6310 return;
6311 memo.ctxt = ctxt;
6312 memo.name = name;
6313
6314 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
6315
6316}
6317
6318/**
Owen Taylor3473f882001-02-23 17:55:21 +00006319 * xmlValidateDocumentFinal:
6320 * @ctxt: the validation context
6321 * @doc: a document instance
6322 *
6323 * Does the final step for the document validation once all the
6324 * incremental validation steps have been completed
6325 *
6326 * basically it does the following checks described by the XML Rec
6327 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006328 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00006329 *
6330 * returns 1 if valid or 0 otherwise
6331 */
6332
6333int
6334xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6335 xmlRefTablePtr table;
6336
Daniel Veillardc0be74b2004-11-03 19:16:55 +00006337 if (ctxt == NULL)
6338 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00006339 if (doc == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00006340 xmlErrValid(ctxt, XML_DTD_NO_DOC,
6341 "xmlValidateDocumentFinal: doc == NULL\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006342 return(0);
6343 }
6344
6345 /*
6346 * Check all the NOTATION/NOTATIONS attributes
6347 */
6348 /*
6349 * Check all the ENTITY/ENTITIES attributes definition for validity
6350 */
6351 /*
6352 * Check all the IDREF/IDREFS attributes definition for validity
6353 */
6354 table = (xmlRefTablePtr) doc->refs;
6355 ctxt->doc = doc;
6356 ctxt->valid = 1;
6357 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6358 return(ctxt->valid);
6359}
6360
6361/**
6362 * xmlValidateDtd:
6363 * @ctxt: the validation context
6364 * @doc: a document instance
6365 * @dtd: a dtd instance
6366 *
6367 * Try to validate the document against the dtd instance
6368 *
William M. Brack367df6e2004-10-23 18:14:36 +00006369 * Basically it does check all the definitions in the DtD.
6370 * Note the the internal subset (if present) is de-coupled
6371 * (i.e. not used), which could give problems if ID or IDREF
6372 * is present.
Owen Taylor3473f882001-02-23 17:55:21 +00006373 *
6374 * returns 1 if valid or 0 otherwise
6375 */
6376
6377int
6378xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6379 int ret;
William M. Brack367df6e2004-10-23 18:14:36 +00006380 xmlDtdPtr oldExt, oldInt;
Owen Taylor3473f882001-02-23 17:55:21 +00006381 xmlNodePtr root;
6382
6383 if (dtd == NULL) return(0);
6384 if (doc == NULL) return(0);
6385 oldExt = doc->extSubset;
William M. Brack367df6e2004-10-23 18:14:36 +00006386 oldInt = doc->intSubset;
Owen Taylor3473f882001-02-23 17:55:21 +00006387 doc->extSubset = dtd;
William M. Brack367df6e2004-10-23 18:14:36 +00006388 doc->intSubset = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00006389 ret = xmlValidateRoot(ctxt, doc);
6390 if (ret == 0) {
6391 doc->extSubset = oldExt;
William M. Brack367df6e2004-10-23 18:14:36 +00006392 doc->intSubset = oldInt;
Owen Taylor3473f882001-02-23 17:55:21 +00006393 return(ret);
6394 }
6395 if (doc->ids != NULL) {
6396 xmlFreeIDTable(doc->ids);
6397 doc->ids = NULL;
6398 }
6399 if (doc->refs != NULL) {
6400 xmlFreeRefTable(doc->refs);
6401 doc->refs = NULL;
6402 }
6403 root = xmlDocGetRootElement(doc);
6404 ret = xmlValidateElement(ctxt, doc, root);
6405 ret &= xmlValidateDocumentFinal(ctxt, doc);
6406 doc->extSubset = oldExt;
William M. Brack367df6e2004-10-23 18:14:36 +00006407 doc->intSubset = oldInt;
Owen Taylor3473f882001-02-23 17:55:21 +00006408 return(ret);
6409}
6410
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006411static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006412xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6413 const xmlChar *name ATTRIBUTE_UNUSED) {
6414 if (cur == NULL)
6415 return;
6416 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6417 xmlChar *notation = cur->content;
6418
Daniel Veillard878eab02002-02-19 13:46:09 +00006419 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006420 int ret;
6421
6422 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6423 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006424 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006425 }
6426 }
6427 }
6428}
6429
6430static void
Owen Taylor3473f882001-02-23 17:55:21 +00006431xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006432 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006433 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006434 xmlDocPtr doc;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006435 xmlElementPtr elem = NULL;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006436
Owen Taylor3473f882001-02-23 17:55:21 +00006437 if (cur == NULL)
6438 return;
6439 switch (cur->atype) {
6440 case XML_ATTRIBUTE_CDATA:
6441 case XML_ATTRIBUTE_ID:
6442 case XML_ATTRIBUTE_IDREF :
6443 case XML_ATTRIBUTE_IDREFS:
6444 case XML_ATTRIBUTE_NMTOKEN:
6445 case XML_ATTRIBUTE_NMTOKENS:
6446 case XML_ATTRIBUTE_ENUMERATION:
6447 break;
6448 case XML_ATTRIBUTE_ENTITY:
6449 case XML_ATTRIBUTE_ENTITIES:
6450 case XML_ATTRIBUTE_NOTATION:
6451 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006452
6453 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6454 cur->atype, cur->defaultValue);
6455 if ((ret == 0) && (ctxt->valid == 1))
6456 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006457 }
6458 if (cur->tree != NULL) {
6459 xmlEnumerationPtr tree = cur->tree;
6460 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006461 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006462 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006463 if ((ret == 0) && (ctxt->valid == 1))
6464 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006465 tree = tree->next;
6466 }
6467 }
6468 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006469 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6470 doc = cur->doc;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006471 if (cur->elem == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006472 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
Daniel Veillard878eab02002-02-19 13:46:09 +00006473 "xmlValidateAttributeCallback(%s): internal error\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006474 (const char *) cur->name);
Daniel Veillard878eab02002-02-19 13:46:09 +00006475 return;
6476 }
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006477
6478 if (doc != NULL)
6479 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6480 if ((elem == NULL) && (doc != NULL))
Daniel Veillard878eab02002-02-19 13:46:09 +00006481 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006482 if ((elem == NULL) && (cur->parent != NULL) &&
6483 (cur->parent->type == XML_DTD_NODE))
6484 elem = xmlGetDtdElementDesc((xmlDtdPtr) cur->parent, cur->elem);
Daniel Veillard878eab02002-02-19 13:46:09 +00006485 if (elem == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006486 xmlErrValidNode(ctxt, NULL, XML_DTD_UNKNOWN_ELEM,
Daniel Veillard878eab02002-02-19 13:46:09 +00006487 "attribute %s: could not find decl for element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006488 cur->name, cur->elem, NULL);
Daniel Veillard878eab02002-02-19 13:46:09 +00006489 return;
6490 }
6491 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006492 xmlErrValidNode(ctxt, NULL, XML_DTD_EMPTY_NOTATION,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006493 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006494 cur->name, cur->elem, NULL);
Daniel Veillard878eab02002-02-19 13:46:09 +00006495 ctxt->valid = 0;
6496 }
6497 }
Owen Taylor3473f882001-02-23 17:55:21 +00006498}
6499
6500/**
6501 * xmlValidateDtdFinal:
6502 * @ctxt: the validation context
6503 * @doc: a document instance
6504 *
6505 * Does the final step for the dtds validation once all the
6506 * subsets have been parsed
6507 *
6508 * basically it does the following checks described by the XML Rec
6509 * - check that ENTITY and ENTITIES type attributes default or
6510 * possible values matches one of the defined entities.
6511 * - check that NOTATION type attributes default or
6512 * possible values matches one of the defined notations.
6513 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006514 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006515 */
6516
6517int
6518xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006519 xmlDtdPtr dtd;
6520 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006521 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006522
6523 if (doc == NULL) return(0);
6524 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6525 return(0);
6526 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006527 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006528 dtd = doc->intSubset;
6529 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6530 table = (xmlAttributeTablePtr) dtd->attributes;
6531 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006532 }
6533 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006534 entities = (xmlEntitiesTablePtr) dtd->entities;
6535 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6536 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006537 }
6538 dtd = doc->extSubset;
6539 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6540 table = (xmlAttributeTablePtr) dtd->attributes;
6541 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006542 }
6543 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006544 entities = (xmlEntitiesTablePtr) dtd->entities;
6545 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6546 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006547 }
6548 return(ctxt->valid);
6549}
6550
6551/**
6552 * xmlValidateDocument:
6553 * @ctxt: the validation context
6554 * @doc: a document instance
6555 *
6556 * Try to validate the document instance
6557 *
6558 * basically it does the all the checks described by the XML Rec
6559 * i.e. validates the internal and external subset (if present)
6560 * and validate the document tree.
6561 *
6562 * returns 1 if valid or 0 otherwise
6563 */
6564
6565int
6566xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6567 int ret;
6568 xmlNodePtr root;
6569
Daniel Veillardc0be74b2004-11-03 19:16:55 +00006570 if (doc == NULL)
6571 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006572 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006573 xmlErrValid(ctxt, XML_DTD_NO_DTD,
6574 "no DTD found!\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006575 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006576 }
Owen Taylor3473f882001-02-23 17:55:21 +00006577 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6578 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
William M. Brack8c22f9f2004-08-06 16:23:27 +00006579 xmlChar *sysID;
6580 if (doc->intSubset->SystemID != NULL) {
William M. Brackbebe7302004-08-05 06:46:47 +00006581 sysID = xmlBuildURI(doc->intSubset->SystemID,
6582 doc->URL);
William M. Brack8c22f9f2004-08-06 16:23:27 +00006583 if (sysID == NULL) {
6584 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
6585 "Could not build URI for external subset \"%s\"\n",
6586 (const char *) doc->intSubset->SystemID);
6587 return 0;
6588 }
6589 } else
William M. Brackbebe7302004-08-05 06:46:47 +00006590 sysID = NULL;
William M. Brack8c22f9f2004-08-06 16:23:27 +00006591 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
William M. Brackbebe7302004-08-05 06:46:47 +00006592 (const xmlChar *)sysID);
William M. Brackbebe7302004-08-05 06:46:47 +00006593 if (sysID != NULL)
6594 xmlFree(sysID);
Owen Taylor3473f882001-02-23 17:55:21 +00006595 if (doc->extSubset == NULL) {
6596 if (doc->intSubset->SystemID != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006597 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00006598 "Could not load the external subset \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006599 (const char *) doc->intSubset->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00006600 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006601 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00006602 "Could not load the external subset \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006603 (const char *) doc->intSubset->ExternalID);
Owen Taylor3473f882001-02-23 17:55:21 +00006604 }
6605 return(0);
6606 }
6607 }
6608
6609 if (doc->ids != NULL) {
6610 xmlFreeIDTable(doc->ids);
6611 doc->ids = NULL;
6612 }
6613 if (doc->refs != NULL) {
6614 xmlFreeRefTable(doc->refs);
6615 doc->refs = NULL;
6616 }
6617 ret = xmlValidateDtdFinal(ctxt, doc);
6618 if (!xmlValidateRoot(ctxt, doc)) return(0);
6619
6620 root = xmlDocGetRootElement(doc);
6621 ret &= xmlValidateElement(ctxt, doc, root);
6622 ret &= xmlValidateDocumentFinal(ctxt, doc);
6623 return(ret);
6624}
6625
Owen Taylor3473f882001-02-23 17:55:21 +00006626/************************************************************************
6627 * *
6628 * Routines for dynamic validation editing *
6629 * *
6630 ************************************************************************/
6631
6632/**
6633 * xmlValidGetPotentialChildren:
6634 * @ctree: an element content tree
6635 * @list: an array to store the list of child names
6636 * @len: a pointer to the number of element in the list
6637 * @max: the size of the array
6638 *
6639 * Build/extend a list of potential children allowed by the content tree
6640 *
6641 * returns the number of element in the list, or -1 in case of error.
6642 */
6643
6644int
6645xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6646 int *len, int max) {
6647 int i;
6648
6649 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6650 return(-1);
6651 if (*len >= max) return(*len);
6652
6653 switch (ctree->type) {
6654 case XML_ELEMENT_CONTENT_PCDATA:
6655 for (i = 0; i < *len;i++)
6656 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6657 list[(*len)++] = BAD_CAST "#PCDATA";
6658 break;
6659 case XML_ELEMENT_CONTENT_ELEMENT:
6660 for (i = 0; i < *len;i++)
6661 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6662 list[(*len)++] = ctree->name;
6663 break;
6664 case XML_ELEMENT_CONTENT_SEQ:
6665 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6666 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6667 break;
6668 case XML_ELEMENT_CONTENT_OR:
6669 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6670 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6671 break;
6672 }
6673
6674 return(*len);
6675}
6676
William M. Brack9333cc22004-06-24 08:33:40 +00006677/*
6678 * Dummy function to suppress messages while we try out valid elements
6679 */
6680static void xmlNoValidityErr(void *ctx ATTRIBUTE_UNUSED,
6681 const char *msg ATTRIBUTE_UNUSED, ...) {
6682 return;
6683}
6684
Owen Taylor3473f882001-02-23 17:55:21 +00006685/**
6686 * xmlValidGetValidElements:
6687 * @prev: an element to insert after
6688 * @next: an element to insert next
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006689 * @names: an array to store the list of child names
Owen Taylor3473f882001-02-23 17:55:21 +00006690 * @max: the size of the array
6691 *
6692 * This function returns the list of authorized children to insert
6693 * within an existing tree while respecting the validity constraints
6694 * forced by the Dtd. The insertion point is defined using @prev and
6695 * @next in the following ways:
6696 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6697 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6698 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6699 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6700 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6701 *
6702 * pointers to the element names are inserted at the beginning of the array
6703 * and do not need to be freed.
6704 *
6705 * returns the number of element in the list, or -1 in case of error. If
6706 * the function returns the value @max the caller is invited to grow the
6707 * receiving array and retry.
6708 */
6709
6710int
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006711xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **names,
Owen Taylor3473f882001-02-23 17:55:21 +00006712 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006713 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006714 int nb_valid_elements = 0;
6715 const xmlChar *elements[256];
6716 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006717 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006718
6719 xmlNode *ref_node;
6720 xmlNode *parent;
6721 xmlNode *test_node;
6722
6723 xmlNode *prev_next;
6724 xmlNode *next_prev;
6725 xmlNode *parent_childs;
6726 xmlNode *parent_last;
6727
6728 xmlElement *element_desc;
6729
6730 if (prev == NULL && next == NULL)
6731 return(-1);
6732
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006733 if (names == NULL) return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00006734 if (max <= 0) return(-1);
6735
William M. Brack9333cc22004-06-24 08:33:40 +00006736 memset(&vctxt, 0, sizeof (xmlValidCtxt));
6737 vctxt.error = xmlNoValidityErr; /* this suppresses err/warn output */
6738
Owen Taylor3473f882001-02-23 17:55:21 +00006739 nb_valid_elements = 0;
6740 ref_node = prev ? prev : next;
6741 parent = ref_node->parent;
6742
6743 /*
6744 * Retrieves the parent element declaration
6745 */
6746 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6747 parent->name);
6748 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6749 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6750 parent->name);
6751 if (element_desc == NULL) return(-1);
6752
6753 /*
6754 * Do a backup of the current tree structure
6755 */
6756 prev_next = prev ? prev->next : NULL;
6757 next_prev = next ? next->prev : NULL;
6758 parent_childs = parent->children;
6759 parent_last = parent->last;
6760
6761 /*
6762 * Creates a dummy node and insert it into the tree
6763 */
Daniel Veillard95ddcd32004-10-26 21:53:55 +00006764 test_node = xmlNewDocNode (ref_node->doc, NULL, BAD_CAST "<!dummy?>", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006765 test_node->parent = parent;
6766 test_node->prev = prev;
6767 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006768 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006769
6770 if (prev) prev->next = test_node;
6771 else parent->children = test_node;
6772
6773 if (next) next->prev = test_node;
6774 else parent->last = test_node;
6775
6776 /*
6777 * Insert each potential child node and check if the parent is
6778 * still valid
6779 */
6780 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6781 elements, &nb_elements, 256);
6782
6783 for (i = 0;i < nb_elements;i++) {
6784 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006785 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006786 int j;
6787
6788 for (j = 0; j < nb_valid_elements;j++)
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006789 if (xmlStrEqual(elements[i], names[j])) break;
6790 names[nb_valid_elements++] = elements[i];
Owen Taylor3473f882001-02-23 17:55:21 +00006791 if (nb_valid_elements >= max) break;
6792 }
6793 }
6794
6795 /*
6796 * Restore the tree structure
6797 */
6798 if (prev) prev->next = prev_next;
6799 if (next) next->prev = next_prev;
6800 parent->children = parent_childs;
6801 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006802
6803 /*
6804 * Free up the dummy node
6805 */
6806 test_node->name = name;
6807 xmlFreeNode(test_node);
6808
Owen Taylor3473f882001-02-23 17:55:21 +00006809 return(nb_valid_elements);
6810}
Daniel Veillard4432df22003-09-28 18:58:27 +00006811#endif /* LIBXML_VALID_ENABLED */
6812