blob: 9fa6c143ad5d4b976b6a815aa24531497d432423 [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 Veillardce244ad2004-11-05 10:03:46 +00002883 if (attr == NULL)
2884 return(0);
2885 if (doc == NULL) {
2886 doc = attr->doc;
2887 if (doc == NULL) return(0);
2888 }
2889
Daniel Veillard37721922001-05-04 15:21:12 +00002890 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2891 return(0);
2892 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2893 /* TODO @@@ */
2894 return(0);
2895 } else {
2896 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002897
Daniel Veillardce244ad2004-11-05 10:03:46 +00002898 if (elem == NULL) return(0);
Daniel Veillard37721922001-05-04 15:21:12 +00002899 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2900 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2901 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2902 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002903
Daniel Veillard37721922001-05-04 15:21:12 +00002904 if ((attrDecl != NULL) &&
2905 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2906 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2907 return(1);
2908 }
2909 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002910}
2911
2912/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002913 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002914 * @doc: the document
2915 * @attr: the attribute
2916 *
2917 * Remove the given attribute from the Ref table maintained internally.
2918 *
2919 * Returns -1 if the lookup failed and 0 otherwise
2920 */
2921int
2922xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002923 xmlListPtr ref_list;
2924 xmlRefTablePtr table;
2925 xmlChar *ID;
2926 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002927
Daniel Veillard37721922001-05-04 15:21:12 +00002928 if (doc == NULL) return(-1);
2929 if (attr == NULL) return(-1);
2930 table = (xmlRefTablePtr) doc->refs;
2931 if (table == NULL)
2932 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002933
Daniel Veillard37721922001-05-04 15:21:12 +00002934 if (attr == NULL)
2935 return(-1);
2936 ID = xmlNodeListGetString(doc, attr->children, 1);
2937 if (ID == NULL)
2938 return(-1);
2939 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002940
Daniel Veillard37721922001-05-04 15:21:12 +00002941 if(ref_list == NULL) {
2942 xmlFree(ID);
2943 return (-1);
2944 }
2945 /* At this point, ref_list refers to a list of references which
2946 * have the same key as the supplied attr. Our list of references
2947 * is ordered by reference address and we don't have that information
2948 * here to use when removing. We'll have to walk the list and
2949 * check for a matching attribute, when we find one stop the walk
2950 * and remove the entry.
2951 * The list is ordered by reference, so that means we don't have the
2952 * key. Passing the list and the reference to the walker means we
2953 * will have enough data to be able to remove the entry.
2954 */
2955 target.l = ref_list;
2956 target.ap = attr;
2957
2958 /* Remove the supplied attr from our list */
2959 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002960
Daniel Veillard37721922001-05-04 15:21:12 +00002961 /*If the list is empty then remove the list entry in the hash */
2962 if (xmlListEmpty(ref_list))
2963 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2964 xmlFreeRefList);
2965 xmlFree(ID);
2966 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002967}
2968
2969/**
2970 * xmlGetRefs:
2971 * @doc: pointer to the document
2972 * @ID: the ID value
2973 *
2974 * Find the set of references for the supplied ID.
2975 *
2976 * Returns NULL if not found, otherwise node set for the ID.
2977 */
2978xmlListPtr
2979xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002980 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002981
Daniel Veillard37721922001-05-04 15:21:12 +00002982 if (doc == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002983 return(NULL);
2984 }
Owen Taylor3473f882001-02-23 17:55:21 +00002985
Daniel Veillard37721922001-05-04 15:21:12 +00002986 if (ID == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002987 return(NULL);
2988 }
Owen Taylor3473f882001-02-23 17:55:21 +00002989
Daniel Veillard37721922001-05-04 15:21:12 +00002990 table = (xmlRefTablePtr) doc->refs;
2991 if (table == NULL)
2992 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002993
Daniel Veillard37721922001-05-04 15:21:12 +00002994 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002995}
2996
2997/************************************************************************
2998 * *
2999 * Routines for validity checking *
3000 * *
3001 ************************************************************************/
3002
3003/**
3004 * xmlGetDtdElementDesc:
3005 * @dtd: a pointer to the DtD to search
3006 * @name: the element name
3007 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003008 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00003009 *
3010 * returns the xmlElementPtr if found or NULL
3011 */
3012
3013xmlElementPtr
3014xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
3015 xmlElementTablePtr table;
3016 xmlElementPtr cur;
3017 xmlChar *uqname = NULL, *prefix = NULL;
3018
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00003019 if ((dtd == NULL) || (name == NULL)) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003020 if (dtd->elements == NULL)
3021 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003022 table = (xmlElementTablePtr) dtd->elements;
3023
3024 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003025 if (uqname != NULL)
3026 name = uqname;
3027 cur = xmlHashLookup2(table, name, prefix);
3028 if (prefix != NULL) xmlFree(prefix);
3029 if (uqname != NULL) xmlFree(uqname);
3030 return(cur);
3031}
3032/**
3033 * xmlGetDtdElementDesc2:
3034 * @dtd: a pointer to the DtD to search
3035 * @name: the element name
3036 * @create: create an empty description if not found
3037 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003038 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00003039 *
3040 * returns the xmlElementPtr if found or NULL
3041 */
3042
Daniel Veillard86fd5a72001-12-13 14:55:21 +00003043static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00003044xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
3045 xmlElementTablePtr table;
3046 xmlElementPtr cur;
3047 xmlChar *uqname = NULL, *prefix = NULL;
3048
3049 if (dtd == NULL) return(NULL);
3050 if (dtd->elements == NULL) {
3051 if (!create)
3052 return(NULL);
3053 /*
3054 * Create the Element table if needed.
3055 */
3056 table = (xmlElementTablePtr) dtd->elements;
3057 if (table == NULL) {
3058 table = xmlCreateElementTable();
3059 dtd->elements = (void *) table;
3060 }
3061 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00003062 xmlVErrMemory(NULL, "element table allocation failed");
Daniel Veillarda10efa82001-04-18 13:09:01 +00003063 return(NULL);
3064 }
3065 }
3066 table = (xmlElementTablePtr) dtd->elements;
3067
3068 uqname = xmlSplitQName2(name, &prefix);
3069 if (uqname != NULL)
3070 name = uqname;
3071 cur = xmlHashLookup2(table, name, prefix);
3072 if ((cur == NULL) && (create)) {
3073 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
3074 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00003075 xmlVErrMemory(NULL, "malloc failed");
Daniel Veillarda10efa82001-04-18 13:09:01 +00003076 return(NULL);
3077 }
3078 memset(cur, 0, sizeof(xmlElement));
3079 cur->type = XML_ELEMENT_DECL;
3080
3081 /*
3082 * fill the structure.
3083 */
3084 cur->name = xmlStrdup(name);
3085 cur->prefix = xmlStrdup(prefix);
3086 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
3087
3088 xmlHashAddEntry2(table, name, prefix, cur);
3089 }
3090 if (prefix != NULL) xmlFree(prefix);
3091 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00003092 return(cur);
3093}
3094
3095/**
3096 * xmlGetDtdQElementDesc:
3097 * @dtd: a pointer to the DtD to search
3098 * @name: the element name
3099 * @prefix: the element namespace prefix
3100 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003101 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00003102 *
3103 * returns the xmlElementPtr if found or NULL
3104 */
3105
Daniel Veillard48da9102001-08-07 01:10:10 +00003106xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00003107xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
3108 const xmlChar *prefix) {
3109 xmlElementTablePtr table;
3110
3111 if (dtd == NULL) return(NULL);
3112 if (dtd->elements == NULL) return(NULL);
3113 table = (xmlElementTablePtr) dtd->elements;
3114
3115 return(xmlHashLookup2(table, name, prefix));
3116}
3117
3118/**
3119 * xmlGetDtdAttrDesc:
3120 * @dtd: a pointer to the DtD to search
3121 * @elem: the element name
3122 * @name: the attribute name
3123 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003124 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00003125 * this element.
3126 *
3127 * returns the xmlAttributePtr if found or NULL
3128 */
3129
3130xmlAttributePtr
3131xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
3132 xmlAttributeTablePtr table;
3133 xmlAttributePtr cur;
3134 xmlChar *uqname = NULL, *prefix = NULL;
3135
3136 if (dtd == NULL) return(NULL);
3137 if (dtd->attributes == NULL) return(NULL);
3138
3139 table = (xmlAttributeTablePtr) dtd->attributes;
3140 if (table == NULL)
3141 return(NULL);
3142
3143 uqname = xmlSplitQName2(name, &prefix);
3144
3145 if (uqname != NULL) {
3146 cur = xmlHashLookup3(table, uqname, prefix, elem);
3147 if (prefix != NULL) xmlFree(prefix);
3148 if (uqname != NULL) xmlFree(uqname);
3149 } else
3150 cur = xmlHashLookup3(table, name, NULL, elem);
3151 return(cur);
3152}
3153
3154/**
3155 * xmlGetDtdQAttrDesc:
3156 * @dtd: a pointer to the DtD to search
3157 * @elem: the element name
3158 * @name: the attribute name
3159 * @prefix: the attribute namespace prefix
3160 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003161 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00003162 * this element.
3163 *
3164 * returns the xmlAttributePtr if found or NULL
3165 */
3166
Daniel Veillard48da9102001-08-07 01:10:10 +00003167xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00003168xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
3169 const xmlChar *prefix) {
3170 xmlAttributeTablePtr table;
3171
3172 if (dtd == NULL) return(NULL);
3173 if (dtd->attributes == NULL) return(NULL);
3174 table = (xmlAttributeTablePtr) dtd->attributes;
3175
3176 return(xmlHashLookup3(table, name, prefix, elem));
3177}
3178
3179/**
3180 * xmlGetDtdNotationDesc:
3181 * @dtd: a pointer to the DtD to search
3182 * @name: the notation name
3183 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003184 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00003185 *
3186 * returns the xmlNotationPtr if found or NULL
3187 */
3188
3189xmlNotationPtr
3190xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
3191 xmlNotationTablePtr table;
3192
3193 if (dtd == NULL) return(NULL);
3194 if (dtd->notations == NULL) return(NULL);
3195 table = (xmlNotationTablePtr) dtd->notations;
3196
3197 return(xmlHashLookup(table, name));
3198}
3199
Daniel Veillardf54cd532004-02-25 11:52:31 +00003200#if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
Owen Taylor3473f882001-02-23 17:55:21 +00003201/**
3202 * xmlValidateNotationUse:
3203 * @ctxt: the validation context
3204 * @doc: the document
3205 * @notationName: the notation name to check
3206 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003207 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00003208 * - [ VC: Notation Declared ]
3209 *
3210 * returns 1 if valid or 0 otherwise
3211 */
3212
3213int
3214xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3215 const xmlChar *notationName) {
3216 xmlNotationPtr notaDecl;
3217 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3218
3219 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
3220 if ((notaDecl == NULL) && (doc->extSubset != NULL))
3221 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
3222
Daniel Veillarde637c4a2003-03-30 21:10:09 +00003223 if ((notaDecl == NULL) && (ctxt != NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003224 xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_NOTATION,
3225 "NOTATION %s is not declared\n",
3226 notationName, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003227 return(0);
3228 }
3229 return(1);
3230}
Daniel Veillardf54cd532004-02-25 11:52:31 +00003231#endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00003232
3233/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00003234 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00003235 * @doc: the document
3236 * @name: the element name
3237 *
3238 * Search in the DtDs whether an element accept Mixed content (or ANY)
3239 * basically if it is supposed to accept text childs
3240 *
3241 * returns 0 if no, 1 if yes, and -1 if no element description is available
3242 */
3243
3244int
3245xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
3246 xmlElementPtr elemDecl;
3247
3248 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3249
3250 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
3251 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3252 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
3253 if (elemDecl == NULL) return(-1);
3254 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003255 case XML_ELEMENT_TYPE_UNDEFINED:
3256 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003257 case XML_ELEMENT_TYPE_ELEMENT:
3258 return(0);
3259 case XML_ELEMENT_TYPE_EMPTY:
3260 /*
3261 * return 1 for EMPTY since we want VC error to pop up
3262 * on <empty> </empty> for example
3263 */
3264 case XML_ELEMENT_TYPE_ANY:
3265 case XML_ELEMENT_TYPE_MIXED:
3266 return(1);
3267 }
3268 return(1);
3269}
3270
Daniel Veillard4432df22003-09-28 18:58:27 +00003271#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00003272/**
3273 * xmlValidateNameValue:
3274 * @value: an Name value
3275 *
3276 * Validate that the given value match Name production
3277 *
3278 * returns 1 if valid or 0 otherwise
3279 */
3280
Daniel Veillard9b731d72002-04-14 12:56:08 +00003281int
Owen Taylor3473f882001-02-23 17:55:21 +00003282xmlValidateNameValue(const xmlChar *value) {
3283 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003284 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003285
3286 if (value == NULL) return(0);
3287 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003288 val = xmlStringCurrentChar(NULL, cur, &len);
3289 cur += len;
3290 if (!IS_LETTER(val) && (val != '_') &&
3291 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003292 return(0);
3293 }
3294
Daniel Veillardd8224e02002-01-13 15:43:22 +00003295 val = xmlStringCurrentChar(NULL, cur, &len);
3296 cur += len;
3297 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3298 (val == '.') || (val == '-') ||
3299 (val == '_') || (val == ':') ||
3300 (IS_COMBINING(val)) ||
3301 (IS_EXTENDER(val))) {
3302 val = xmlStringCurrentChar(NULL, cur, &len);
3303 cur += len;
3304 }
Owen Taylor3473f882001-02-23 17:55:21 +00003305
Daniel Veillardd8224e02002-01-13 15:43:22 +00003306 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003307
3308 return(1);
3309}
3310
3311/**
3312 * xmlValidateNamesValue:
3313 * @value: an Names value
3314 *
3315 * Validate that the given value match Names production
3316 *
3317 * returns 1 if valid or 0 otherwise
3318 */
3319
Daniel Veillard9b731d72002-04-14 12:56:08 +00003320int
Owen Taylor3473f882001-02-23 17:55:21 +00003321xmlValidateNamesValue(const xmlChar *value) {
3322 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003323 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003324
3325 if (value == NULL) return(0);
3326 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003327 val = xmlStringCurrentChar(NULL, cur, &len);
3328 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003329
Daniel Veillardd8224e02002-01-13 15:43:22 +00003330 if (!IS_LETTER(val) && (val != '_') &&
3331 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003332 return(0);
3333 }
3334
Daniel Veillardd8224e02002-01-13 15:43:22 +00003335 val = xmlStringCurrentChar(NULL, cur, &len);
3336 cur += len;
3337 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3338 (val == '.') || (val == '-') ||
3339 (val == '_') || (val == ':') ||
3340 (IS_COMBINING(val)) ||
3341 (IS_EXTENDER(val))) {
3342 val = xmlStringCurrentChar(NULL, cur, &len);
3343 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003344 }
3345
Daniel Veillard807b4de2004-09-26 14:42:56 +00003346 /* Should not test IS_BLANK(val) here -- see erratum E20*/
3347 while (val == 0x20) {
3348 while (val == 0x20) {
Daniel Veillardd8224e02002-01-13 15:43:22 +00003349 val = xmlStringCurrentChar(NULL, cur, &len);
3350 cur += len;
3351 }
3352
3353 if (!IS_LETTER(val) && (val != '_') &&
3354 (val != ':')) {
3355 return(0);
3356 }
3357 val = xmlStringCurrentChar(NULL, cur, &len);
3358 cur += len;
3359
3360 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3361 (val == '.') || (val == '-') ||
3362 (val == '_') || (val == ':') ||
3363 (IS_COMBINING(val)) ||
3364 (IS_EXTENDER(val))) {
3365 val = xmlStringCurrentChar(NULL, cur, &len);
3366 cur += len;
3367 }
3368 }
3369
3370 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003371
3372 return(1);
3373}
3374
3375/**
3376 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003377 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003378 *
3379 * Validate that the given value match Nmtoken production
3380 *
3381 * [ VC: Name Token ]
3382 *
3383 * returns 1 if valid or 0 otherwise
3384 */
3385
Daniel Veillard9b731d72002-04-14 12:56:08 +00003386int
Owen Taylor3473f882001-02-23 17:55:21 +00003387xmlValidateNmtokenValue(const xmlChar *value) {
3388 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003389 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003390
3391 if (value == NULL) return(0);
3392 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003393 val = xmlStringCurrentChar(NULL, cur, &len);
3394 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003395
Daniel Veillardd8224e02002-01-13 15:43:22 +00003396 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3397 (val != '.') && (val != '-') &&
3398 (val != '_') && (val != ':') &&
3399 (!IS_COMBINING(val)) &&
3400 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003401 return(0);
3402
Daniel Veillardd8224e02002-01-13 15:43:22 +00003403 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3404 (val == '.') || (val == '-') ||
3405 (val == '_') || (val == ':') ||
3406 (IS_COMBINING(val)) ||
3407 (IS_EXTENDER(val))) {
3408 val = xmlStringCurrentChar(NULL, cur, &len);
3409 cur += len;
3410 }
Owen Taylor3473f882001-02-23 17:55:21 +00003411
Daniel Veillardd8224e02002-01-13 15:43:22 +00003412 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003413
3414 return(1);
3415}
3416
3417/**
3418 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003419 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003420 *
3421 * Validate that the given value match Nmtokens production
3422 *
3423 * [ VC: Name Token ]
3424 *
3425 * returns 1 if valid or 0 otherwise
3426 */
3427
Daniel Veillard9b731d72002-04-14 12:56:08 +00003428int
Owen Taylor3473f882001-02-23 17:55:21 +00003429xmlValidateNmtokensValue(const xmlChar *value) {
3430 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003431 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003432
3433 if (value == NULL) return(0);
3434 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003435 val = xmlStringCurrentChar(NULL, cur, &len);
3436 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003437
Daniel Veillardd8224e02002-01-13 15:43:22 +00003438 while (IS_BLANK(val)) {
3439 val = xmlStringCurrentChar(NULL, cur, &len);
3440 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003441 }
3442
Daniel Veillardd8224e02002-01-13 15:43:22 +00003443 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3444 (val != '.') && (val != '-') &&
3445 (val != '_') && (val != ':') &&
3446 (!IS_COMBINING(val)) &&
3447 (!IS_EXTENDER(val)))
3448 return(0);
3449
3450 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3451 (val == '.') || (val == '-') ||
3452 (val == '_') || (val == ':') ||
3453 (IS_COMBINING(val)) ||
3454 (IS_EXTENDER(val))) {
3455 val = xmlStringCurrentChar(NULL, cur, &len);
3456 cur += len;
3457 }
3458
Daniel Veillard807b4de2004-09-26 14:42:56 +00003459 /* Should not test IS_BLANK(val) here -- see erratum E20*/
3460 while (val == 0x20) {
3461 while (val == 0x20) {
Daniel Veillardd8224e02002-01-13 15:43:22 +00003462 val = xmlStringCurrentChar(NULL, cur, &len);
3463 cur += len;
3464 }
3465 if (val == 0) return(1);
3466
3467 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3468 (val != '.') && (val != '-') &&
3469 (val != '_') && (val != ':') &&
3470 (!IS_COMBINING(val)) &&
3471 (!IS_EXTENDER(val)))
3472 return(0);
3473
3474 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3475 (val == '.') || (val == '-') ||
3476 (val == '_') || (val == ':') ||
3477 (IS_COMBINING(val)) ||
3478 (IS_EXTENDER(val))) {
3479 val = xmlStringCurrentChar(NULL, cur, &len);
3480 cur += len;
3481 }
3482 }
3483
3484 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003485
3486 return(1);
3487}
3488
3489/**
3490 * xmlValidateNotationDecl:
3491 * @ctxt: the validation context
3492 * @doc: a document instance
3493 * @nota: a notation definition
3494 *
3495 * Try to validate a single notation definition
3496 * basically it does the following checks as described by the
3497 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003498 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003499 * But this function get called anyway ...
3500 *
3501 * returns 1 if valid or 0 otherwise
3502 */
3503
3504int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003505xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3506 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003507 int ret = 1;
3508
3509 return(ret);
3510}
3511
3512/**
3513 * xmlValidateAttributeValue:
3514 * @type: an attribute type
3515 * @value: an attribute value
3516 *
3517 * Validate that the given attribute value match the proper production
3518 *
3519 * [ VC: ID ]
3520 * Values of type ID must match the Name production....
3521 *
3522 * [ VC: IDREF ]
3523 * Values of type IDREF must match the Name production, and values
3524 * of type IDREFS must match Names ...
3525 *
3526 * [ VC: Entity Name ]
3527 * Values of type ENTITY must match the Name production, values
3528 * of type ENTITIES must match Names ...
3529 *
3530 * [ VC: Name Token ]
3531 * Values of type NMTOKEN must match the Nmtoken production; values
3532 * of type NMTOKENS must match Nmtokens.
3533 *
3534 * returns 1 if valid or 0 otherwise
3535 */
3536
3537int
3538xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3539 switch (type) {
3540 case XML_ATTRIBUTE_ENTITIES:
3541 case XML_ATTRIBUTE_IDREFS:
3542 return(xmlValidateNamesValue(value));
3543 case XML_ATTRIBUTE_ENTITY:
3544 case XML_ATTRIBUTE_IDREF:
3545 case XML_ATTRIBUTE_ID:
3546 case XML_ATTRIBUTE_NOTATION:
3547 return(xmlValidateNameValue(value));
3548 case XML_ATTRIBUTE_NMTOKENS:
3549 case XML_ATTRIBUTE_ENUMERATION:
3550 return(xmlValidateNmtokensValue(value));
3551 case XML_ATTRIBUTE_NMTOKEN:
3552 return(xmlValidateNmtokenValue(value));
3553 case XML_ATTRIBUTE_CDATA:
3554 break;
3555 }
3556 return(1);
3557}
3558
3559/**
3560 * xmlValidateAttributeValue2:
3561 * @ctxt: the validation context
3562 * @doc: the document
3563 * @name: the attribute name (used for error reporting only)
3564 * @type: the attribute type
3565 * @value: the attribute value
3566 *
3567 * Validate that the given attribute value match a given type.
3568 * This typically cannot be done before having finished parsing
3569 * the subsets.
3570 *
3571 * [ VC: IDREF ]
3572 * Values of type IDREF must match one of the declared IDs
3573 * Values of type IDREFS must match a sequence of the declared IDs
3574 * each Name must match the value of an ID attribute on some element
3575 * in the XML document; i.e. IDREF values must match the value of
3576 * some ID attribute
3577 *
3578 * [ VC: Entity Name ]
3579 * Values of type ENTITY must match one declared entity
3580 * Values of type ENTITIES must match a sequence of declared entities
3581 *
3582 * [ VC: Notation Attributes ]
3583 * all notation names in the declaration must be declared.
3584 *
3585 * returns 1 if valid or 0 otherwise
3586 */
3587
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003588static int
Owen Taylor3473f882001-02-23 17:55:21 +00003589xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3590 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3591 int ret = 1;
3592 switch (type) {
3593 case XML_ATTRIBUTE_IDREFS:
3594 case XML_ATTRIBUTE_IDREF:
3595 case XML_ATTRIBUTE_ID:
3596 case XML_ATTRIBUTE_NMTOKENS:
3597 case XML_ATTRIBUTE_ENUMERATION:
3598 case XML_ATTRIBUTE_NMTOKEN:
3599 case XML_ATTRIBUTE_CDATA:
3600 break;
3601 case XML_ATTRIBUTE_ENTITY: {
3602 xmlEntityPtr ent;
3603
3604 ent = xmlGetDocEntity(doc, value);
Daniel Veillard62998c02003-09-15 12:56:36 +00003605 /* yeah it's a bit messy... */
Daniel Veillard878eab02002-02-19 13:46:09 +00003606 if ((ent == NULL) && (doc->standalone == 1)) {
3607 doc->standalone = 0;
3608 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003609 }
Owen Taylor3473f882001-02-23 17:55:21 +00003610 if (ent == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003611 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3612 XML_DTD_UNKNOWN_ENTITY,
Owen Taylor3473f882001-02-23 17:55:21 +00003613 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003614 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003615 ret = 0;
3616 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003617 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3618 XML_DTD_ENTITY_TYPE,
Owen Taylor3473f882001-02-23 17:55:21 +00003619 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003620 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003621 ret = 0;
3622 }
3623 break;
3624 }
3625 case XML_ATTRIBUTE_ENTITIES: {
3626 xmlChar *dup, *nam = NULL, *cur, save;
3627 xmlEntityPtr ent;
3628
3629 dup = xmlStrdup(value);
3630 if (dup == NULL)
3631 return(0);
3632 cur = dup;
3633 while (*cur != 0) {
3634 nam = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00003635 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00003636 save = *cur;
3637 *cur = 0;
3638 ent = xmlGetDocEntity(doc, nam);
3639 if (ent == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003640 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3641 XML_DTD_UNKNOWN_ENTITY,
Owen Taylor3473f882001-02-23 17:55:21 +00003642 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003643 name, nam, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003644 ret = 0;
3645 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003646 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3647 XML_DTD_ENTITY_TYPE,
Owen Taylor3473f882001-02-23 17:55:21 +00003648 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003649 name, nam, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003650 ret = 0;
3651 }
3652 if (save == 0)
3653 break;
3654 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00003655 while (IS_BLANK_CH(*cur)) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00003656 }
3657 xmlFree(dup);
3658 break;
3659 }
3660 case XML_ATTRIBUTE_NOTATION: {
3661 xmlNotationPtr nota;
3662
3663 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3664 if ((nota == NULL) && (doc->extSubset != NULL))
3665 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3666
3667 if (nota == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003668 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3669 XML_DTD_UNKNOWN_NOTATION,
Owen Taylor3473f882001-02-23 17:55:21 +00003670 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003671 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003672 ret = 0;
3673 }
3674 break;
3675 }
3676 }
3677 return(ret);
3678}
3679
3680/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003681 * xmlValidCtxtNormalizeAttributeValue:
3682 * @ctxt: the validation context
3683 * @doc: the document
3684 * @elem: the parent
3685 * @name: the attribute name
3686 * @value: the attribute value
3687 * @ctxt: the validation context or NULL
3688 *
3689 * Does the validation related extra step of the normalization of attribute
3690 * values:
3691 *
3692 * If the declared value is not CDATA, then the XML processor must further
3693 * process the normalized attribute value by discarding any leading and
3694 * trailing space (#x20) characters, and by replacing sequences of space
3695 * (#x20) characters by single space (#x20) character.
3696 *
3697 * Also check VC: Standalone Document Declaration in P32, and update
3698 * ctxt->valid accordingly
3699 *
3700 * returns a new normalized string if normalization is needed, NULL otherwise
3701 * the caller must free the returned value.
3702 */
3703
3704xmlChar *
3705xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3706 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3707 xmlChar *ret, *dst;
3708 const xmlChar *src;
3709 xmlAttributePtr attrDecl = NULL;
3710 int extsubset = 0;
3711
3712 if (doc == NULL) return(NULL);
3713 if (elem == NULL) return(NULL);
3714 if (name == NULL) return(NULL);
3715 if (value == NULL) return(NULL);
3716
3717 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003718 xmlChar fn[50];
3719 xmlChar *fullname;
3720
3721 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3722 if (fullname == NULL)
3723 return(0);
3724 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003725 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003726 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003727 if (attrDecl != NULL)
3728 extsubset = 1;
3729 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003730 if ((fullname != fn) && (fullname != elem->name))
3731 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003732 }
3733 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3734 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3735 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3736 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3737 if (attrDecl != NULL)
3738 extsubset = 1;
3739 }
3740
3741 if (attrDecl == NULL)
3742 return(NULL);
3743 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3744 return(NULL);
3745
3746 ret = xmlStrdup(value);
3747 if (ret == NULL)
3748 return(NULL);
3749 src = value;
3750 dst = ret;
3751 while (*src == 0x20) src++;
3752 while (*src != 0) {
3753 if (*src == 0x20) {
3754 while (*src == 0x20) src++;
3755 if (*src != 0)
3756 *dst++ = 0x20;
3757 } else {
3758 *dst++ = *src++;
3759 }
3760 }
3761 *dst = 0;
3762 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003763 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE,
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003764"standalone: %s on %s value had to be normalized based on external subset declaration\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003765 name, elem->name, NULL);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003766 ctxt->valid = 0;
3767 }
3768 return(ret);
3769}
3770
3771/**
Owen Taylor3473f882001-02-23 17:55:21 +00003772 * xmlValidNormalizeAttributeValue:
3773 * @doc: the document
3774 * @elem: the parent
3775 * @name: the attribute name
3776 * @value: the attribute value
3777 *
3778 * Does the validation related extra step of the normalization of attribute
3779 * values:
3780 *
3781 * If the declared value is not CDATA, then the XML processor must further
3782 * process the normalized attribute value by discarding any leading and
3783 * trailing space (#x20) characters, and by replacing sequences of space
3784 * (#x20) characters by single space (#x20) character.
3785 *
Daniel Veillard652327a2003-09-29 18:02:38 +00003786 * Returns a new normalized string if normalization is needed, NULL otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00003787 * the caller must free the returned value.
3788 */
3789
3790xmlChar *
3791xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3792 const xmlChar *name, const xmlChar *value) {
3793 xmlChar *ret, *dst;
3794 const xmlChar *src;
3795 xmlAttributePtr attrDecl = NULL;
3796
3797 if (doc == NULL) return(NULL);
3798 if (elem == NULL) return(NULL);
3799 if (name == NULL) return(NULL);
3800 if (value == NULL) return(NULL);
3801
3802 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003803 xmlChar fn[50];
3804 xmlChar *fullname;
3805
3806 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3807 if (fullname == NULL)
3808 return(0);
3809 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003810 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003811 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3812 if ((fullname != fn) && (fullname != elem->name))
3813 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003814 }
3815 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3816 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3817 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3818
3819 if (attrDecl == NULL)
3820 return(NULL);
3821 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3822 return(NULL);
3823
3824 ret = xmlStrdup(value);
3825 if (ret == NULL)
3826 return(NULL);
3827 src = value;
3828 dst = ret;
3829 while (*src == 0x20) src++;
3830 while (*src != 0) {
3831 if (*src == 0x20) {
3832 while (*src == 0x20) src++;
3833 if (*src != 0)
3834 *dst++ = 0x20;
3835 } else {
3836 *dst++ = *src++;
3837 }
3838 }
3839 *dst = 0;
3840 return(ret);
3841}
3842
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003843static void
Owen Taylor3473f882001-02-23 17:55:21 +00003844xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003845 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003846 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3847}
3848
3849/**
3850 * xmlValidateAttributeDecl:
3851 * @ctxt: the validation context
3852 * @doc: a document instance
3853 * @attr: an attribute definition
3854 *
3855 * Try to validate a single attribute definition
3856 * basically it does the following checks as described by the
3857 * XML-1.0 recommendation:
3858 * - [ VC: Attribute Default Legal ]
3859 * - [ VC: Enumeration ]
3860 * - [ VC: ID Attribute Default ]
3861 *
3862 * The ID/IDREF uniqueness and matching are done separately
3863 *
3864 * returns 1 if valid or 0 otherwise
3865 */
3866
3867int
3868xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3869 xmlAttributePtr attr) {
3870 int ret = 1;
3871 int val;
3872 CHECK_DTD;
3873 if(attr == NULL) return(1);
3874
3875 /* Attribute Default Legal */
3876 /* Enumeration */
3877 if (attr->defaultValue != NULL) {
3878 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3879 if (val == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003880 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003881 "Syntax of default value for attribute %s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003882 attr->name, attr->elem, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003883 }
3884 ret &= val;
3885 }
3886
3887 /* ID Attribute Default */
3888 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3889 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3890 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003891 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_FIXED,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003892 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003893 attr->name, attr->elem, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003894 ret = 0;
3895 }
3896
3897 /* One ID per Element Type */
3898 if (attr->atype == XML_ATTRIBUTE_ID) {
3899 int nbId;
3900
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003901 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003902 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3903 attr->elem);
3904 if (elem != NULL) {
3905 nbId = xmlScanIDAttributeDecl(NULL, elem);
3906 } else {
3907 xmlAttributeTablePtr table;
3908
3909 /*
3910 * The attribute may be declared in the internal subset and the
3911 * element in the external subset.
3912 */
3913 nbId = 0;
3914 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3915 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3916 xmlValidateAttributeIdCallback, &nbId);
3917 }
3918 if (nbId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003919
3920 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003921 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3922 attr->elem, nbId, attr->name);
3923 } else if (doc->extSubset != NULL) {
3924 int extId = 0;
3925 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3926 if (elem != NULL) {
3927 extId = xmlScanIDAttributeDecl(NULL, elem);
3928 }
3929 if (extId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003930 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003931 "Element %s has %d ID attribute defined in the external subset : %s\n",
3932 attr->elem, extId, attr->name);
3933 } else if (extId + nbId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003934 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00003935"Element %s has ID attributes defined in the internal and external subset : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003936 attr->elem, attr->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003937 }
3938 }
3939 }
3940
3941 /* Validity Constraint: Enumeration */
3942 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3943 xmlEnumerationPtr tree = attr->tree;
3944 while (tree != NULL) {
3945 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3946 tree = tree->next;
3947 }
3948 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003949 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003950"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003951 attr->defaultValue, attr->name, attr->elem);
3952 ret = 0;
3953 }
3954 }
3955
3956 return(ret);
3957}
3958
3959/**
3960 * xmlValidateElementDecl:
3961 * @ctxt: the validation context
3962 * @doc: a document instance
3963 * @elem: an element definition
3964 *
3965 * Try to validate a single element definition
3966 * basically it does the following checks as described by the
3967 * XML-1.0 recommendation:
3968 * - [ VC: One ID per Element Type ]
3969 * - [ VC: No Duplicate Types ]
3970 * - [ VC: Unique Element Type Declaration ]
3971 *
3972 * returns 1 if valid or 0 otherwise
3973 */
3974
3975int
3976xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3977 xmlElementPtr elem) {
3978 int ret = 1;
3979 xmlElementPtr tst;
3980
3981 CHECK_DTD;
3982
3983 if (elem == NULL) return(1);
3984
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003985#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003986#ifdef LIBXML_REGEXP_ENABLED
3987 /* Build the regexp associated to the content model */
3988 ret = xmlValidBuildContentModel(ctxt, elem);
3989#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003990#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003991
Owen Taylor3473f882001-02-23 17:55:21 +00003992 /* No Duplicate Types */
3993 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3994 xmlElementContentPtr cur, next;
3995 const xmlChar *name;
3996
3997 cur = elem->content;
3998 while (cur != NULL) {
3999 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
4000 if (cur->c1 == NULL) break;
4001 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4002 name = cur->c1->name;
4003 next = cur->c2;
4004 while (next != NULL) {
4005 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard7b68df92003-08-03 22:58:54 +00004006 if ((xmlStrEqual(next->name, name)) &&
4007 (xmlStrEqual(next->prefix, cur->prefix))) {
4008 if (cur->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004009 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00004010 "Definition of %s has duplicate references of %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004011 elem->name, name, NULL);
Daniel Veillard7b68df92003-08-03 22:58:54 +00004012 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004013 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00004014 "Definition of %s has duplicate references of %s:%s\n",
4015 elem->name, cur->prefix, name);
4016 }
Owen Taylor3473f882001-02-23 17:55:21 +00004017 ret = 0;
4018 }
4019 break;
4020 }
4021 if (next->c1 == NULL) break;
4022 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
Daniel Veillard7b68df92003-08-03 22:58:54 +00004023 if ((xmlStrEqual(next->c1->name, name)) &&
4024 (xmlStrEqual(next->c1->prefix, cur->prefix))) {
4025 if (cur->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004026 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00004027 "Definition of %s has duplicate references to %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004028 elem->name, name, NULL);
Daniel Veillard7b68df92003-08-03 22:58:54 +00004029 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004030 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00004031 "Definition of %s has duplicate references to %s:%s\n",
4032 elem->name, cur->prefix, name);
4033 }
Owen Taylor3473f882001-02-23 17:55:21 +00004034 ret = 0;
4035 }
4036 next = next->c2;
4037 }
4038 }
4039 cur = cur->c2;
4040 }
4041 }
4042
4043 /* VC: Unique Element Type Declaration */
4044 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004045 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004046 ((tst->prefix == elem->prefix) ||
4047 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00004048 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004049 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
4050 "Redefinition of element %s\n",
4051 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004052 ret = 0;
4053 }
4054 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004055 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004056 ((tst->prefix == elem->prefix) ||
4057 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00004058 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004059 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
4060 "Redefinition of element %s\n",
4061 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004062 ret = 0;
4063 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00004064 /* One ID per Element Type
4065 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00004066 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
4067 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00004068 } */
Owen Taylor3473f882001-02-23 17:55:21 +00004069 return(ret);
4070}
4071
4072/**
4073 * xmlValidateOneAttribute:
4074 * @ctxt: the validation context
4075 * @doc: a document instance
4076 * @elem: an element instance
4077 * @attr: an attribute instance
4078 * @value: the attribute value (without entities processing)
4079 *
4080 * Try to validate a single attribute for an element
4081 * basically it does the following checks as described by the
4082 * XML-1.0 recommendation:
4083 * - [ VC: Attribute Value Type ]
4084 * - [ VC: Fixed Attribute Default ]
4085 * - [ VC: Entity Name ]
4086 * - [ VC: Name Token ]
4087 * - [ VC: ID ]
4088 * - [ VC: IDREF ]
4089 * - [ VC: Entity Name ]
4090 * - [ VC: Notation Attributes ]
4091 *
4092 * The ID/IDREF uniqueness and matching are done separately
4093 *
4094 * returns 1 if valid or 0 otherwise
4095 */
4096
4097int
4098xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
Daniel Veillard07cb8222003-09-10 10:51:05 +00004099 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value)
4100{
Owen Taylor3473f882001-02-23 17:55:21 +00004101 xmlAttributePtr attrDecl = NULL;
4102 int val;
4103 int ret = 1;
4104
4105 CHECK_DTD;
4106 if ((elem == NULL) || (elem->name == NULL)) return(0);
4107 if ((attr == NULL) || (attr->name == NULL)) return(0);
4108
4109 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004110 xmlChar fn[50];
4111 xmlChar *fullname;
4112
4113 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
4114 if (fullname == NULL)
4115 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004116 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004117 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00004118 attr->name, attr->ns->prefix);
4119 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004120 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00004121 attr->name, attr->ns->prefix);
4122 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004123 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004124 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4125 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00004126 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004127 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004128 if ((fullname != fn) && (fullname != elem->name))
4129 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00004130 }
4131 if (attrDecl == NULL) {
4132 if (attr->ns != NULL) {
4133 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4134 attr->name, attr->ns->prefix);
4135 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4136 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4137 attr->name, attr->ns->prefix);
4138 } else {
4139 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4140 elem->name, attr->name);
4141 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4142 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4143 elem->name, attr->name);
4144 }
4145 }
4146
4147
4148 /* Validity Constraint: Attribute Value Type */
4149 if (attrDecl == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004150 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004151 "No declaration for attribute %s of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004152 attr->name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004153 return(0);
4154 }
4155 attr->atype = attrDecl->atype;
4156
4157 val = xmlValidateAttributeValue(attrDecl->atype, value);
4158 if (val == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004159 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004160 "Syntax of value for attribute %s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004161 attr->name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004162 ret = 0;
4163 }
4164
4165 /* Validity constraint: Fixed Attribute Default */
4166 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4167 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004168 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004169 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004170 attr->name, elem->name, attrDecl->defaultValue);
4171 ret = 0;
4172 }
4173 }
4174
4175 /* Validity Constraint: ID uniqueness */
4176 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4177 if (xmlAddID(ctxt, doc, value, attr) == NULL)
4178 ret = 0;
4179 }
4180
4181 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4182 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4183 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
4184 ret = 0;
4185 }
4186
4187 /* Validity Constraint: Notation Attributes */
4188 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4189 xmlEnumerationPtr tree = attrDecl->tree;
4190 xmlNotationPtr nota;
4191
4192 /* First check that the given NOTATION was declared */
4193 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4194 if (nota == NULL)
4195 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4196
4197 if (nota == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004198 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004199 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004200 value, attr->name, elem->name);
4201 ret = 0;
4202 }
4203
4204 /* Second, verify that it's among the list */
4205 while (tree != NULL) {
4206 if (xmlStrEqual(tree->name, value)) break;
4207 tree = tree->next;
4208 }
4209 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004210 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004211"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004212 value, attr->name, elem->name);
4213 ret = 0;
4214 }
4215 }
4216
4217 /* Validity Constraint: Enumeration */
4218 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4219 xmlEnumerationPtr tree = attrDecl->tree;
4220 while (tree != NULL) {
4221 if (xmlStrEqual(tree->name, value)) break;
4222 tree = tree->next;
4223 }
4224 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004225 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004226 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004227 value, attr->name, elem->name);
4228 ret = 0;
4229 }
4230 }
4231
4232 /* Fixed Attribute Default */
4233 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4234 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004235 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004236 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004237 attr->name, elem->name, attrDecl->defaultValue);
4238 ret = 0;
4239 }
4240
4241 /* Extra check for the attribute value */
4242 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
4243 attrDecl->atype, value);
4244
4245 return(ret);
4246}
4247
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004248/**
4249 * xmlValidateOneNamespace:
4250 * @ctxt: the validation context
4251 * @doc: a document instance
4252 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00004253 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004254 * @ns: an namespace declaration instance
4255 * @value: the attribute value (without entities processing)
4256 *
4257 * Try to validate a single namespace declaration for an element
4258 * basically it does the following checks as described by the
4259 * XML-1.0 recommendation:
4260 * - [ VC: Attribute Value Type ]
4261 * - [ VC: Fixed Attribute Default ]
4262 * - [ VC: Entity Name ]
4263 * - [ VC: Name Token ]
4264 * - [ VC: ID ]
4265 * - [ VC: IDREF ]
4266 * - [ VC: Entity Name ]
4267 * - [ VC: Notation Attributes ]
4268 *
4269 * The ID/IDREF uniqueness and matching are done separately
4270 *
4271 * returns 1 if valid or 0 otherwise
4272 */
4273
4274int
4275xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4276xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
4277 /* xmlElementPtr elemDecl; */
4278 xmlAttributePtr attrDecl = NULL;
4279 int val;
4280 int ret = 1;
4281
4282 CHECK_DTD;
4283 if ((elem == NULL) || (elem->name == NULL)) return(0);
4284 if ((ns == NULL) || (ns->href == NULL)) return(0);
4285
4286 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004287 xmlChar fn[50];
4288 xmlChar *fullname;
4289
4290 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
4291 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00004292 xmlVErrMemory(ctxt, "Validating namespace");
Daniel Veillardc00cda82003-04-07 10:22:39 +00004293 return(0);
4294 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004295 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004296 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004297 ns->prefix, BAD_CAST "xmlns");
4298 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004299 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004300 ns->prefix, BAD_CAST "xmlns");
4301 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004302 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004303 BAD_CAST "xmlns");
4304 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004305 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004306 BAD_CAST "xmlns");
4307 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004308 if ((fullname != fn) && (fullname != elem->name))
4309 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004310 }
4311 if (attrDecl == NULL) {
4312 if (ns->prefix != NULL) {
4313 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4314 ns->prefix, BAD_CAST "xmlns");
4315 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4316 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4317 ns->prefix, BAD_CAST "xmlns");
4318 } else {
4319 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4320 elem->name, BAD_CAST "xmlns");
4321 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4322 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4323 elem->name, BAD_CAST "xmlns");
4324 }
4325 }
4326
4327
4328 /* Validity Constraint: Attribute Value Type */
4329 if (attrDecl == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004330 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004331 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004332 "No declaration for attribute xmlns:%s of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004333 ns->prefix, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004334 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004335 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004336 "No declaration for attribute xmlns of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004337 elem->name, NULL, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004338 }
4339 return(0);
4340 }
4341
4342 val = xmlValidateAttributeValue(attrDecl->atype, value);
4343 if (val == 0) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004344 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004345 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004346 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004347 ns->prefix, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004348 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004349 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004350 "Syntax of value for attribute xmlns of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004351 elem->name, NULL, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004352 }
4353 ret = 0;
4354 }
4355
4356 /* Validity constraint: Fixed Attribute Default */
4357 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4358 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004359 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004360 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004361 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4362 ns->prefix, elem->name, attrDecl->defaultValue);
4363 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004364 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004365 "Value for attribute xmlns of %s is different from default \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004366 elem->name, attrDecl->defaultValue, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004367 }
4368 ret = 0;
4369 }
4370 }
4371
4372 /* Validity Constraint: ID uniqueness */
4373 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4374 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4375 ret = 0;
4376 }
4377
4378 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4379 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4380 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4381 ret = 0;
4382 }
4383
4384 /* Validity Constraint: Notation Attributes */
4385 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4386 xmlEnumerationPtr tree = attrDecl->tree;
4387 xmlNotationPtr nota;
4388
4389 /* First check that the given NOTATION was declared */
4390 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4391 if (nota == NULL)
4392 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4393
4394 if (nota == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004395 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004396 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004397 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4398 value, ns->prefix, elem->name);
4399 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004400 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004401 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004402 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004403 }
4404 ret = 0;
4405 }
4406
4407 /* Second, verify that it's among the list */
4408 while (tree != NULL) {
4409 if (xmlStrEqual(tree->name, value)) break;
4410 tree = tree->next;
4411 }
4412 if (tree == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004413 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004414 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004415"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4416 value, ns->prefix, elem->name);
4417 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004418 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004419"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004420 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004421 }
4422 ret = 0;
4423 }
4424 }
4425
4426 /* Validity Constraint: Enumeration */
4427 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4428 xmlEnumerationPtr tree = attrDecl->tree;
4429 while (tree != NULL) {
4430 if (xmlStrEqual(tree->name, value)) break;
4431 tree = tree->next;
4432 }
4433 if (tree == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004434 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004435 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004436"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4437 value, ns->prefix, elem->name);
4438 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004439 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004440"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004441 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004442 }
4443 ret = 0;
4444 }
4445 }
4446
4447 /* Fixed Attribute Default */
4448 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4449 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004450 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004451 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004452 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4453 ns->prefix, elem->name, attrDecl->defaultValue);
4454 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004455 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004456 "Value for attribute xmlns of %s must be \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004457 elem->name, attrDecl->defaultValue, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004458 }
4459 ret = 0;
4460 }
4461
4462 /* Extra check for the attribute value */
4463 if (ns->prefix != NULL) {
4464 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4465 attrDecl->atype, value);
4466 } else {
4467 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4468 attrDecl->atype, value);
4469 }
4470
4471 return(ret);
4472}
4473
Daniel Veillard118aed72002-09-24 14:13:13 +00004474#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004475/**
4476 * xmlValidateSkipIgnorable:
4477 * @ctxt: the validation context
4478 * @child: the child list
4479 *
4480 * Skip ignorable elements w.r.t. the validation process
4481 *
4482 * returns the first element to consider for validation of the content model
4483 */
4484
4485static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004486xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004487 while (child != NULL) {
4488 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004489 /* These things are ignored (skipped) during validation. */
4490 case XML_PI_NODE:
4491 case XML_COMMENT_NODE:
4492 case XML_XINCLUDE_START:
4493 case XML_XINCLUDE_END:
4494 child = child->next;
4495 break;
4496 case XML_TEXT_NODE:
4497 if (xmlIsBlankNode(child))
4498 child = child->next;
4499 else
4500 return(child);
4501 break;
4502 /* keep current node */
4503 default:
4504 return(child);
4505 }
4506 }
4507 return(child);
4508}
4509
4510/**
4511 * xmlValidateElementType:
4512 * @ctxt: the validation context
4513 *
4514 * Try to validate the content model of an element internal function
4515 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004516 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4517 * reference is found and -3 if the validation succeeded but
4518 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004519 */
4520
4521static int
4522xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004523 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004524 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004525
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004526 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004527 if ((NODE == NULL) && (CONT == NULL))
4528 return(1);
4529 if ((NODE == NULL) &&
4530 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4531 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4532 return(1);
4533 }
4534 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004535 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004536 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004537
4538 /*
4539 * We arrive here when more states need to be examined
4540 */
4541cont:
4542
4543 /*
4544 * We just recovered from a rollback generated by a possible
4545 * epsilon transition, go directly to the analysis phase
4546 */
4547 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004548 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004549 DEBUG_VALID_STATE(NODE, CONT)
4550 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004551 goto analyze;
4552 }
4553
4554 DEBUG_VALID_STATE(NODE, CONT)
4555 /*
4556 * we may have to save a backup state here. This is the equivalent
4557 * of handling epsilon transition in NFAs.
4558 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004559 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004560 ((CONT->parent == NULL) ||
4561 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004562 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004563 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004564 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004565 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004566 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4567 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004568 }
4569
4570
4571 /*
4572 * Check first if the content matches
4573 */
4574 switch (CONT->type) {
4575 case XML_ELEMENT_CONTENT_PCDATA:
4576 if (NODE == NULL) {
4577 DEBUG_VALID_MSG("pcdata failed no node");
4578 ret = 0;
4579 break;
4580 }
4581 if (NODE->type == XML_TEXT_NODE) {
4582 DEBUG_VALID_MSG("pcdata found, skip to next");
4583 /*
4584 * go to next element in the content model
4585 * skipping ignorable elems
4586 */
4587 do {
4588 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004589 NODE = xmlValidateSkipIgnorable(NODE);
4590 if ((NODE != NULL) &&
4591 (NODE->type == XML_ENTITY_REF_NODE))
4592 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004593 } while ((NODE != NULL) &&
4594 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004595 (NODE->type != XML_TEXT_NODE) &&
4596 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004597 ret = 1;
4598 break;
4599 } else {
4600 DEBUG_VALID_MSG("pcdata failed");
4601 ret = 0;
4602 break;
4603 }
4604 break;
4605 case XML_ELEMENT_CONTENT_ELEMENT:
4606 if (NODE == NULL) {
4607 DEBUG_VALID_MSG("element failed no node");
4608 ret = 0;
4609 break;
4610 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004611 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4612 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004613 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004614 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4615 ret = (CONT->prefix == NULL);
4616 } else if (CONT->prefix == NULL) {
4617 ret = 0;
4618 } else {
4619 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4620 }
4621 }
4622 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004623 DEBUG_VALID_MSG("element found, skip to next");
4624 /*
4625 * go to next element in the content model
4626 * skipping ignorable elems
4627 */
4628 do {
4629 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004630 NODE = xmlValidateSkipIgnorable(NODE);
4631 if ((NODE != NULL) &&
4632 (NODE->type == XML_ENTITY_REF_NODE))
4633 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004634 } while ((NODE != NULL) &&
4635 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004636 (NODE->type != XML_TEXT_NODE) &&
4637 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004638 } else {
4639 DEBUG_VALID_MSG("element failed");
4640 ret = 0;
4641 break;
4642 }
4643 break;
4644 case XML_ELEMENT_CONTENT_OR:
4645 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004646 * Small optimization.
4647 */
4648 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4649 if ((NODE == NULL) ||
4650 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4651 DEPTH++;
4652 CONT = CONT->c2;
4653 goto cont;
4654 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004655 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4656 ret = (CONT->c1->prefix == NULL);
4657 } else if (CONT->c1->prefix == NULL) {
4658 ret = 0;
4659 } else {
4660 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4661 }
4662 if (ret == 0) {
4663 DEPTH++;
4664 CONT = CONT->c2;
4665 goto cont;
4666 }
Daniel Veillard85349052001-04-20 13:48:21 +00004667 }
4668
4669 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004670 * save the second branch 'or' branch
4671 */
4672 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004673 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4674 OCCURS, ROLLBACK_OR) < 0)
4675 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004676 DEPTH++;
4677 CONT = CONT->c1;
4678 goto cont;
4679 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004680 /*
4681 * Small optimization.
4682 */
4683 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4684 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4685 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4686 if ((NODE == NULL) ||
4687 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4688 DEPTH++;
4689 CONT = CONT->c2;
4690 goto cont;
4691 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004692 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4693 ret = (CONT->c1->prefix == NULL);
4694 } else if (CONT->c1->prefix == NULL) {
4695 ret = 0;
4696 } else {
4697 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4698 }
4699 if (ret == 0) {
4700 DEPTH++;
4701 CONT = CONT->c2;
4702 goto cont;
4703 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004704 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004705 DEPTH++;
4706 CONT = CONT->c1;
4707 goto cont;
4708 }
4709
4710 /*
4711 * At this point handle going up in the tree
4712 */
4713 if (ret == -1) {
4714 DEBUG_VALID_MSG("error found returning");
4715 return(ret);
4716 }
4717analyze:
4718 while (CONT != NULL) {
4719 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004720 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004721 * this level.
4722 */
4723 if (ret == 0) {
4724 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004725 xmlNodePtr cur;
4726
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004727 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004728 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004729 DEBUG_VALID_MSG("Once branch failed, rollback");
4730 if (vstateVPop(ctxt) < 0 ) {
4731 DEBUG_VALID_MSG("exhaustion, failed");
4732 return(0);
4733 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004734 if (cur != ctxt->vstate->node)
4735 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004736 goto cont;
4737 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004738 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004739 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004740 DEBUG_VALID_MSG("Plus branch failed, rollback");
4741 if (vstateVPop(ctxt) < 0 ) {
4742 DEBUG_VALID_MSG("exhaustion, failed");
4743 return(0);
4744 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004745 if (cur != ctxt->vstate->node)
4746 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004747 goto cont;
4748 }
4749 DEBUG_VALID_MSG("Plus branch found");
4750 ret = 1;
4751 break;
4752 case XML_ELEMENT_CONTENT_MULT:
4753#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004754 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004755 DEBUG_VALID_MSG("Mult branch failed");
4756 } else {
4757 DEBUG_VALID_MSG("Mult branch found");
4758 }
4759#endif
4760 ret = 1;
4761 break;
4762 case XML_ELEMENT_CONTENT_OPT:
4763 DEBUG_VALID_MSG("Option branch failed");
4764 ret = 1;
4765 break;
4766 }
4767 } else {
4768 switch (CONT->ocur) {
4769 case XML_ELEMENT_CONTENT_OPT:
4770 DEBUG_VALID_MSG("Option branch succeeded");
4771 ret = 1;
4772 break;
4773 case XML_ELEMENT_CONTENT_ONCE:
4774 DEBUG_VALID_MSG("Once branch succeeded");
4775 ret = 1;
4776 break;
4777 case XML_ELEMENT_CONTENT_PLUS:
4778 if (STATE == ROLLBACK_PARENT) {
4779 DEBUG_VALID_MSG("Plus branch rollback");
4780 ret = 1;
4781 break;
4782 }
4783 if (NODE == NULL) {
4784 DEBUG_VALID_MSG("Plus branch exhausted");
4785 ret = 1;
4786 break;
4787 }
4788 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004789 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004790 goto cont;
4791 case XML_ELEMENT_CONTENT_MULT:
4792 if (STATE == ROLLBACK_PARENT) {
4793 DEBUG_VALID_MSG("Mult branch rollback");
4794 ret = 1;
4795 break;
4796 }
4797 if (NODE == NULL) {
4798 DEBUG_VALID_MSG("Mult branch exhausted");
4799 ret = 1;
4800 break;
4801 }
4802 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004803 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004804 goto cont;
4805 }
4806 }
4807 STATE = 0;
4808
4809 /*
4810 * Then act accordingly at the parent level
4811 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004812 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004813 if (CONT->parent == NULL)
4814 break;
4815
4816 switch (CONT->parent->type) {
4817 case XML_ELEMENT_CONTENT_PCDATA:
4818 DEBUG_VALID_MSG("Error: parent pcdata");
4819 return(-1);
4820 case XML_ELEMENT_CONTENT_ELEMENT:
4821 DEBUG_VALID_MSG("Error: parent element");
4822 return(-1);
4823 case XML_ELEMENT_CONTENT_OR:
4824 if (ret == 1) {
4825 DEBUG_VALID_MSG("Or succeeded");
4826 CONT = CONT->parent;
4827 DEPTH--;
4828 } else {
4829 DEBUG_VALID_MSG("Or failed");
4830 CONT = CONT->parent;
4831 DEPTH--;
4832 }
4833 break;
4834 case XML_ELEMENT_CONTENT_SEQ:
4835 if (ret == 0) {
4836 DEBUG_VALID_MSG("Sequence failed");
4837 CONT = CONT->parent;
4838 DEPTH--;
4839 } else if (CONT == CONT->parent->c1) {
4840 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4841 CONT = CONT->parent->c2;
4842 goto cont;
4843 } else {
4844 DEBUG_VALID_MSG("Sequence succeeded");
4845 CONT = CONT->parent;
4846 DEPTH--;
4847 }
4848 }
4849 }
4850 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004851 xmlNodePtr cur;
4852
4853 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004854 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4855 if (vstateVPop(ctxt) < 0 ) {
4856 DEBUG_VALID_MSG("exhaustion, failed");
4857 return(0);
4858 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004859 if (cur != ctxt->vstate->node)
4860 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004861 goto cont;
4862 }
4863 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004864 xmlNodePtr cur;
4865
4866 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004867 DEBUG_VALID_MSG("Failure, rollback");
4868 if (vstateVPop(ctxt) < 0 ) {
4869 DEBUG_VALID_MSG("exhaustion, failed");
4870 return(0);
4871 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004872 if (cur != ctxt->vstate->node)
4873 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004874 goto cont;
4875 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004876 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004877}
Daniel Veillard23e73572002-09-19 19:56:43 +00004878#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004879
4880/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004881 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004882 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004883 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004884 * @content: An element
4885 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4886 *
4887 * This will dump the list of elements to the buffer
4888 * Intended just for the debug routine
4889 */
4890static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004891xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004892 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004893 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004894
4895 if (node == NULL) return;
4896 if (glob) strcat(buf, "(");
4897 cur = node;
4898 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004899 len = strlen(buf);
4900 if (size - len < 50) {
4901 if ((size - len > 4) && (buf[len - 1] != '.'))
4902 strcat(buf, " ...");
4903 return;
4904 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004905 switch (cur->type) {
4906 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004907 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004908 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004909 if ((size - len > 4) && (buf[len - 1] != '.'))
4910 strcat(buf, " ...");
4911 return;
4912 }
4913 strcat(buf, (char *) cur->ns->prefix);
4914 strcat(buf, ":");
4915 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004916 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004917 if ((size - len > 4) && (buf[len - 1] != '.'))
4918 strcat(buf, " ...");
4919 return;
4920 }
4921 strcat(buf, (char *) cur->name);
4922 if (cur->next != NULL)
4923 strcat(buf, " ");
4924 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004925 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004926 if (xmlIsBlankNode(cur))
4927 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004928 case XML_CDATA_SECTION_NODE:
4929 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004930 strcat(buf, "CDATA");
4931 if (cur->next != NULL)
4932 strcat(buf, " ");
4933 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004934 case XML_ATTRIBUTE_NODE:
4935 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004936#ifdef LIBXML_DOCB_ENABLED
4937 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004938#endif
4939 case XML_HTML_DOCUMENT_NODE:
4940 case XML_DOCUMENT_TYPE_NODE:
4941 case XML_DOCUMENT_FRAG_NODE:
4942 case XML_NOTATION_NODE:
4943 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004944 strcat(buf, "???");
4945 if (cur->next != NULL)
4946 strcat(buf, " ");
4947 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004948 case XML_ENTITY_NODE:
4949 case XML_PI_NODE:
4950 case XML_DTD_NODE:
4951 case XML_COMMENT_NODE:
4952 case XML_ELEMENT_DECL:
4953 case XML_ATTRIBUTE_DECL:
4954 case XML_ENTITY_DECL:
4955 case XML_XINCLUDE_START:
4956 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004957 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004958 }
4959 cur = cur->next;
4960 }
4961 if (glob) strcat(buf, ")");
4962}
4963
4964/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004965 * xmlValidateElementContent:
4966 * @ctxt: the validation context
4967 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004968 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004969 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004970 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004971 *
4972 * Try to validate the content model of an element
4973 *
4974 * returns 1 if valid or 0 if not and -1 in case of error
4975 */
4976
4977static int
4978xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004979 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004980 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004981#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004982 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004983#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004984 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004985 xmlElementContentPtr cont;
4986 const xmlChar *name;
4987
4988 if (elemDecl == NULL)
4989 return(-1);
4990 cont = elemDecl->content;
4991 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004992
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004993#ifdef LIBXML_REGEXP_ENABLED
4994 /* Build the regexp associated to the content model */
4995 if (elemDecl->contModel == NULL)
4996 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4997 if (elemDecl->contModel == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004998 return(-1);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004999 } else {
5000 xmlRegExecCtxtPtr exec;
5001
Daniel Veillardec498e12003-02-05 11:01:50 +00005002 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
5003 return(-1);
5004 }
Daniel Veillard01992e02002-10-09 10:20:30 +00005005 ctxt->nodeMax = 0;
5006 ctxt->nodeNr = 0;
5007 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005008 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
5009 if (exec != NULL) {
5010 cur = child;
5011 while (cur != NULL) {
5012 switch (cur->type) {
5013 case XML_ENTITY_REF_NODE:
5014 /*
5015 * Push the current node to be able to roll back
5016 * and process within the entity
5017 */
5018 if ((cur->children != NULL) &&
5019 (cur->children->children != NULL)) {
5020 nodeVPush(ctxt, cur);
5021 cur = cur->children->children;
5022 continue;
5023 }
5024 break;
5025 case XML_TEXT_NODE:
5026 if (xmlIsBlankNode(cur))
5027 break;
5028 ret = 0;
5029 goto fail;
5030 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00005031 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005032 ret = 0;
5033 goto fail;
5034 case XML_ELEMENT_NODE:
5035 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005036 xmlChar fn[50];
5037 xmlChar *fullname;
5038
5039 fullname = xmlBuildQName(cur->name,
5040 cur->ns->prefix, fn, 50);
5041 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005042 ret = -1;
5043 goto fail;
5044 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005045 ret = xmlRegExecPushString(exec, fullname, NULL);
5046 if ((fullname != fn) && (fullname != cur->name))
5047 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005048 } else {
5049 ret = xmlRegExecPushString(exec, cur->name, NULL);
5050 }
5051 break;
5052 default:
5053 break;
5054 }
5055 /*
5056 * Switch to next element
5057 */
5058 cur = cur->next;
5059 while (cur == NULL) {
5060 cur = nodeVPop(ctxt);
5061 if (cur == NULL)
5062 break;
5063 cur = cur->next;
5064 }
5065 }
5066 ret = xmlRegExecPushString(exec, NULL, NULL);
5067fail:
5068 xmlRegFreeExecCtxt(exec);
5069 }
5070 }
5071#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005072 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005073 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005074 */
5075 ctxt->vstateMax = 8;
5076 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
5077 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
5078 if (ctxt->vstateTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00005079 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005080 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005081 }
5082 /*
5083 * The first entry in the stack is reserved to the current state
5084 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00005085 ctxt->nodeMax = 0;
5086 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00005087 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005088 ctxt->vstate = &ctxt->vstateTab[0];
5089 ctxt->vstateNr = 1;
5090 CONT = cont;
5091 NODE = child;
5092 DEPTH = 0;
5093 OCCURS = 0;
5094 STATE = 0;
5095 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005096 if ((ret == -3) && (warn)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005097 xmlErrValidWarning(ctxt, child, XML_DTD_CONTENT_NOT_DETERMINIST,
5098 "Content model for Element %s is ambiguous\n",
Daniel Veillard0cc72772003-10-13 14:00:21 +00005099 name, NULL, NULL);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005100 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005101 /*
5102 * An entities reference appeared at this level.
5103 * Buid a minimal representation of this node content
5104 * sufficient to run the validation process on it
5105 */
5106 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005107 cur = child;
5108 while (cur != NULL) {
5109 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005110 case XML_ENTITY_REF_NODE:
5111 /*
5112 * Push the current node to be able to roll back
5113 * and process within the entity
5114 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005115 if ((cur->children != NULL) &&
5116 (cur->children->children != NULL)) {
5117 nodeVPush(ctxt, cur);
5118 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005119 continue;
5120 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00005121 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005122 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005123 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005124 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005125 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00005126 case XML_CDATA_SECTION_NODE:
5127 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005128 case XML_ELEMENT_NODE:
5129 /*
5130 * Allocate a new node and minimally fills in
5131 * what's required
5132 */
5133 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
5134 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00005135 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005136 xmlFreeNodeList(repl);
5137 ret = -1;
5138 goto done;
5139 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005140 tmp->type = cur->type;
5141 tmp->name = cur->name;
5142 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005143 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00005144 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005145 if (repl == NULL)
5146 repl = last = tmp;
5147 else {
5148 last->next = tmp;
5149 last = tmp;
5150 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00005151 if (cur->type == XML_CDATA_SECTION_NODE) {
5152 /*
5153 * E59 spaces in CDATA does not match the
5154 * nonterminal S
5155 */
5156 tmp->content = xmlStrdup(BAD_CAST "CDATA");
5157 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005158 break;
5159 default:
5160 break;
5161 }
5162 /*
5163 * Switch to next element
5164 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005165 cur = cur->next;
5166 while (cur == NULL) {
5167 cur = nodeVPop(ctxt);
5168 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005169 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005170 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005171 }
5172 }
5173
5174 /*
5175 * Relaunch the validation
5176 */
5177 ctxt->vstate = &ctxt->vstateTab[0];
5178 ctxt->vstateNr = 1;
5179 CONT = cont;
5180 NODE = repl;
5181 DEPTH = 0;
5182 OCCURS = 0;
5183 STATE = 0;
5184 ret = xmlValidateElementType(ctxt);
5185 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005186#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005187 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005188 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
5189 char expr[5000];
5190 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005191
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005192 expr[0] = 0;
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005193 xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005194 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00005195#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005196 if (repl != NULL)
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005197 xmlSnprintfElements(&list[0], 5000, repl, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005198 else
Daniel Veillard01992e02002-10-09 10:20:30 +00005199#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005200 xmlSnprintfElements(&list[0], 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005201
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005202 if (name != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005203 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5204 "Element %s content does not follow the DTD, expecting %s, got %s\n",
5205 name, BAD_CAST expr, BAD_CAST list);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005206 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005207 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5208 "Element content does not follow the DTD, expecting %s, got %s\n",
5209 BAD_CAST expr, BAD_CAST list, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005210 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005211 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005212 if (name != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005213 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005214 "Element %s content does not follow the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005215 name, NULL, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005216 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005217 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5218 "Element content does not follow the DTD\n",
5219 NULL, NULL, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005220 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005221 }
5222 ret = 0;
5223 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005224 if (ret == -3)
5225 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005226
Daniel Veillard23e73572002-09-19 19:56:43 +00005227#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005228done:
5229 /*
5230 * Deallocate the copy if done, and free up the validation stack
5231 */
5232 while (repl != NULL) {
5233 tmp = repl->next;
5234 xmlFree(repl);
5235 repl = tmp;
5236 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005237 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005238 if (ctxt->vstateTab != NULL) {
5239 xmlFree(ctxt->vstateTab);
5240 ctxt->vstateTab = NULL;
5241 }
Daniel Veillard01992e02002-10-09 10:20:30 +00005242#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005243 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00005244 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005245 if (ctxt->nodeTab != NULL) {
5246 xmlFree(ctxt->nodeTab);
5247 ctxt->nodeTab = NULL;
5248 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005249 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005250
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005251}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005252
Owen Taylor3473f882001-02-23 17:55:21 +00005253/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005254 * xmlValidateCdataElement:
5255 * @ctxt: the validation context
5256 * @doc: a document instance
5257 * @elem: an element instance
5258 *
5259 * Check that an element follows #CDATA
5260 *
5261 * returns 1 if valid or 0 otherwise
5262 */
5263static int
5264xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5265 xmlNodePtr elem) {
5266 int ret = 1;
5267 xmlNodePtr cur, child;
5268
Daniel Veillardceb09b92002-10-04 11:46:37 +00005269 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005270 return(0);
5271
5272 child = elem->children;
5273
5274 cur = child;
5275 while (cur != NULL) {
5276 switch (cur->type) {
5277 case XML_ENTITY_REF_NODE:
5278 /*
5279 * Push the current node to be able to roll back
5280 * and process within the entity
5281 */
5282 if ((cur->children != NULL) &&
5283 (cur->children->children != NULL)) {
5284 nodeVPush(ctxt, cur);
5285 cur = cur->children->children;
5286 continue;
5287 }
5288 break;
5289 case XML_COMMENT_NODE:
5290 case XML_PI_NODE:
5291 case XML_TEXT_NODE:
5292 case XML_CDATA_SECTION_NODE:
5293 break;
5294 default:
5295 ret = 0;
5296 goto done;
5297 }
5298 /*
5299 * Switch to next element
5300 */
5301 cur = cur->next;
5302 while (cur == NULL) {
5303 cur = nodeVPop(ctxt);
5304 if (cur == NULL)
5305 break;
5306 cur = cur->next;
5307 }
5308 }
5309done:
5310 ctxt->nodeMax = 0;
5311 ctxt->nodeNr = 0;
5312 if (ctxt->nodeTab != NULL) {
5313 xmlFree(ctxt->nodeTab);
5314 ctxt->nodeTab = NULL;
5315 }
5316 return(ret);
5317}
5318
5319/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005320 * xmlValidateCheckMixed:
5321 * @ctxt: the validation context
5322 * @cont: the mixed content model
5323 * @qname: the qualified name as appearing in the serialization
5324 *
5325 * Check if the given node is part of the content model.
5326 *
5327 * Returns 1 if yes, 0 if no, -1 in case of error
5328 */
5329static int
William M. Brackedb65a72004-02-06 07:36:04 +00005330xmlValidateCheckMixed(xmlValidCtxtPtr ctxt,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005331 xmlElementContentPtr cont, const xmlChar *qname) {
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005332 const xmlChar *name;
5333 int plen;
5334 name = xmlSplitQName3(qname, &plen);
5335
5336 if (name == NULL) {
5337 while (cont != NULL) {
5338 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5339 if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname)))
5340 return(1);
5341 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5342 (cont->c1 != NULL) &&
5343 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5344 if ((cont->c1->prefix == NULL) &&
5345 (xmlStrEqual(cont->c1->name, qname)))
5346 return(1);
5347 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5348 (cont->c1 == NULL) ||
5349 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005350 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5351 "Internal: MIXED struct corrupted\n",
5352 NULL);
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005353 break;
5354 }
5355 cont = cont->c2;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005356 }
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005357 } else {
5358 while (cont != NULL) {
5359 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5360 if ((cont->prefix != NULL) &&
5361 (xmlStrncmp(cont->prefix, qname, plen) == 0) &&
5362 (xmlStrEqual(cont->name, name)))
5363 return(1);
5364 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5365 (cont->c1 != NULL) &&
5366 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5367 if ((cont->c1->prefix != NULL) &&
5368 (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) &&
5369 (xmlStrEqual(cont->c1->name, name)))
5370 return(1);
5371 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5372 (cont->c1 == NULL) ||
5373 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005374 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
5375 "Internal: MIXED struct corrupted\n",
5376 NULL);
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005377 break;
5378 }
5379 cont = cont->c2;
5380 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005381 }
5382 return(0);
5383}
5384
5385/**
5386 * xmlValidGetElemDecl:
5387 * @ctxt: the validation context
5388 * @doc: a document instance
5389 * @elem: an element instance
5390 * @extsubset: pointer, (out) indicate if the declaration was found
5391 * in the external subset.
5392 *
5393 * Finds a declaration associated to an element in the document.
5394 *
5395 * returns the pointer to the declaration or NULL if not found.
5396 */
5397static xmlElementPtr
5398xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5399 xmlNodePtr elem, int *extsubset) {
5400 xmlElementPtr elemDecl = NULL;
5401 const xmlChar *prefix = NULL;
5402
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005403 if ((ctxt == NULL) || (doc == NULL) ||
5404 (elem == NULL) || (elem->name == NULL))
5405 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005406 if (extsubset != NULL)
5407 *extsubset = 0;
5408
5409 /*
5410 * Fetch the declaration for the qualified name
5411 */
5412 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5413 prefix = elem->ns->prefix;
5414
5415 if (prefix != NULL) {
5416 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5417 elem->name, prefix);
5418 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5419 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5420 elem->name, prefix);
5421 if ((elemDecl != NULL) && (extsubset != NULL))
5422 *extsubset = 1;
5423 }
5424 }
5425
5426 /*
5427 * Fetch the declaration for the non qualified name
5428 * This is "non-strict" validation should be done on the
5429 * full QName but in that case being flexible makes sense.
5430 */
5431 if (elemDecl == NULL) {
5432 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5433 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5434 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5435 if ((elemDecl != NULL) && (extsubset != NULL))
5436 *extsubset = 1;
5437 }
5438 }
5439 if (elemDecl == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005440 xmlErrValidNode(ctxt, elem,
5441 XML_DTD_UNKNOWN_ELEM,
5442 "No declaration for element %s\n",
5443 elem->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005444 }
5445 return(elemDecl);
5446}
5447
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005448#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005449/**
5450 * xmlValidatePushElement:
5451 * @ctxt: the validation context
5452 * @doc: a document instance
5453 * @elem: an element instance
5454 * @qname: the qualified name as appearing in the serialization
5455 *
5456 * Push a new element start on the validation stack.
5457 *
5458 * returns 1 if no validation problem was found or 0 otherwise
5459 */
5460int
5461xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5462 xmlNodePtr elem, const xmlChar *qname) {
5463 int ret = 1;
5464 xmlElementPtr eDecl;
5465 int extsubset = 0;
5466
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005467 if (ctxt == NULL)
5468 return(0);
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005469/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005470 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5471 xmlValidStatePtr state = ctxt->vstate;
5472 xmlElementPtr elemDecl;
5473
5474 /*
5475 * Check the new element agaisnt the content model of the new elem.
5476 */
5477 if (state->elemDecl != NULL) {
5478 elemDecl = state->elemDecl;
5479
5480 switch(elemDecl->etype) {
5481 case XML_ELEMENT_TYPE_UNDEFINED:
5482 ret = 0;
5483 break;
5484 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005485 xmlErrValidNode(ctxt, state->node,
5486 XML_DTD_NOT_EMPTY,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005487 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005488 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005489 ret = 0;
5490 break;
5491 case XML_ELEMENT_TYPE_ANY:
5492 /* I don't think anything is required then */
5493 break;
5494 case XML_ELEMENT_TYPE_MIXED:
5495 /* simple case of declared as #PCDATA */
5496 if ((elemDecl->content != NULL) &&
5497 (elemDecl->content->type ==
5498 XML_ELEMENT_CONTENT_PCDATA)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005499 xmlErrValidNode(ctxt, state->node,
5500 XML_DTD_NOT_PCDATA,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005501 "Element %s was declared #PCDATA but contains non text nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005502 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005503 ret = 0;
5504 } else {
5505 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5506 qname);
5507 if (ret != 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005508 xmlErrValidNode(ctxt, state->node,
5509 XML_DTD_INVALID_CHILD,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005510 "Element %s is not declared in %s list of possible children\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005511 qname, state->node->name, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005512 }
5513 }
5514 break;
5515 case XML_ELEMENT_TYPE_ELEMENT:
5516 /*
5517 * TODO:
5518 * VC: Standalone Document Declaration
5519 * - element types with element content, if white space
5520 * occurs directly within any instance of those types.
5521 */
5522 if (state->exec != NULL) {
5523 ret = xmlRegExecPushString(state->exec, qname, NULL);
5524 if (ret < 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005525 xmlErrValidNode(ctxt, state->node,
5526 XML_DTD_CONTENT_MODEL,
5527 "Element %s content does not follow the DTD, Misplaced %s\n",
5528 state->node->name, qname, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005529 ret = 0;
5530 } else {
5531 ret = 1;
5532 }
5533 }
5534 break;
5535 }
5536 }
5537 }
5538 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5539 vstateVPush(ctxt, eDecl, elem);
5540 return(ret);
5541}
5542
5543/**
5544 * xmlValidatePushCData:
5545 * @ctxt: the validation context
5546 * @data: some character data read
5547 * @len: the lenght of the data
5548 *
5549 * check the CData parsed for validation in the current stack
5550 *
5551 * returns 1 if no validation problem was found or 0 otherwise
5552 */
5553int
5554xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5555 int ret = 1;
5556
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005557/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005558 if (ctxt == NULL)
5559 return(0);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005560 if (len <= 0)
5561 return(ret);
5562 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5563 xmlValidStatePtr state = ctxt->vstate;
5564 xmlElementPtr elemDecl;
5565
5566 /*
5567 * Check the new element agaisnt the content model of the new elem.
5568 */
5569 if (state->elemDecl != NULL) {
5570 elemDecl = state->elemDecl;
5571
5572 switch(elemDecl->etype) {
5573 case XML_ELEMENT_TYPE_UNDEFINED:
5574 ret = 0;
5575 break;
5576 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005577 xmlErrValidNode(ctxt, state->node,
5578 XML_DTD_NOT_EMPTY,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005579 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005580 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005581 ret = 0;
5582 break;
5583 case XML_ELEMENT_TYPE_ANY:
5584 break;
5585 case XML_ELEMENT_TYPE_MIXED:
5586 break;
5587 case XML_ELEMENT_TYPE_ELEMENT:
5588 if (len > 0) {
5589 int i;
5590
5591 for (i = 0;i < len;i++) {
William M. Brack76e95df2003-10-18 16:20:14 +00005592 if (!IS_BLANK_CH(data[i])) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005593 xmlErrValidNode(ctxt, state->node,
5594 XML_DTD_CONTENT_MODEL,
5595 "Element %s content does not follow the DTD, Text not allowed\n",
5596 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005597 ret = 0;
5598 goto done;
5599 }
5600 }
5601 /*
5602 * TODO:
5603 * VC: Standalone Document Declaration
5604 * element types with element content, if white space
5605 * occurs directly within any instance of those types.
5606 */
5607 }
5608 break;
5609 }
5610 }
5611 }
5612done:
5613 return(ret);
5614}
5615
5616/**
5617 * xmlValidatePopElement:
5618 * @ctxt: the validation context
5619 * @doc: a document instance
5620 * @elem: an element instance
5621 * @qname: the qualified name as appearing in the serialization
5622 *
5623 * Pop the element end from the validation stack.
5624 *
5625 * returns 1 if no validation problem was found or 0 otherwise
5626 */
5627int
5628xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005629 xmlNodePtr elem ATTRIBUTE_UNUSED,
5630 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005631 int ret = 1;
5632
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005633 if (ctxt == NULL)
5634 return(0);
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005635/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005636 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5637 xmlValidStatePtr state = ctxt->vstate;
5638 xmlElementPtr elemDecl;
5639
5640 /*
5641 * Check the new element agaisnt the content model of the new elem.
5642 */
5643 if (state->elemDecl != NULL) {
5644 elemDecl = state->elemDecl;
5645
5646 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5647 if (state->exec != NULL) {
5648 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5649 if (ret == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005650 xmlErrValidNode(ctxt, state->node,
5651 XML_DTD_CONTENT_MODEL,
5652 "Element %s content does not follow the DTD, Expecting more child\n",
5653 state->node->name, NULL,NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005654 } else {
5655 /*
5656 * previous validation errors should not generate
5657 * a new one here
5658 */
5659 ret = 1;
5660 }
5661 }
5662 }
5663 }
5664 vstateVPop(ctxt);
5665 }
5666 return(ret);
5667}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005668#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005669
5670/**
Owen Taylor3473f882001-02-23 17:55:21 +00005671 * xmlValidateOneElement:
5672 * @ctxt: the validation context
5673 * @doc: a document instance
5674 * @elem: an element instance
5675 *
5676 * Try to validate a single element and it's attributes,
5677 * basically it does the following checks as described by the
5678 * XML-1.0 recommendation:
5679 * - [ VC: Element Valid ]
5680 * - [ VC: Required Attribute ]
5681 * Then call xmlValidateOneAttribute() for each attribute present.
5682 *
5683 * The ID/IDREF checkings are done separately
5684 *
5685 * returns 1 if valid or 0 otherwise
5686 */
5687
5688int
5689xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5690 xmlNodePtr elem) {
5691 xmlElementPtr elemDecl = NULL;
5692 xmlElementContentPtr cont;
5693 xmlAttributePtr attr;
5694 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005695 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005696 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005697 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005698
5699 CHECK_DTD;
5700
5701 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005702 switch (elem->type) {
5703 case XML_ATTRIBUTE_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005704 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5705 "Attribute element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005706 return(0);
5707 case XML_TEXT_NODE:
5708 if (elem->children != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005709 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5710 "Text element has children !\n",
5711 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005712 return(0);
5713 }
5714 if (elem->properties != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005715 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5716 "Text element has attribute !\n",
5717 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005718 return(0);
5719 }
5720 if (elem->ns != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005721 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5722 "Text element has namespace !\n",
5723 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005724 return(0);
5725 }
5726 if (elem->nsDef != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005727 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5728 "Text element has namespace !\n",
5729 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005730 return(0);
5731 }
5732 if (elem->content == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005733 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5734 "Text element has no content !\n",
5735 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005736 return(0);
5737 }
5738 return(1);
5739 case XML_XINCLUDE_START:
5740 case XML_XINCLUDE_END:
5741 return(1);
5742 case XML_CDATA_SECTION_NODE:
5743 case XML_ENTITY_REF_NODE:
5744 case XML_PI_NODE:
5745 case XML_COMMENT_NODE:
5746 return(1);
5747 case XML_ENTITY_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005748 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5749 "Entity element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005750 return(0);
5751 case XML_NOTATION_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005752 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5753 "Notation element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005754 return(0);
5755 case XML_DOCUMENT_NODE:
5756 case XML_DOCUMENT_TYPE_NODE:
5757 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005758 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5759 "Document element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005760 return(0);
5761 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005762 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5763 "HTML Document not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005764 return(0);
5765 case XML_ELEMENT_NODE:
5766 break;
5767 default:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005768 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5769 "unknown element type\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005770 return(0);
5771 }
Owen Taylor3473f882001-02-23 17:55:21 +00005772
5773 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005774 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005775 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005776 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5777 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005778 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005779
Daniel Veillardea7751d2002-12-20 00:16:24 +00005780 /*
5781 * If vstateNr is not zero that means continuous validation is
5782 * activated, do not try to check the content model at that level.
5783 */
5784 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005785 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005786 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005787 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005788 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ELEM,
5789 "No declaration for element %s\n",
5790 elem->name, NULL, NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005791 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005792 case XML_ELEMENT_TYPE_EMPTY:
5793 if (elem->children != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005794 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_EMPTY,
Owen Taylor3473f882001-02-23 17:55:21 +00005795 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005796 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005797 ret = 0;
5798 }
5799 break;
5800 case XML_ELEMENT_TYPE_ANY:
5801 /* I don't think anything is required then */
5802 break;
5803 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005804
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005805 /* simple case of declared as #PCDATA */
5806 if ((elemDecl->content != NULL) &&
5807 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5808 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5809 if (!ret) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005810 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_PCDATA,
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005811 "Element %s was declared #PCDATA but contains non text nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005812 elem->name, NULL, NULL);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005813 }
5814 break;
5815 }
Owen Taylor3473f882001-02-23 17:55:21 +00005816 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005817 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005818 while (child != NULL) {
5819 if (child->type == XML_ELEMENT_NODE) {
5820 name = child->name;
5821 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005822 xmlChar fn[50];
5823 xmlChar *fullname;
5824
5825 fullname = xmlBuildQName(child->name, child->ns->prefix,
5826 fn, 50);
5827 if (fullname == NULL)
5828 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005829 cont = elemDecl->content;
5830 while (cont != NULL) {
5831 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005832 if (xmlStrEqual(cont->name, fullname))
5833 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005834 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5835 (cont->c1 != NULL) &&
5836 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005837 if (xmlStrEqual(cont->c1->name, fullname))
5838 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005839 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5840 (cont->c1 == NULL) ||
5841 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005842 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5843 "Internal: MIXED struct corrupted\n",
5844 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005845 break;
5846 }
5847 cont = cont->c2;
5848 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005849 if ((fullname != fn) && (fullname != child->name))
5850 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005851 if (cont != NULL)
5852 goto child_ok;
5853 }
5854 cont = elemDecl->content;
5855 while (cont != NULL) {
5856 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5857 if (xmlStrEqual(cont->name, name)) break;
5858 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5859 (cont->c1 != NULL) &&
5860 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5861 if (xmlStrEqual(cont->c1->name, name)) break;
5862 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5863 (cont->c1 == NULL) ||
5864 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005865 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
5866 "Internal: MIXED struct corrupted\n",
5867 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005868 break;
5869 }
5870 cont = cont->c2;
5871 }
5872 if (cont == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005873 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_CHILD,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005874 "Element %s is not declared in %s list of possible children\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005875 name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005876 ret = 0;
5877 }
5878 }
5879child_ok:
5880 child = child->next;
5881 }
5882 break;
5883 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005884 if ((doc->standalone == 1) && (extsubset == 1)) {
5885 /*
5886 * VC: Standalone Document Declaration
5887 * - element types with element content, if white space
5888 * occurs directly within any instance of those types.
5889 */
5890 child = elem->children;
5891 while (child != NULL) {
5892 if (child->type == XML_TEXT_NODE) {
5893 const xmlChar *content = child->content;
5894
William M. Brack76e95df2003-10-18 16:20:14 +00005895 while (IS_BLANK_CH(*content))
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005896 content++;
5897 if (*content == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005898 xmlErrValidNode(ctxt, elem,
5899 XML_DTD_STANDALONE_WHITE_SPACE,
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005900"standalone: %s declared in the external subset contains white spaces nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005901 elem->name, NULL, NULL);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005902 ret = 0;
5903 break;
5904 }
5905 }
5906 child =child->next;
5907 }
5908 }
Owen Taylor3473f882001-02-23 17:55:21 +00005909 child = elem->children;
5910 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005911 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005912 if (tmp <= 0)
5913 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005914 break;
5915 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005916 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005917
5918 /* [ VC: Required Attribute ] */
5919 attr = elemDecl->attributes;
5920 while (attr != NULL) {
5921 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005922 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005923
Daniel Veillarde4301c82002-02-13 13:32:35 +00005924 if ((attr->prefix == NULL) &&
5925 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5926 xmlNsPtr ns;
5927
5928 ns = elem->nsDef;
5929 while (ns != NULL) {
5930 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005931 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005932 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005933 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005934 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5935 xmlNsPtr ns;
5936
5937 ns = elem->nsDef;
5938 while (ns != NULL) {
5939 if (xmlStrEqual(attr->name, ns->prefix))
5940 goto found;
5941 ns = ns->next;
5942 }
5943 } else {
5944 xmlAttrPtr attrib;
5945
5946 attrib = elem->properties;
5947 while (attrib != NULL) {
5948 if (xmlStrEqual(attrib->name, attr->name)) {
5949 if (attr->prefix != NULL) {
5950 xmlNsPtr nameSpace = attrib->ns;
5951
5952 if (nameSpace == NULL)
5953 nameSpace = elem->ns;
5954 /*
5955 * qualified names handling is problematic, having a
5956 * different prefix should be possible but DTDs don't
5957 * allow to define the URI instead of the prefix :-(
5958 */
5959 if (nameSpace == NULL) {
5960 if (qualified < 0)
5961 qualified = 0;
5962 } else if (!xmlStrEqual(nameSpace->prefix,
5963 attr->prefix)) {
5964 if (qualified < 1)
5965 qualified = 1;
5966 } else
5967 goto found;
5968 } else {
5969 /*
5970 * We should allow applications to define namespaces
5971 * for their application even if the DTD doesn't
5972 * carry one, otherwise, basically we would always
5973 * break.
5974 */
5975 goto found;
5976 }
5977 }
5978 attrib = attrib->next;
5979 }
Owen Taylor3473f882001-02-23 17:55:21 +00005980 }
5981 if (qualified == -1) {
5982 if (attr->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005983 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005984 "Element %s does not carry attribute %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005985 elem->name, attr->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005986 ret = 0;
5987 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005988 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005989 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005990 elem->name, attr->prefix,attr->name);
5991 ret = 0;
5992 }
5993 } else if (qualified == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005994 xmlErrValidWarning(ctxt, elem, XML_DTD_NO_PREFIX,
Owen Taylor3473f882001-02-23 17:55:21 +00005995 "Element %s required attribute %s:%s has no prefix\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005996 elem->name, attr->prefix, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005997 } else if (qualified == 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005998 xmlErrValidWarning(ctxt, elem, XML_DTD_DIFFERENT_PREFIX,
Owen Taylor3473f882001-02-23 17:55:21 +00005999 "Element %s required attribute %s:%s has different prefix\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006000 elem->name, attr->prefix, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00006001 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00006002 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
6003 /*
6004 * Special tests checking #FIXED namespace declarations
6005 * have the right value since this is not done as an
6006 * attribute checking
6007 */
6008 if ((attr->prefix == NULL) &&
6009 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
6010 xmlNsPtr ns;
6011
6012 ns = elem->nsDef;
6013 while (ns != NULL) {
6014 if (ns->prefix == NULL) {
6015 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006016 xmlErrValidNode(ctxt, elem,
6017 XML_DTD_ELEM_DEFAULT_NAMESPACE,
Daniel Veillarde4301c82002-02-13 13:32:35 +00006018 "Element %s namespace name for default namespace does not match the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006019 elem->name, NULL, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00006020 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00006021 }
6022 goto found;
6023 }
6024 ns = ns->next;
6025 }
6026 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
6027 xmlNsPtr ns;
6028
6029 ns = elem->nsDef;
6030 while (ns != NULL) {
6031 if (xmlStrEqual(attr->name, ns->prefix)) {
6032 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006033 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006034 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006035 elem->name, ns->prefix, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00006036 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00006037 }
6038 goto found;
6039 }
6040 ns = ns->next;
6041 }
6042 }
Owen Taylor3473f882001-02-23 17:55:21 +00006043 }
6044found:
6045 attr = attr->nexth;
6046 }
6047 return(ret);
6048}
6049
6050/**
6051 * xmlValidateRoot:
6052 * @ctxt: the validation context
6053 * @doc: a document instance
6054 *
6055 * Try to validate a the root element
6056 * basically it does the following check as described by the
6057 * XML-1.0 recommendation:
6058 * - [ VC: Root Element Type ]
6059 * it doesn't try to recurse or apply other check to the element
6060 *
6061 * returns 1 if valid or 0 otherwise
6062 */
6063
6064int
6065xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6066 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00006067 int ret;
6068
Owen Taylor3473f882001-02-23 17:55:21 +00006069 if (doc == NULL) return(0);
6070
6071 root = xmlDocGetRootElement(doc);
6072 if ((root == NULL) || (root->name == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006073 xmlErrValid(ctxt, XML_DTD_NO_ROOT,
6074 "no root element\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006075 return(0);
6076 }
6077
6078 /*
6079 * When doing post validation against a separate DTD, those may
6080 * no internal subset has been generated
6081 */
6082 if ((doc->intSubset != NULL) &&
6083 (doc->intSubset->name != NULL)) {
6084 /*
6085 * Check first the document root against the NQName
6086 */
6087 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
6088 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00006089 xmlChar fn[50];
6090 xmlChar *fullname;
6091
6092 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
6093 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00006094 xmlVErrMemory(ctxt, NULL);
Daniel Veillardc00cda82003-04-07 10:22:39 +00006095 return(0);
6096 }
6097 ret = xmlStrEqual(doc->intSubset->name, fullname);
6098 if ((fullname != fn) && (fullname != root->name))
6099 xmlFree(fullname);
6100 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00006101 goto name_ok;
6102 }
6103 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
6104 (xmlStrEqual(root->name, BAD_CAST "html")))
6105 goto name_ok;
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006106 xmlErrValidNode(ctxt, root, XML_DTD_ROOT_NAME,
6107 "root and DTD name do not match '%s' and '%s'\n",
6108 root->name, doc->intSubset->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006109 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00006110 }
6111 }
6112name_ok:
6113 return(1);
6114}
6115
6116
6117/**
6118 * xmlValidateElement:
6119 * @ctxt: the validation context
6120 * @doc: a document instance
6121 * @elem: an element instance
6122 *
6123 * Try to validate the subtree under an element
6124 *
6125 * returns 1 if valid or 0 otherwise
6126 */
6127
6128int
6129xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
6130 xmlNodePtr child;
6131 xmlAttrPtr attr;
Daniel Veillarde133dd82003-10-30 10:42:20 +00006132 xmlNsPtr ns;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006133 const xmlChar *value;
Owen Taylor3473f882001-02-23 17:55:21 +00006134 int ret = 1;
6135
6136 if (elem == NULL) return(0);
6137
6138 /*
6139 * XInclude elements were added after parsing in the infoset,
6140 * they don't really mean anything validation wise.
6141 */
6142 if ((elem->type == XML_XINCLUDE_START) ||
6143 (elem->type == XML_XINCLUDE_END))
6144 return(1);
6145
6146 CHECK_DTD;
6147
Daniel Veillard10ea86c2001-06-20 13:55:33 +00006148 /*
6149 * Entities references have to be handled separately
6150 */
6151 if (elem->type == XML_ENTITY_REF_NODE) {
6152 return(1);
6153 }
6154
Owen Taylor3473f882001-02-23 17:55:21 +00006155 ret &= xmlValidateOneElement(ctxt, doc, elem);
6156 attr = elem->properties;
Daniel Veillarde133dd82003-10-30 10:42:20 +00006157 while (attr != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00006158 value = xmlNodeListGetString(doc, attr->children, 0);
6159 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
6160 if (value != NULL)
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006161 xmlFree((char *)value);
Owen Taylor3473f882001-02-23 17:55:21 +00006162 attr= attr->next;
6163 }
Daniel Veillarde133dd82003-10-30 10:42:20 +00006164 ns = elem->nsDef;
6165 while (ns != NULL) {
Daniel Veillard1f5c9892003-12-29 17:09:55 +00006166 if (elem->ns == NULL)
6167 ret &= xmlValidateOneNamespace(ctxt, doc, elem, NULL,
6168 ns, ns->href);
6169 else
6170 ret &= xmlValidateOneNamespace(ctxt, doc, elem, elem->ns->prefix,
6171 ns, ns->href);
Daniel Veillarde133dd82003-10-30 10:42:20 +00006172 ns = ns->next;
6173 }
Owen Taylor3473f882001-02-23 17:55:21 +00006174 child = elem->children;
6175 while (child != NULL) {
6176 ret &= xmlValidateElement(ctxt, doc, child);
6177 child = child->next;
6178 }
6179
6180 return(ret);
6181}
6182
Daniel Veillard8730c562001-02-26 10:49:57 +00006183/**
6184 * xmlValidateRef:
6185 * @ref: A reference to be validated
6186 * @ctxt: Validation context
6187 * @name: Name of ID we are searching for
6188 *
6189 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006190static void
Daniel Veillard8730c562001-02-26 10:49:57 +00006191xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00006192 const xmlChar *name) {
6193 xmlAttrPtr id;
6194 xmlAttrPtr attr;
6195
6196 if (ref == NULL)
6197 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006198 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00006199 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006200 attr = ref->attr;
6201 if (attr == NULL) {
6202 xmlChar *dup, *str = NULL, *cur, save;
6203
6204 dup = xmlStrdup(name);
6205 if (dup == NULL) {
6206 ctxt->valid = 0;
6207 return;
6208 }
6209 cur = dup;
6210 while (*cur != 0) {
6211 str = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00006212 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006213 save = *cur;
6214 *cur = 0;
6215 id = xmlGetID(ctxt->doc, str);
6216 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006217 xmlErrValidNodeNr(ctxt, NULL, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006218 "attribute %s line %d references an unknown ID \"%s\"\n",
6219 ref->name, ref->lineno, str);
6220 ctxt->valid = 0;
6221 }
6222 if (save == 0)
6223 break;
6224 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00006225 while (IS_BLANK_CH(*cur)) cur++;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006226 }
6227 xmlFree(dup);
6228 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00006229 id = xmlGetID(ctxt->doc, name);
6230 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006231 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006232 "IDREF attribute %s references an unknown ID \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006233 attr->name, name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006234 ctxt->valid = 0;
6235 }
6236 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
6237 xmlChar *dup, *str = NULL, *cur, save;
6238
6239 dup = xmlStrdup(name);
6240 if (dup == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00006241 xmlVErrMemory(ctxt, "IDREFS split");
Owen Taylor3473f882001-02-23 17:55:21 +00006242 ctxt->valid = 0;
6243 return;
6244 }
6245 cur = dup;
6246 while (*cur != 0) {
6247 str = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00006248 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00006249 save = *cur;
6250 *cur = 0;
6251 id = xmlGetID(ctxt->doc, str);
6252 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006253 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006254 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006255 attr->name, str, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006256 ctxt->valid = 0;
6257 }
6258 if (save == 0)
6259 break;
6260 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00006261 while (IS_BLANK_CH(*cur)) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00006262 }
6263 xmlFree(dup);
6264 }
6265}
6266
6267/**
Daniel Veillard8730c562001-02-26 10:49:57 +00006268 * xmlWalkValidateList:
6269 * @data: Contents of current link
6270 * @user: Value supplied by the user
6271 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00006272 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00006273 */
6274static int
6275xmlWalkValidateList(const void *data, const void *user)
6276{
6277 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
6278 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
6279 return 1;
6280}
6281
6282/**
6283 * xmlValidateCheckRefCallback:
6284 * @ref_list: List of references
6285 * @ctxt: Validation context
6286 * @name: Name of ID we are searching for
6287 *
6288 */
6289static void
6290xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
6291 const xmlChar *name) {
6292 xmlValidateMemo memo;
6293
6294 if (ref_list == NULL)
6295 return;
6296 memo.ctxt = ctxt;
6297 memo.name = name;
6298
6299 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
6300
6301}
6302
6303/**
Owen Taylor3473f882001-02-23 17:55:21 +00006304 * xmlValidateDocumentFinal:
6305 * @ctxt: the validation context
6306 * @doc: a document instance
6307 *
6308 * Does the final step for the document validation once all the
6309 * incremental validation steps have been completed
6310 *
6311 * basically it does the following checks described by the XML Rec
6312 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006313 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00006314 *
6315 * returns 1 if valid or 0 otherwise
6316 */
6317
6318int
6319xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6320 xmlRefTablePtr table;
6321
Daniel Veillardc0be74b2004-11-03 19:16:55 +00006322 if (ctxt == NULL)
6323 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00006324 if (doc == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00006325 xmlErrValid(ctxt, XML_DTD_NO_DOC,
6326 "xmlValidateDocumentFinal: doc == NULL\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006327 return(0);
6328 }
6329
6330 /*
6331 * Check all the NOTATION/NOTATIONS attributes
6332 */
6333 /*
6334 * Check all the ENTITY/ENTITIES attributes definition for validity
6335 */
6336 /*
6337 * Check all the IDREF/IDREFS attributes definition for validity
6338 */
6339 table = (xmlRefTablePtr) doc->refs;
6340 ctxt->doc = doc;
6341 ctxt->valid = 1;
6342 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6343 return(ctxt->valid);
6344}
6345
6346/**
6347 * xmlValidateDtd:
6348 * @ctxt: the validation context
6349 * @doc: a document instance
6350 * @dtd: a dtd instance
6351 *
6352 * Try to validate the document against the dtd instance
6353 *
William M. Brack367df6e2004-10-23 18:14:36 +00006354 * Basically it does check all the definitions in the DtD.
6355 * Note the the internal subset (if present) is de-coupled
6356 * (i.e. not used), which could give problems if ID or IDREF
6357 * is present.
Owen Taylor3473f882001-02-23 17:55:21 +00006358 *
6359 * returns 1 if valid or 0 otherwise
6360 */
6361
6362int
6363xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6364 int ret;
William M. Brack367df6e2004-10-23 18:14:36 +00006365 xmlDtdPtr oldExt, oldInt;
Owen Taylor3473f882001-02-23 17:55:21 +00006366 xmlNodePtr root;
6367
6368 if (dtd == NULL) return(0);
6369 if (doc == NULL) return(0);
6370 oldExt = doc->extSubset;
William M. Brack367df6e2004-10-23 18:14:36 +00006371 oldInt = doc->intSubset;
Owen Taylor3473f882001-02-23 17:55:21 +00006372 doc->extSubset = dtd;
William M. Brack367df6e2004-10-23 18:14:36 +00006373 doc->intSubset = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00006374 ret = xmlValidateRoot(ctxt, doc);
6375 if (ret == 0) {
6376 doc->extSubset = oldExt;
William M. Brack367df6e2004-10-23 18:14:36 +00006377 doc->intSubset = oldInt;
Owen Taylor3473f882001-02-23 17:55:21 +00006378 return(ret);
6379 }
6380 if (doc->ids != NULL) {
6381 xmlFreeIDTable(doc->ids);
6382 doc->ids = NULL;
6383 }
6384 if (doc->refs != NULL) {
6385 xmlFreeRefTable(doc->refs);
6386 doc->refs = NULL;
6387 }
6388 root = xmlDocGetRootElement(doc);
6389 ret = xmlValidateElement(ctxt, doc, root);
6390 ret &= xmlValidateDocumentFinal(ctxt, doc);
6391 doc->extSubset = oldExt;
William M. Brack367df6e2004-10-23 18:14:36 +00006392 doc->intSubset = oldInt;
Owen Taylor3473f882001-02-23 17:55:21 +00006393 return(ret);
6394}
6395
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006396static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006397xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6398 const xmlChar *name ATTRIBUTE_UNUSED) {
6399 if (cur == NULL)
6400 return;
6401 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6402 xmlChar *notation = cur->content;
6403
Daniel Veillard878eab02002-02-19 13:46:09 +00006404 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006405 int ret;
6406
6407 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6408 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006409 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006410 }
6411 }
6412 }
6413}
6414
6415static void
Owen Taylor3473f882001-02-23 17:55:21 +00006416xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006417 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006418 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006419 xmlDocPtr doc;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006420 xmlElementPtr elem = NULL;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006421
Owen Taylor3473f882001-02-23 17:55:21 +00006422 if (cur == NULL)
6423 return;
6424 switch (cur->atype) {
6425 case XML_ATTRIBUTE_CDATA:
6426 case XML_ATTRIBUTE_ID:
6427 case XML_ATTRIBUTE_IDREF :
6428 case XML_ATTRIBUTE_IDREFS:
6429 case XML_ATTRIBUTE_NMTOKEN:
6430 case XML_ATTRIBUTE_NMTOKENS:
6431 case XML_ATTRIBUTE_ENUMERATION:
6432 break;
6433 case XML_ATTRIBUTE_ENTITY:
6434 case XML_ATTRIBUTE_ENTITIES:
6435 case XML_ATTRIBUTE_NOTATION:
6436 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006437
6438 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6439 cur->atype, cur->defaultValue);
6440 if ((ret == 0) && (ctxt->valid == 1))
6441 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006442 }
6443 if (cur->tree != NULL) {
6444 xmlEnumerationPtr tree = cur->tree;
6445 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006446 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006447 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006448 if ((ret == 0) && (ctxt->valid == 1))
6449 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006450 tree = tree->next;
6451 }
6452 }
6453 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006454 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6455 doc = cur->doc;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006456 if (cur->elem == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006457 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
Daniel Veillard878eab02002-02-19 13:46:09 +00006458 "xmlValidateAttributeCallback(%s): internal error\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006459 (const char *) cur->name);
Daniel Veillard878eab02002-02-19 13:46:09 +00006460 return;
6461 }
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006462
6463 if (doc != NULL)
6464 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6465 if ((elem == NULL) && (doc != NULL))
Daniel Veillard878eab02002-02-19 13:46:09 +00006466 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006467 if ((elem == NULL) && (cur->parent != NULL) &&
6468 (cur->parent->type == XML_DTD_NODE))
6469 elem = xmlGetDtdElementDesc((xmlDtdPtr) cur->parent, cur->elem);
Daniel Veillard878eab02002-02-19 13:46:09 +00006470 if (elem == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006471 xmlErrValidNode(ctxt, NULL, XML_DTD_UNKNOWN_ELEM,
Daniel Veillard878eab02002-02-19 13:46:09 +00006472 "attribute %s: could not find decl for element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006473 cur->name, cur->elem, NULL);
Daniel Veillard878eab02002-02-19 13:46:09 +00006474 return;
6475 }
6476 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006477 xmlErrValidNode(ctxt, NULL, XML_DTD_EMPTY_NOTATION,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006478 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006479 cur->name, cur->elem, NULL);
Daniel Veillard878eab02002-02-19 13:46:09 +00006480 ctxt->valid = 0;
6481 }
6482 }
Owen Taylor3473f882001-02-23 17:55:21 +00006483}
6484
6485/**
6486 * xmlValidateDtdFinal:
6487 * @ctxt: the validation context
6488 * @doc: a document instance
6489 *
6490 * Does the final step for the dtds validation once all the
6491 * subsets have been parsed
6492 *
6493 * basically it does the following checks described by the XML Rec
6494 * - check that ENTITY and ENTITIES type attributes default or
6495 * possible values matches one of the defined entities.
6496 * - check that NOTATION type attributes default or
6497 * possible values matches one of the defined notations.
6498 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006499 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006500 */
6501
6502int
6503xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006504 xmlDtdPtr dtd;
6505 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006506 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006507
6508 if (doc == NULL) return(0);
6509 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6510 return(0);
6511 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006512 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006513 dtd = doc->intSubset;
6514 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6515 table = (xmlAttributeTablePtr) dtd->attributes;
6516 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006517 }
6518 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006519 entities = (xmlEntitiesTablePtr) dtd->entities;
6520 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6521 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006522 }
6523 dtd = doc->extSubset;
6524 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6525 table = (xmlAttributeTablePtr) dtd->attributes;
6526 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006527 }
6528 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006529 entities = (xmlEntitiesTablePtr) dtd->entities;
6530 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6531 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006532 }
6533 return(ctxt->valid);
6534}
6535
6536/**
6537 * xmlValidateDocument:
6538 * @ctxt: the validation context
6539 * @doc: a document instance
6540 *
6541 * Try to validate the document instance
6542 *
6543 * basically it does the all the checks described by the XML Rec
6544 * i.e. validates the internal and external subset (if present)
6545 * and validate the document tree.
6546 *
6547 * returns 1 if valid or 0 otherwise
6548 */
6549
6550int
6551xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6552 int ret;
6553 xmlNodePtr root;
6554
Daniel Veillardc0be74b2004-11-03 19:16:55 +00006555 if (doc == NULL)
6556 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006557 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006558 xmlErrValid(ctxt, XML_DTD_NO_DTD,
6559 "no DTD found!\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006560 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006561 }
Owen Taylor3473f882001-02-23 17:55:21 +00006562 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6563 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
William M. Brack8c22f9f2004-08-06 16:23:27 +00006564 xmlChar *sysID;
6565 if (doc->intSubset->SystemID != NULL) {
William M. Brackbebe7302004-08-05 06:46:47 +00006566 sysID = xmlBuildURI(doc->intSubset->SystemID,
6567 doc->URL);
William M. Brack8c22f9f2004-08-06 16:23:27 +00006568 if (sysID == NULL) {
6569 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
6570 "Could not build URI for external subset \"%s\"\n",
6571 (const char *) doc->intSubset->SystemID);
6572 return 0;
6573 }
6574 } else
William M. Brackbebe7302004-08-05 06:46:47 +00006575 sysID = NULL;
William M. Brack8c22f9f2004-08-06 16:23:27 +00006576 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
William M. Brackbebe7302004-08-05 06:46:47 +00006577 (const xmlChar *)sysID);
William M. Brackbebe7302004-08-05 06:46:47 +00006578 if (sysID != NULL)
6579 xmlFree(sysID);
Owen Taylor3473f882001-02-23 17:55:21 +00006580 if (doc->extSubset == NULL) {
6581 if (doc->intSubset->SystemID != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006582 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00006583 "Could not load the external subset \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006584 (const char *) doc->intSubset->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00006585 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006586 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00006587 "Could not load the external subset \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006588 (const char *) doc->intSubset->ExternalID);
Owen Taylor3473f882001-02-23 17:55:21 +00006589 }
6590 return(0);
6591 }
6592 }
6593
6594 if (doc->ids != NULL) {
6595 xmlFreeIDTable(doc->ids);
6596 doc->ids = NULL;
6597 }
6598 if (doc->refs != NULL) {
6599 xmlFreeRefTable(doc->refs);
6600 doc->refs = NULL;
6601 }
6602 ret = xmlValidateDtdFinal(ctxt, doc);
6603 if (!xmlValidateRoot(ctxt, doc)) return(0);
6604
6605 root = xmlDocGetRootElement(doc);
6606 ret &= xmlValidateElement(ctxt, doc, root);
6607 ret &= xmlValidateDocumentFinal(ctxt, doc);
6608 return(ret);
6609}
6610
Owen Taylor3473f882001-02-23 17:55:21 +00006611/************************************************************************
6612 * *
6613 * Routines for dynamic validation editing *
6614 * *
6615 ************************************************************************/
6616
6617/**
6618 * xmlValidGetPotentialChildren:
6619 * @ctree: an element content tree
6620 * @list: an array to store the list of child names
6621 * @len: a pointer to the number of element in the list
6622 * @max: the size of the array
6623 *
6624 * Build/extend a list of potential children allowed by the content tree
6625 *
6626 * returns the number of element in the list, or -1 in case of error.
6627 */
6628
6629int
6630xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6631 int *len, int max) {
6632 int i;
6633
6634 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6635 return(-1);
6636 if (*len >= max) return(*len);
6637
6638 switch (ctree->type) {
6639 case XML_ELEMENT_CONTENT_PCDATA:
6640 for (i = 0; i < *len;i++)
6641 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6642 list[(*len)++] = BAD_CAST "#PCDATA";
6643 break;
6644 case XML_ELEMENT_CONTENT_ELEMENT:
6645 for (i = 0; i < *len;i++)
6646 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6647 list[(*len)++] = ctree->name;
6648 break;
6649 case XML_ELEMENT_CONTENT_SEQ:
6650 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6651 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6652 break;
6653 case XML_ELEMENT_CONTENT_OR:
6654 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6655 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6656 break;
6657 }
6658
6659 return(*len);
6660}
6661
William M. Brack9333cc22004-06-24 08:33:40 +00006662/*
6663 * Dummy function to suppress messages while we try out valid elements
6664 */
6665static void xmlNoValidityErr(void *ctx ATTRIBUTE_UNUSED,
6666 const char *msg ATTRIBUTE_UNUSED, ...) {
6667 return;
6668}
6669
Owen Taylor3473f882001-02-23 17:55:21 +00006670/**
6671 * xmlValidGetValidElements:
6672 * @prev: an element to insert after
6673 * @next: an element to insert next
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006674 * @names: an array to store the list of child names
Owen Taylor3473f882001-02-23 17:55:21 +00006675 * @max: the size of the array
6676 *
6677 * This function returns the list of authorized children to insert
6678 * within an existing tree while respecting the validity constraints
6679 * forced by the Dtd. The insertion point is defined using @prev and
6680 * @next in the following ways:
6681 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6682 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6683 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6684 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6685 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6686 *
6687 * pointers to the element names are inserted at the beginning of the array
6688 * and do not need to be freed.
6689 *
6690 * returns the number of element in the list, or -1 in case of error. If
6691 * the function returns the value @max the caller is invited to grow the
6692 * receiving array and retry.
6693 */
6694
6695int
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006696xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **names,
Owen Taylor3473f882001-02-23 17:55:21 +00006697 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006698 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006699 int nb_valid_elements = 0;
6700 const xmlChar *elements[256];
6701 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006702 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006703
6704 xmlNode *ref_node;
6705 xmlNode *parent;
6706 xmlNode *test_node;
6707
6708 xmlNode *prev_next;
6709 xmlNode *next_prev;
6710 xmlNode *parent_childs;
6711 xmlNode *parent_last;
6712
6713 xmlElement *element_desc;
6714
6715 if (prev == NULL && next == NULL)
6716 return(-1);
6717
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006718 if (names == NULL) return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00006719 if (max <= 0) return(-1);
6720
William M. Brack9333cc22004-06-24 08:33:40 +00006721 memset(&vctxt, 0, sizeof (xmlValidCtxt));
6722 vctxt.error = xmlNoValidityErr; /* this suppresses err/warn output */
6723
Owen Taylor3473f882001-02-23 17:55:21 +00006724 nb_valid_elements = 0;
6725 ref_node = prev ? prev : next;
6726 parent = ref_node->parent;
6727
6728 /*
6729 * Retrieves the parent element declaration
6730 */
6731 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6732 parent->name);
6733 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6734 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6735 parent->name);
6736 if (element_desc == NULL) return(-1);
6737
6738 /*
6739 * Do a backup of the current tree structure
6740 */
6741 prev_next = prev ? prev->next : NULL;
6742 next_prev = next ? next->prev : NULL;
6743 parent_childs = parent->children;
6744 parent_last = parent->last;
6745
6746 /*
6747 * Creates a dummy node and insert it into the tree
6748 */
Daniel Veillard95ddcd32004-10-26 21:53:55 +00006749 test_node = xmlNewDocNode (ref_node->doc, NULL, BAD_CAST "<!dummy?>", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006750 test_node->parent = parent;
6751 test_node->prev = prev;
6752 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006753 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006754
6755 if (prev) prev->next = test_node;
6756 else parent->children = test_node;
6757
6758 if (next) next->prev = test_node;
6759 else parent->last = test_node;
6760
6761 /*
6762 * Insert each potential child node and check if the parent is
6763 * still valid
6764 */
6765 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6766 elements, &nb_elements, 256);
6767
6768 for (i = 0;i < nb_elements;i++) {
6769 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006770 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006771 int j;
6772
6773 for (j = 0; j < nb_valid_elements;j++)
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006774 if (xmlStrEqual(elements[i], names[j])) break;
6775 names[nb_valid_elements++] = elements[i];
Owen Taylor3473f882001-02-23 17:55:21 +00006776 if (nb_valid_elements >= max) break;
6777 }
6778 }
6779
6780 /*
6781 * Restore the tree structure
6782 */
6783 if (prev) prev->next = prev_next;
6784 if (next) next->prev = next_prev;
6785 parent->children = parent_childs;
6786 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006787
6788 /*
6789 * Free up the dummy node
6790 */
6791 test_node->name = name;
6792 xmlFreeNode(test_node);
6793
Owen Taylor3473f882001-02-23 17:55:21 +00006794 return(nb_valid_elements);
6795}
Daniel Veillard4432df22003-09-28 18:58:27 +00006796#endif /* LIBXML_VALID_ENABLED */
6797