blob: bf653031a2123042e692e0f7d41843be5ae0fe8e [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) {
223 channel = ctxt->error;
224 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) {
1520 switch (elem->etype) {
1521 case XML_ELEMENT_TYPE_EMPTY:
1522 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001523 if (elem->prefix != NULL) {
1524 xmlBufferWriteCHAR(buf, elem->prefix);
1525 xmlBufferWriteChar(buf, ":");
1526 }
Owen Taylor3473f882001-02-23 17:55:21 +00001527 xmlBufferWriteCHAR(buf, elem->name);
1528 xmlBufferWriteChar(buf, " EMPTY>\n");
1529 break;
1530 case XML_ELEMENT_TYPE_ANY:
1531 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001532 if (elem->prefix != NULL) {
1533 xmlBufferWriteCHAR(buf, elem->prefix);
1534 xmlBufferWriteChar(buf, ":");
1535 }
Owen Taylor3473f882001-02-23 17:55:21 +00001536 xmlBufferWriteCHAR(buf, elem->name);
1537 xmlBufferWriteChar(buf, " ANY>\n");
1538 break;
1539 case XML_ELEMENT_TYPE_MIXED:
1540 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001541 if (elem->prefix != NULL) {
1542 xmlBufferWriteCHAR(buf, elem->prefix);
1543 xmlBufferWriteChar(buf, ":");
1544 }
Owen Taylor3473f882001-02-23 17:55:21 +00001545 xmlBufferWriteCHAR(buf, elem->name);
1546 xmlBufferWriteChar(buf, " ");
1547 xmlDumpElementContent(buf, elem->content, 1);
1548 xmlBufferWriteChar(buf, ">\n");
1549 break;
1550 case XML_ELEMENT_TYPE_ELEMENT:
1551 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001552 if (elem->prefix != NULL) {
1553 xmlBufferWriteCHAR(buf, elem->prefix);
1554 xmlBufferWriteChar(buf, ":");
1555 }
Owen Taylor3473f882001-02-23 17:55:21 +00001556 xmlBufferWriteCHAR(buf, elem->name);
1557 xmlBufferWriteChar(buf, " ");
1558 xmlDumpElementContent(buf, elem->content, 1);
1559 xmlBufferWriteChar(buf, ">\n");
1560 break;
1561 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001562 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
1563 "Internal: ELEMENT struct corrupted invalid type\n",
1564 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001565 }
1566}
1567
1568/**
William M. Brack9e660592003-10-20 14:56:06 +00001569 * xmlDumpElementDeclScan:
1570 * @elem: An element table
1571 * @buf: the XML buffer output
1572 *
1573 * This routine is used by the hash scan function. It just reverses
1574 * the arguments.
1575 */
1576static void
1577xmlDumpElementDeclScan(xmlElementPtr elem, xmlBufferPtr buf) {
1578 xmlDumpElementDecl(buf, elem);
1579}
1580
1581/**
Owen Taylor3473f882001-02-23 17:55:21 +00001582 * xmlDumpElementTable:
1583 * @buf: the XML buffer output
1584 * @table: An element table
1585 *
1586 * This will dump the content of the element table as an XML DTD definition
1587 */
1588void
1589xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
William M. Brack9e660592003-10-20 14:56:06 +00001590 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDeclScan, buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001591}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001592#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001593
1594/**
1595 * xmlCreateEnumeration:
1596 * @name: the enumeration name or NULL
1597 *
1598 * create and initialize an enumeration attribute node.
1599 *
1600 * Returns the xmlEnumerationPtr just created or NULL in case
1601 * of error.
1602 */
1603xmlEnumerationPtr
Daniel Veillard2fdbd322003-08-18 12:15:38 +00001604xmlCreateEnumeration(const xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00001605 xmlEnumerationPtr ret;
1606
1607 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1608 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001609 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001610 return(NULL);
1611 }
1612 memset(ret, 0, sizeof(xmlEnumeration));
1613
1614 if (name != NULL)
1615 ret->name = xmlStrdup(name);
1616 return(ret);
1617}
1618
1619/**
1620 * xmlFreeEnumeration:
1621 * @cur: the tree to free.
1622 *
1623 * free an enumeration attribute node (recursive).
1624 */
1625void
1626xmlFreeEnumeration(xmlEnumerationPtr cur) {
1627 if (cur == NULL) return;
1628
1629 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1630
1631 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001632 xmlFree(cur);
1633}
1634
Daniel Veillard652327a2003-09-29 18:02:38 +00001635#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001636/**
1637 * xmlCopyEnumeration:
1638 * @cur: the tree to copy.
1639 *
1640 * Copy an enumeration attribute node (recursive).
1641 *
1642 * Returns the xmlEnumerationPtr just created or NULL in case
1643 * of error.
1644 */
1645xmlEnumerationPtr
1646xmlCopyEnumeration(xmlEnumerationPtr cur) {
1647 xmlEnumerationPtr ret;
1648
1649 if (cur == NULL) return(NULL);
1650 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1651
1652 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1653 else ret->next = NULL;
1654
1655 return(ret);
1656}
Daniel Veillard652327a2003-09-29 18:02:38 +00001657#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001658
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001659#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001660/**
1661 * xmlDumpEnumeration:
1662 * @buf: the XML buffer output
1663 * @enum: An enumeration
1664 *
1665 * This will dump the content of the enumeration
1666 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001667static void
Owen Taylor3473f882001-02-23 17:55:21 +00001668xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1669 if (cur == NULL) return;
1670
1671 xmlBufferWriteCHAR(buf, cur->name);
1672 if (cur->next == NULL)
1673 xmlBufferWriteChar(buf, ")");
1674 else {
1675 xmlBufferWriteChar(buf, " | ");
1676 xmlDumpEnumeration(buf, cur->next);
1677 }
1678}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001679#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001680
1681/**
1682 * xmlCreateAttributeTable:
1683 *
1684 * create and initialize an empty attribute hash table.
1685 *
1686 * Returns the xmlAttributeTablePtr just created or NULL in case
1687 * of error.
1688 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001689static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001690xmlCreateAttributeTable(void) {
1691 return(xmlHashCreate(0));
1692}
1693
Daniel Veillard4432df22003-09-28 18:58:27 +00001694#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001695/**
1696 * xmlScanAttributeDeclCallback:
1697 * @attr: the attribute decl
1698 * @list: the list to update
1699 *
1700 * Callback called by xmlScanAttributeDecl when a new attribute
1701 * has to be entered in the list.
1702 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001703static void
Owen Taylor3473f882001-02-23 17:55:21 +00001704xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001705 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001706 attr->nexth = *list;
1707 *list = attr;
1708}
1709
1710/**
1711 * xmlScanAttributeDecl:
1712 * @dtd: pointer to the DTD
1713 * @elem: the element name
1714 *
1715 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001716 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001717 *
1718 * Returns the pointer to the first attribute decl in the chain,
1719 * possibly NULL.
1720 */
1721xmlAttributePtr
1722xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1723 xmlAttributePtr ret = NULL;
1724 xmlAttributeTablePtr table;
1725
1726 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001727 return(NULL);
1728 }
1729 if (elem == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001730 return(NULL);
1731 }
1732 table = (xmlAttributeTablePtr) dtd->attributes;
1733 if (table == NULL)
1734 return(NULL);
1735
1736 /* WRONG !!! */
1737 xmlHashScan3(table, NULL, NULL, elem,
1738 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1739 return(ret);
1740}
1741
1742/**
1743 * xmlScanIDAttributeDecl:
1744 * @ctxt: the validation context
1745 * @elem: the element name
1746 *
1747 * Verify that the element don't have too many ID attributes
1748 * declared.
1749 *
1750 * Returns the number of ID attributes found.
1751 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001752static int
Owen Taylor3473f882001-02-23 17:55:21 +00001753xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1754 xmlAttributePtr cur;
1755 int ret = 0;
1756
1757 if (elem == NULL) return(0);
1758 cur = elem->attributes;
1759 while (cur != NULL) {
1760 if (cur->atype == XML_ATTRIBUTE_ID) {
1761 ret ++;
1762 if (ret > 1)
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001763 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_MULTIPLE_ID,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001764 "Element %s has too many ID attributes defined : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001765 elem->name, cur->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001766 }
1767 cur = cur->nexth;
1768 }
1769 return(ret);
1770}
Daniel Veillard4432df22003-09-28 18:58:27 +00001771#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001772
1773/**
1774 * xmlFreeAttribute:
1775 * @elem: An attribute
1776 *
1777 * Deallocate the memory used by an attribute definition
1778 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001779static void
Owen Taylor3473f882001-02-23 17:55:21 +00001780xmlFreeAttribute(xmlAttributePtr attr) {
1781 if (attr == NULL) return;
1782 xmlUnlinkNode((xmlNodePtr) attr);
1783 if (attr->tree != NULL)
1784 xmlFreeEnumeration(attr->tree);
1785 if (attr->elem != NULL)
1786 xmlFree((xmlChar *) attr->elem);
1787 if (attr->name != NULL)
1788 xmlFree((xmlChar *) attr->name);
1789 if (attr->defaultValue != NULL)
1790 xmlFree((xmlChar *) attr->defaultValue);
1791 if (attr->prefix != NULL)
1792 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001793 xmlFree(attr);
1794}
1795
1796
1797/**
1798 * xmlAddAttributeDecl:
1799 * @ctxt: the validation context
1800 * @dtd: pointer to the DTD
1801 * @elem: the element name
1802 * @name: the attribute name
1803 * @ns: the attribute namespace prefix
1804 * @type: the attribute type
1805 * @def: the attribute default type
1806 * @defaultValue: the attribute default value
1807 * @tree: if it's an enumeration, the associated list
1808 *
1809 * Register a new attribute declaration
1810 * Note that @tree becomes the ownership of the DTD
1811 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001812 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001813 */
1814xmlAttributePtr
William M. Brackedb65a72004-02-06 07:36:04 +00001815xmlAddAttributeDecl(xmlValidCtxtPtr ctxt,
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001816 xmlDtdPtr dtd, const xmlChar *elem,
Owen Taylor3473f882001-02-23 17:55:21 +00001817 const xmlChar *name, const xmlChar *ns,
1818 xmlAttributeType type, xmlAttributeDefault def,
1819 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1820 xmlAttributePtr ret;
1821 xmlAttributeTablePtr table;
1822 xmlElementPtr elemDef;
1823
1824 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001825 xmlFreeEnumeration(tree);
1826 return(NULL);
1827 }
1828 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001829 xmlFreeEnumeration(tree);
1830 return(NULL);
1831 }
1832 if (elem == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001833 xmlFreeEnumeration(tree);
1834 return(NULL);
1835 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001836
Daniel Veillard4432df22003-09-28 18:58:27 +00001837#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001838 /*
1839 * Check the type and possibly the default value.
1840 */
1841 switch (type) {
1842 case XML_ATTRIBUTE_CDATA:
1843 break;
1844 case XML_ATTRIBUTE_ID:
1845 break;
1846 case XML_ATTRIBUTE_IDREF:
1847 break;
1848 case XML_ATTRIBUTE_IDREFS:
1849 break;
1850 case XML_ATTRIBUTE_ENTITY:
1851 break;
1852 case XML_ATTRIBUTE_ENTITIES:
1853 break;
1854 case XML_ATTRIBUTE_NMTOKEN:
1855 break;
1856 case XML_ATTRIBUTE_NMTOKENS:
1857 break;
1858 case XML_ATTRIBUTE_ENUMERATION:
1859 break;
1860 case XML_ATTRIBUTE_NOTATION:
1861 break;
1862 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001863 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1864 "Internal: ATTRIBUTE struct corrupted invalid type\n",
1865 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001866 xmlFreeEnumeration(tree);
1867 return(NULL);
1868 }
1869 if ((defaultValue != NULL) &&
1870 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001871 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_DEFAULT,
1872 "Attribute %s of %s: invalid default value\n",
1873 elem, name, defaultValue);
Owen Taylor3473f882001-02-23 17:55:21 +00001874 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001875 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001876 }
Daniel Veillard4432df22003-09-28 18:58:27 +00001877#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001878
1879 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001880 * Check first that an attribute defined in the external subset wasn't
1881 * already defined in the internal subset
1882 */
1883 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1884 (dtd->doc->intSubset != NULL) &&
1885 (dtd->doc->intSubset->attributes != NULL)) {
1886 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1887 if (ret != NULL)
1888 return(NULL);
1889 }
1890
1891 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001892 * Create the Attribute table if needed.
1893 */
1894 table = (xmlAttributeTablePtr) dtd->attributes;
1895 if (table == NULL) {
1896 table = xmlCreateAttributeTable();
1897 dtd->attributes = (void *) table;
1898 }
1899 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001900 xmlVErrMemory(ctxt,
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001901 "xmlAddAttributeDecl: Table creation failed!\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001902 return(NULL);
1903 }
1904
1905
1906 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1907 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001908 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001909 return(NULL);
1910 }
1911 memset(ret, 0, sizeof(xmlAttribute));
1912 ret->type = XML_ATTRIBUTE_DECL;
1913
1914 /*
1915 * fill the structure.
1916 */
1917 ret->atype = type;
1918 ret->name = xmlStrdup(name);
1919 ret->prefix = xmlStrdup(ns);
1920 ret->elem = xmlStrdup(elem);
1921 ret->def = def;
1922 ret->tree = tree;
1923 if (defaultValue != NULL)
1924 ret->defaultValue = xmlStrdup(defaultValue);
1925
1926 /*
1927 * Validity Check:
1928 * Search the DTD for previous declarations of the ATTLIST
1929 */
1930 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001931#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001932 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001933 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001934 */
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001935 xmlErrValidWarning(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_REDEFINED,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001936 "Attribute %s of element %s: already defined\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001937 name, elem, NULL);
Daniel Veillard4432df22003-09-28 18:58:27 +00001938#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001939 xmlFreeAttribute(ret);
1940 return(NULL);
1941 }
1942
1943 /*
1944 * Validity Check:
1945 * Multiple ID per element
1946 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001947 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001948 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001949
Daniel Veillard4432df22003-09-28 18:58:27 +00001950#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001951 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001952 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001953 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID,
Owen Taylor3473f882001-02-23 17:55:21 +00001954 "Element %s has too may ID attributes defined : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001955 elem, name, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00001956 ctxt->valid = 0;
1957 }
Daniel Veillard4432df22003-09-28 18:58:27 +00001958#endif /* LIBXML_VALID_ENABLED */
Daniel Veillardc7612992002-02-17 22:47:37 +00001959
Daniel Veillard48da9102001-08-07 01:10:10 +00001960 /*
1961 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001962 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001963 */
1964 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1965 ((ret->prefix != NULL &&
1966 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1967 ret->nexth = elemDef->attributes;
1968 elemDef->attributes = ret;
1969 } else {
1970 xmlAttributePtr tmp = elemDef->attributes;
1971
1972 while ((tmp != NULL) &&
1973 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1974 ((ret->prefix != NULL &&
1975 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1976 if (tmp->nexth == NULL)
1977 break;
1978 tmp = tmp->nexth;
1979 }
1980 if (tmp != NULL) {
1981 ret->nexth = tmp->nexth;
1982 tmp->nexth = ret;
1983 } else {
1984 ret->nexth = elemDef->attributes;
1985 elemDef->attributes = ret;
1986 }
1987 }
Owen Taylor3473f882001-02-23 17:55:21 +00001988 }
1989
1990 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001991 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001992 */
1993 ret->parent = dtd;
1994 ret->doc = dtd->doc;
1995 if (dtd->last == NULL) {
1996 dtd->children = dtd->last = (xmlNodePtr) ret;
1997 } else {
1998 dtd->last->next = (xmlNodePtr) ret;
1999 ret->prev = dtd->last;
2000 dtd->last = (xmlNodePtr) ret;
2001 }
2002 return(ret);
2003}
2004
2005/**
2006 * xmlFreeAttributeTable:
2007 * @table: An attribute table
2008 *
2009 * Deallocate the memory used by an entities hash table.
2010 */
2011void
2012xmlFreeAttributeTable(xmlAttributeTablePtr table) {
2013 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
2014}
2015
Daniel Veillard652327a2003-09-29 18:02:38 +00002016#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002017/**
2018 * xmlCopyAttribute:
2019 * @attr: An attribute
2020 *
2021 * Build a copy of an attribute.
2022 *
2023 * Returns the new xmlAttributePtr or NULL in case of error.
2024 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002025static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002026xmlCopyAttribute(xmlAttributePtr attr) {
2027 xmlAttributePtr cur;
2028
2029 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
2030 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002031 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002032 return(NULL);
2033 }
2034 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00002035 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00002036 cur->atype = attr->atype;
2037 cur->def = attr->def;
2038 cur->tree = xmlCopyEnumeration(attr->tree);
2039 if (attr->elem != NULL)
2040 cur->elem = xmlStrdup(attr->elem);
2041 if (attr->name != NULL)
2042 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00002043 if (attr->prefix != NULL)
2044 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00002045 if (attr->defaultValue != NULL)
2046 cur->defaultValue = xmlStrdup(attr->defaultValue);
2047 return(cur);
2048}
2049
2050/**
2051 * xmlCopyAttributeTable:
2052 * @table: An attribute table
2053 *
2054 * Build a copy of an attribute table.
2055 *
2056 * Returns the new xmlAttributeTablePtr or NULL in case of error.
2057 */
2058xmlAttributeTablePtr
2059xmlCopyAttributeTable(xmlAttributeTablePtr table) {
2060 return((xmlAttributeTablePtr) xmlHashCopy(table,
2061 (xmlHashCopier) xmlCopyAttribute));
2062}
Daniel Veillard652327a2003-09-29 18:02:38 +00002063#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002064
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002065#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002066/**
2067 * xmlDumpAttributeDecl:
2068 * @buf: the XML buffer output
2069 * @attr: An attribute declaration
2070 *
2071 * This will dump the content of the attribute declaration as an XML
2072 * DTD definition
2073 */
2074void
2075xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
2076 xmlBufferWriteChar(buf, "<!ATTLIST ");
2077 xmlBufferWriteCHAR(buf, attr->elem);
2078 xmlBufferWriteChar(buf, " ");
2079 if (attr->prefix != NULL) {
2080 xmlBufferWriteCHAR(buf, attr->prefix);
2081 xmlBufferWriteChar(buf, ":");
2082 }
2083 xmlBufferWriteCHAR(buf, attr->name);
2084 switch (attr->atype) {
2085 case XML_ATTRIBUTE_CDATA:
2086 xmlBufferWriteChar(buf, " CDATA");
2087 break;
2088 case XML_ATTRIBUTE_ID:
2089 xmlBufferWriteChar(buf, " ID");
2090 break;
2091 case XML_ATTRIBUTE_IDREF:
2092 xmlBufferWriteChar(buf, " IDREF");
2093 break;
2094 case XML_ATTRIBUTE_IDREFS:
2095 xmlBufferWriteChar(buf, " IDREFS");
2096 break;
2097 case XML_ATTRIBUTE_ENTITY:
2098 xmlBufferWriteChar(buf, " ENTITY");
2099 break;
2100 case XML_ATTRIBUTE_ENTITIES:
2101 xmlBufferWriteChar(buf, " ENTITIES");
2102 break;
2103 case XML_ATTRIBUTE_NMTOKEN:
2104 xmlBufferWriteChar(buf, " NMTOKEN");
2105 break;
2106 case XML_ATTRIBUTE_NMTOKENS:
2107 xmlBufferWriteChar(buf, " NMTOKENS");
2108 break;
2109 case XML_ATTRIBUTE_ENUMERATION:
2110 xmlBufferWriteChar(buf, " (");
2111 xmlDumpEnumeration(buf, attr->tree);
2112 break;
2113 case XML_ATTRIBUTE_NOTATION:
2114 xmlBufferWriteChar(buf, " NOTATION (");
2115 xmlDumpEnumeration(buf, attr->tree);
2116 break;
2117 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002118 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2119 "Internal: ATTRIBUTE struct corrupted invalid type\n",
2120 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002121 }
2122 switch (attr->def) {
2123 case XML_ATTRIBUTE_NONE:
2124 break;
2125 case XML_ATTRIBUTE_REQUIRED:
2126 xmlBufferWriteChar(buf, " #REQUIRED");
2127 break;
2128 case XML_ATTRIBUTE_IMPLIED:
2129 xmlBufferWriteChar(buf, " #IMPLIED");
2130 break;
2131 case XML_ATTRIBUTE_FIXED:
2132 xmlBufferWriteChar(buf, " #FIXED");
2133 break;
2134 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002135 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2136 "Internal: ATTRIBUTE struct corrupted invalid def\n",
2137 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002138 }
2139 if (attr->defaultValue != NULL) {
2140 xmlBufferWriteChar(buf, " ");
2141 xmlBufferWriteQuotedString(buf, attr->defaultValue);
2142 }
2143 xmlBufferWriteChar(buf, ">\n");
2144}
2145
2146/**
William M. Brack9e660592003-10-20 14:56:06 +00002147 * xmlDumpAttributeDeclScan:
2148 * @attr: An attribute declaration
2149 * @buf: the XML buffer output
2150 *
2151 * This is used with the hash scan function - just reverses arguments
2152 */
2153static void
2154xmlDumpAttributeDeclScan(xmlAttributePtr attr, xmlBufferPtr buf) {
2155 xmlDumpAttributeDecl(buf, attr);
2156}
2157
2158/**
Owen Taylor3473f882001-02-23 17:55:21 +00002159 * xmlDumpAttributeTable:
2160 * @buf: the XML buffer output
2161 * @table: An attribute table
2162 *
2163 * This will dump the content of the attribute table as an XML DTD definition
2164 */
2165void
2166xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
William M. Brack9e660592003-10-20 14:56:06 +00002167 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDeclScan, buf);
Owen Taylor3473f882001-02-23 17:55:21 +00002168}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002169#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002170
2171/************************************************************************
2172 * *
2173 * NOTATIONs *
2174 * *
2175 ************************************************************************/
2176/**
2177 * xmlCreateNotationTable:
2178 *
2179 * create and initialize an empty notation hash table.
2180 *
2181 * Returns the xmlNotationTablePtr just created or NULL in case
2182 * of error.
2183 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002184static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002185xmlCreateNotationTable(void) {
2186 return(xmlHashCreate(0));
2187}
2188
2189/**
2190 * xmlFreeNotation:
2191 * @not: A notation
2192 *
2193 * Deallocate the memory used by an notation definition
2194 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002195static void
Owen Taylor3473f882001-02-23 17:55:21 +00002196xmlFreeNotation(xmlNotationPtr nota) {
2197 if (nota == NULL) return;
2198 if (nota->name != NULL)
2199 xmlFree((xmlChar *) nota->name);
2200 if (nota->PublicID != NULL)
2201 xmlFree((xmlChar *) nota->PublicID);
2202 if (nota->SystemID != NULL)
2203 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002204 xmlFree(nota);
2205}
2206
2207
2208/**
2209 * xmlAddNotationDecl:
2210 * @dtd: pointer to the DTD
2211 * @ctxt: the validation context
2212 * @name: the entity name
2213 * @PublicID: the public identifier or NULL
2214 * @SystemID: the system identifier or NULL
2215 *
2216 * Register a new notation declaration
2217 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002218 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00002219 */
2220xmlNotationPtr
William M. Brackedb65a72004-02-06 07:36:04 +00002221xmlAddNotationDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002222 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00002223 const xmlChar *PublicID, const xmlChar *SystemID) {
2224 xmlNotationPtr ret;
2225 xmlNotationTablePtr table;
2226
2227 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002228 return(NULL);
2229 }
2230 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002231 return(NULL);
2232 }
2233 if ((PublicID == NULL) && (SystemID == NULL)) {
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002234 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002235 }
2236
2237 /*
2238 * Create the Notation table if needed.
2239 */
2240 table = (xmlNotationTablePtr) dtd->notations;
2241 if (table == NULL)
2242 dtd->notations = table = xmlCreateNotationTable();
2243 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002244 xmlVErrMemory(ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00002245 "xmlAddNotationDecl: Table creation failed!\n");
2246 return(NULL);
2247 }
2248
2249 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2250 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002251 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002252 return(NULL);
2253 }
2254 memset(ret, 0, sizeof(xmlNotation));
2255
2256 /*
2257 * fill the structure.
2258 */
2259 ret->name = xmlStrdup(name);
2260 if (SystemID != NULL)
2261 ret->SystemID = xmlStrdup(SystemID);
2262 if (PublicID != NULL)
2263 ret->PublicID = xmlStrdup(PublicID);
2264
2265 /*
2266 * Validity Check:
2267 * Check the DTD for previous declarations of the ATTLIST
2268 */
2269 if (xmlHashAddEntry(table, name, ret)) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002270#ifdef LIBXML_VALID_ENABLED
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002271 xmlErrValid(NULL, XML_DTD_NOTATION_REDEFINED,
2272 "xmlAddNotationDecl: %s already defined\n",
2273 (const char *) name);
Daniel Veillard4432df22003-09-28 18:58:27 +00002274#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002275 xmlFreeNotation(ret);
2276 return(NULL);
2277 }
2278 return(ret);
2279}
2280
2281/**
2282 * xmlFreeNotationTable:
2283 * @table: An notation table
2284 *
2285 * Deallocate the memory used by an entities hash table.
2286 */
2287void
2288xmlFreeNotationTable(xmlNotationTablePtr table) {
2289 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2290}
2291
Daniel Veillard652327a2003-09-29 18:02:38 +00002292#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002293/**
2294 * xmlCopyNotation:
2295 * @nota: A notation
2296 *
2297 * Build a copy of a notation.
2298 *
2299 * Returns the new xmlNotationPtr or NULL in case of error.
2300 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002301static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002302xmlCopyNotation(xmlNotationPtr nota) {
2303 xmlNotationPtr cur;
2304
2305 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2306 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002307 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002308 return(NULL);
2309 }
2310 if (nota->name != NULL)
2311 cur->name = xmlStrdup(nota->name);
2312 else
2313 cur->name = NULL;
2314 if (nota->PublicID != NULL)
2315 cur->PublicID = xmlStrdup(nota->PublicID);
2316 else
2317 cur->PublicID = NULL;
2318 if (nota->SystemID != NULL)
2319 cur->SystemID = xmlStrdup(nota->SystemID);
2320 else
2321 cur->SystemID = NULL;
2322 return(cur);
2323}
2324
2325/**
2326 * xmlCopyNotationTable:
2327 * @table: A notation table
2328 *
2329 * Build a copy of a notation table.
2330 *
2331 * Returns the new xmlNotationTablePtr or NULL in case of error.
2332 */
2333xmlNotationTablePtr
2334xmlCopyNotationTable(xmlNotationTablePtr table) {
2335 return((xmlNotationTablePtr) xmlHashCopy(table,
2336 (xmlHashCopier) xmlCopyNotation));
2337}
Daniel Veillard652327a2003-09-29 18:02:38 +00002338#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002339
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002340#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002341/**
2342 * xmlDumpNotationDecl:
2343 * @buf: the XML buffer output
2344 * @nota: A notation declaration
2345 *
2346 * This will dump the content the notation declaration as an XML DTD definition
2347 */
2348void
2349xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2350 xmlBufferWriteChar(buf, "<!NOTATION ");
2351 xmlBufferWriteCHAR(buf, nota->name);
2352 if (nota->PublicID != NULL) {
2353 xmlBufferWriteChar(buf, " PUBLIC ");
2354 xmlBufferWriteQuotedString(buf, nota->PublicID);
2355 if (nota->SystemID != NULL) {
2356 xmlBufferWriteChar(buf, " ");
Daniel Veillard41c4a752004-09-08 20:55:38 +00002357 xmlBufferWriteQuotedString(buf, nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002358 }
2359 } else {
2360 xmlBufferWriteChar(buf, " SYSTEM ");
Daniel Veillard41c4a752004-09-08 20:55:38 +00002361 xmlBufferWriteQuotedString(buf, nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002362 }
2363 xmlBufferWriteChar(buf, " >\n");
2364}
2365
2366/**
William M. Brack9e660592003-10-20 14:56:06 +00002367 * xmlDumpNotationDeclScan:
2368 * @nota: A notation declaration
2369 * @buf: the XML buffer output
2370 *
2371 * This is called with the hash scan function, and just reverses args
2372 */
2373static void
2374xmlDumpNotationDeclScan(xmlNotationPtr nota, xmlBufferPtr buf) {
2375 xmlDumpNotationDecl(buf, nota);
2376}
2377
2378/**
Owen Taylor3473f882001-02-23 17:55:21 +00002379 * xmlDumpNotationTable:
2380 * @buf: the XML buffer output
2381 * @table: A notation table
2382 *
2383 * This will dump the content of the notation table as an XML DTD definition
2384 */
2385void
2386xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
William M. Brack9e660592003-10-20 14:56:06 +00002387 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDeclScan, buf);
Owen Taylor3473f882001-02-23 17:55:21 +00002388}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002389#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002390
2391/************************************************************************
2392 * *
2393 * IDs *
2394 * *
2395 ************************************************************************/
2396/**
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002397 * DICT_FREE:
2398 * @str: a string
2399 *
2400 * Free a string if it is not owned by the "dict" dictionnary in the
2401 * current scope
2402 */
2403#define DICT_FREE(str) \
2404 if ((str) && ((!dict) || \
2405 (xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \
2406 xmlFree((char *)(str));
2407
2408/**
Owen Taylor3473f882001-02-23 17:55:21 +00002409 * xmlCreateIDTable:
2410 *
2411 * create and initialize an empty id hash table.
2412 *
2413 * Returns the xmlIDTablePtr just created or NULL in case
2414 * of error.
2415 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002416static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002417xmlCreateIDTable(void) {
2418 return(xmlHashCreate(0));
2419}
2420
2421/**
2422 * xmlFreeID:
2423 * @not: A id
2424 *
2425 * Deallocate the memory used by an id definition
2426 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002427static void
Owen Taylor3473f882001-02-23 17:55:21 +00002428xmlFreeID(xmlIDPtr id) {
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002429 xmlDictPtr dict = NULL;
2430
Owen Taylor3473f882001-02-23 17:55:21 +00002431 if (id == NULL) return;
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002432
2433 if (id->doc != NULL)
2434 dict = id->doc->dict;
2435
Owen Taylor3473f882001-02-23 17:55:21 +00002436 if (id->value != NULL)
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002437 DICT_FREE(id->value)
Daniel Veillardea7751d2002-12-20 00:16:24 +00002438 if (id->name != NULL)
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002439 DICT_FREE(id->name)
Owen Taylor3473f882001-02-23 17:55:21 +00002440 xmlFree(id);
2441}
2442
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002443
Owen Taylor3473f882001-02-23 17:55:21 +00002444/**
2445 * xmlAddID:
2446 * @ctxt: the validation context
2447 * @doc: pointer to the document
2448 * @value: the value name
2449 * @attr: the attribute holding the ID
2450 *
2451 * Register a new id declaration
2452 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002453 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002454 */
2455xmlIDPtr
2456xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2457 xmlAttrPtr attr) {
2458 xmlIDPtr ret;
2459 xmlIDTablePtr table;
2460
2461 if (doc == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002462 return(NULL);
2463 }
2464 if (value == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002465 return(NULL);
2466 }
2467 if (attr == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002468 return(NULL);
2469 }
2470
2471 /*
2472 * Create the ID table if needed.
2473 */
2474 table = (xmlIDTablePtr) doc->ids;
2475 if (table == NULL)
2476 doc->ids = table = xmlCreateIDTable();
2477 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002478 xmlVErrMemory(ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00002479 "xmlAddID: Table creation failed!\n");
2480 return(NULL);
2481 }
2482
2483 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2484 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002485 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002486 return(NULL);
2487 }
2488
2489 /*
2490 * fill the structure.
2491 */
2492 ret->value = xmlStrdup(value);
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002493 ret->doc = doc;
Daniel Veillardea7751d2002-12-20 00:16:24 +00002494 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2495 /*
2496 * Operating in streaming mode, attr is gonna disapear
2497 */
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002498 if (doc->dict != NULL)
2499 ret->name = xmlDictLookup(doc->dict, attr->name, -1);
2500 else
2501 ret->name = xmlStrdup(attr->name);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002502 ret->attr = NULL;
2503 } else {
2504 ret->attr = attr;
2505 ret->name = NULL;
2506 }
2507 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002508
2509 if (xmlHashAddEntry(table, value, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002510#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002511 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002512 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002513 */
Daniel Veillardd3669b22004-02-25 12:34:55 +00002514 if ((ctxt != NULL) && (ctxt->error != NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00002515 xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED,
2516 "ID %s already defined\n",
2517 value, NULL, NULL);
Daniel Veillard76575762002-09-05 14:21:15 +00002518 }
Daniel Veillard4432df22003-09-28 18:58:27 +00002519#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002520 xmlFreeID(ret);
2521 return(NULL);
2522 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002523 if (attr != NULL)
2524 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002525 return(ret);
2526}
2527
2528/**
2529 * xmlFreeIDTable:
2530 * @table: An id table
2531 *
2532 * Deallocate the memory used by an ID hash table.
2533 */
2534void
2535xmlFreeIDTable(xmlIDTablePtr table) {
2536 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2537}
2538
2539/**
2540 * xmlIsID:
2541 * @doc: the document
2542 * @elem: the element carrying the attribute
2543 * @attr: the attribute
2544 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002545 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002546 * then this is done if DTD loading has been requested. In the case
2547 * of HTML documents parsed with the HTML parser, then ID detection is
2548 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002549 *
2550 * Returns 0 or 1 depending on the lookup result
2551 */
2552int
2553xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2554 if (doc == NULL) return(0);
2555 if (attr == NULL) return(0);
2556 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2557 return(0);
2558 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
Daniel Veillard9ba8e382003-10-28 21:31:45 +00002559 if (((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2560 (xmlStrEqual(BAD_CAST "name", attr->name))) &&
2561 ((elem != NULL) && (!xmlStrEqual(elem->name, BAD_CAST "input"))))
Owen Taylor3473f882001-02-23 17:55:21 +00002562 return(1);
2563 return(0);
2564 } else {
2565 xmlAttributePtr attrDecl;
2566
2567 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002568 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00002569 xmlChar fn[50];
Daniel Veillard37f961d2002-07-06 17:53:56 +00002570 xmlChar *fullname;
Daniel Veillardc00cda82003-04-07 10:22:39 +00002571
2572 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002573 if (fullname == NULL)
2574 return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002575 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2576 attr->name);
2577 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2578 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2579 attr->name);
Daniel Veillardc00cda82003-04-07 10:22:39 +00002580 if ((fullname != fn) && (fullname != elem->name))
2581 xmlFree(fullname);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002582 } else {
2583 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2584 attr->name);
2585 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2586 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2587 attr->name);
2588 }
Owen Taylor3473f882001-02-23 17:55:21 +00002589
2590 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2591 return(1);
2592 }
2593 return(0);
2594}
2595
2596/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002597 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002598 * @doc: the document
2599 * @attr: the attribute
2600 *
2601 * Remove the given attribute from the ID table maintained internally.
2602 *
2603 * Returns -1 if the lookup failed and 0 otherwise
2604 */
2605int
2606xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
Owen Taylor3473f882001-02-23 17:55:21 +00002607 xmlIDTablePtr table;
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002608 xmlIDPtr id;
Owen Taylor3473f882001-02-23 17:55:21 +00002609 xmlChar *ID;
2610
2611 if (doc == NULL) return(-1);
2612 if (attr == NULL) return(-1);
2613 table = (xmlIDTablePtr) doc->ids;
2614 if (table == NULL)
2615 return(-1);
2616
2617 if (attr == NULL)
2618 return(-1);
2619 ID = xmlNodeListGetString(doc, attr->children, 1);
2620 if (ID == NULL)
2621 return(-1);
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002622 id = xmlHashLookup(table, ID);
2623 if (id == NULL || id->attr != attr) {
Owen Taylor3473f882001-02-23 17:55:21 +00002624 xmlFree(ID);
2625 return(-1);
2626 }
2627 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2628 xmlFree(ID);
2629 return(0);
2630}
2631
2632/**
2633 * xmlGetID:
2634 * @doc: pointer to the document
2635 * @ID: the ID value
2636 *
2637 * Search the attribute declaring the given ID
2638 *
2639 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2640 */
2641xmlAttrPtr
2642xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2643 xmlIDTablePtr table;
2644 xmlIDPtr id;
2645
2646 if (doc == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002647 return(NULL);
2648 }
2649
2650 if (ID == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002651 return(NULL);
2652 }
2653
2654 table = (xmlIDTablePtr) doc->ids;
2655 if (table == NULL)
2656 return(NULL);
2657
2658 id = xmlHashLookup(table, ID);
2659 if (id == NULL)
2660 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002661 if (id->attr == NULL) {
2662 /*
2663 * We are operating on a stream, return a well known reference
2664 * since the attribute node doesn't exist anymore
2665 */
2666 return((xmlAttrPtr) doc);
2667 }
Owen Taylor3473f882001-02-23 17:55:21 +00002668 return(id->attr);
2669}
2670
2671/************************************************************************
2672 * *
2673 * Refs *
2674 * *
2675 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002676typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002677{
2678 xmlListPtr l;
2679 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002680} xmlRemoveMemo;
2681
2682typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2683
2684typedef struct xmlValidateMemo_t
2685{
2686 xmlValidCtxtPtr ctxt;
2687 const xmlChar *name;
2688} xmlValidateMemo;
2689
2690typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002691
2692/**
2693 * xmlCreateRefTable:
2694 *
2695 * create and initialize an empty ref hash table.
2696 *
2697 * Returns the xmlRefTablePtr just created or NULL in case
2698 * of error.
2699 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002700static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002701xmlCreateRefTable(void) {
2702 return(xmlHashCreate(0));
2703}
2704
2705/**
2706 * xmlFreeRef:
2707 * @lk: A list link
2708 *
2709 * Deallocate the memory used by a ref definition
2710 */
2711static void
2712xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002713 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2714 if (ref == NULL) return;
2715 if (ref->value != NULL)
2716 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002717 if (ref->name != NULL)
2718 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002719 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002720}
2721
2722/**
2723 * xmlFreeRefList:
2724 * @list_ref: A list of references.
2725 *
2726 * Deallocate the memory used by a list of references
2727 */
2728static void
2729xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002730 if (list_ref == NULL) return;
2731 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002732}
2733
2734/**
2735 * xmlWalkRemoveRef:
2736 * @data: Contents of current link
2737 * @user: Value supplied by the user
2738 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002739 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002740 */
2741static int
2742xmlWalkRemoveRef(const void *data, const void *user)
2743{
Daniel Veillard37721922001-05-04 15:21:12 +00002744 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2745 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2746 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002747
Daniel Veillard37721922001-05-04 15:21:12 +00002748 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2749 xmlListRemoveFirst(ref_list, (void *)data);
2750 return 0;
2751 }
2752 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002753}
2754
2755/**
Daniel Veillard770075b2004-02-25 10:44:30 +00002756 * xmlDummyCompare
2757 * @data0: Value supplied by the user
2758 * @data1: Value supplied by the user
2759 *
2760 * Do nothing, return 0. Used to create unordered lists.
2761 */
2762static int
Daniel Veillardf54cd532004-02-25 11:52:31 +00002763xmlDummyCompare(const void *data0 ATTRIBUTE_UNUSED,
2764 const void *data1 ATTRIBUTE_UNUSED)
Daniel Veillard770075b2004-02-25 10:44:30 +00002765{
2766 return (0);
2767}
2768
2769/**
Owen Taylor3473f882001-02-23 17:55:21 +00002770 * xmlAddRef:
2771 * @ctxt: the validation context
2772 * @doc: pointer to the document
2773 * @value: the value name
2774 * @attr: the attribute holding the Ref
2775 *
2776 * Register a new ref declaration
2777 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002778 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002779 */
2780xmlRefPtr
William M. Brackedb65a72004-02-06 07:36:04 +00002781xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002782 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002783 xmlRefPtr ret;
2784 xmlRefTablePtr table;
2785 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002786
Daniel Veillard37721922001-05-04 15:21:12 +00002787 if (doc == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002788 return(NULL);
2789 }
2790 if (value == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002791 return(NULL);
2792 }
2793 if (attr == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002794 return(NULL);
2795 }
Owen Taylor3473f882001-02-23 17:55:21 +00002796
Daniel Veillard37721922001-05-04 15:21:12 +00002797 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002798 * Create the Ref table if needed.
2799 */
Daniel Veillard37721922001-05-04 15:21:12 +00002800 table = (xmlRefTablePtr) doc->refs;
2801 if (table == NULL)
2802 doc->refs = table = xmlCreateRefTable();
2803 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002804 xmlVErrMemory(ctxt,
Daniel Veillard37721922001-05-04 15:21:12 +00002805 "xmlAddRef: Table creation failed!\n");
2806 return(NULL);
2807 }
Owen Taylor3473f882001-02-23 17:55:21 +00002808
Daniel Veillard37721922001-05-04 15:21:12 +00002809 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2810 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002811 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard37721922001-05-04 15:21:12 +00002812 return(NULL);
2813 }
Owen Taylor3473f882001-02-23 17:55:21 +00002814
Daniel Veillard37721922001-05-04 15:21:12 +00002815 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002816 * fill the structure.
2817 */
Daniel Veillard37721922001-05-04 15:21:12 +00002818 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002819 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2820 /*
2821 * Operating in streaming mode, attr is gonna disapear
2822 */
2823 ret->name = xmlStrdup(attr->name);
2824 ret->attr = NULL;
2825 } else {
2826 ret->name = NULL;
2827 ret->attr = attr;
2828 }
2829 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002830
Daniel Veillard37721922001-05-04 15:21:12 +00002831 /* To add a reference :-
2832 * References are maintained as a list of references,
2833 * Lookup the entry, if no entry create new nodelist
2834 * Add the owning node to the NodeList
2835 * Return the ref
2836 */
Owen Taylor3473f882001-02-23 17:55:21 +00002837
Daniel Veillard37721922001-05-04 15:21:12 +00002838 if (NULL == (ref_list = xmlHashLookup(table, value))) {
Daniel Veillard770075b2004-02-25 10:44:30 +00002839 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, xmlDummyCompare))) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002840 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2841 "xmlAddRef: Reference list creation failed!\n",
2842 NULL);
Daniel Veillard37721922001-05-04 15:21:12 +00002843 return(NULL);
2844 }
2845 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2846 xmlListDelete(ref_list);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002847 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2848 "xmlAddRef: Reference list insertion failed!\n",
2849 NULL);
Daniel Veillard37721922001-05-04 15:21:12 +00002850 return(NULL);
2851 }
2852 }
Daniel Veillard965983a2004-02-17 16:30:24 +00002853/* xmlListInsert(ref_list, ret); */
2854 xmlListAppend(ref_list, ret);
Daniel Veillard37721922001-05-04 15:21:12 +00002855 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002856}
2857
2858/**
2859 * xmlFreeRefTable:
2860 * @table: An ref table
2861 *
2862 * Deallocate the memory used by an Ref hash table.
2863 */
2864void
2865xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002866 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002867}
2868
2869/**
2870 * xmlIsRef:
2871 * @doc: the document
2872 * @elem: the element carrying the attribute
2873 * @attr: the attribute
2874 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002875 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002876 * then this is simple, otherwise we use an heuristic: name Ref (upper
2877 * or lowercase).
2878 *
2879 * Returns 0 or 1 depending on the lookup result
2880 */
2881int
2882xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002883 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2884 return(0);
2885 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2886 /* TODO @@@ */
2887 return(0);
2888 } else {
2889 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002890
Daniel Veillard37721922001-05-04 15:21:12 +00002891 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2892 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2893 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2894 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002895
Daniel Veillard37721922001-05-04 15:21:12 +00002896 if ((attrDecl != NULL) &&
2897 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2898 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2899 return(1);
2900 }
2901 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002902}
2903
2904/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002905 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002906 * @doc: the document
2907 * @attr: the attribute
2908 *
2909 * Remove the given attribute from the Ref table maintained internally.
2910 *
2911 * Returns -1 if the lookup failed and 0 otherwise
2912 */
2913int
2914xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002915 xmlListPtr ref_list;
2916 xmlRefTablePtr table;
2917 xmlChar *ID;
2918 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002919
Daniel Veillard37721922001-05-04 15:21:12 +00002920 if (doc == NULL) return(-1);
2921 if (attr == NULL) return(-1);
2922 table = (xmlRefTablePtr) doc->refs;
2923 if (table == NULL)
2924 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002925
Daniel Veillard37721922001-05-04 15:21:12 +00002926 if (attr == NULL)
2927 return(-1);
2928 ID = xmlNodeListGetString(doc, attr->children, 1);
2929 if (ID == NULL)
2930 return(-1);
2931 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002932
Daniel Veillard37721922001-05-04 15:21:12 +00002933 if(ref_list == NULL) {
2934 xmlFree(ID);
2935 return (-1);
2936 }
2937 /* At this point, ref_list refers to a list of references which
2938 * have the same key as the supplied attr. Our list of references
2939 * is ordered by reference address and we don't have that information
2940 * here to use when removing. We'll have to walk the list and
2941 * check for a matching attribute, when we find one stop the walk
2942 * and remove the entry.
2943 * The list is ordered by reference, so that means we don't have the
2944 * key. Passing the list and the reference to the walker means we
2945 * will have enough data to be able to remove the entry.
2946 */
2947 target.l = ref_list;
2948 target.ap = attr;
2949
2950 /* Remove the supplied attr from our list */
2951 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002952
Daniel Veillard37721922001-05-04 15:21:12 +00002953 /*If the list is empty then remove the list entry in the hash */
2954 if (xmlListEmpty(ref_list))
2955 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2956 xmlFreeRefList);
2957 xmlFree(ID);
2958 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002959}
2960
2961/**
2962 * xmlGetRefs:
2963 * @doc: pointer to the document
2964 * @ID: the ID value
2965 *
2966 * Find the set of references for the supplied ID.
2967 *
2968 * Returns NULL if not found, otherwise node set for the ID.
2969 */
2970xmlListPtr
2971xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002972 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002973
Daniel Veillard37721922001-05-04 15:21:12 +00002974 if (doc == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002975 return(NULL);
2976 }
Owen Taylor3473f882001-02-23 17:55:21 +00002977
Daniel Veillard37721922001-05-04 15:21:12 +00002978 if (ID == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002979 return(NULL);
2980 }
Owen Taylor3473f882001-02-23 17:55:21 +00002981
Daniel Veillard37721922001-05-04 15:21:12 +00002982 table = (xmlRefTablePtr) doc->refs;
2983 if (table == NULL)
2984 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002985
Daniel Veillard37721922001-05-04 15:21:12 +00002986 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002987}
2988
2989/************************************************************************
2990 * *
2991 * Routines for validity checking *
2992 * *
2993 ************************************************************************/
2994
2995/**
2996 * xmlGetDtdElementDesc:
2997 * @dtd: a pointer to the DtD to search
2998 * @name: the element name
2999 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003000 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00003001 *
3002 * returns the xmlElementPtr if found or NULL
3003 */
3004
3005xmlElementPtr
3006xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
3007 xmlElementTablePtr table;
3008 xmlElementPtr cur;
3009 xmlChar *uqname = NULL, *prefix = NULL;
3010
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00003011 if ((dtd == NULL) || (name == NULL)) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003012 if (dtd->elements == NULL)
3013 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003014 table = (xmlElementTablePtr) dtd->elements;
3015
3016 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003017 if (uqname != NULL)
3018 name = uqname;
3019 cur = xmlHashLookup2(table, name, prefix);
3020 if (prefix != NULL) xmlFree(prefix);
3021 if (uqname != NULL) xmlFree(uqname);
3022 return(cur);
3023}
3024/**
3025 * xmlGetDtdElementDesc2:
3026 * @dtd: a pointer to the DtD to search
3027 * @name: the element name
3028 * @create: create an empty description if not found
3029 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003030 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00003031 *
3032 * returns the xmlElementPtr if found or NULL
3033 */
3034
Daniel Veillard86fd5a72001-12-13 14:55:21 +00003035static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00003036xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
3037 xmlElementTablePtr table;
3038 xmlElementPtr cur;
3039 xmlChar *uqname = NULL, *prefix = NULL;
3040
3041 if (dtd == NULL) return(NULL);
3042 if (dtd->elements == NULL) {
3043 if (!create)
3044 return(NULL);
3045 /*
3046 * Create the Element table if needed.
3047 */
3048 table = (xmlElementTablePtr) dtd->elements;
3049 if (table == NULL) {
3050 table = xmlCreateElementTable();
3051 dtd->elements = (void *) table;
3052 }
3053 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00003054 xmlVErrMemory(NULL, "element table allocation failed");
Daniel Veillarda10efa82001-04-18 13:09:01 +00003055 return(NULL);
3056 }
3057 }
3058 table = (xmlElementTablePtr) dtd->elements;
3059
3060 uqname = xmlSplitQName2(name, &prefix);
3061 if (uqname != NULL)
3062 name = uqname;
3063 cur = xmlHashLookup2(table, name, prefix);
3064 if ((cur == NULL) && (create)) {
3065 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
3066 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00003067 xmlVErrMemory(NULL, "malloc failed");
Daniel Veillarda10efa82001-04-18 13:09:01 +00003068 return(NULL);
3069 }
3070 memset(cur, 0, sizeof(xmlElement));
3071 cur->type = XML_ELEMENT_DECL;
3072
3073 /*
3074 * fill the structure.
3075 */
3076 cur->name = xmlStrdup(name);
3077 cur->prefix = xmlStrdup(prefix);
3078 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
3079
3080 xmlHashAddEntry2(table, name, prefix, cur);
3081 }
3082 if (prefix != NULL) xmlFree(prefix);
3083 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00003084 return(cur);
3085}
3086
3087/**
3088 * xmlGetDtdQElementDesc:
3089 * @dtd: a pointer to the DtD to search
3090 * @name: the element name
3091 * @prefix: the element namespace prefix
3092 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003093 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00003094 *
3095 * returns the xmlElementPtr if found or NULL
3096 */
3097
Daniel Veillard48da9102001-08-07 01:10:10 +00003098xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00003099xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
3100 const xmlChar *prefix) {
3101 xmlElementTablePtr table;
3102
3103 if (dtd == NULL) return(NULL);
3104 if (dtd->elements == NULL) return(NULL);
3105 table = (xmlElementTablePtr) dtd->elements;
3106
3107 return(xmlHashLookup2(table, name, prefix));
3108}
3109
3110/**
3111 * xmlGetDtdAttrDesc:
3112 * @dtd: a pointer to the DtD to search
3113 * @elem: the element name
3114 * @name: the attribute name
3115 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003116 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00003117 * this element.
3118 *
3119 * returns the xmlAttributePtr if found or NULL
3120 */
3121
3122xmlAttributePtr
3123xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
3124 xmlAttributeTablePtr table;
3125 xmlAttributePtr cur;
3126 xmlChar *uqname = NULL, *prefix = NULL;
3127
3128 if (dtd == NULL) return(NULL);
3129 if (dtd->attributes == NULL) return(NULL);
3130
3131 table = (xmlAttributeTablePtr) dtd->attributes;
3132 if (table == NULL)
3133 return(NULL);
3134
3135 uqname = xmlSplitQName2(name, &prefix);
3136
3137 if (uqname != NULL) {
3138 cur = xmlHashLookup3(table, uqname, prefix, elem);
3139 if (prefix != NULL) xmlFree(prefix);
3140 if (uqname != NULL) xmlFree(uqname);
3141 } else
3142 cur = xmlHashLookup3(table, name, NULL, elem);
3143 return(cur);
3144}
3145
3146/**
3147 * xmlGetDtdQAttrDesc:
3148 * @dtd: a pointer to the DtD to search
3149 * @elem: the element name
3150 * @name: the attribute name
3151 * @prefix: the attribute namespace prefix
3152 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003153 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00003154 * this element.
3155 *
3156 * returns the xmlAttributePtr if found or NULL
3157 */
3158
Daniel Veillard48da9102001-08-07 01:10:10 +00003159xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00003160xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
3161 const xmlChar *prefix) {
3162 xmlAttributeTablePtr table;
3163
3164 if (dtd == NULL) return(NULL);
3165 if (dtd->attributes == NULL) return(NULL);
3166 table = (xmlAttributeTablePtr) dtd->attributes;
3167
3168 return(xmlHashLookup3(table, name, prefix, elem));
3169}
3170
3171/**
3172 * xmlGetDtdNotationDesc:
3173 * @dtd: a pointer to the DtD to search
3174 * @name: the notation name
3175 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003176 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00003177 *
3178 * returns the xmlNotationPtr if found or NULL
3179 */
3180
3181xmlNotationPtr
3182xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
3183 xmlNotationTablePtr table;
3184
3185 if (dtd == NULL) return(NULL);
3186 if (dtd->notations == NULL) return(NULL);
3187 table = (xmlNotationTablePtr) dtd->notations;
3188
3189 return(xmlHashLookup(table, name));
3190}
3191
Daniel Veillardf54cd532004-02-25 11:52:31 +00003192#if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
Owen Taylor3473f882001-02-23 17:55:21 +00003193/**
3194 * xmlValidateNotationUse:
3195 * @ctxt: the validation context
3196 * @doc: the document
3197 * @notationName: the notation name to check
3198 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003199 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00003200 * - [ VC: Notation Declared ]
3201 *
3202 * returns 1 if valid or 0 otherwise
3203 */
3204
3205int
3206xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3207 const xmlChar *notationName) {
3208 xmlNotationPtr notaDecl;
3209 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3210
3211 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
3212 if ((notaDecl == NULL) && (doc->extSubset != NULL))
3213 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
3214
Daniel Veillarde637c4a2003-03-30 21:10:09 +00003215 if ((notaDecl == NULL) && (ctxt != NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003216 xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_NOTATION,
3217 "NOTATION %s is not declared\n",
3218 notationName, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003219 return(0);
3220 }
3221 return(1);
3222}
Daniel Veillardf54cd532004-02-25 11:52:31 +00003223#endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00003224
3225/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00003226 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00003227 * @doc: the document
3228 * @name: the element name
3229 *
3230 * Search in the DtDs whether an element accept Mixed content (or ANY)
3231 * basically if it is supposed to accept text childs
3232 *
3233 * returns 0 if no, 1 if yes, and -1 if no element description is available
3234 */
3235
3236int
3237xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
3238 xmlElementPtr elemDecl;
3239
3240 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3241
3242 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
3243 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3244 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
3245 if (elemDecl == NULL) return(-1);
3246 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003247 case XML_ELEMENT_TYPE_UNDEFINED:
3248 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003249 case XML_ELEMENT_TYPE_ELEMENT:
3250 return(0);
3251 case XML_ELEMENT_TYPE_EMPTY:
3252 /*
3253 * return 1 for EMPTY since we want VC error to pop up
3254 * on <empty> </empty> for example
3255 */
3256 case XML_ELEMENT_TYPE_ANY:
3257 case XML_ELEMENT_TYPE_MIXED:
3258 return(1);
3259 }
3260 return(1);
3261}
3262
Daniel Veillard4432df22003-09-28 18:58:27 +00003263#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00003264/**
3265 * xmlValidateNameValue:
3266 * @value: an Name value
3267 *
3268 * Validate that the given value match Name production
3269 *
3270 * returns 1 if valid or 0 otherwise
3271 */
3272
Daniel Veillard9b731d72002-04-14 12:56:08 +00003273int
Owen Taylor3473f882001-02-23 17:55:21 +00003274xmlValidateNameValue(const xmlChar *value) {
3275 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003276 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003277
3278 if (value == NULL) return(0);
3279 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003280 val = xmlStringCurrentChar(NULL, cur, &len);
3281 cur += len;
3282 if (!IS_LETTER(val) && (val != '_') &&
3283 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003284 return(0);
3285 }
3286
Daniel Veillardd8224e02002-01-13 15:43:22 +00003287 val = xmlStringCurrentChar(NULL, cur, &len);
3288 cur += len;
3289 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3290 (val == '.') || (val == '-') ||
3291 (val == '_') || (val == ':') ||
3292 (IS_COMBINING(val)) ||
3293 (IS_EXTENDER(val))) {
3294 val = xmlStringCurrentChar(NULL, cur, &len);
3295 cur += len;
3296 }
Owen Taylor3473f882001-02-23 17:55:21 +00003297
Daniel Veillardd8224e02002-01-13 15:43:22 +00003298 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003299
3300 return(1);
3301}
3302
3303/**
3304 * xmlValidateNamesValue:
3305 * @value: an Names value
3306 *
3307 * Validate that the given value match Names production
3308 *
3309 * returns 1 if valid or 0 otherwise
3310 */
3311
Daniel Veillard9b731d72002-04-14 12:56:08 +00003312int
Owen Taylor3473f882001-02-23 17:55:21 +00003313xmlValidateNamesValue(const xmlChar *value) {
3314 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003315 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003316
3317 if (value == NULL) return(0);
3318 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003319 val = xmlStringCurrentChar(NULL, cur, &len);
3320 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003321
Daniel Veillardd8224e02002-01-13 15:43:22 +00003322 if (!IS_LETTER(val) && (val != '_') &&
3323 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003324 return(0);
3325 }
3326
Daniel Veillardd8224e02002-01-13 15:43:22 +00003327 val = xmlStringCurrentChar(NULL, cur, &len);
3328 cur += len;
3329 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3330 (val == '.') || (val == '-') ||
3331 (val == '_') || (val == ':') ||
3332 (IS_COMBINING(val)) ||
3333 (IS_EXTENDER(val))) {
3334 val = xmlStringCurrentChar(NULL, cur, &len);
3335 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003336 }
3337
Daniel Veillard807b4de2004-09-26 14:42:56 +00003338 /* Should not test IS_BLANK(val) here -- see erratum E20*/
3339 while (val == 0x20) {
3340 while (val == 0x20) {
Daniel Veillardd8224e02002-01-13 15:43:22 +00003341 val = xmlStringCurrentChar(NULL, cur, &len);
3342 cur += len;
3343 }
3344
3345 if (!IS_LETTER(val) && (val != '_') &&
3346 (val != ':')) {
3347 return(0);
3348 }
3349 val = xmlStringCurrentChar(NULL, cur, &len);
3350 cur += len;
3351
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;
3359 }
3360 }
3361
3362 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003363
3364 return(1);
3365}
3366
3367/**
3368 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003369 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003370 *
3371 * Validate that the given value match Nmtoken production
3372 *
3373 * [ VC: Name Token ]
3374 *
3375 * returns 1 if valid or 0 otherwise
3376 */
3377
Daniel Veillard9b731d72002-04-14 12:56:08 +00003378int
Owen Taylor3473f882001-02-23 17:55:21 +00003379xmlValidateNmtokenValue(const xmlChar *value) {
3380 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003381 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003382
3383 if (value == NULL) return(0);
3384 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003385 val = xmlStringCurrentChar(NULL, cur, &len);
3386 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003387
Daniel Veillardd8224e02002-01-13 15:43:22 +00003388 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3389 (val != '.') && (val != '-') &&
3390 (val != '_') && (val != ':') &&
3391 (!IS_COMBINING(val)) &&
3392 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003393 return(0);
3394
Daniel Veillardd8224e02002-01-13 15:43:22 +00003395 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3396 (val == '.') || (val == '-') ||
3397 (val == '_') || (val == ':') ||
3398 (IS_COMBINING(val)) ||
3399 (IS_EXTENDER(val))) {
3400 val = xmlStringCurrentChar(NULL, cur, &len);
3401 cur += len;
3402 }
Owen Taylor3473f882001-02-23 17:55:21 +00003403
Daniel Veillardd8224e02002-01-13 15:43:22 +00003404 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003405
3406 return(1);
3407}
3408
3409/**
3410 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003411 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003412 *
3413 * Validate that the given value match Nmtokens production
3414 *
3415 * [ VC: Name Token ]
3416 *
3417 * returns 1 if valid or 0 otherwise
3418 */
3419
Daniel Veillard9b731d72002-04-14 12:56:08 +00003420int
Owen Taylor3473f882001-02-23 17:55:21 +00003421xmlValidateNmtokensValue(const xmlChar *value) {
3422 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003423 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003424
3425 if (value == NULL) return(0);
3426 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003427 val = xmlStringCurrentChar(NULL, cur, &len);
3428 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003429
Daniel Veillardd8224e02002-01-13 15:43:22 +00003430 while (IS_BLANK(val)) {
3431 val = xmlStringCurrentChar(NULL, cur, &len);
3432 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003433 }
3434
Daniel Veillardd8224e02002-01-13 15:43:22 +00003435 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3436 (val != '.') && (val != '-') &&
3437 (val != '_') && (val != ':') &&
3438 (!IS_COMBINING(val)) &&
3439 (!IS_EXTENDER(val)))
3440 return(0);
3441
3442 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3443 (val == '.') || (val == '-') ||
3444 (val == '_') || (val == ':') ||
3445 (IS_COMBINING(val)) ||
3446 (IS_EXTENDER(val))) {
3447 val = xmlStringCurrentChar(NULL, cur, &len);
3448 cur += len;
3449 }
3450
Daniel Veillard807b4de2004-09-26 14:42:56 +00003451 /* Should not test IS_BLANK(val) here -- see erratum E20*/
3452 while (val == 0x20) {
3453 while (val == 0x20) {
Daniel Veillardd8224e02002-01-13 15:43:22 +00003454 val = xmlStringCurrentChar(NULL, cur, &len);
3455 cur += len;
3456 }
3457 if (val == 0) return(1);
3458
3459 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3460 (val != '.') && (val != '-') &&
3461 (val != '_') && (val != ':') &&
3462 (!IS_COMBINING(val)) &&
3463 (!IS_EXTENDER(val)))
3464 return(0);
3465
3466 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3467 (val == '.') || (val == '-') ||
3468 (val == '_') || (val == ':') ||
3469 (IS_COMBINING(val)) ||
3470 (IS_EXTENDER(val))) {
3471 val = xmlStringCurrentChar(NULL, cur, &len);
3472 cur += len;
3473 }
3474 }
3475
3476 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003477
3478 return(1);
3479}
3480
3481/**
3482 * xmlValidateNotationDecl:
3483 * @ctxt: the validation context
3484 * @doc: a document instance
3485 * @nota: a notation definition
3486 *
3487 * Try to validate a single notation definition
3488 * basically it does the following checks as described by the
3489 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003490 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003491 * But this function get called anyway ...
3492 *
3493 * returns 1 if valid or 0 otherwise
3494 */
3495
3496int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003497xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3498 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003499 int ret = 1;
3500
3501 return(ret);
3502}
3503
3504/**
3505 * xmlValidateAttributeValue:
3506 * @type: an attribute type
3507 * @value: an attribute value
3508 *
3509 * Validate that the given attribute value match the proper production
3510 *
3511 * [ VC: ID ]
3512 * Values of type ID must match the Name production....
3513 *
3514 * [ VC: IDREF ]
3515 * Values of type IDREF must match the Name production, and values
3516 * of type IDREFS must match Names ...
3517 *
3518 * [ VC: Entity Name ]
3519 * Values of type ENTITY must match the Name production, values
3520 * of type ENTITIES must match Names ...
3521 *
3522 * [ VC: Name Token ]
3523 * Values of type NMTOKEN must match the Nmtoken production; values
3524 * of type NMTOKENS must match Nmtokens.
3525 *
3526 * returns 1 if valid or 0 otherwise
3527 */
3528
3529int
3530xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3531 switch (type) {
3532 case XML_ATTRIBUTE_ENTITIES:
3533 case XML_ATTRIBUTE_IDREFS:
3534 return(xmlValidateNamesValue(value));
3535 case XML_ATTRIBUTE_ENTITY:
3536 case XML_ATTRIBUTE_IDREF:
3537 case XML_ATTRIBUTE_ID:
3538 case XML_ATTRIBUTE_NOTATION:
3539 return(xmlValidateNameValue(value));
3540 case XML_ATTRIBUTE_NMTOKENS:
3541 case XML_ATTRIBUTE_ENUMERATION:
3542 return(xmlValidateNmtokensValue(value));
3543 case XML_ATTRIBUTE_NMTOKEN:
3544 return(xmlValidateNmtokenValue(value));
3545 case XML_ATTRIBUTE_CDATA:
3546 break;
3547 }
3548 return(1);
3549}
3550
3551/**
3552 * xmlValidateAttributeValue2:
3553 * @ctxt: the validation context
3554 * @doc: the document
3555 * @name: the attribute name (used for error reporting only)
3556 * @type: the attribute type
3557 * @value: the attribute value
3558 *
3559 * Validate that the given attribute value match a given type.
3560 * This typically cannot be done before having finished parsing
3561 * the subsets.
3562 *
3563 * [ VC: IDREF ]
3564 * Values of type IDREF must match one of the declared IDs
3565 * Values of type IDREFS must match a sequence of the declared IDs
3566 * each Name must match the value of an ID attribute on some element
3567 * in the XML document; i.e. IDREF values must match the value of
3568 * some ID attribute
3569 *
3570 * [ VC: Entity Name ]
3571 * Values of type ENTITY must match one declared entity
3572 * Values of type ENTITIES must match a sequence of declared entities
3573 *
3574 * [ VC: Notation Attributes ]
3575 * all notation names in the declaration must be declared.
3576 *
3577 * returns 1 if valid or 0 otherwise
3578 */
3579
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003580static int
Owen Taylor3473f882001-02-23 17:55:21 +00003581xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3582 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3583 int ret = 1;
3584 switch (type) {
3585 case XML_ATTRIBUTE_IDREFS:
3586 case XML_ATTRIBUTE_IDREF:
3587 case XML_ATTRIBUTE_ID:
3588 case XML_ATTRIBUTE_NMTOKENS:
3589 case XML_ATTRIBUTE_ENUMERATION:
3590 case XML_ATTRIBUTE_NMTOKEN:
3591 case XML_ATTRIBUTE_CDATA:
3592 break;
3593 case XML_ATTRIBUTE_ENTITY: {
3594 xmlEntityPtr ent;
3595
3596 ent = xmlGetDocEntity(doc, value);
Daniel Veillard62998c02003-09-15 12:56:36 +00003597 /* yeah it's a bit messy... */
Daniel Veillard878eab02002-02-19 13:46:09 +00003598 if ((ent == NULL) && (doc->standalone == 1)) {
3599 doc->standalone = 0;
3600 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003601 }
Owen Taylor3473f882001-02-23 17:55:21 +00003602 if (ent == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003603 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3604 XML_DTD_UNKNOWN_ENTITY,
Owen Taylor3473f882001-02-23 17:55:21 +00003605 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003606 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003607 ret = 0;
3608 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003609 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3610 XML_DTD_ENTITY_TYPE,
Owen Taylor3473f882001-02-23 17:55:21 +00003611 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003612 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003613 ret = 0;
3614 }
3615 break;
3616 }
3617 case XML_ATTRIBUTE_ENTITIES: {
3618 xmlChar *dup, *nam = NULL, *cur, save;
3619 xmlEntityPtr ent;
3620
3621 dup = xmlStrdup(value);
3622 if (dup == NULL)
3623 return(0);
3624 cur = dup;
3625 while (*cur != 0) {
3626 nam = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00003627 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00003628 save = *cur;
3629 *cur = 0;
3630 ent = xmlGetDocEntity(doc, nam);
3631 if (ent == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003632 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3633 XML_DTD_UNKNOWN_ENTITY,
Owen Taylor3473f882001-02-23 17:55:21 +00003634 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003635 name, nam, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003636 ret = 0;
3637 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003638 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3639 XML_DTD_ENTITY_TYPE,
Owen Taylor3473f882001-02-23 17:55:21 +00003640 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003641 name, nam, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003642 ret = 0;
3643 }
3644 if (save == 0)
3645 break;
3646 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00003647 while (IS_BLANK_CH(*cur)) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00003648 }
3649 xmlFree(dup);
3650 break;
3651 }
3652 case XML_ATTRIBUTE_NOTATION: {
3653 xmlNotationPtr nota;
3654
3655 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3656 if ((nota == NULL) && (doc->extSubset != NULL))
3657 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3658
3659 if (nota == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003660 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3661 XML_DTD_UNKNOWN_NOTATION,
Owen Taylor3473f882001-02-23 17:55:21 +00003662 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003663 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003664 ret = 0;
3665 }
3666 break;
3667 }
3668 }
3669 return(ret);
3670}
3671
3672/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003673 * xmlValidCtxtNormalizeAttributeValue:
3674 * @ctxt: the validation context
3675 * @doc: the document
3676 * @elem: the parent
3677 * @name: the attribute name
3678 * @value: the attribute value
3679 * @ctxt: the validation context or NULL
3680 *
3681 * Does the validation related extra step of the normalization of attribute
3682 * values:
3683 *
3684 * If the declared value is not CDATA, then the XML processor must further
3685 * process the normalized attribute value by discarding any leading and
3686 * trailing space (#x20) characters, and by replacing sequences of space
3687 * (#x20) characters by single space (#x20) character.
3688 *
3689 * Also check VC: Standalone Document Declaration in P32, and update
3690 * ctxt->valid accordingly
3691 *
3692 * returns a new normalized string if normalization is needed, NULL otherwise
3693 * the caller must free the returned value.
3694 */
3695
3696xmlChar *
3697xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3698 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3699 xmlChar *ret, *dst;
3700 const xmlChar *src;
3701 xmlAttributePtr attrDecl = NULL;
3702 int extsubset = 0;
3703
3704 if (doc == NULL) return(NULL);
3705 if (elem == NULL) return(NULL);
3706 if (name == NULL) return(NULL);
3707 if (value == NULL) return(NULL);
3708
3709 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003710 xmlChar fn[50];
3711 xmlChar *fullname;
3712
3713 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3714 if (fullname == NULL)
3715 return(0);
3716 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003717 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003718 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003719 if (attrDecl != NULL)
3720 extsubset = 1;
3721 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003722 if ((fullname != fn) && (fullname != elem->name))
3723 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003724 }
3725 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3726 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3727 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3728 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3729 if (attrDecl != NULL)
3730 extsubset = 1;
3731 }
3732
3733 if (attrDecl == NULL)
3734 return(NULL);
3735 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3736 return(NULL);
3737
3738 ret = xmlStrdup(value);
3739 if (ret == NULL)
3740 return(NULL);
3741 src = value;
3742 dst = ret;
3743 while (*src == 0x20) src++;
3744 while (*src != 0) {
3745 if (*src == 0x20) {
3746 while (*src == 0x20) src++;
3747 if (*src != 0)
3748 *dst++ = 0x20;
3749 } else {
3750 *dst++ = *src++;
3751 }
3752 }
3753 *dst = 0;
3754 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003755 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE,
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003756"standalone: %s on %s value had to be normalized based on external subset declaration\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003757 name, elem->name, NULL);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003758 ctxt->valid = 0;
3759 }
3760 return(ret);
3761}
3762
3763/**
Owen Taylor3473f882001-02-23 17:55:21 +00003764 * xmlValidNormalizeAttributeValue:
3765 * @doc: the document
3766 * @elem: the parent
3767 * @name: the attribute name
3768 * @value: the attribute value
3769 *
3770 * Does the validation related extra step of the normalization of attribute
3771 * values:
3772 *
3773 * If the declared value is not CDATA, then the XML processor must further
3774 * process the normalized attribute value by discarding any leading and
3775 * trailing space (#x20) characters, and by replacing sequences of space
3776 * (#x20) characters by single space (#x20) character.
3777 *
Daniel Veillard652327a2003-09-29 18:02:38 +00003778 * Returns a new normalized string if normalization is needed, NULL otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00003779 * the caller must free the returned value.
3780 */
3781
3782xmlChar *
3783xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3784 const xmlChar *name, const xmlChar *value) {
3785 xmlChar *ret, *dst;
3786 const xmlChar *src;
3787 xmlAttributePtr attrDecl = NULL;
3788
3789 if (doc == NULL) return(NULL);
3790 if (elem == NULL) return(NULL);
3791 if (name == NULL) return(NULL);
3792 if (value == NULL) return(NULL);
3793
3794 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003795 xmlChar fn[50];
3796 xmlChar *fullname;
3797
3798 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3799 if (fullname == NULL)
3800 return(0);
3801 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003802 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003803 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3804 if ((fullname != fn) && (fullname != elem->name))
3805 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003806 }
3807 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3808 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3809 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3810
3811 if (attrDecl == NULL)
3812 return(NULL);
3813 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3814 return(NULL);
3815
3816 ret = xmlStrdup(value);
3817 if (ret == NULL)
3818 return(NULL);
3819 src = value;
3820 dst = ret;
3821 while (*src == 0x20) src++;
3822 while (*src != 0) {
3823 if (*src == 0x20) {
3824 while (*src == 0x20) src++;
3825 if (*src != 0)
3826 *dst++ = 0x20;
3827 } else {
3828 *dst++ = *src++;
3829 }
3830 }
3831 *dst = 0;
3832 return(ret);
3833}
3834
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003835static void
Owen Taylor3473f882001-02-23 17:55:21 +00003836xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003837 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003838 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3839}
3840
3841/**
3842 * xmlValidateAttributeDecl:
3843 * @ctxt: the validation context
3844 * @doc: a document instance
3845 * @attr: an attribute definition
3846 *
3847 * Try to validate a single attribute definition
3848 * basically it does the following checks as described by the
3849 * XML-1.0 recommendation:
3850 * - [ VC: Attribute Default Legal ]
3851 * - [ VC: Enumeration ]
3852 * - [ VC: ID Attribute Default ]
3853 *
3854 * The ID/IDREF uniqueness and matching are done separately
3855 *
3856 * returns 1 if valid or 0 otherwise
3857 */
3858
3859int
3860xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3861 xmlAttributePtr attr) {
3862 int ret = 1;
3863 int val;
3864 CHECK_DTD;
3865 if(attr == NULL) return(1);
3866
3867 /* Attribute Default Legal */
3868 /* Enumeration */
3869 if (attr->defaultValue != NULL) {
3870 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3871 if (val == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003872 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003873 "Syntax of default value for attribute %s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003874 attr->name, attr->elem, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003875 }
3876 ret &= val;
3877 }
3878
3879 /* ID Attribute Default */
3880 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3881 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3882 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003883 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_FIXED,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003884 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003885 attr->name, attr->elem, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003886 ret = 0;
3887 }
3888
3889 /* One ID per Element Type */
3890 if (attr->atype == XML_ATTRIBUTE_ID) {
3891 int nbId;
3892
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003893 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003894 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3895 attr->elem);
3896 if (elem != NULL) {
3897 nbId = xmlScanIDAttributeDecl(NULL, elem);
3898 } else {
3899 xmlAttributeTablePtr table;
3900
3901 /*
3902 * The attribute may be declared in the internal subset and the
3903 * element in the external subset.
3904 */
3905 nbId = 0;
3906 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3907 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3908 xmlValidateAttributeIdCallback, &nbId);
3909 }
3910 if (nbId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003911
3912 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003913 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3914 attr->elem, nbId, attr->name);
3915 } else if (doc->extSubset != NULL) {
3916 int extId = 0;
3917 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3918 if (elem != NULL) {
3919 extId = xmlScanIDAttributeDecl(NULL, elem);
3920 }
3921 if (extId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003922 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003923 "Element %s has %d ID attribute defined in the external subset : %s\n",
3924 attr->elem, extId, attr->name);
3925 } else if (extId + nbId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003926 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003927"Element %s has ID attributes defined in the internal and external subset : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003928 attr->elem, attr->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003929 }
3930 }
3931 }
3932
3933 /* Validity Constraint: Enumeration */
3934 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3935 xmlEnumerationPtr tree = attr->tree;
3936 while (tree != NULL) {
3937 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3938 tree = tree->next;
3939 }
3940 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003941 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003942"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003943 attr->defaultValue, attr->name, attr->elem);
3944 ret = 0;
3945 }
3946 }
3947
3948 return(ret);
3949}
3950
3951/**
3952 * xmlValidateElementDecl:
3953 * @ctxt: the validation context
3954 * @doc: a document instance
3955 * @elem: an element definition
3956 *
3957 * Try to validate a single element definition
3958 * basically it does the following checks as described by the
3959 * XML-1.0 recommendation:
3960 * - [ VC: One ID per Element Type ]
3961 * - [ VC: No Duplicate Types ]
3962 * - [ VC: Unique Element Type Declaration ]
3963 *
3964 * returns 1 if valid or 0 otherwise
3965 */
3966
3967int
3968xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3969 xmlElementPtr elem) {
3970 int ret = 1;
3971 xmlElementPtr tst;
3972
3973 CHECK_DTD;
3974
3975 if (elem == NULL) return(1);
3976
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003977#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003978#ifdef LIBXML_REGEXP_ENABLED
3979 /* Build the regexp associated to the content model */
3980 ret = xmlValidBuildContentModel(ctxt, elem);
3981#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003982#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003983
Owen Taylor3473f882001-02-23 17:55:21 +00003984 /* No Duplicate Types */
3985 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3986 xmlElementContentPtr cur, next;
3987 const xmlChar *name;
3988
3989 cur = elem->content;
3990 while (cur != NULL) {
3991 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3992 if (cur->c1 == NULL) break;
3993 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3994 name = cur->c1->name;
3995 next = cur->c2;
3996 while (next != NULL) {
3997 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard7b68df92003-08-03 22:58:54 +00003998 if ((xmlStrEqual(next->name, name)) &&
3999 (xmlStrEqual(next->prefix, cur->prefix))) {
4000 if (cur->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004001 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00004002 "Definition of %s has duplicate references of %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004003 elem->name, name, NULL);
Daniel Veillard7b68df92003-08-03 22:58:54 +00004004 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004005 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00004006 "Definition of %s has duplicate references of %s:%s\n",
4007 elem->name, cur->prefix, name);
4008 }
Owen Taylor3473f882001-02-23 17:55:21 +00004009 ret = 0;
4010 }
4011 break;
4012 }
4013 if (next->c1 == NULL) break;
4014 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
Daniel Veillard7b68df92003-08-03 22:58:54 +00004015 if ((xmlStrEqual(next->c1->name, name)) &&
4016 (xmlStrEqual(next->c1->prefix, cur->prefix))) {
4017 if (cur->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004018 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00004019 "Definition of %s has duplicate references to %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004020 elem->name, name, NULL);
Daniel Veillard7b68df92003-08-03 22:58:54 +00004021 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004022 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00004023 "Definition of %s has duplicate references to %s:%s\n",
4024 elem->name, cur->prefix, name);
4025 }
Owen Taylor3473f882001-02-23 17:55:21 +00004026 ret = 0;
4027 }
4028 next = next->c2;
4029 }
4030 }
4031 cur = cur->c2;
4032 }
4033 }
4034
4035 /* VC: Unique Element Type Declaration */
4036 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004037 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004038 ((tst->prefix == elem->prefix) ||
4039 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00004040 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004041 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
4042 "Redefinition of element %s\n",
4043 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004044 ret = 0;
4045 }
4046 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004047 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004048 ((tst->prefix == elem->prefix) ||
4049 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00004050 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004051 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
4052 "Redefinition of element %s\n",
4053 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004054 ret = 0;
4055 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00004056 /* One ID per Element Type
4057 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00004058 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
4059 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00004060 } */
Owen Taylor3473f882001-02-23 17:55:21 +00004061 return(ret);
4062}
4063
4064/**
4065 * xmlValidateOneAttribute:
4066 * @ctxt: the validation context
4067 * @doc: a document instance
4068 * @elem: an element instance
4069 * @attr: an attribute instance
4070 * @value: the attribute value (without entities processing)
4071 *
4072 * Try to validate a single attribute for an element
4073 * basically it does the following checks as described by the
4074 * XML-1.0 recommendation:
4075 * - [ VC: Attribute Value Type ]
4076 * - [ VC: Fixed Attribute Default ]
4077 * - [ VC: Entity Name ]
4078 * - [ VC: Name Token ]
4079 * - [ VC: ID ]
4080 * - [ VC: IDREF ]
4081 * - [ VC: Entity Name ]
4082 * - [ VC: Notation Attributes ]
4083 *
4084 * The ID/IDREF uniqueness and matching are done separately
4085 *
4086 * returns 1 if valid or 0 otherwise
4087 */
4088
4089int
4090xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
Daniel Veillard07cb8222003-09-10 10:51:05 +00004091 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value)
4092{
Owen Taylor3473f882001-02-23 17:55:21 +00004093 xmlAttributePtr attrDecl = NULL;
4094 int val;
4095 int ret = 1;
4096
4097 CHECK_DTD;
4098 if ((elem == NULL) || (elem->name == NULL)) return(0);
4099 if ((attr == NULL) || (attr->name == NULL)) return(0);
4100
4101 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004102 xmlChar fn[50];
4103 xmlChar *fullname;
4104
4105 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
4106 if (fullname == NULL)
4107 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004108 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004109 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00004110 attr->name, attr->ns->prefix);
4111 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004112 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00004113 attr->name, attr->ns->prefix);
4114 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004115 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004116 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4117 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00004118 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004119 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004120 if ((fullname != fn) && (fullname != elem->name))
4121 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00004122 }
4123 if (attrDecl == NULL) {
4124 if (attr->ns != NULL) {
4125 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4126 attr->name, attr->ns->prefix);
4127 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4128 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4129 attr->name, attr->ns->prefix);
4130 } else {
4131 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4132 elem->name, attr->name);
4133 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4134 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4135 elem->name, attr->name);
4136 }
4137 }
4138
4139
4140 /* Validity Constraint: Attribute Value Type */
4141 if (attrDecl == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004142 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004143 "No declaration for attribute %s of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004144 attr->name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004145 return(0);
4146 }
4147 attr->atype = attrDecl->atype;
4148
4149 val = xmlValidateAttributeValue(attrDecl->atype, value);
4150 if (val == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004151 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004152 "Syntax of value for attribute %s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004153 attr->name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004154 ret = 0;
4155 }
4156
4157 /* Validity constraint: Fixed Attribute Default */
4158 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4159 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004160 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004161 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004162 attr->name, elem->name, attrDecl->defaultValue);
4163 ret = 0;
4164 }
4165 }
4166
4167 /* Validity Constraint: ID uniqueness */
4168 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4169 if (xmlAddID(ctxt, doc, value, attr) == NULL)
4170 ret = 0;
4171 }
4172
4173 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4174 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4175 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
4176 ret = 0;
4177 }
4178
4179 /* Validity Constraint: Notation Attributes */
4180 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4181 xmlEnumerationPtr tree = attrDecl->tree;
4182 xmlNotationPtr nota;
4183
4184 /* First check that the given NOTATION was declared */
4185 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4186 if (nota == NULL)
4187 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4188
4189 if (nota == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004190 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004191 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004192 value, attr->name, elem->name);
4193 ret = 0;
4194 }
4195
4196 /* Second, verify that it's among the list */
4197 while (tree != NULL) {
4198 if (xmlStrEqual(tree->name, value)) break;
4199 tree = tree->next;
4200 }
4201 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004202 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004203"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004204 value, attr->name, elem->name);
4205 ret = 0;
4206 }
4207 }
4208
4209 /* Validity Constraint: Enumeration */
4210 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4211 xmlEnumerationPtr tree = attrDecl->tree;
4212 while (tree != NULL) {
4213 if (xmlStrEqual(tree->name, value)) break;
4214 tree = tree->next;
4215 }
4216 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004217 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004218 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004219 value, attr->name, elem->name);
4220 ret = 0;
4221 }
4222 }
4223
4224 /* Fixed Attribute Default */
4225 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4226 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004227 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004228 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004229 attr->name, elem->name, attrDecl->defaultValue);
4230 ret = 0;
4231 }
4232
4233 /* Extra check for the attribute value */
4234 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
4235 attrDecl->atype, value);
4236
4237 return(ret);
4238}
4239
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004240/**
4241 * xmlValidateOneNamespace:
4242 * @ctxt: the validation context
4243 * @doc: a document instance
4244 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00004245 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004246 * @ns: an namespace declaration instance
4247 * @value: the attribute value (without entities processing)
4248 *
4249 * Try to validate a single namespace declaration for an element
4250 * basically it does the following checks as described by the
4251 * XML-1.0 recommendation:
4252 * - [ VC: Attribute Value Type ]
4253 * - [ VC: Fixed Attribute Default ]
4254 * - [ VC: Entity Name ]
4255 * - [ VC: Name Token ]
4256 * - [ VC: ID ]
4257 * - [ VC: IDREF ]
4258 * - [ VC: Entity Name ]
4259 * - [ VC: Notation Attributes ]
4260 *
4261 * The ID/IDREF uniqueness and matching are done separately
4262 *
4263 * returns 1 if valid or 0 otherwise
4264 */
4265
4266int
4267xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4268xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
4269 /* xmlElementPtr elemDecl; */
4270 xmlAttributePtr attrDecl = NULL;
4271 int val;
4272 int ret = 1;
4273
4274 CHECK_DTD;
4275 if ((elem == NULL) || (elem->name == NULL)) return(0);
4276 if ((ns == NULL) || (ns->href == NULL)) return(0);
4277
4278 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004279 xmlChar fn[50];
4280 xmlChar *fullname;
4281
4282 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
4283 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00004284 xmlVErrMemory(ctxt, "Validating namespace");
Daniel Veillardc00cda82003-04-07 10:22:39 +00004285 return(0);
4286 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004287 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004288 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004289 ns->prefix, BAD_CAST "xmlns");
4290 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004291 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004292 ns->prefix, BAD_CAST "xmlns");
4293 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004294 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004295 BAD_CAST "xmlns");
4296 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004297 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004298 BAD_CAST "xmlns");
4299 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004300 if ((fullname != fn) && (fullname != elem->name))
4301 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004302 }
4303 if (attrDecl == NULL) {
4304 if (ns->prefix != NULL) {
4305 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4306 ns->prefix, BAD_CAST "xmlns");
4307 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4308 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4309 ns->prefix, BAD_CAST "xmlns");
4310 } else {
4311 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4312 elem->name, BAD_CAST "xmlns");
4313 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4314 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4315 elem->name, BAD_CAST "xmlns");
4316 }
4317 }
4318
4319
4320 /* Validity Constraint: Attribute Value Type */
4321 if (attrDecl == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004322 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004323 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004324 "No declaration for attribute xmlns:%s of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004325 ns->prefix, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004326 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004327 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004328 "No declaration for attribute xmlns of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004329 elem->name, NULL, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004330 }
4331 return(0);
4332 }
4333
4334 val = xmlValidateAttributeValue(attrDecl->atype, value);
4335 if (val == 0) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004336 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004337 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004338 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004339 ns->prefix, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004340 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004341 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004342 "Syntax of value for attribute xmlns of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004343 elem->name, NULL, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004344 }
4345 ret = 0;
4346 }
4347
4348 /* Validity constraint: Fixed Attribute Default */
4349 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4350 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004351 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004352 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004353 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4354 ns->prefix, elem->name, attrDecl->defaultValue);
4355 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004356 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004357 "Value for attribute xmlns of %s is different from default \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004358 elem->name, attrDecl->defaultValue, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004359 }
4360 ret = 0;
4361 }
4362 }
4363
4364 /* Validity Constraint: ID uniqueness */
4365 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4366 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4367 ret = 0;
4368 }
4369
4370 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4371 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4372 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4373 ret = 0;
4374 }
4375
4376 /* Validity Constraint: Notation Attributes */
4377 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4378 xmlEnumerationPtr tree = attrDecl->tree;
4379 xmlNotationPtr nota;
4380
4381 /* First check that the given NOTATION was declared */
4382 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4383 if (nota == NULL)
4384 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4385
4386 if (nota == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004387 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004388 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004389 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4390 value, ns->prefix, elem->name);
4391 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004392 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004393 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004394 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004395 }
4396 ret = 0;
4397 }
4398
4399 /* Second, verify that it's among the list */
4400 while (tree != NULL) {
4401 if (xmlStrEqual(tree->name, value)) break;
4402 tree = tree->next;
4403 }
4404 if (tree == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004405 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004406 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004407"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4408 value, ns->prefix, elem->name);
4409 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004410 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004411"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004412 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004413 }
4414 ret = 0;
4415 }
4416 }
4417
4418 /* Validity Constraint: Enumeration */
4419 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4420 xmlEnumerationPtr tree = attrDecl->tree;
4421 while (tree != NULL) {
4422 if (xmlStrEqual(tree->name, value)) break;
4423 tree = tree->next;
4424 }
4425 if (tree == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004426 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004427 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004428"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4429 value, ns->prefix, elem->name);
4430 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004431 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004432"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004433 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004434 }
4435 ret = 0;
4436 }
4437 }
4438
4439 /* Fixed Attribute Default */
4440 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4441 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004442 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004443 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004444 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4445 ns->prefix, elem->name, attrDecl->defaultValue);
4446 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004447 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004448 "Value for attribute xmlns of %s must be \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004449 elem->name, attrDecl->defaultValue, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004450 }
4451 ret = 0;
4452 }
4453
4454 /* Extra check for the attribute value */
4455 if (ns->prefix != NULL) {
4456 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4457 attrDecl->atype, value);
4458 } else {
4459 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4460 attrDecl->atype, value);
4461 }
4462
4463 return(ret);
4464}
4465
Daniel Veillard118aed72002-09-24 14:13:13 +00004466#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004467/**
4468 * xmlValidateSkipIgnorable:
4469 * @ctxt: the validation context
4470 * @child: the child list
4471 *
4472 * Skip ignorable elements w.r.t. the validation process
4473 *
4474 * returns the first element to consider for validation of the content model
4475 */
4476
4477static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004478xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004479 while (child != NULL) {
4480 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004481 /* These things are ignored (skipped) during validation. */
4482 case XML_PI_NODE:
4483 case XML_COMMENT_NODE:
4484 case XML_XINCLUDE_START:
4485 case XML_XINCLUDE_END:
4486 child = child->next;
4487 break;
4488 case XML_TEXT_NODE:
4489 if (xmlIsBlankNode(child))
4490 child = child->next;
4491 else
4492 return(child);
4493 break;
4494 /* keep current node */
4495 default:
4496 return(child);
4497 }
4498 }
4499 return(child);
4500}
4501
4502/**
4503 * xmlValidateElementType:
4504 * @ctxt: the validation context
4505 *
4506 * Try to validate the content model of an element internal function
4507 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004508 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4509 * reference is found and -3 if the validation succeeded but
4510 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004511 */
4512
4513static int
4514xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004515 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004516 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004517
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004518 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004519 if ((NODE == NULL) && (CONT == NULL))
4520 return(1);
4521 if ((NODE == NULL) &&
4522 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4523 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4524 return(1);
4525 }
4526 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004527 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004528 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004529
4530 /*
4531 * We arrive here when more states need to be examined
4532 */
4533cont:
4534
4535 /*
4536 * We just recovered from a rollback generated by a possible
4537 * epsilon transition, go directly to the analysis phase
4538 */
4539 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004540 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004541 DEBUG_VALID_STATE(NODE, CONT)
4542 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004543 goto analyze;
4544 }
4545
4546 DEBUG_VALID_STATE(NODE, CONT)
4547 /*
4548 * we may have to save a backup state here. This is the equivalent
4549 * of handling epsilon transition in NFAs.
4550 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004551 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004552 ((CONT->parent == NULL) ||
4553 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004554 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004555 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004556 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004557 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004558 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4559 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004560 }
4561
4562
4563 /*
4564 * Check first if the content matches
4565 */
4566 switch (CONT->type) {
4567 case XML_ELEMENT_CONTENT_PCDATA:
4568 if (NODE == NULL) {
4569 DEBUG_VALID_MSG("pcdata failed no node");
4570 ret = 0;
4571 break;
4572 }
4573 if (NODE->type == XML_TEXT_NODE) {
4574 DEBUG_VALID_MSG("pcdata found, skip to next");
4575 /*
4576 * go to next element in the content model
4577 * skipping ignorable elems
4578 */
4579 do {
4580 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004581 NODE = xmlValidateSkipIgnorable(NODE);
4582 if ((NODE != NULL) &&
4583 (NODE->type == XML_ENTITY_REF_NODE))
4584 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004585 } while ((NODE != NULL) &&
4586 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004587 (NODE->type != XML_TEXT_NODE) &&
4588 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004589 ret = 1;
4590 break;
4591 } else {
4592 DEBUG_VALID_MSG("pcdata failed");
4593 ret = 0;
4594 break;
4595 }
4596 break;
4597 case XML_ELEMENT_CONTENT_ELEMENT:
4598 if (NODE == NULL) {
4599 DEBUG_VALID_MSG("element failed no node");
4600 ret = 0;
4601 break;
4602 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004603 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4604 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004605 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004606 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4607 ret = (CONT->prefix == NULL);
4608 } else if (CONT->prefix == NULL) {
4609 ret = 0;
4610 } else {
4611 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4612 }
4613 }
4614 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004615 DEBUG_VALID_MSG("element found, skip to next");
4616 /*
4617 * go to next element in the content model
4618 * skipping ignorable elems
4619 */
4620 do {
4621 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004622 NODE = xmlValidateSkipIgnorable(NODE);
4623 if ((NODE != NULL) &&
4624 (NODE->type == XML_ENTITY_REF_NODE))
4625 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004626 } while ((NODE != NULL) &&
4627 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004628 (NODE->type != XML_TEXT_NODE) &&
4629 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004630 } else {
4631 DEBUG_VALID_MSG("element failed");
4632 ret = 0;
4633 break;
4634 }
4635 break;
4636 case XML_ELEMENT_CONTENT_OR:
4637 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004638 * Small optimization.
4639 */
4640 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4641 if ((NODE == NULL) ||
4642 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4643 DEPTH++;
4644 CONT = CONT->c2;
4645 goto cont;
4646 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004647 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4648 ret = (CONT->c1->prefix == NULL);
4649 } else if (CONT->c1->prefix == NULL) {
4650 ret = 0;
4651 } else {
4652 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4653 }
4654 if (ret == 0) {
4655 DEPTH++;
4656 CONT = CONT->c2;
4657 goto cont;
4658 }
Daniel Veillard85349052001-04-20 13:48:21 +00004659 }
4660
4661 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004662 * save the second branch 'or' branch
4663 */
4664 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004665 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4666 OCCURS, ROLLBACK_OR) < 0)
4667 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004668 DEPTH++;
4669 CONT = CONT->c1;
4670 goto cont;
4671 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004672 /*
4673 * Small optimization.
4674 */
4675 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4676 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4677 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4678 if ((NODE == NULL) ||
4679 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4680 DEPTH++;
4681 CONT = CONT->c2;
4682 goto cont;
4683 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004684 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4685 ret = (CONT->c1->prefix == NULL);
4686 } else if (CONT->c1->prefix == NULL) {
4687 ret = 0;
4688 } else {
4689 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4690 }
4691 if (ret == 0) {
4692 DEPTH++;
4693 CONT = CONT->c2;
4694 goto cont;
4695 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004696 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004697 DEPTH++;
4698 CONT = CONT->c1;
4699 goto cont;
4700 }
4701
4702 /*
4703 * At this point handle going up in the tree
4704 */
4705 if (ret == -1) {
4706 DEBUG_VALID_MSG("error found returning");
4707 return(ret);
4708 }
4709analyze:
4710 while (CONT != NULL) {
4711 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004712 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004713 * this level.
4714 */
4715 if (ret == 0) {
4716 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004717 xmlNodePtr cur;
4718
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004719 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004720 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004721 DEBUG_VALID_MSG("Once branch failed, rollback");
4722 if (vstateVPop(ctxt) < 0 ) {
4723 DEBUG_VALID_MSG("exhaustion, failed");
4724 return(0);
4725 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004726 if (cur != ctxt->vstate->node)
4727 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004728 goto cont;
4729 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004730 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004731 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004732 DEBUG_VALID_MSG("Plus branch failed, rollback");
4733 if (vstateVPop(ctxt) < 0 ) {
4734 DEBUG_VALID_MSG("exhaustion, failed");
4735 return(0);
4736 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004737 if (cur != ctxt->vstate->node)
4738 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004739 goto cont;
4740 }
4741 DEBUG_VALID_MSG("Plus branch found");
4742 ret = 1;
4743 break;
4744 case XML_ELEMENT_CONTENT_MULT:
4745#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004746 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004747 DEBUG_VALID_MSG("Mult branch failed");
4748 } else {
4749 DEBUG_VALID_MSG("Mult branch found");
4750 }
4751#endif
4752 ret = 1;
4753 break;
4754 case XML_ELEMENT_CONTENT_OPT:
4755 DEBUG_VALID_MSG("Option branch failed");
4756 ret = 1;
4757 break;
4758 }
4759 } else {
4760 switch (CONT->ocur) {
4761 case XML_ELEMENT_CONTENT_OPT:
4762 DEBUG_VALID_MSG("Option branch succeeded");
4763 ret = 1;
4764 break;
4765 case XML_ELEMENT_CONTENT_ONCE:
4766 DEBUG_VALID_MSG("Once branch succeeded");
4767 ret = 1;
4768 break;
4769 case XML_ELEMENT_CONTENT_PLUS:
4770 if (STATE == ROLLBACK_PARENT) {
4771 DEBUG_VALID_MSG("Plus branch rollback");
4772 ret = 1;
4773 break;
4774 }
4775 if (NODE == NULL) {
4776 DEBUG_VALID_MSG("Plus branch exhausted");
4777 ret = 1;
4778 break;
4779 }
4780 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004781 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004782 goto cont;
4783 case XML_ELEMENT_CONTENT_MULT:
4784 if (STATE == ROLLBACK_PARENT) {
4785 DEBUG_VALID_MSG("Mult branch rollback");
4786 ret = 1;
4787 break;
4788 }
4789 if (NODE == NULL) {
4790 DEBUG_VALID_MSG("Mult branch exhausted");
4791 ret = 1;
4792 break;
4793 }
4794 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004795 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004796 goto cont;
4797 }
4798 }
4799 STATE = 0;
4800
4801 /*
4802 * Then act accordingly at the parent level
4803 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004804 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004805 if (CONT->parent == NULL)
4806 break;
4807
4808 switch (CONT->parent->type) {
4809 case XML_ELEMENT_CONTENT_PCDATA:
4810 DEBUG_VALID_MSG("Error: parent pcdata");
4811 return(-1);
4812 case XML_ELEMENT_CONTENT_ELEMENT:
4813 DEBUG_VALID_MSG("Error: parent element");
4814 return(-1);
4815 case XML_ELEMENT_CONTENT_OR:
4816 if (ret == 1) {
4817 DEBUG_VALID_MSG("Or succeeded");
4818 CONT = CONT->parent;
4819 DEPTH--;
4820 } else {
4821 DEBUG_VALID_MSG("Or failed");
4822 CONT = CONT->parent;
4823 DEPTH--;
4824 }
4825 break;
4826 case XML_ELEMENT_CONTENT_SEQ:
4827 if (ret == 0) {
4828 DEBUG_VALID_MSG("Sequence failed");
4829 CONT = CONT->parent;
4830 DEPTH--;
4831 } else if (CONT == CONT->parent->c1) {
4832 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4833 CONT = CONT->parent->c2;
4834 goto cont;
4835 } else {
4836 DEBUG_VALID_MSG("Sequence succeeded");
4837 CONT = CONT->parent;
4838 DEPTH--;
4839 }
4840 }
4841 }
4842 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004843 xmlNodePtr cur;
4844
4845 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004846 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4847 if (vstateVPop(ctxt) < 0 ) {
4848 DEBUG_VALID_MSG("exhaustion, failed");
4849 return(0);
4850 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004851 if (cur != ctxt->vstate->node)
4852 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004853 goto cont;
4854 }
4855 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004856 xmlNodePtr cur;
4857
4858 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004859 DEBUG_VALID_MSG("Failure, rollback");
4860 if (vstateVPop(ctxt) < 0 ) {
4861 DEBUG_VALID_MSG("exhaustion, failed");
4862 return(0);
4863 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004864 if (cur != ctxt->vstate->node)
4865 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004866 goto cont;
4867 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004868 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004869}
Daniel Veillard23e73572002-09-19 19:56:43 +00004870#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004871
4872/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004873 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004874 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004875 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004876 * @content: An element
4877 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4878 *
4879 * This will dump the list of elements to the buffer
4880 * Intended just for the debug routine
4881 */
4882static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004883xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004884 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004885 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004886
4887 if (node == NULL) return;
4888 if (glob) strcat(buf, "(");
4889 cur = node;
4890 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004891 len = strlen(buf);
4892 if (size - len < 50) {
4893 if ((size - len > 4) && (buf[len - 1] != '.'))
4894 strcat(buf, " ...");
4895 return;
4896 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004897 switch (cur->type) {
4898 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004899 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004900 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004901 if ((size - len > 4) && (buf[len - 1] != '.'))
4902 strcat(buf, " ...");
4903 return;
4904 }
4905 strcat(buf, (char *) cur->ns->prefix);
4906 strcat(buf, ":");
4907 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004908 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004909 if ((size - len > 4) && (buf[len - 1] != '.'))
4910 strcat(buf, " ...");
4911 return;
4912 }
4913 strcat(buf, (char *) cur->name);
4914 if (cur->next != NULL)
4915 strcat(buf, " ");
4916 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004917 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004918 if (xmlIsBlankNode(cur))
4919 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004920 case XML_CDATA_SECTION_NODE:
4921 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004922 strcat(buf, "CDATA");
4923 if (cur->next != NULL)
4924 strcat(buf, " ");
4925 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004926 case XML_ATTRIBUTE_NODE:
4927 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004928#ifdef LIBXML_DOCB_ENABLED
4929 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004930#endif
4931 case XML_HTML_DOCUMENT_NODE:
4932 case XML_DOCUMENT_TYPE_NODE:
4933 case XML_DOCUMENT_FRAG_NODE:
4934 case XML_NOTATION_NODE:
4935 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004936 strcat(buf, "???");
4937 if (cur->next != NULL)
4938 strcat(buf, " ");
4939 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004940 case XML_ENTITY_NODE:
4941 case XML_PI_NODE:
4942 case XML_DTD_NODE:
4943 case XML_COMMENT_NODE:
4944 case XML_ELEMENT_DECL:
4945 case XML_ATTRIBUTE_DECL:
4946 case XML_ENTITY_DECL:
4947 case XML_XINCLUDE_START:
4948 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004949 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004950 }
4951 cur = cur->next;
4952 }
4953 if (glob) strcat(buf, ")");
4954}
4955
4956/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004957 * xmlValidateElementContent:
4958 * @ctxt: the validation context
4959 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004960 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004961 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004962 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004963 *
4964 * Try to validate the content model of an element
4965 *
4966 * returns 1 if valid or 0 if not and -1 in case of error
4967 */
4968
4969static int
4970xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004971 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004972 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004973#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004974 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004975#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004976 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004977 xmlElementContentPtr cont;
4978 const xmlChar *name;
4979
4980 if (elemDecl == NULL)
4981 return(-1);
4982 cont = elemDecl->content;
4983 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004984
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004985#ifdef LIBXML_REGEXP_ENABLED
4986 /* Build the regexp associated to the content model */
4987 if (elemDecl->contModel == NULL)
4988 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4989 if (elemDecl->contModel == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004990 return(-1);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004991 } else {
4992 xmlRegExecCtxtPtr exec;
4993
Daniel Veillardec498e12003-02-05 11:01:50 +00004994 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
4995 return(-1);
4996 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004997 ctxt->nodeMax = 0;
4998 ctxt->nodeNr = 0;
4999 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005000 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
5001 if (exec != NULL) {
5002 cur = child;
5003 while (cur != NULL) {
5004 switch (cur->type) {
5005 case XML_ENTITY_REF_NODE:
5006 /*
5007 * Push the current node to be able to roll back
5008 * and process within the entity
5009 */
5010 if ((cur->children != NULL) &&
5011 (cur->children->children != NULL)) {
5012 nodeVPush(ctxt, cur);
5013 cur = cur->children->children;
5014 continue;
5015 }
5016 break;
5017 case XML_TEXT_NODE:
5018 if (xmlIsBlankNode(cur))
5019 break;
5020 ret = 0;
5021 goto fail;
5022 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00005023 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005024 ret = 0;
5025 goto fail;
5026 case XML_ELEMENT_NODE:
5027 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005028 xmlChar fn[50];
5029 xmlChar *fullname;
5030
5031 fullname = xmlBuildQName(cur->name,
5032 cur->ns->prefix, fn, 50);
5033 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005034 ret = -1;
5035 goto fail;
5036 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005037 ret = xmlRegExecPushString(exec, fullname, NULL);
5038 if ((fullname != fn) && (fullname != cur->name))
5039 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005040 } else {
5041 ret = xmlRegExecPushString(exec, cur->name, NULL);
5042 }
5043 break;
5044 default:
5045 break;
5046 }
5047 /*
5048 * Switch to next element
5049 */
5050 cur = cur->next;
5051 while (cur == NULL) {
5052 cur = nodeVPop(ctxt);
5053 if (cur == NULL)
5054 break;
5055 cur = cur->next;
5056 }
5057 }
5058 ret = xmlRegExecPushString(exec, NULL, NULL);
5059fail:
5060 xmlRegFreeExecCtxt(exec);
5061 }
5062 }
5063#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005064 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005065 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005066 */
5067 ctxt->vstateMax = 8;
5068 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
5069 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
5070 if (ctxt->vstateTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00005071 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005072 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005073 }
5074 /*
5075 * The first entry in the stack is reserved to the current state
5076 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00005077 ctxt->nodeMax = 0;
5078 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00005079 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005080 ctxt->vstate = &ctxt->vstateTab[0];
5081 ctxt->vstateNr = 1;
5082 CONT = cont;
5083 NODE = child;
5084 DEPTH = 0;
5085 OCCURS = 0;
5086 STATE = 0;
5087 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005088 if ((ret == -3) && (warn)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005089 xmlErrValidWarning(ctxt, child, XML_DTD_CONTENT_NOT_DETERMINIST,
5090 "Content model for Element %s is ambiguous\n",
Daniel Veillard0cc72772003-10-13 14:00:21 +00005091 name, NULL, NULL);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005092 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005093 /*
5094 * An entities reference appeared at this level.
5095 * Buid a minimal representation of this node content
5096 * sufficient to run the validation process on it
5097 */
5098 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005099 cur = child;
5100 while (cur != NULL) {
5101 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005102 case XML_ENTITY_REF_NODE:
5103 /*
5104 * Push the current node to be able to roll back
5105 * and process within the entity
5106 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005107 if ((cur->children != NULL) &&
5108 (cur->children->children != NULL)) {
5109 nodeVPush(ctxt, cur);
5110 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005111 continue;
5112 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00005113 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005114 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005115 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005116 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005117 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00005118 case XML_CDATA_SECTION_NODE:
5119 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005120 case XML_ELEMENT_NODE:
5121 /*
5122 * Allocate a new node and minimally fills in
5123 * what's required
5124 */
5125 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
5126 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00005127 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005128 xmlFreeNodeList(repl);
5129 ret = -1;
5130 goto done;
5131 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005132 tmp->type = cur->type;
5133 tmp->name = cur->name;
5134 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005135 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00005136 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005137 if (repl == NULL)
5138 repl = last = tmp;
5139 else {
5140 last->next = tmp;
5141 last = tmp;
5142 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00005143 if (cur->type == XML_CDATA_SECTION_NODE) {
5144 /*
5145 * E59 spaces in CDATA does not match the
5146 * nonterminal S
5147 */
5148 tmp->content = xmlStrdup(BAD_CAST "CDATA");
5149 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005150 break;
5151 default:
5152 break;
5153 }
5154 /*
5155 * Switch to next element
5156 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005157 cur = cur->next;
5158 while (cur == NULL) {
5159 cur = nodeVPop(ctxt);
5160 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005161 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005162 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005163 }
5164 }
5165
5166 /*
5167 * Relaunch the validation
5168 */
5169 ctxt->vstate = &ctxt->vstateTab[0];
5170 ctxt->vstateNr = 1;
5171 CONT = cont;
5172 NODE = repl;
5173 DEPTH = 0;
5174 OCCURS = 0;
5175 STATE = 0;
5176 ret = xmlValidateElementType(ctxt);
5177 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005178#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005179 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005180 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
5181 char expr[5000];
5182 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005183
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005184 expr[0] = 0;
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005185 xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005186 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00005187#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005188 if (repl != NULL)
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005189 xmlSnprintfElements(&list[0], 5000, repl, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005190 else
Daniel Veillard01992e02002-10-09 10:20:30 +00005191#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005192 xmlSnprintfElements(&list[0], 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005193
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005194 if (name != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005195 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5196 "Element %s content does not follow the DTD, expecting %s, got %s\n",
5197 name, BAD_CAST expr, BAD_CAST list);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005198 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005199 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5200 "Element content does not follow the DTD, expecting %s, got %s\n",
5201 BAD_CAST expr, BAD_CAST list, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005202 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005203 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005204 if (name != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005205 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005206 "Element %s content does not follow the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005207 name, NULL, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005208 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005209 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5210 "Element content does not follow the DTD\n",
5211 NULL, NULL, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005212 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005213 }
5214 ret = 0;
5215 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005216 if (ret == -3)
5217 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005218
Daniel Veillard23e73572002-09-19 19:56:43 +00005219#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005220done:
5221 /*
5222 * Deallocate the copy if done, and free up the validation stack
5223 */
5224 while (repl != NULL) {
5225 tmp = repl->next;
5226 xmlFree(repl);
5227 repl = tmp;
5228 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005229 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005230 if (ctxt->vstateTab != NULL) {
5231 xmlFree(ctxt->vstateTab);
5232 ctxt->vstateTab = NULL;
5233 }
Daniel Veillard01992e02002-10-09 10:20:30 +00005234#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005235 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00005236 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005237 if (ctxt->nodeTab != NULL) {
5238 xmlFree(ctxt->nodeTab);
5239 ctxt->nodeTab = NULL;
5240 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005241 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005242
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005243}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005244
Owen Taylor3473f882001-02-23 17:55:21 +00005245/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005246 * xmlValidateCdataElement:
5247 * @ctxt: the validation context
5248 * @doc: a document instance
5249 * @elem: an element instance
5250 *
5251 * Check that an element follows #CDATA
5252 *
5253 * returns 1 if valid or 0 otherwise
5254 */
5255static int
5256xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5257 xmlNodePtr elem) {
5258 int ret = 1;
5259 xmlNodePtr cur, child;
5260
Daniel Veillardceb09b92002-10-04 11:46:37 +00005261 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005262 return(0);
5263
5264 child = elem->children;
5265
5266 cur = child;
5267 while (cur != NULL) {
5268 switch (cur->type) {
5269 case XML_ENTITY_REF_NODE:
5270 /*
5271 * Push the current node to be able to roll back
5272 * and process within the entity
5273 */
5274 if ((cur->children != NULL) &&
5275 (cur->children->children != NULL)) {
5276 nodeVPush(ctxt, cur);
5277 cur = cur->children->children;
5278 continue;
5279 }
5280 break;
5281 case XML_COMMENT_NODE:
5282 case XML_PI_NODE:
5283 case XML_TEXT_NODE:
5284 case XML_CDATA_SECTION_NODE:
5285 break;
5286 default:
5287 ret = 0;
5288 goto done;
5289 }
5290 /*
5291 * Switch to next element
5292 */
5293 cur = cur->next;
5294 while (cur == NULL) {
5295 cur = nodeVPop(ctxt);
5296 if (cur == NULL)
5297 break;
5298 cur = cur->next;
5299 }
5300 }
5301done:
5302 ctxt->nodeMax = 0;
5303 ctxt->nodeNr = 0;
5304 if (ctxt->nodeTab != NULL) {
5305 xmlFree(ctxt->nodeTab);
5306 ctxt->nodeTab = NULL;
5307 }
5308 return(ret);
5309}
5310
5311/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005312 * xmlValidateCheckMixed:
5313 * @ctxt: the validation context
5314 * @cont: the mixed content model
5315 * @qname: the qualified name as appearing in the serialization
5316 *
5317 * Check if the given node is part of the content model.
5318 *
5319 * Returns 1 if yes, 0 if no, -1 in case of error
5320 */
5321static int
William M. Brackedb65a72004-02-06 07:36:04 +00005322xmlValidateCheckMixed(xmlValidCtxtPtr ctxt,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005323 xmlElementContentPtr cont, const xmlChar *qname) {
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005324 const xmlChar *name;
5325 int plen;
5326 name = xmlSplitQName3(qname, &plen);
5327
5328 if (name == NULL) {
5329 while (cont != NULL) {
5330 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5331 if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname)))
5332 return(1);
5333 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5334 (cont->c1 != NULL) &&
5335 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5336 if ((cont->c1->prefix == NULL) &&
5337 (xmlStrEqual(cont->c1->name, qname)))
5338 return(1);
5339 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5340 (cont->c1 == NULL) ||
5341 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005342 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5343 "Internal: MIXED struct corrupted\n",
5344 NULL);
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005345 break;
5346 }
5347 cont = cont->c2;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005348 }
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005349 } else {
5350 while (cont != NULL) {
5351 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5352 if ((cont->prefix != NULL) &&
5353 (xmlStrncmp(cont->prefix, qname, plen) == 0) &&
5354 (xmlStrEqual(cont->name, name)))
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 (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) &&
5361 (xmlStrEqual(cont->c1->name, name)))
5362 return(1);
5363 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5364 (cont->c1 == NULL) ||
5365 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005366 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
5367 "Internal: MIXED struct corrupted\n",
5368 NULL);
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005369 break;
5370 }
5371 cont = cont->c2;
5372 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005373 }
5374 return(0);
5375}
5376
5377/**
5378 * xmlValidGetElemDecl:
5379 * @ctxt: the validation context
5380 * @doc: a document instance
5381 * @elem: an element instance
5382 * @extsubset: pointer, (out) indicate if the declaration was found
5383 * in the external subset.
5384 *
5385 * Finds a declaration associated to an element in the document.
5386 *
5387 * returns the pointer to the declaration or NULL if not found.
5388 */
5389static xmlElementPtr
5390xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5391 xmlNodePtr elem, int *extsubset) {
5392 xmlElementPtr elemDecl = NULL;
5393 const xmlChar *prefix = NULL;
5394
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005395 if ((ctxt == NULL) || (doc == NULL) ||
5396 (elem == NULL) || (elem->name == NULL))
5397 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005398 if (extsubset != NULL)
5399 *extsubset = 0;
5400
5401 /*
5402 * Fetch the declaration for the qualified name
5403 */
5404 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5405 prefix = elem->ns->prefix;
5406
5407 if (prefix != NULL) {
5408 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5409 elem->name, prefix);
5410 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5411 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5412 elem->name, prefix);
5413 if ((elemDecl != NULL) && (extsubset != NULL))
5414 *extsubset = 1;
5415 }
5416 }
5417
5418 /*
5419 * Fetch the declaration for the non qualified name
5420 * This is "non-strict" validation should be done on the
5421 * full QName but in that case being flexible makes sense.
5422 */
5423 if (elemDecl == NULL) {
5424 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5425 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5426 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5427 if ((elemDecl != NULL) && (extsubset != NULL))
5428 *extsubset = 1;
5429 }
5430 }
5431 if (elemDecl == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005432 xmlErrValidNode(ctxt, elem,
5433 XML_DTD_UNKNOWN_ELEM,
5434 "No declaration for element %s\n",
5435 elem->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005436 }
5437 return(elemDecl);
5438}
5439
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005440#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005441/**
5442 * xmlValidatePushElement:
5443 * @ctxt: the validation context
5444 * @doc: a document instance
5445 * @elem: an element instance
5446 * @qname: the qualified name as appearing in the serialization
5447 *
5448 * Push a new element start on the validation stack.
5449 *
5450 * returns 1 if no validation problem was found or 0 otherwise
5451 */
5452int
5453xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5454 xmlNodePtr elem, const xmlChar *qname) {
5455 int ret = 1;
5456 xmlElementPtr eDecl;
5457 int extsubset = 0;
5458
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005459 if (ctxt == NULL)
5460 return(0);
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005461/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005462 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5463 xmlValidStatePtr state = ctxt->vstate;
5464 xmlElementPtr elemDecl;
5465
5466 /*
5467 * Check the new element agaisnt the content model of the new elem.
5468 */
5469 if (state->elemDecl != NULL) {
5470 elemDecl = state->elemDecl;
5471
5472 switch(elemDecl->etype) {
5473 case XML_ELEMENT_TYPE_UNDEFINED:
5474 ret = 0;
5475 break;
5476 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005477 xmlErrValidNode(ctxt, state->node,
5478 XML_DTD_NOT_EMPTY,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005479 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005480 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005481 ret = 0;
5482 break;
5483 case XML_ELEMENT_TYPE_ANY:
5484 /* I don't think anything is required then */
5485 break;
5486 case XML_ELEMENT_TYPE_MIXED:
5487 /* simple case of declared as #PCDATA */
5488 if ((elemDecl->content != NULL) &&
5489 (elemDecl->content->type ==
5490 XML_ELEMENT_CONTENT_PCDATA)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005491 xmlErrValidNode(ctxt, state->node,
5492 XML_DTD_NOT_PCDATA,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005493 "Element %s was declared #PCDATA but contains non text nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005494 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005495 ret = 0;
5496 } else {
5497 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5498 qname);
5499 if (ret != 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005500 xmlErrValidNode(ctxt, state->node,
5501 XML_DTD_INVALID_CHILD,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005502 "Element %s is not declared in %s list of possible children\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005503 qname, state->node->name, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005504 }
5505 }
5506 break;
5507 case XML_ELEMENT_TYPE_ELEMENT:
5508 /*
5509 * TODO:
5510 * VC: Standalone Document Declaration
5511 * - element types with element content, if white space
5512 * occurs directly within any instance of those types.
5513 */
5514 if (state->exec != NULL) {
5515 ret = xmlRegExecPushString(state->exec, qname, NULL);
5516 if (ret < 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005517 xmlErrValidNode(ctxt, state->node,
5518 XML_DTD_CONTENT_MODEL,
5519 "Element %s content does not follow the DTD, Misplaced %s\n",
5520 state->node->name, qname, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005521 ret = 0;
5522 } else {
5523 ret = 1;
5524 }
5525 }
5526 break;
5527 }
5528 }
5529 }
5530 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5531 vstateVPush(ctxt, eDecl, elem);
5532 return(ret);
5533}
5534
5535/**
5536 * xmlValidatePushCData:
5537 * @ctxt: the validation context
5538 * @data: some character data read
5539 * @len: the lenght of the data
5540 *
5541 * check the CData parsed for validation in the current stack
5542 *
5543 * returns 1 if no validation problem was found or 0 otherwise
5544 */
5545int
5546xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5547 int ret = 1;
5548
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005549/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005550 if (ctxt == NULL)
5551 return(0);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005552 if (len <= 0)
5553 return(ret);
5554 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5555 xmlValidStatePtr state = ctxt->vstate;
5556 xmlElementPtr elemDecl;
5557
5558 /*
5559 * Check the new element agaisnt the content model of the new elem.
5560 */
5561 if (state->elemDecl != NULL) {
5562 elemDecl = state->elemDecl;
5563
5564 switch(elemDecl->etype) {
5565 case XML_ELEMENT_TYPE_UNDEFINED:
5566 ret = 0;
5567 break;
5568 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005569 xmlErrValidNode(ctxt, state->node,
5570 XML_DTD_NOT_EMPTY,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005571 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005572 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005573 ret = 0;
5574 break;
5575 case XML_ELEMENT_TYPE_ANY:
5576 break;
5577 case XML_ELEMENT_TYPE_MIXED:
5578 break;
5579 case XML_ELEMENT_TYPE_ELEMENT:
5580 if (len > 0) {
5581 int i;
5582
5583 for (i = 0;i < len;i++) {
William M. Brack76e95df2003-10-18 16:20:14 +00005584 if (!IS_BLANK_CH(data[i])) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005585 xmlErrValidNode(ctxt, state->node,
5586 XML_DTD_CONTENT_MODEL,
5587 "Element %s content does not follow the DTD, Text not allowed\n",
5588 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005589 ret = 0;
5590 goto done;
5591 }
5592 }
5593 /*
5594 * TODO:
5595 * VC: Standalone Document Declaration
5596 * element types with element content, if white space
5597 * occurs directly within any instance of those types.
5598 */
5599 }
5600 break;
5601 }
5602 }
5603 }
5604done:
5605 return(ret);
5606}
5607
5608/**
5609 * xmlValidatePopElement:
5610 * @ctxt: the validation context
5611 * @doc: a document instance
5612 * @elem: an element instance
5613 * @qname: the qualified name as appearing in the serialization
5614 *
5615 * Pop the element end from the validation stack.
5616 *
5617 * returns 1 if no validation problem was found or 0 otherwise
5618 */
5619int
5620xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005621 xmlNodePtr elem ATTRIBUTE_UNUSED,
5622 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005623 int ret = 1;
5624
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005625 if (ctxt == NULL)
5626 return(0);
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005627/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005628 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5629 xmlValidStatePtr state = ctxt->vstate;
5630 xmlElementPtr elemDecl;
5631
5632 /*
5633 * Check the new element agaisnt the content model of the new elem.
5634 */
5635 if (state->elemDecl != NULL) {
5636 elemDecl = state->elemDecl;
5637
5638 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5639 if (state->exec != NULL) {
5640 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5641 if (ret == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005642 xmlErrValidNode(ctxt, state->node,
5643 XML_DTD_CONTENT_MODEL,
5644 "Element %s content does not follow the DTD, Expecting more child\n",
5645 state->node->name, NULL,NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005646 } else {
5647 /*
5648 * previous validation errors should not generate
5649 * a new one here
5650 */
5651 ret = 1;
5652 }
5653 }
5654 }
5655 }
5656 vstateVPop(ctxt);
5657 }
5658 return(ret);
5659}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005660#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005661
5662/**
Owen Taylor3473f882001-02-23 17:55:21 +00005663 * xmlValidateOneElement:
5664 * @ctxt: the validation context
5665 * @doc: a document instance
5666 * @elem: an element instance
5667 *
5668 * Try to validate a single element and it's attributes,
5669 * basically it does the following checks as described by the
5670 * XML-1.0 recommendation:
5671 * - [ VC: Element Valid ]
5672 * - [ VC: Required Attribute ]
5673 * Then call xmlValidateOneAttribute() for each attribute present.
5674 *
5675 * The ID/IDREF checkings are done separately
5676 *
5677 * returns 1 if valid or 0 otherwise
5678 */
5679
5680int
5681xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5682 xmlNodePtr elem) {
5683 xmlElementPtr elemDecl = NULL;
5684 xmlElementContentPtr cont;
5685 xmlAttributePtr attr;
5686 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005687 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005688 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005689 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005690
5691 CHECK_DTD;
5692
5693 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005694 switch (elem->type) {
5695 case XML_ATTRIBUTE_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005696 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5697 "Attribute element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005698 return(0);
5699 case XML_TEXT_NODE:
5700 if (elem->children != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005701 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5702 "Text element has children !\n",
5703 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005704 return(0);
5705 }
5706 if (elem->properties != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005707 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5708 "Text element has attribute !\n",
5709 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005710 return(0);
5711 }
5712 if (elem->ns != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005713 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5714 "Text element has namespace !\n",
5715 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005716 return(0);
5717 }
5718 if (elem->nsDef != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005719 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5720 "Text element has namespace !\n",
5721 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005722 return(0);
5723 }
5724 if (elem->content == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005725 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5726 "Text element has no content !\n",
5727 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005728 return(0);
5729 }
5730 return(1);
5731 case XML_XINCLUDE_START:
5732 case XML_XINCLUDE_END:
5733 return(1);
5734 case XML_CDATA_SECTION_NODE:
5735 case XML_ENTITY_REF_NODE:
5736 case XML_PI_NODE:
5737 case XML_COMMENT_NODE:
5738 return(1);
5739 case XML_ENTITY_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005740 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5741 "Entity element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005742 return(0);
5743 case XML_NOTATION_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005744 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5745 "Notation element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005746 return(0);
5747 case XML_DOCUMENT_NODE:
5748 case XML_DOCUMENT_TYPE_NODE:
5749 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005750 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5751 "Document element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005752 return(0);
5753 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005754 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5755 "HTML Document not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005756 return(0);
5757 case XML_ELEMENT_NODE:
5758 break;
5759 default:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005760 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5761 "unknown element type\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005762 return(0);
5763 }
Owen Taylor3473f882001-02-23 17:55:21 +00005764
5765 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005766 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005767 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005768 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5769 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005770 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005771
Daniel Veillardea7751d2002-12-20 00:16:24 +00005772 /*
5773 * If vstateNr is not zero that means continuous validation is
5774 * activated, do not try to check the content model at that level.
5775 */
5776 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005777 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005778 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005779 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005780 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ELEM,
5781 "No declaration for element %s\n",
5782 elem->name, NULL, NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005783 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005784 case XML_ELEMENT_TYPE_EMPTY:
5785 if (elem->children != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005786 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_EMPTY,
Owen Taylor3473f882001-02-23 17:55:21 +00005787 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005788 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005789 ret = 0;
5790 }
5791 break;
5792 case XML_ELEMENT_TYPE_ANY:
5793 /* I don't think anything is required then */
5794 break;
5795 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005796
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005797 /* simple case of declared as #PCDATA */
5798 if ((elemDecl->content != NULL) &&
5799 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5800 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5801 if (!ret) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005802 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_PCDATA,
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005803 "Element %s was declared #PCDATA but contains non text nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005804 elem->name, NULL, NULL);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005805 }
5806 break;
5807 }
Owen Taylor3473f882001-02-23 17:55:21 +00005808 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005809 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005810 while (child != NULL) {
5811 if (child->type == XML_ELEMENT_NODE) {
5812 name = child->name;
5813 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005814 xmlChar fn[50];
5815 xmlChar *fullname;
5816
5817 fullname = xmlBuildQName(child->name, child->ns->prefix,
5818 fn, 50);
5819 if (fullname == NULL)
5820 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005821 cont = elemDecl->content;
5822 while (cont != NULL) {
5823 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005824 if (xmlStrEqual(cont->name, fullname))
5825 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005826 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5827 (cont->c1 != NULL) &&
5828 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005829 if (xmlStrEqual(cont->c1->name, fullname))
5830 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005831 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5832 (cont->c1 == NULL) ||
5833 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005834 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5835 "Internal: MIXED struct corrupted\n",
5836 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005837 break;
5838 }
5839 cont = cont->c2;
5840 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005841 if ((fullname != fn) && (fullname != child->name))
5842 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005843 if (cont != NULL)
5844 goto child_ok;
5845 }
5846 cont = elemDecl->content;
5847 while (cont != NULL) {
5848 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5849 if (xmlStrEqual(cont->name, name)) break;
5850 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5851 (cont->c1 != NULL) &&
5852 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5853 if (xmlStrEqual(cont->c1->name, name)) break;
5854 } 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(ctxt, 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 }
5864 if (cont == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005865 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_CHILD,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005866 "Element %s is not declared in %s list of possible children\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005867 name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005868 ret = 0;
5869 }
5870 }
5871child_ok:
5872 child = child->next;
5873 }
5874 break;
5875 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005876 if ((doc->standalone == 1) && (extsubset == 1)) {
5877 /*
5878 * VC: Standalone Document Declaration
5879 * - element types with element content, if white space
5880 * occurs directly within any instance of those types.
5881 */
5882 child = elem->children;
5883 while (child != NULL) {
5884 if (child->type == XML_TEXT_NODE) {
5885 const xmlChar *content = child->content;
5886
William M. Brack76e95df2003-10-18 16:20:14 +00005887 while (IS_BLANK_CH(*content))
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005888 content++;
5889 if (*content == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005890 xmlErrValidNode(ctxt, elem,
5891 XML_DTD_STANDALONE_WHITE_SPACE,
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005892"standalone: %s declared in the external subset contains white spaces nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005893 elem->name, NULL, NULL);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005894 ret = 0;
5895 break;
5896 }
5897 }
5898 child =child->next;
5899 }
5900 }
Owen Taylor3473f882001-02-23 17:55:21 +00005901 child = elem->children;
5902 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005903 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005904 if (tmp <= 0)
5905 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005906 break;
5907 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005908 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005909
5910 /* [ VC: Required Attribute ] */
5911 attr = elemDecl->attributes;
5912 while (attr != NULL) {
5913 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005914 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005915
Daniel Veillarde4301c82002-02-13 13:32:35 +00005916 if ((attr->prefix == NULL) &&
5917 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5918 xmlNsPtr ns;
5919
5920 ns = elem->nsDef;
5921 while (ns != NULL) {
5922 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005923 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005924 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005925 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005926 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5927 xmlNsPtr ns;
5928
5929 ns = elem->nsDef;
5930 while (ns != NULL) {
5931 if (xmlStrEqual(attr->name, ns->prefix))
5932 goto found;
5933 ns = ns->next;
5934 }
5935 } else {
5936 xmlAttrPtr attrib;
5937
5938 attrib = elem->properties;
5939 while (attrib != NULL) {
5940 if (xmlStrEqual(attrib->name, attr->name)) {
5941 if (attr->prefix != NULL) {
5942 xmlNsPtr nameSpace = attrib->ns;
5943
5944 if (nameSpace == NULL)
5945 nameSpace = elem->ns;
5946 /*
5947 * qualified names handling is problematic, having a
5948 * different prefix should be possible but DTDs don't
5949 * allow to define the URI instead of the prefix :-(
5950 */
5951 if (nameSpace == NULL) {
5952 if (qualified < 0)
5953 qualified = 0;
5954 } else if (!xmlStrEqual(nameSpace->prefix,
5955 attr->prefix)) {
5956 if (qualified < 1)
5957 qualified = 1;
5958 } else
5959 goto found;
5960 } else {
5961 /*
5962 * We should allow applications to define namespaces
5963 * for their application even if the DTD doesn't
5964 * carry one, otherwise, basically we would always
5965 * break.
5966 */
5967 goto found;
5968 }
5969 }
5970 attrib = attrib->next;
5971 }
Owen Taylor3473f882001-02-23 17:55:21 +00005972 }
5973 if (qualified == -1) {
5974 if (attr->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005975 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005976 "Element %s does not carry attribute %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005977 elem->name, attr->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005978 ret = 0;
5979 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005980 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005981 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005982 elem->name, attr->prefix,attr->name);
5983 ret = 0;
5984 }
5985 } else if (qualified == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005986 xmlErrValidWarning(ctxt, elem, XML_DTD_NO_PREFIX,
Owen Taylor3473f882001-02-23 17:55:21 +00005987 "Element %s required attribute %s:%s has no prefix\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005988 elem->name, attr->prefix, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005989 } else if (qualified == 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005990 xmlErrValidWarning(ctxt, elem, XML_DTD_DIFFERENT_PREFIX,
Owen Taylor3473f882001-02-23 17:55:21 +00005991 "Element %s required attribute %s:%s has different prefix\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005992 elem->name, attr->prefix, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005993 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005994 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5995 /*
5996 * Special tests checking #FIXED namespace declarations
5997 * have the right value since this is not done as an
5998 * attribute checking
5999 */
6000 if ((attr->prefix == NULL) &&
6001 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
6002 xmlNsPtr ns;
6003
6004 ns = elem->nsDef;
6005 while (ns != NULL) {
6006 if (ns->prefix == NULL) {
6007 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006008 xmlErrValidNode(ctxt, elem,
6009 XML_DTD_ELEM_DEFAULT_NAMESPACE,
Daniel Veillarde4301c82002-02-13 13:32:35 +00006010 "Element %s namespace name for default namespace does not match the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006011 elem->name, NULL, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00006012 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00006013 }
6014 goto found;
6015 }
6016 ns = ns->next;
6017 }
6018 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
6019 xmlNsPtr ns;
6020
6021 ns = elem->nsDef;
6022 while (ns != NULL) {
6023 if (xmlStrEqual(attr->name, ns->prefix)) {
6024 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006025 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006026 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006027 elem->name, ns->prefix, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00006028 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00006029 }
6030 goto found;
6031 }
6032 ns = ns->next;
6033 }
6034 }
Owen Taylor3473f882001-02-23 17:55:21 +00006035 }
6036found:
6037 attr = attr->nexth;
6038 }
6039 return(ret);
6040}
6041
6042/**
6043 * xmlValidateRoot:
6044 * @ctxt: the validation context
6045 * @doc: a document instance
6046 *
6047 * Try to validate a the root element
6048 * basically it does the following check as described by the
6049 * XML-1.0 recommendation:
6050 * - [ VC: Root Element Type ]
6051 * it doesn't try to recurse or apply other check to the element
6052 *
6053 * returns 1 if valid or 0 otherwise
6054 */
6055
6056int
6057xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6058 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00006059 int ret;
6060
Owen Taylor3473f882001-02-23 17:55:21 +00006061 if (doc == NULL) return(0);
6062
6063 root = xmlDocGetRootElement(doc);
6064 if ((root == NULL) || (root->name == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006065 xmlErrValid(ctxt, XML_DTD_NO_ROOT,
6066 "no root element\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006067 return(0);
6068 }
6069
6070 /*
6071 * When doing post validation against a separate DTD, those may
6072 * no internal subset has been generated
6073 */
6074 if ((doc->intSubset != NULL) &&
6075 (doc->intSubset->name != NULL)) {
6076 /*
6077 * Check first the document root against the NQName
6078 */
6079 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
6080 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00006081 xmlChar fn[50];
6082 xmlChar *fullname;
6083
6084 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
6085 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00006086 xmlVErrMemory(ctxt, NULL);
Daniel Veillardc00cda82003-04-07 10:22:39 +00006087 return(0);
6088 }
6089 ret = xmlStrEqual(doc->intSubset->name, fullname);
6090 if ((fullname != fn) && (fullname != root->name))
6091 xmlFree(fullname);
6092 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00006093 goto name_ok;
6094 }
6095 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
6096 (xmlStrEqual(root->name, BAD_CAST "html")))
6097 goto name_ok;
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006098 xmlErrValidNode(ctxt, root, XML_DTD_ROOT_NAME,
6099 "root and DTD name do not match '%s' and '%s'\n",
6100 root->name, doc->intSubset->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006101 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00006102 }
6103 }
6104name_ok:
6105 return(1);
6106}
6107
6108
6109/**
6110 * xmlValidateElement:
6111 * @ctxt: the validation context
6112 * @doc: a document instance
6113 * @elem: an element instance
6114 *
6115 * Try to validate the subtree under an element
6116 *
6117 * returns 1 if valid or 0 otherwise
6118 */
6119
6120int
6121xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
6122 xmlNodePtr child;
6123 xmlAttrPtr attr;
Daniel Veillarde133dd82003-10-30 10:42:20 +00006124 xmlNsPtr ns;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006125 const xmlChar *value;
Owen Taylor3473f882001-02-23 17:55:21 +00006126 int ret = 1;
6127
6128 if (elem == NULL) return(0);
6129
6130 /*
6131 * XInclude elements were added after parsing in the infoset,
6132 * they don't really mean anything validation wise.
6133 */
6134 if ((elem->type == XML_XINCLUDE_START) ||
6135 (elem->type == XML_XINCLUDE_END))
6136 return(1);
6137
6138 CHECK_DTD;
6139
Daniel Veillard10ea86c2001-06-20 13:55:33 +00006140 /*
6141 * Entities references have to be handled separately
6142 */
6143 if (elem->type == XML_ENTITY_REF_NODE) {
6144 return(1);
6145 }
6146
Owen Taylor3473f882001-02-23 17:55:21 +00006147 ret &= xmlValidateOneElement(ctxt, doc, elem);
6148 attr = elem->properties;
Daniel Veillarde133dd82003-10-30 10:42:20 +00006149 while (attr != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00006150 value = xmlNodeListGetString(doc, attr->children, 0);
6151 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
6152 if (value != NULL)
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006153 xmlFree((char *)value);
Owen Taylor3473f882001-02-23 17:55:21 +00006154 attr= attr->next;
6155 }
Daniel Veillarde133dd82003-10-30 10:42:20 +00006156 ns = elem->nsDef;
6157 while (ns != NULL) {
Daniel Veillard1f5c9892003-12-29 17:09:55 +00006158 if (elem->ns == NULL)
6159 ret &= xmlValidateOneNamespace(ctxt, doc, elem, NULL,
6160 ns, ns->href);
6161 else
6162 ret &= xmlValidateOneNamespace(ctxt, doc, elem, elem->ns->prefix,
6163 ns, ns->href);
Daniel Veillarde133dd82003-10-30 10:42:20 +00006164 ns = ns->next;
6165 }
Owen Taylor3473f882001-02-23 17:55:21 +00006166 child = elem->children;
6167 while (child != NULL) {
6168 ret &= xmlValidateElement(ctxt, doc, child);
6169 child = child->next;
6170 }
6171
6172 return(ret);
6173}
6174
Daniel Veillard8730c562001-02-26 10:49:57 +00006175/**
6176 * xmlValidateRef:
6177 * @ref: A reference to be validated
6178 * @ctxt: Validation context
6179 * @name: Name of ID we are searching for
6180 *
6181 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006182static void
Daniel Veillard8730c562001-02-26 10:49:57 +00006183xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00006184 const xmlChar *name) {
6185 xmlAttrPtr id;
6186 xmlAttrPtr attr;
6187
6188 if (ref == NULL)
6189 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006190 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00006191 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006192 attr = ref->attr;
6193 if (attr == NULL) {
6194 xmlChar *dup, *str = NULL, *cur, save;
6195
6196 dup = xmlStrdup(name);
6197 if (dup == NULL) {
6198 ctxt->valid = 0;
6199 return;
6200 }
6201 cur = dup;
6202 while (*cur != 0) {
6203 str = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00006204 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006205 save = *cur;
6206 *cur = 0;
6207 id = xmlGetID(ctxt->doc, str);
6208 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006209 xmlErrValidNodeNr(ctxt, NULL, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006210 "attribute %s line %d references an unknown ID \"%s\"\n",
6211 ref->name, ref->lineno, str);
6212 ctxt->valid = 0;
6213 }
6214 if (save == 0)
6215 break;
6216 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00006217 while (IS_BLANK_CH(*cur)) cur++;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006218 }
6219 xmlFree(dup);
6220 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00006221 id = xmlGetID(ctxt->doc, name);
6222 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006223 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006224 "IDREF attribute %s references an unknown ID \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006225 attr->name, name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006226 ctxt->valid = 0;
6227 }
6228 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
6229 xmlChar *dup, *str = NULL, *cur, save;
6230
6231 dup = xmlStrdup(name);
6232 if (dup == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00006233 xmlVErrMemory(ctxt, "IDREFS split");
Owen Taylor3473f882001-02-23 17:55:21 +00006234 ctxt->valid = 0;
6235 return;
6236 }
6237 cur = dup;
6238 while (*cur != 0) {
6239 str = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00006240 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00006241 save = *cur;
6242 *cur = 0;
6243 id = xmlGetID(ctxt->doc, str);
6244 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006245 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006246 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006247 attr->name, str, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006248 ctxt->valid = 0;
6249 }
6250 if (save == 0)
6251 break;
6252 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00006253 while (IS_BLANK_CH(*cur)) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00006254 }
6255 xmlFree(dup);
6256 }
6257}
6258
6259/**
Daniel Veillard8730c562001-02-26 10:49:57 +00006260 * xmlWalkValidateList:
6261 * @data: Contents of current link
6262 * @user: Value supplied by the user
6263 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00006264 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00006265 */
6266static int
6267xmlWalkValidateList(const void *data, const void *user)
6268{
6269 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
6270 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
6271 return 1;
6272}
6273
6274/**
6275 * xmlValidateCheckRefCallback:
6276 * @ref_list: List of references
6277 * @ctxt: Validation context
6278 * @name: Name of ID we are searching for
6279 *
6280 */
6281static void
6282xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
6283 const xmlChar *name) {
6284 xmlValidateMemo memo;
6285
6286 if (ref_list == NULL)
6287 return;
6288 memo.ctxt = ctxt;
6289 memo.name = name;
6290
6291 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
6292
6293}
6294
6295/**
Owen Taylor3473f882001-02-23 17:55:21 +00006296 * xmlValidateDocumentFinal:
6297 * @ctxt: the validation context
6298 * @doc: a document instance
6299 *
6300 * Does the final step for the document validation once all the
6301 * incremental validation steps have been completed
6302 *
6303 * basically it does the following checks described by the XML Rec
6304 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006305 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00006306 *
6307 * returns 1 if valid or 0 otherwise
6308 */
6309
6310int
6311xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6312 xmlRefTablePtr table;
6313
Daniel Veillardc0be74b2004-11-03 19:16:55 +00006314 if (ctxt == NULL)
6315 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00006316 if (doc == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00006317 xmlErrValid(ctxt, XML_DTD_NO_DOC,
6318 "xmlValidateDocumentFinal: doc == NULL\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006319 return(0);
6320 }
6321
6322 /*
6323 * Check all the NOTATION/NOTATIONS attributes
6324 */
6325 /*
6326 * Check all the ENTITY/ENTITIES attributes definition for validity
6327 */
6328 /*
6329 * Check all the IDREF/IDREFS attributes definition for validity
6330 */
6331 table = (xmlRefTablePtr) doc->refs;
6332 ctxt->doc = doc;
6333 ctxt->valid = 1;
6334 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6335 return(ctxt->valid);
6336}
6337
6338/**
6339 * xmlValidateDtd:
6340 * @ctxt: the validation context
6341 * @doc: a document instance
6342 * @dtd: a dtd instance
6343 *
6344 * Try to validate the document against the dtd instance
6345 *
William M. Brack367df6e2004-10-23 18:14:36 +00006346 * Basically it does check all the definitions in the DtD.
6347 * Note the the internal subset (if present) is de-coupled
6348 * (i.e. not used), which could give problems if ID or IDREF
6349 * is present.
Owen Taylor3473f882001-02-23 17:55:21 +00006350 *
6351 * returns 1 if valid or 0 otherwise
6352 */
6353
6354int
6355xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6356 int ret;
William M. Brack367df6e2004-10-23 18:14:36 +00006357 xmlDtdPtr oldExt, oldInt;
Owen Taylor3473f882001-02-23 17:55:21 +00006358 xmlNodePtr root;
6359
6360 if (dtd == NULL) return(0);
6361 if (doc == NULL) return(0);
6362 oldExt = doc->extSubset;
William M. Brack367df6e2004-10-23 18:14:36 +00006363 oldInt = doc->intSubset;
Owen Taylor3473f882001-02-23 17:55:21 +00006364 doc->extSubset = dtd;
William M. Brack367df6e2004-10-23 18:14:36 +00006365 doc->intSubset = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00006366 ret = xmlValidateRoot(ctxt, doc);
6367 if (ret == 0) {
6368 doc->extSubset = oldExt;
William M. Brack367df6e2004-10-23 18:14:36 +00006369 doc->intSubset = oldInt;
Owen Taylor3473f882001-02-23 17:55:21 +00006370 return(ret);
6371 }
6372 if (doc->ids != NULL) {
6373 xmlFreeIDTable(doc->ids);
6374 doc->ids = NULL;
6375 }
6376 if (doc->refs != NULL) {
6377 xmlFreeRefTable(doc->refs);
6378 doc->refs = NULL;
6379 }
6380 root = xmlDocGetRootElement(doc);
6381 ret = xmlValidateElement(ctxt, doc, root);
6382 ret &= xmlValidateDocumentFinal(ctxt, doc);
6383 doc->extSubset = oldExt;
William M. Brack367df6e2004-10-23 18:14:36 +00006384 doc->intSubset = oldInt;
Owen Taylor3473f882001-02-23 17:55:21 +00006385 return(ret);
6386}
6387
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006388static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006389xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6390 const xmlChar *name ATTRIBUTE_UNUSED) {
6391 if (cur == NULL)
6392 return;
6393 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6394 xmlChar *notation = cur->content;
6395
Daniel Veillard878eab02002-02-19 13:46:09 +00006396 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006397 int ret;
6398
6399 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6400 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006401 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006402 }
6403 }
6404 }
6405}
6406
6407static void
Owen Taylor3473f882001-02-23 17:55:21 +00006408xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006409 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006410 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006411 xmlDocPtr doc;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006412 xmlElementPtr elem = NULL;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006413
Owen Taylor3473f882001-02-23 17:55:21 +00006414 if (cur == NULL)
6415 return;
6416 switch (cur->atype) {
6417 case XML_ATTRIBUTE_CDATA:
6418 case XML_ATTRIBUTE_ID:
6419 case XML_ATTRIBUTE_IDREF :
6420 case XML_ATTRIBUTE_IDREFS:
6421 case XML_ATTRIBUTE_NMTOKEN:
6422 case XML_ATTRIBUTE_NMTOKENS:
6423 case XML_ATTRIBUTE_ENUMERATION:
6424 break;
6425 case XML_ATTRIBUTE_ENTITY:
6426 case XML_ATTRIBUTE_ENTITIES:
6427 case XML_ATTRIBUTE_NOTATION:
6428 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006429
6430 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6431 cur->atype, cur->defaultValue);
6432 if ((ret == 0) && (ctxt->valid == 1))
6433 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006434 }
6435 if (cur->tree != NULL) {
6436 xmlEnumerationPtr tree = cur->tree;
6437 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006438 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006439 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006440 if ((ret == 0) && (ctxt->valid == 1))
6441 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006442 tree = tree->next;
6443 }
6444 }
6445 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006446 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6447 doc = cur->doc;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006448 if (cur->elem == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006449 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
Daniel Veillard878eab02002-02-19 13:46:09 +00006450 "xmlValidateAttributeCallback(%s): internal error\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006451 (const char *) cur->name);
Daniel Veillard878eab02002-02-19 13:46:09 +00006452 return;
6453 }
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006454
6455 if (doc != NULL)
6456 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6457 if ((elem == NULL) && (doc != NULL))
Daniel Veillard878eab02002-02-19 13:46:09 +00006458 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006459 if ((elem == NULL) && (cur->parent != NULL) &&
6460 (cur->parent->type == XML_DTD_NODE))
6461 elem = xmlGetDtdElementDesc((xmlDtdPtr) cur->parent, cur->elem);
Daniel Veillard878eab02002-02-19 13:46:09 +00006462 if (elem == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006463 xmlErrValidNode(ctxt, NULL, XML_DTD_UNKNOWN_ELEM,
Daniel Veillard878eab02002-02-19 13:46:09 +00006464 "attribute %s: could not find decl for element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006465 cur->name, cur->elem, NULL);
Daniel Veillard878eab02002-02-19 13:46:09 +00006466 return;
6467 }
6468 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006469 xmlErrValidNode(ctxt, NULL, XML_DTD_EMPTY_NOTATION,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006470 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006471 cur->name, cur->elem, NULL);
Daniel Veillard878eab02002-02-19 13:46:09 +00006472 ctxt->valid = 0;
6473 }
6474 }
Owen Taylor3473f882001-02-23 17:55:21 +00006475}
6476
6477/**
6478 * xmlValidateDtdFinal:
6479 * @ctxt: the validation context
6480 * @doc: a document instance
6481 *
6482 * Does the final step for the dtds validation once all the
6483 * subsets have been parsed
6484 *
6485 * basically it does the following checks described by the XML Rec
6486 * - check that ENTITY and ENTITIES type attributes default or
6487 * possible values matches one of the defined entities.
6488 * - check that NOTATION type attributes default or
6489 * possible values matches one of the defined notations.
6490 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006491 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006492 */
6493
6494int
6495xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006496 xmlDtdPtr dtd;
6497 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006498 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006499
6500 if (doc == NULL) return(0);
6501 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6502 return(0);
6503 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006504 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006505 dtd = doc->intSubset;
6506 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6507 table = (xmlAttributeTablePtr) dtd->attributes;
6508 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006509 }
6510 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006511 entities = (xmlEntitiesTablePtr) dtd->entities;
6512 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6513 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006514 }
6515 dtd = doc->extSubset;
6516 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6517 table = (xmlAttributeTablePtr) dtd->attributes;
6518 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006519 }
6520 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006521 entities = (xmlEntitiesTablePtr) dtd->entities;
6522 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6523 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006524 }
6525 return(ctxt->valid);
6526}
6527
6528/**
6529 * xmlValidateDocument:
6530 * @ctxt: the validation context
6531 * @doc: a document instance
6532 *
6533 * Try to validate the document instance
6534 *
6535 * basically it does the all the checks described by the XML Rec
6536 * i.e. validates the internal and external subset (if present)
6537 * and validate the document tree.
6538 *
6539 * returns 1 if valid or 0 otherwise
6540 */
6541
6542int
6543xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6544 int ret;
6545 xmlNodePtr root;
6546
Daniel Veillardc0be74b2004-11-03 19:16:55 +00006547 if (doc == NULL)
6548 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006549 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006550 xmlErrValid(ctxt, XML_DTD_NO_DTD,
6551 "no DTD found!\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006552 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006553 }
Owen Taylor3473f882001-02-23 17:55:21 +00006554 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6555 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
William M. Brack8c22f9f2004-08-06 16:23:27 +00006556 xmlChar *sysID;
6557 if (doc->intSubset->SystemID != NULL) {
William M. Brackbebe7302004-08-05 06:46:47 +00006558 sysID = xmlBuildURI(doc->intSubset->SystemID,
6559 doc->URL);
William M. Brack8c22f9f2004-08-06 16:23:27 +00006560 if (sysID == NULL) {
6561 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
6562 "Could not build URI for external subset \"%s\"\n",
6563 (const char *) doc->intSubset->SystemID);
6564 return 0;
6565 }
6566 } else
William M. Brackbebe7302004-08-05 06:46:47 +00006567 sysID = NULL;
William M. Brack8c22f9f2004-08-06 16:23:27 +00006568 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
William M. Brackbebe7302004-08-05 06:46:47 +00006569 (const xmlChar *)sysID);
William M. Brackbebe7302004-08-05 06:46:47 +00006570 if (sysID != NULL)
6571 xmlFree(sysID);
Owen Taylor3473f882001-02-23 17:55:21 +00006572 if (doc->extSubset == NULL) {
6573 if (doc->intSubset->SystemID != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006574 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00006575 "Could not load the external subset \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006576 (const char *) doc->intSubset->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00006577 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006578 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00006579 "Could not load the external subset \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006580 (const char *) doc->intSubset->ExternalID);
Owen Taylor3473f882001-02-23 17:55:21 +00006581 }
6582 return(0);
6583 }
6584 }
6585
6586 if (doc->ids != NULL) {
6587 xmlFreeIDTable(doc->ids);
6588 doc->ids = NULL;
6589 }
6590 if (doc->refs != NULL) {
6591 xmlFreeRefTable(doc->refs);
6592 doc->refs = NULL;
6593 }
6594 ret = xmlValidateDtdFinal(ctxt, doc);
6595 if (!xmlValidateRoot(ctxt, doc)) return(0);
6596
6597 root = xmlDocGetRootElement(doc);
6598 ret &= xmlValidateElement(ctxt, doc, root);
6599 ret &= xmlValidateDocumentFinal(ctxt, doc);
6600 return(ret);
6601}
6602
Owen Taylor3473f882001-02-23 17:55:21 +00006603/************************************************************************
6604 * *
6605 * Routines for dynamic validation editing *
6606 * *
6607 ************************************************************************/
6608
6609/**
6610 * xmlValidGetPotentialChildren:
6611 * @ctree: an element content tree
6612 * @list: an array to store the list of child names
6613 * @len: a pointer to the number of element in the list
6614 * @max: the size of the array
6615 *
6616 * Build/extend a list of potential children allowed by the content tree
6617 *
6618 * returns the number of element in the list, or -1 in case of error.
6619 */
6620
6621int
6622xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6623 int *len, int max) {
6624 int i;
6625
6626 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6627 return(-1);
6628 if (*len >= max) return(*len);
6629
6630 switch (ctree->type) {
6631 case XML_ELEMENT_CONTENT_PCDATA:
6632 for (i = 0; i < *len;i++)
6633 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6634 list[(*len)++] = BAD_CAST "#PCDATA";
6635 break;
6636 case XML_ELEMENT_CONTENT_ELEMENT:
6637 for (i = 0; i < *len;i++)
6638 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6639 list[(*len)++] = ctree->name;
6640 break;
6641 case XML_ELEMENT_CONTENT_SEQ:
6642 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6643 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6644 break;
6645 case XML_ELEMENT_CONTENT_OR:
6646 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6647 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6648 break;
6649 }
6650
6651 return(*len);
6652}
6653
William M. Brack9333cc22004-06-24 08:33:40 +00006654/*
6655 * Dummy function to suppress messages while we try out valid elements
6656 */
6657static void xmlNoValidityErr(void *ctx ATTRIBUTE_UNUSED,
6658 const char *msg ATTRIBUTE_UNUSED, ...) {
6659 return;
6660}
6661
Owen Taylor3473f882001-02-23 17:55:21 +00006662/**
6663 * xmlValidGetValidElements:
6664 * @prev: an element to insert after
6665 * @next: an element to insert next
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006666 * @names: an array to store the list of child names
Owen Taylor3473f882001-02-23 17:55:21 +00006667 * @max: the size of the array
6668 *
6669 * This function returns the list of authorized children to insert
6670 * within an existing tree while respecting the validity constraints
6671 * forced by the Dtd. The insertion point is defined using @prev and
6672 * @next in the following ways:
6673 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6674 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6675 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6676 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6677 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6678 *
6679 * pointers to the element names are inserted at the beginning of the array
6680 * and do not need to be freed.
6681 *
6682 * returns the number of element in the list, or -1 in case of error. If
6683 * the function returns the value @max the caller is invited to grow the
6684 * receiving array and retry.
6685 */
6686
6687int
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006688xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **names,
Owen Taylor3473f882001-02-23 17:55:21 +00006689 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006690 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006691 int nb_valid_elements = 0;
6692 const xmlChar *elements[256];
6693 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006694 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006695
6696 xmlNode *ref_node;
6697 xmlNode *parent;
6698 xmlNode *test_node;
6699
6700 xmlNode *prev_next;
6701 xmlNode *next_prev;
6702 xmlNode *parent_childs;
6703 xmlNode *parent_last;
6704
6705 xmlElement *element_desc;
6706
6707 if (prev == NULL && next == NULL)
6708 return(-1);
6709
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006710 if (names == NULL) return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00006711 if (max <= 0) return(-1);
6712
William M. Brack9333cc22004-06-24 08:33:40 +00006713 memset(&vctxt, 0, sizeof (xmlValidCtxt));
6714 vctxt.error = xmlNoValidityErr; /* this suppresses err/warn output */
6715
Owen Taylor3473f882001-02-23 17:55:21 +00006716 nb_valid_elements = 0;
6717 ref_node = prev ? prev : next;
6718 parent = ref_node->parent;
6719
6720 /*
6721 * Retrieves the parent element declaration
6722 */
6723 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6724 parent->name);
6725 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6726 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6727 parent->name);
6728 if (element_desc == NULL) return(-1);
6729
6730 /*
6731 * Do a backup of the current tree structure
6732 */
6733 prev_next = prev ? prev->next : NULL;
6734 next_prev = next ? next->prev : NULL;
6735 parent_childs = parent->children;
6736 parent_last = parent->last;
6737
6738 /*
6739 * Creates a dummy node and insert it into the tree
6740 */
Daniel Veillard95ddcd32004-10-26 21:53:55 +00006741 test_node = xmlNewDocNode (ref_node->doc, NULL, BAD_CAST "<!dummy?>", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006742 test_node->parent = parent;
6743 test_node->prev = prev;
6744 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006745 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006746
6747 if (prev) prev->next = test_node;
6748 else parent->children = test_node;
6749
6750 if (next) next->prev = test_node;
6751 else parent->last = test_node;
6752
6753 /*
6754 * Insert each potential child node and check if the parent is
6755 * still valid
6756 */
6757 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6758 elements, &nb_elements, 256);
6759
6760 for (i = 0;i < nb_elements;i++) {
6761 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006762 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006763 int j;
6764
6765 for (j = 0; j < nb_valid_elements;j++)
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006766 if (xmlStrEqual(elements[i], names[j])) break;
6767 names[nb_valid_elements++] = elements[i];
Owen Taylor3473f882001-02-23 17:55:21 +00006768 if (nb_valid_elements >= max) break;
6769 }
6770 }
6771
6772 /*
6773 * Restore the tree structure
6774 */
6775 if (prev) prev->next = prev_next;
6776 if (next) next->prev = next_prev;
6777 parent->children = parent_childs;
6778 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006779
6780 /*
6781 * Free up the dummy node
6782 */
6783 test_node->name = name;
6784 xmlFreeNode(test_node);
6785
Owen Taylor3473f882001-02-23 17:55:21 +00006786 return(nb_valid_elements);
6787}
Daniel Veillard4432df22003-09-28 18:58:27 +00006788#endif /* LIBXML_VALID_ENABLED */
6789