blob: f9daada8a8bce40018e2da8594d914245d141e53 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * valid.c : part of the code use to do the DTD handling and the validity
3 * checking
4 *
5 * See Copyright for the status of this software.
6 *
Daniel Veillardc5d64342001-06-24 12:13:24 +00007 * daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +00008 */
9
Daniel Veillard34ce8be2002-03-18 19:37:11 +000010#define IN_LIBXML
Bjorn Reese70a9da52001-04-21 16:57:29 +000011#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000012
Owen Taylor3473f882001-02-23 17:55:21 +000013#include <string.h>
14
15#ifdef HAVE_STDLIB_H
16#include <stdlib.h>
17#endif
18
19#include <libxml/xmlmemory.h>
20#include <libxml/hash.h>
Daniel Veillard29b17482004-08-16 00:39:03 +000021#include <libxml/uri.h>
Owen Taylor3473f882001-02-23 17:55:21 +000022#include <libxml/valid.h>
23#include <libxml/parser.h>
24#include <libxml/parserInternals.h>
25#include <libxml/xmlerror.h>
26#include <libxml/list.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000027#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000028
Daniel Veillard4432df22003-09-28 18:58:27 +000029static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
30 int create);
Daniel Veillarde62d36c2001-05-15 08:53:16 +000031/* #define DEBUG_VALID_ALGO */
Daniel Veillard5acfd6b2002-09-18 16:29:02 +000032/* #define DEBUG_REGEXP_ALGO */
Daniel Veillarde62d36c2001-05-15 08:53:16 +000033
Daniel Veillarda646cfd2002-09-17 21:50:03 +000034#define TODO \
35 xmlGenericError(xmlGenericErrorContext, \
36 "Unimplemented block at %s:%d\n", \
37 __FILE__, __LINE__);
Owen Taylor3473f882001-02-23 17:55:21 +000038
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000039/************************************************************************
40 * *
41 * Error handling routines *
42 * *
43 ************************************************************************/
44
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000045/**
Daniel Veillardce9457f2003-10-05 21:33:18 +000046 * xmlVErrMemory:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000047 * @ctxt: an XML validation parser context
48 * @extra: extra informations
49 *
50 * Handle an out of memory error
51 */
52static void
Daniel Veillardce9457f2003-10-05 21:33:18 +000053xmlVErrMemory(xmlValidCtxtPtr ctxt, const char *extra)
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000054{
Daniel Veillardbb5abab2003-10-03 22:21:51 +000055 xmlGenericErrorFunc channel = NULL;
56 xmlParserCtxtPtr pctxt = NULL;
57 void *data = NULL;
58
59 if (ctxt != NULL) {
60 channel = ctxt->error;
61 data = ctxt->userData;
Daniel Veillardeff45a92004-10-29 12:10:55 +000062 /* Use the special values to detect if it is part of a parsing
63 context */
64 if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
65 (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
66 pctxt = ctxt->userData;
67 }
Daniel Veillardbb5abab2003-10-03 22:21:51 +000068 }
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000069 if (extra)
Daniel Veillard73000572003-10-11 11:26:42 +000070 __xmlRaiseError(NULL, channel, data,
Daniel Veillard72b9e292003-10-28 15:44:17 +000071 pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000072 XML_ERR_FATAL, NULL, 0, extra, NULL, NULL, 0, 0,
73 "Memory allocation failed : %s\n", extra);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000074 else
Daniel Veillard73000572003-10-11 11:26:42 +000075 __xmlRaiseError(NULL, channel, data,
Daniel Veillard72b9e292003-10-28 15:44:17 +000076 pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000077 XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, 0, 0,
78 "Memory allocation failed\n");
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000079}
80
81/**
82 * xmlErrValid:
83 * @ctxt: an XML validation parser context
Daniel Veillardbb5abab2003-10-03 22:21:51 +000084 * @error: the error number
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000085 * @extra: extra informations
86 *
87 * Handle a validation error
88 */
89static void
Daniel Veillardf88d8cf2003-12-08 10:25:02 +000090xmlErrValid(xmlValidCtxtPtr ctxt, xmlParserErrors error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +000091 const char *msg, const char *extra)
Daniel Veillard2b8c4a12003-10-02 22:28:19 +000092{
Daniel Veillardbb5abab2003-10-03 22:21:51 +000093 xmlGenericErrorFunc channel = NULL;
94 xmlParserCtxtPtr pctxt = NULL;
95 void *data = NULL;
96
97 if (ctxt != NULL) {
98 channel = ctxt->error;
99 data = ctxt->userData;
Daniel Veillardeff45a92004-10-29 12:10:55 +0000100 /* Use the special values to detect if it is part of a parsing
101 context */
102 if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
103 (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
104 pctxt = ctxt->userData;
105 }
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000106 }
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000107 if (extra)
Daniel Veillard73000572003-10-11 11:26:42 +0000108 __xmlRaiseError(NULL, channel, data,
Daniel Veillard72b9e292003-10-28 15:44:17 +0000109 pctxt, NULL, XML_FROM_VALID, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000110 XML_ERR_ERROR, NULL, 0, extra, NULL, NULL, 0, 0,
111 msg, extra);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000112 else
Daniel Veillard73000572003-10-11 11:26:42 +0000113 __xmlRaiseError(NULL, channel, data,
Daniel Veillard72b9e292003-10-28 15:44:17 +0000114 pctxt, NULL, XML_FROM_VALID, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000115 XML_ERR_ERROR, NULL, 0, NULL, NULL, NULL, 0, 0,
116 msg);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000117}
118
Daniel Veillardf54cd532004-02-25 11:52:31 +0000119#if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
120/**
121 * xmlErrValidNode:
122 * @ctxt: an XML validation parser context
123 * @node: the node raising the error
124 * @error: the error number
125 * @str1: extra informations
126 * @str2: extra informations
127 * @str3: extra informations
128 *
129 * Handle a validation error, provide contextual informations
130 */
131static void
132xmlErrValidNode(xmlValidCtxtPtr ctxt,
133 xmlNodePtr node, xmlParserErrors error,
134 const char *msg, const xmlChar * str1,
135 const xmlChar * str2, const xmlChar * str3)
136{
137 xmlStructuredErrorFunc schannel = NULL;
138 xmlGenericErrorFunc channel = NULL;
139 xmlParserCtxtPtr pctxt = NULL;
140 void *data = NULL;
141
142 if (ctxt != NULL) {
143 channel = ctxt->error;
144 data = ctxt->userData;
Daniel Veillardeff45a92004-10-29 12:10:55 +0000145 /* Use the special values to detect if it is part of a parsing
146 context */
147 if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
148 (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
149 pctxt = ctxt->userData;
150 }
Daniel Veillardf54cd532004-02-25 11:52:31 +0000151 }
152 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error,
153 XML_ERR_ERROR, NULL, 0,
154 (const char *) str1,
155 (const char *) str1,
156 (const char *) str3, 0, 0, msg, str1, str2, str3);
157}
158#endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */
159
Daniel Veillardd61e8fb2003-10-19 21:59:17 +0000160#ifdef LIBXML_VALID_ENABLED
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000161/**
162 * xmlErrValidNodeNr:
163 * @ctxt: an XML validation parser context
164 * @node: the node raising the error
165 * @error: the error number
166 * @str1: extra informations
167 * @int2: extra informations
168 * @str3: extra informations
169 *
170 * Handle a validation error, provide contextual informations
171 */
172static void
Daniel Veillard72b9e292003-10-28 15:44:17 +0000173xmlErrValidNodeNr(xmlValidCtxtPtr ctxt,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000174 xmlNodePtr node, xmlParserErrors error,
175 const char *msg, const xmlChar * str1,
176 int int2, const xmlChar * str3)
177{
Daniel Veillard659e71e2003-10-10 14:10:40 +0000178 xmlStructuredErrorFunc schannel = NULL;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000179 xmlGenericErrorFunc channel = NULL;
180 xmlParserCtxtPtr pctxt = NULL;
181 void *data = NULL;
182
183 if (ctxt != NULL) {
184 channel = ctxt->error;
185 data = ctxt->userData;
Daniel Veillardeff45a92004-10-29 12:10:55 +0000186 /* Use the special values to detect if it is part of a parsing
187 context */
188 if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
189 (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
190 pctxt = ctxt->userData;
191 }
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000192 }
Daniel Veillard72b9e292003-10-28 15:44:17 +0000193 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000194 XML_ERR_ERROR, NULL, 0,
195 (const char *) str1,
196 (const char *) str3,
197 NULL, int2, 0, msg, str1, int2, str3);
198}
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000199
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000200/**
201 * xmlErrValidWarning:
202 * @ctxt: an XML validation parser context
203 * @node: the node raising the error
204 * @error: the error number
William M. Brackedb65a72004-02-06 07:36:04 +0000205 * @str1: extra information
206 * @str2: extra information
207 * @str3: extra information
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000208 *
William M. Brackedb65a72004-02-06 07:36:04 +0000209 * Handle a validation error, provide contextual information
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000210 */
211static void
William M. Brackedb65a72004-02-06 07:36:04 +0000212xmlErrValidWarning(xmlValidCtxtPtr ctxt,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000213 xmlNodePtr node, xmlParserErrors error,
214 const char *msg, const xmlChar * str1,
215 const xmlChar * str2, const xmlChar * str3)
216{
Daniel Veillard659e71e2003-10-10 14:10:40 +0000217 xmlStructuredErrorFunc schannel = NULL;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000218 xmlGenericErrorFunc channel = NULL;
219 xmlParserCtxtPtr pctxt = NULL;
220 void *data = NULL;
221
222 if (ctxt != NULL) {
William M. Bracke4d526f2004-12-18 00:01:21 +0000223 channel = ctxt->warning;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000224 data = ctxt->userData;
Daniel Veillardeff45a92004-10-29 12:10:55 +0000225 /* Use the special values to detect if it is part of a parsing
226 context */
227 if ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
228 (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1)) {
229 pctxt = ctxt->userData;
230 }
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000231 }
Daniel Veillard72b9e292003-10-28 15:44:17 +0000232 __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error,
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000233 XML_ERR_WARNING, NULL, 0,
234 (const char *) str1,
235 (const char *) str1,
236 (const char *) str3, 0, 0, msg, str1, str2, str3);
237}
238
239
Daniel Veillardea7751d2002-12-20 00:16:24 +0000240
241#ifdef LIBXML_REGEXP_ENABLED
242/*
243 * If regexp are enabled we can do continuous validation without the
244 * need of a tree to validate the content model. this is done in each
245 * callbacks.
246 * Each xmlValidState represent the validation state associated to the
247 * set of nodes currently open from the document root to the current element.
248 */
249
250
251typedef struct _xmlValidState {
252 xmlElementPtr elemDecl; /* pointer to the content model */
253 xmlNodePtr node; /* pointer to the current node */
254 xmlRegExecCtxtPtr exec; /* regexp runtime */
255} _xmlValidState;
256
257
258static int
259vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000260 if ((ctxt->vstateMax == 0) || (ctxt->vstateTab == NULL)) {
Daniel Veillardea7751d2002-12-20 00:16:24 +0000261 ctxt->vstateMax = 10;
262 ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax *
263 sizeof(ctxt->vstateTab[0]));
264 if (ctxt->vstateTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000265 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillardea7751d2002-12-20 00:16:24 +0000266 return(-1);
267 }
268 }
269
270 if (ctxt->vstateNr >= ctxt->vstateMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000271 xmlValidState *tmp;
272
273 tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
274 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
275 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000276 xmlVErrMemory(ctxt, "realloc failed");
Daniel Veillardea7751d2002-12-20 00:16:24 +0000277 return(-1);
278 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000279 ctxt->vstateMax *= 2;
280 ctxt->vstateTab = tmp;
Daniel Veillardea7751d2002-12-20 00:16:24 +0000281 }
282 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr];
283 ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl;
284 ctxt->vstateTab[ctxt->vstateNr].node = node;
285 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
286 if (elemDecl->contModel == NULL)
287 xmlValidBuildContentModel(ctxt, elemDecl);
288 if (elemDecl->contModel != NULL) {
289 ctxt->vstateTab[ctxt->vstateNr].exec =
290 xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
291 } else {
292 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000293 xmlErrValidNode(ctxt, (xmlNodePtr) elemDecl,
294 XML_ERR_INTERNAL_ERROR,
295 "Failed to build content model regexp for %s\n",
296 node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +0000297 }
298 }
299 return(ctxt->vstateNr++);
300}
301
302static int
303vstateVPop(xmlValidCtxtPtr ctxt) {
304 xmlElementPtr elemDecl;
305
Daniel Veillard336fc7d2002-12-27 19:37:04 +0000306 if (ctxt->vstateNr < 1) return(-1);
Daniel Veillardea7751d2002-12-20 00:16:24 +0000307 ctxt->vstateNr--;
308 elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
309 ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
310 ctxt->vstateTab[ctxt->vstateNr].node = NULL;
311 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
312 xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
313 }
314 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
315 if (ctxt->vstateNr >= 1)
316 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
317 else
318 ctxt->vstate = NULL;
319 return(ctxt->vstateNr);
320}
321
322#else /* not LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000323/*
Daniel Veillard1c732d22002-11-30 11:22:59 +0000324 * If regexp are not enabled, it uses a home made algorithm less
325 * complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000326 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000327 * only restriction is on the deepness of the tree limited by the
328 * size of the occurs bitfield
329 *
330 * this is the content of a saved state for rollbacks
331 */
332
333#define ROLLBACK_OR 0
334#define ROLLBACK_PARENT 1
335
Daniel Veillardb44025c2001-10-11 22:55:55 +0000336typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000337 xmlElementContentPtr cont; /* pointer to the content model subtree */
338 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000339 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000340 unsigned char depth; /* current depth in the overall tree */
341 unsigned char state; /* ROLLBACK_XXX */
342} _xmlValidState;
343
Daniel Veillardfc57b412002-04-29 15:50:14 +0000344#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000345#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
346#define CONT ctxt->vstate->cont
347#define NODE ctxt->vstate->node
348#define DEPTH ctxt->vstate->depth
349#define OCCURS ctxt->vstate->occurs
350#define STATE ctxt->vstate->state
351
Daniel Veillard5344c602001-12-31 16:37:34 +0000352#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
353#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000354
Daniel Veillard5344c602001-12-31 16:37:34 +0000355#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
356#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000357
358static int
359vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
360 xmlNodePtr node, unsigned char depth, long occurs,
361 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000362 int i = ctxt->vstateNr - 1;
363
Daniel Veillard940492d2002-04-15 10:15:25 +0000364 if (ctxt->vstateNr > MAX_RECURSE) {
365 return(-1);
366 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000367 if (ctxt->vstateTab == NULL) {
368 ctxt->vstateMax = 8;
369 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
370 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
371 if (ctxt->vstateTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000372 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000373 return(-1);
374 }
375 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000376 if (ctxt->vstateNr >= ctxt->vstateMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000377 xmlValidState *tmp;
378
379 tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
380 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
381 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000382 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard940492d2002-04-15 10:15:25 +0000383 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000384 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000385 ctxt->vstateMax *= 2;
386 ctxt->vstateTab = tmp;
Daniel Veillard06803992001-04-22 10:35:56 +0000387 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000388 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000389 /*
390 * Don't push on the stack a state already here
391 */
392 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
393 (ctxt->vstateTab[i].node == node) &&
394 (ctxt->vstateTab[i].depth == depth) &&
395 (ctxt->vstateTab[i].occurs == occurs) &&
396 (ctxt->vstateTab[i].state == state))
397 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000398 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
399 ctxt->vstateTab[ctxt->vstateNr].node = node;
400 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
401 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
402 ctxt->vstateTab[ctxt->vstateNr].state = state;
403 return(ctxt->vstateNr++);
404}
405
406static int
407vstateVPop(xmlValidCtxtPtr ctxt) {
408 if (ctxt->vstateNr <= 1) return(-1);
409 ctxt->vstateNr--;
410 ctxt->vstate = &ctxt->vstateTab[0];
411 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
412 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
413 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
414 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
415 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
416 return(ctxt->vstateNr);
417}
418
Daniel Veillard118aed72002-09-24 14:13:13 +0000419#endif /* LIBXML_REGEXP_ENABLED */
420
Daniel Veillard1c732d22002-11-30 11:22:59 +0000421static int
422nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value)
423{
424 if (ctxt->nodeMax <= 0) {
425 ctxt->nodeMax = 4;
426 ctxt->nodeTab =
427 (xmlNodePtr *) xmlMalloc(ctxt->nodeMax *
428 sizeof(ctxt->nodeTab[0]));
429 if (ctxt->nodeTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000430 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c732d22002-11-30 11:22:59 +0000431 ctxt->nodeMax = 0;
432 return (0);
433 }
434 }
435 if (ctxt->nodeNr >= ctxt->nodeMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000436 xmlNodePtr *tmp;
437 tmp = (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
438 ctxt->nodeMax * 2 * sizeof(ctxt->nodeTab[0]));
439 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000440 xmlVErrMemory(ctxt, "realloc failed");
Daniel Veillard1c732d22002-11-30 11:22:59 +0000441 return (0);
442 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000443 ctxt->nodeMax *= 2;
444 ctxt->nodeTab = tmp;
Daniel Veillard1c732d22002-11-30 11:22:59 +0000445 }
446 ctxt->nodeTab[ctxt->nodeNr] = value;
447 ctxt->node = value;
448 return (ctxt->nodeNr++);
449}
450static xmlNodePtr
451nodeVPop(xmlValidCtxtPtr ctxt)
452{
453 xmlNodePtr ret;
454
455 if (ctxt->nodeNr <= 0)
456 return (0);
457 ctxt->nodeNr--;
458 if (ctxt->nodeNr > 0)
459 ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1];
460 else
461 ctxt->node = NULL;
462 ret = ctxt->nodeTab[ctxt->nodeNr];
463 ctxt->nodeTab[ctxt->nodeNr] = 0;
464 return (ret);
465}
Owen Taylor3473f882001-02-23 17:55:21 +0000466
Owen Taylor3473f882001-02-23 17:55:21 +0000467#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000468static void
469xmlValidPrintNode(xmlNodePtr cur) {
470 if (cur == NULL) {
471 xmlGenericError(xmlGenericErrorContext, "null");
472 return;
473 }
474 switch (cur->type) {
475 case XML_ELEMENT_NODE:
476 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
477 break;
478 case XML_TEXT_NODE:
479 xmlGenericError(xmlGenericErrorContext, "text ");
480 break;
481 case XML_CDATA_SECTION_NODE:
482 xmlGenericError(xmlGenericErrorContext, "cdata ");
483 break;
484 case XML_ENTITY_REF_NODE:
485 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
486 break;
487 case XML_PI_NODE:
488 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
489 break;
490 case XML_COMMENT_NODE:
491 xmlGenericError(xmlGenericErrorContext, "comment ");
492 break;
493 case XML_ATTRIBUTE_NODE:
494 xmlGenericError(xmlGenericErrorContext, "?attr? ");
495 break;
496 case XML_ENTITY_NODE:
497 xmlGenericError(xmlGenericErrorContext, "?ent? ");
498 break;
499 case XML_DOCUMENT_NODE:
500 xmlGenericError(xmlGenericErrorContext, "?doc? ");
501 break;
502 case XML_DOCUMENT_TYPE_NODE:
503 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
504 break;
505 case XML_DOCUMENT_FRAG_NODE:
506 xmlGenericError(xmlGenericErrorContext, "?frag? ");
507 break;
508 case XML_NOTATION_NODE:
509 xmlGenericError(xmlGenericErrorContext, "?nota? ");
510 break;
511 case XML_HTML_DOCUMENT_NODE:
512 xmlGenericError(xmlGenericErrorContext, "?html? ");
513 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000514#ifdef LIBXML_DOCB_ENABLED
515 case XML_DOCB_DOCUMENT_NODE:
516 xmlGenericError(xmlGenericErrorContext, "?docb? ");
517 break;
518#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000519 case XML_DTD_NODE:
520 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
521 break;
522 case XML_ELEMENT_DECL:
523 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
524 break;
525 case XML_ATTRIBUTE_DECL:
526 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
527 break;
528 case XML_ENTITY_DECL:
529 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
530 break;
531 case XML_NAMESPACE_DECL:
532 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
533 break;
534 case XML_XINCLUDE_START:
535 xmlGenericError(xmlGenericErrorContext, "incstart ");
536 break;
537 case XML_XINCLUDE_END:
538 xmlGenericError(xmlGenericErrorContext, "incend ");
539 break;
540 }
541}
542
543static void
544xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000545 if (cur == NULL)
546 xmlGenericError(xmlGenericErrorContext, "null ");
547 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000548 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000549 cur = cur->next;
550 }
551}
552
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000553static void
554xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Daniel Veillard83391282003-03-06 21:37:30 +0000555 char expr[5000];
Owen Taylor3473f882001-02-23 17:55:21 +0000556
557 expr[0] = 0;
558 xmlGenericError(xmlGenericErrorContext, "valid: ");
559 xmlValidPrintNodeList(cur);
560 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000561 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000562 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
563}
564
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000565static void
566xmlValidDebugState(xmlValidStatePtr state) {
567 xmlGenericError(xmlGenericErrorContext, "(");
568 if (state->cont == NULL)
569 xmlGenericError(xmlGenericErrorContext, "null,");
570 else
571 switch (state->cont->type) {
572 case XML_ELEMENT_CONTENT_PCDATA:
573 xmlGenericError(xmlGenericErrorContext, "pcdata,");
574 break;
575 case XML_ELEMENT_CONTENT_ELEMENT:
576 xmlGenericError(xmlGenericErrorContext, "%s,",
577 state->cont->name);
578 break;
579 case XML_ELEMENT_CONTENT_SEQ:
580 xmlGenericError(xmlGenericErrorContext, "seq,");
581 break;
582 case XML_ELEMENT_CONTENT_OR:
583 xmlGenericError(xmlGenericErrorContext, "or,");
584 break;
585 }
586 xmlValidPrintNode(state->node);
587 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
588 state->depth, state->occurs, state->state);
589}
590
591static void
592xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
593 int i, j;
594
595 xmlGenericError(xmlGenericErrorContext, "state: ");
596 xmlValidDebugState(ctxt->vstate);
597 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
598 ctxt->vstateNr - 1);
599 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
600 xmlValidDebugState(&ctxt->vstateTab[j]);
601 xmlGenericError(xmlGenericErrorContext, "\n");
602}
603
604/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000605#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000606 *****/
607
608#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000609#define DEBUG_VALID_MSG(m) \
610 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
611
Owen Taylor3473f882001-02-23 17:55:21 +0000612#else
613#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000614#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000615#endif
616
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000617/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000618
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000619
Owen Taylor3473f882001-02-23 17:55:21 +0000620#define CHECK_DTD \
621 if (doc == NULL) return(0); \
622 else if ((doc->intSubset == NULL) && \
623 (doc->extSubset == NULL)) return(0)
624
Owen Taylor3473f882001-02-23 17:55:21 +0000625xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
626
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000627#ifdef LIBXML_REGEXP_ENABLED
628
629/************************************************************************
630 * *
631 * Content model validation based on the regexps *
632 * *
633 ************************************************************************/
634
635/**
636 * xmlValidBuildAContentModel:
637 * @content: the content model
638 * @ctxt: the schema parser context
639 * @name: the element name whose content is being built
640 *
641 * Generate the automata sequence needed for that type
642 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000643 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000644 */
645static int
646xmlValidBuildAContentModel(xmlElementContentPtr content,
647 xmlValidCtxtPtr ctxt,
648 const xmlChar *name) {
649 if (content == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000650 xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
651 "Found NULL content in content model of %s\n",
652 name, NULL, NULL);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000653 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000654 }
655 switch (content->type) {
656 case XML_ELEMENT_CONTENT_PCDATA:
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000657 xmlErrValidNode(ctxt, NULL, XML_ERR_INTERNAL_ERROR,
658 "Found PCDATA in content model of %s\n",
659 name, NULL, NULL);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000660 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000661 break;
662 case XML_ELEMENT_CONTENT_ELEMENT: {
663 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillardc00cda82003-04-07 10:22:39 +0000664 xmlChar fn[50];
665 xmlChar *fullname;
666
667 fullname = xmlBuildQName(content->name, content->prefix, fn, 50);
668 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000669 xmlVErrMemory(ctxt, "Building content model");
Daniel Veillardc00cda82003-04-07 10:22:39 +0000670 return(0);
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000671 }
672
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000673 switch (content->ocur) {
674 case XML_ELEMENT_CONTENT_ONCE:
675 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000676 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000677 break;
678 case XML_ELEMENT_CONTENT_OPT:
679 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000680 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000681 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
682 break;
683 case XML_ELEMENT_CONTENT_PLUS:
684 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000685 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000686 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000687 ctxt->state, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000688 break;
689 case XML_ELEMENT_CONTENT_MULT:
William M. Brack8b0cbb02004-04-17 13:31:06 +0000690 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
691 ctxt->state, NULL);
692 xmlAutomataNewTransition(ctxt->am,
693 ctxt->state, ctxt->state, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000694 break;
695 }
Daniel Veillardc00cda82003-04-07 10:22:39 +0000696 if ((fullname != fn) && (fullname != content->name))
697 xmlFree(fullname);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000698 break;
699 }
700 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000701 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000702 xmlElementContentOccur ocur;
703
704 /*
705 * Simply iterate over the content
706 */
707 oldstate = ctxt->state;
708 ocur = content->ocur;
Daniel Veillardf4be0182003-02-24 19:54:33 +0000709 if (ocur != XML_ELEMENT_CONTENT_ONCE) {
710 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
711 oldstate = ctxt->state;
712 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000713 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000714 xmlValidBuildAContentModel(content->c1, ctxt, name);
715 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000716 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
717 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
718 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000719 oldend = ctxt->state;
720 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000721 switch (ocur) {
722 case XML_ELEMENT_CONTENT_ONCE:
723 break;
724 case XML_ELEMENT_CONTENT_OPT:
725 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
726 break;
727 case XML_ELEMENT_CONTENT_MULT:
728 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000729 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000730 break;
731 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000732 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000733 break;
734 }
735 break;
736 }
737 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000738 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000739 xmlElementContentOccur ocur;
740
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000741 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000742 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
743 (ocur == XML_ELEMENT_CONTENT_MULT)) {
744 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
745 ctxt->state, NULL);
746 }
747 oldstate = ctxt->state;
748 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000749
750 /*
751 * iterate over the subtypes and remerge the end with an
752 * epsilon transition
753 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000754 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000755 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000756 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000757 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000758 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000759 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
760 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000761 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000762 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000763 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
764 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000765 switch (ocur) {
766 case XML_ELEMENT_CONTENT_ONCE:
767 break;
768 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000769 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000770 break;
771 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000772 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
773 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000774 break;
775 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000776 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000777 break;
778 }
779 break;
780 }
781 default:
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000782 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
783 "ContentModel broken for element %s\n",
784 (const char *) name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000785 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000786 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000787 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000788}
789/**
790 * xmlValidBuildContentModel:
791 * @ctxt: a validation context
792 * @elem: an element declaration node
793 *
794 * (Re)Build the automata associated to the content model of this
795 * element
796 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000797 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000798 */
799int
800xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000801
802 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000803 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000804 if (elem->type != XML_ELEMENT_DECL)
805 return(0);
806 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
807 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000808 /* TODO: should we rebuild in this case ? */
Daniel Veillardec498e12003-02-05 11:01:50 +0000809 if (elem->contModel != NULL) {
810 if (!xmlRegexpIsDeterminist(elem->contModel)) {
811 ctxt->valid = 0;
812 return(0);
813 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000814 return(1);
Daniel Veillardec498e12003-02-05 11:01:50 +0000815 }
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000816
817 ctxt->am = xmlNewAutomata();
818 if (ctxt->am == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000819 xmlErrValidNode(ctxt, (xmlNodePtr) elem,
820 XML_ERR_INTERNAL_ERROR,
821 "Cannot create automata for element %s\n",
822 elem->name, NULL, NULL);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000823 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000824 }
William M. Brack78637da2003-07-31 14:47:38 +0000825 ctxt->state = xmlAutomataGetInitState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000826 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
827 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000828 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000829 if (xmlRegexpIsDeterminist(elem->contModel) != 1) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000830 char expr[5000];
831 expr[0] = 0;
832 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
Daniel Veillardbb5abab2003-10-03 22:21:51 +0000833 xmlErrValidNode(ctxt, (xmlNodePtr) elem,
834 XML_DTD_CONTENT_NOT_DETERMINIST,
835 "Content model of %s is not determinist: %s\n",
836 elem->name, BAD_CAST expr, NULL);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000837#ifdef DEBUG_REGEXP_ALGO
838 xmlRegexpPrint(stderr, elem->contModel);
839#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000840 ctxt->valid = 0;
Daniel Veillardec498e12003-02-05 11:01:50 +0000841 ctxt->state = NULL;
842 xmlFreeAutomata(ctxt->am);
843 ctxt->am = NULL;
844 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000845 }
846 ctxt->state = NULL;
847 xmlFreeAutomata(ctxt->am);
848 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000849 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000850}
851
852#endif /* LIBXML_REGEXP_ENABLED */
853
Owen Taylor3473f882001-02-23 17:55:21 +0000854/****************************************************************
855 * *
856 * Util functions for data allocation/deallocation *
857 * *
858 ****************************************************************/
859
860/**
Daniel Veillarda37aab82003-06-09 09:10:36 +0000861 * xmlNewValidCtxt:
862 *
863 * Allocate a validation context structure.
864 *
865 * Returns NULL if not, otherwise the new validation context structure
866 */
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000867xmlValidCtxtPtr xmlNewValidCtxt(void) {
Daniel Veillarda37aab82003-06-09 09:10:36 +0000868 xmlValidCtxtPtr ret;
869
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000870 if ((ret = xmlMalloc(sizeof (xmlValidCtxt))) == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000871 xmlVErrMemory(NULL, "malloc failed");
Daniel Veillarda37aab82003-06-09 09:10:36 +0000872 return (NULL);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000873 }
Daniel Veillarda37aab82003-06-09 09:10:36 +0000874
875 (void) memset(ret, 0, sizeof (xmlValidCtxt));
876
877 return (ret);
878}
879
880/**
881 * xmlFreeValidCtxt:
882 * @cur: the validation context to free
883 *
884 * Free a validation context structure.
885 */
886void
887xmlFreeValidCtxt(xmlValidCtxtPtr cur) {
Daniel Veillardc0be74b2004-11-03 19:16:55 +0000888 if (cur->vstateTab != NULL)
889 xmlFree(cur->vstateTab);
890 if (cur->nodeTab != NULL)
891 xmlFree(cur->nodeTab);
Daniel Veillarda37aab82003-06-09 09:10:36 +0000892 xmlFree(cur);
893}
894
Daniel Veillard4432df22003-09-28 18:58:27 +0000895#endif /* LIBXML_VALID_ENABLED */
896
Daniel Veillarda37aab82003-06-09 09:10:36 +0000897/**
Daniel Veillardcee2b3a2005-01-25 00:22:52 +0000898 * xmlNewDocElementContent:
899 * @doc: the document
Owen Taylor3473f882001-02-23 17:55:21 +0000900 * @name: the subelement name or NULL
901 * @type: the type of element content decl
902 *
Daniel Veillardcee2b3a2005-01-25 00:22:52 +0000903 * Allocate an element content structure for the document.
Owen Taylor3473f882001-02-23 17:55:21 +0000904 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000905 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000906 */
907xmlElementContentPtr
Daniel Veillardcee2b3a2005-01-25 00:22:52 +0000908xmlNewDocElementContent(xmlDocPtr doc, const xmlChar *name,
909 xmlElementContentType type) {
Owen Taylor3473f882001-02-23 17:55:21 +0000910 xmlElementContentPtr ret;
Daniel Veillardcee2b3a2005-01-25 00:22:52 +0000911 xmlDictPtr dict = NULL;
912
913 if (doc != NULL)
914 dict = doc->dict;
Owen Taylor3473f882001-02-23 17:55:21 +0000915
916 switch(type) {
917 case XML_ELEMENT_CONTENT_ELEMENT:
918 if (name == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000919 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
920 "xmlNewElementContent : name == NULL !\n",
921 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000922 }
923 break;
924 case XML_ELEMENT_CONTENT_PCDATA:
925 case XML_ELEMENT_CONTENT_SEQ:
926 case XML_ELEMENT_CONTENT_OR:
927 if (name != NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000928 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
929 "xmlNewElementContent : name != NULL !\n",
930 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000931 }
932 break;
933 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +0000934 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
935 "Internal: ELEMENT content corrupted invalid type\n",
936 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +0000937 return(NULL);
938 }
939 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
940 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +0000941 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +0000942 return(NULL);
943 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000944 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000945 ret->type = type;
946 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000947 if (name != NULL) {
Daniel Veillardcee2b3a2005-01-25 00:22:52 +0000948 int l;
949 const xmlChar *tmp;
950
951 tmp = xmlSplitQName3(name, &l);
952 if (tmp == NULL) {
953 if (dict == NULL)
954 ret->name = xmlStrdup(name);
955 else
956 ret->name = xmlDictLookup(dict, name, -1);
957 } else {
958 if (dict == NULL) {
959 ret->prefix = xmlStrndup(name, l);
960 ret->name = xmlStrdup(tmp);
961 } else {
962 ret->prefix = xmlDictLookup(dict, name, l);
963 ret->name = xmlDictLookup(dict, tmp, -1);
964 }
965 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000966 }
Daniel Veillardcee2b3a2005-01-25 00:22:52 +0000967 return(ret);
968}
969
970/**
971 * xmlNewElementContent:
972 * @name: the subelement name or NULL
973 * @type: the type of element content decl
974 *
975 * Allocate an element content structure.
976 * Deprecated in favor of xmlNewDocElementContent
977 *
978 * Returns NULL if not, otherwise the new element content structure
979 */
980xmlElementContentPtr
981xmlNewElementContent(const xmlChar *name, xmlElementContentType type) {
982 return(xmlNewDocElementContent(NULL, name, type));
983}
984
985/**
986 * xmlCopyDocElementContent:
987 * @doc: the document owning the element declaration
988 * @cur: An element content pointer.
989 *
990 * Build a copy of an element content description.
991 *
992 * Returns the new xmlElementContentPtr or NULL in case of error.
993 */
994xmlElementContentPtr
995xmlCopyDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) {
996 xmlElementContentPtr ret = NULL, prev = NULL, tmp;
997 xmlDictPtr dict = NULL;
998
999 if (cur == NULL) return(NULL);
1000
1001 if (doc != NULL)
1002 dict = doc->dict;
1003
1004 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
1005 if (ret == NULL) {
1006 xmlVErrMemory(NULL, "malloc failed");
1007 return(NULL);
1008 }
1009 memset(ret, 0, sizeof(xmlElementContent));
1010 ret->type = cur->type;
1011 ret->ocur = cur->ocur;
1012 if (cur->name != NULL) {
1013 if (dict)
1014 ret->name = xmlDictLookup(dict, cur->name, -1);
1015 else
1016 ret->name = xmlStrdup(cur->name);
1017 }
1018
1019 if (cur->prefix != NULL) {
1020 if (dict)
1021 ret->prefix = xmlDictLookup(dict, cur->prefix, -1);
1022 else
1023 ret->prefix = xmlStrdup(cur->prefix);
1024 }
1025 if (cur->c1 != NULL)
1026 ret->c1 = xmlCopyDocElementContent(doc, cur->c1);
1027 if (ret->c1 != NULL)
1028 ret->c1->parent = ret;
1029 if (cur->c2 != NULL) {
1030 prev = ret;
1031 cur = cur->c2;
1032 while (cur != NULL) {
1033 tmp = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
1034 if (tmp == NULL) {
1035 xmlVErrMemory(NULL, "malloc failed");
1036 return(ret);
1037 }
1038 memset(tmp, 0, sizeof(xmlElementContent));
1039 tmp->type = cur->type;
1040 tmp->ocur = cur->ocur;
1041 prev->c2 = tmp;
1042 if (cur->name != NULL) {
1043 if (dict)
1044 tmp->name = xmlDictLookup(dict, cur->name, -1);
1045 else
1046 tmp->name = xmlStrdup(cur->name);
1047 }
1048
1049 if (cur->prefix != NULL) {
1050 if (dict)
1051 tmp->prefix = xmlDictLookup(dict, cur->prefix, -1);
1052 else
1053 tmp->prefix = xmlStrdup(cur->prefix);
1054 }
1055 if (cur->c1 != NULL)
1056 tmp->c1 = xmlCopyDocElementContent(doc,cur->c1);
1057 if (tmp->c1 != NULL)
1058 tmp->c1->parent = ret;
1059 prev = tmp;
1060 cur = cur->c2;
1061 }
1062 }
Owen Taylor3473f882001-02-23 17:55:21 +00001063 return(ret);
1064}
1065
1066/**
1067 * xmlCopyElementContent:
Daniel Veillard01c13b52002-12-10 15:19:08 +00001068 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +00001069 *
1070 * Build a copy of an element content description.
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001071 * Deprecated, use xmlCopyDocElementContent instead
Owen Taylor3473f882001-02-23 17:55:21 +00001072 *
1073 * Returns the new xmlElementContentPtr or NULL in case of error.
1074 */
1075xmlElementContentPtr
1076xmlCopyElementContent(xmlElementContentPtr cur) {
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001077 return(xmlCopyDocElementContent(NULL, cur));
1078}
Owen Taylor3473f882001-02-23 17:55:21 +00001079
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001080/**
1081 * xmlFreeDocElementContent:
1082 * @doc: the document owning the element declaration
1083 * @cur: the element content tree to free
1084 *
1085 * Free an element content structure. The whole subtree is removed.
1086 */
1087void
1088xmlFreeDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) {
1089 xmlElementContentPtr next;
1090 xmlDictPtr dict = NULL;
1091
1092 if (doc != NULL)
1093 dict = doc->dict;
1094
1095 while (cur != NULL) {
1096 next = cur->c2;
1097 switch (cur->type) {
1098 case XML_ELEMENT_CONTENT_PCDATA:
1099 case XML_ELEMENT_CONTENT_ELEMENT:
1100 case XML_ELEMENT_CONTENT_SEQ:
1101 case XML_ELEMENT_CONTENT_OR:
1102 break;
1103 default:
1104 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
1105 "Internal: ELEMENT content corrupted invalid type\n",
1106 NULL);
1107 return;
1108 }
1109 if (cur->c1 != NULL) xmlFreeDocElementContent(doc, cur->c1);
1110 if (dict) {
1111 if ((cur->name != NULL) && (!xmlDictOwns(dict, cur->name)))
1112 xmlFree((xmlChar *) cur->name);
1113 if ((cur->prefix != NULL) && (!xmlDictOwns(dict, cur->prefix)))
1114 xmlFree((xmlChar *) cur->prefix);
1115 } else {
1116 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
1117 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
1118 }
1119 xmlFree(cur);
1120 cur = next;
Owen Taylor3473f882001-02-23 17:55:21 +00001121 }
Owen Taylor3473f882001-02-23 17:55:21 +00001122}
1123
1124/**
1125 * xmlFreeElementContent:
1126 * @cur: the element content tree to free
1127 *
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001128 * Free an element content structure. The whole subtree is removed.
1129 * Deprecated, use xmlFreeDocElementContent instead
Owen Taylor3473f882001-02-23 17:55:21 +00001130 */
1131void
1132xmlFreeElementContent(xmlElementContentPtr cur) {
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001133 xmlFreeDocElementContent(NULL, cur);
Owen Taylor3473f882001-02-23 17:55:21 +00001134}
1135
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001136#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001137/**
1138 * xmlDumpElementContent:
1139 * @buf: An XML buffer
1140 * @content: An element table
1141 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
1142 *
1143 * This will dump the content of the element table as an XML DTD definition
1144 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001145static void
Owen Taylor3473f882001-02-23 17:55:21 +00001146xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
1147 if (content == NULL) return;
1148
1149 if (glob) xmlBufferWriteChar(buf, "(");
1150 switch (content->type) {
1151 case XML_ELEMENT_CONTENT_PCDATA:
1152 xmlBufferWriteChar(buf, "#PCDATA");
1153 break;
1154 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001155 if (content->prefix != NULL) {
1156 xmlBufferWriteCHAR(buf, content->prefix);
1157 xmlBufferWriteChar(buf, ":");
1158 }
Owen Taylor3473f882001-02-23 17:55:21 +00001159 xmlBufferWriteCHAR(buf, content->name);
1160 break;
1161 case XML_ELEMENT_CONTENT_SEQ:
1162 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1163 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
1164 xmlDumpElementContent(buf, content->c1, 1);
1165 else
1166 xmlDumpElementContent(buf, content->c1, 0);
1167 xmlBufferWriteChar(buf, " , ");
William M. Brack4119d1c2004-06-24 02:24:44 +00001168 if ((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
1169 ((content->c2->type == XML_ELEMENT_CONTENT_SEQ) &&
1170 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)))
Owen Taylor3473f882001-02-23 17:55:21 +00001171 xmlDumpElementContent(buf, content->c2, 1);
1172 else
1173 xmlDumpElementContent(buf, content->c2, 0);
1174 break;
1175 case XML_ELEMENT_CONTENT_OR:
1176 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1177 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
1178 xmlDumpElementContent(buf, content->c1, 1);
1179 else
1180 xmlDumpElementContent(buf, content->c1, 0);
1181 xmlBufferWriteChar(buf, " | ");
William M. Brack4119d1c2004-06-24 02:24:44 +00001182 if ((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
1183 ((content->c2->type == XML_ELEMENT_CONTENT_OR) &&
1184 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)))
Owen Taylor3473f882001-02-23 17:55:21 +00001185 xmlDumpElementContent(buf, content->c2, 1);
1186 else
1187 xmlDumpElementContent(buf, content->c2, 0);
1188 break;
1189 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001190 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
1191 "Internal: ELEMENT content corrupted invalid type\n",
1192 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001193 }
1194 if (glob)
1195 xmlBufferWriteChar(buf, ")");
1196 switch (content->ocur) {
1197 case XML_ELEMENT_CONTENT_ONCE:
1198 break;
1199 case XML_ELEMENT_CONTENT_OPT:
1200 xmlBufferWriteChar(buf, "?");
1201 break;
1202 case XML_ELEMENT_CONTENT_MULT:
1203 xmlBufferWriteChar(buf, "*");
1204 break;
1205 case XML_ELEMENT_CONTENT_PLUS:
1206 xmlBufferWriteChar(buf, "+");
1207 break;
1208 }
1209}
1210
1211/**
1212 * xmlSprintfElementContent:
1213 * @buf: an output buffer
1214 * @content: An element table
1215 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
1216 *
Daniel Veillardd3d06722001-08-15 12:06:36 +00001217 * Deprecated, unsafe, use xmlSnprintfElementContent
1218 */
1219void
1220xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
1221 xmlElementContentPtr content ATTRIBUTE_UNUSED,
1222 int glob ATTRIBUTE_UNUSED) {
1223}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001224#endif /* LIBXML_OUTPUT_ENABLED */
Daniel Veillardd3d06722001-08-15 12:06:36 +00001225
1226/**
1227 * xmlSnprintfElementContent:
1228 * @buf: an output buffer
1229 * @size: the buffer size
1230 * @content: An element table
1231 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
1232 *
Owen Taylor3473f882001-02-23 17:55:21 +00001233 * This will dump the content of the element content definition
1234 * Intended just for the debug routine
1235 */
1236void
Daniel Veillardd3d06722001-08-15 12:06:36 +00001237xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
1238 int len;
1239
Owen Taylor3473f882001-02-23 17:55:21 +00001240 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +00001241 len = strlen(buf);
1242 if (size - len < 50) {
1243 if ((size - len > 4) && (buf[len - 1] != '.'))
1244 strcat(buf, " ...");
1245 return;
1246 }
Owen Taylor3473f882001-02-23 17:55:21 +00001247 if (glob) strcat(buf, "(");
1248 switch (content->type) {
1249 case XML_ELEMENT_CONTENT_PCDATA:
1250 strcat(buf, "#PCDATA");
1251 break;
1252 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001253 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00001254 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001255 strcat(buf, " ...");
1256 return;
1257 }
1258 strcat(buf, (char *) content->prefix);
1259 strcat(buf, ":");
1260 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00001261 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00001262 strcat(buf, " ...");
1263 return;
1264 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001265 if (content->name != NULL)
1266 strcat(buf, (char *) content->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001267 break;
1268 case XML_ELEMENT_CONTENT_SEQ:
1269 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1270 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001271 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001272 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001273 xmlSnprintfElementContent(buf, size, content->c1, 0);
1274 len = strlen(buf);
1275 if (size - len < 50) {
1276 if ((size - len > 4) && (buf[len - 1] != '.'))
1277 strcat(buf, " ...");
1278 return;
1279 }
Owen Taylor3473f882001-02-23 17:55:21 +00001280 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00001281 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
1282 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1283 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001284 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001285 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001286 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001287 break;
1288 case XML_ELEMENT_CONTENT_OR:
1289 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1290 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001291 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001292 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001293 xmlSnprintfElementContent(buf, size, content->c1, 0);
1294 len = strlen(buf);
1295 if (size - len < 50) {
1296 if ((size - len > 4) && (buf[len - 1] != '.'))
1297 strcat(buf, " ...");
1298 return;
1299 }
Owen Taylor3473f882001-02-23 17:55:21 +00001300 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00001301 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
1302 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1303 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001304 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001305 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001306 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001307 break;
1308 }
1309 if (glob)
1310 strcat(buf, ")");
1311 switch (content->ocur) {
1312 case XML_ELEMENT_CONTENT_ONCE:
1313 break;
1314 case XML_ELEMENT_CONTENT_OPT:
1315 strcat(buf, "?");
1316 break;
1317 case XML_ELEMENT_CONTENT_MULT:
1318 strcat(buf, "*");
1319 break;
1320 case XML_ELEMENT_CONTENT_PLUS:
1321 strcat(buf, "+");
1322 break;
1323 }
1324}
1325
1326/****************************************************************
1327 * *
1328 * Registration of DTD declarations *
1329 * *
1330 ****************************************************************/
1331
1332/**
Owen Taylor3473f882001-02-23 17:55:21 +00001333 * xmlFreeElement:
1334 * @elem: An element
1335 *
1336 * Deallocate the memory used by an element definition
1337 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001338static void
Owen Taylor3473f882001-02-23 17:55:21 +00001339xmlFreeElement(xmlElementPtr elem) {
1340 if (elem == NULL) return;
1341 xmlUnlinkNode((xmlNodePtr) elem);
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001342 xmlFreeDocElementContent(elem->doc, elem->content);
Owen Taylor3473f882001-02-23 17:55:21 +00001343 if (elem->name != NULL)
1344 xmlFree((xmlChar *) elem->name);
1345 if (elem->prefix != NULL)
1346 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001347#ifdef LIBXML_REGEXP_ENABLED
1348 if (elem->contModel != NULL)
1349 xmlRegFreeRegexp(elem->contModel);
1350#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001351 xmlFree(elem);
1352}
1353
1354
1355/**
1356 * xmlAddElementDecl:
1357 * @ctxt: the validation context
1358 * @dtd: pointer to the DTD
1359 * @name: the entity name
1360 * @type: the element type
1361 * @content: the element content tree or NULL
1362 *
1363 * Register a new element declaration
1364 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001365 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001366 */
1367xmlElementPtr
William M. Brackedb65a72004-02-06 07:36:04 +00001368xmlAddElementDecl(xmlValidCtxtPtr ctxt,
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001369 xmlDtdPtr dtd, const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001370 xmlElementTypeVal type,
1371 xmlElementContentPtr content) {
1372 xmlElementPtr ret;
1373 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001374 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001375 xmlChar *ns, *uqname;
1376
1377 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001378 return(NULL);
1379 }
1380 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001381 return(NULL);
1382 }
Daniel Veillard316a5c32005-01-23 22:56:39 +00001383
Owen Taylor3473f882001-02-23 17:55:21 +00001384 switch (type) {
1385 case XML_ELEMENT_TYPE_EMPTY:
1386 if (content != NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001387 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1388 "xmlAddElementDecl: content != NULL for EMPTY\n",
1389 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001390 return(NULL);
1391 }
1392 break;
1393 case XML_ELEMENT_TYPE_ANY:
1394 if (content != NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001395 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1396 "xmlAddElementDecl: content != NULL for ANY\n",
1397 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001398 return(NULL);
1399 }
1400 break;
1401 case XML_ELEMENT_TYPE_MIXED:
1402 if (content == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001403 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1404 "xmlAddElementDecl: content == NULL for MIXED\n",
1405 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001406 return(NULL);
1407 }
1408 break;
1409 case XML_ELEMENT_TYPE_ELEMENT:
1410 if (content == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001411 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1412 "xmlAddElementDecl: content == NULL for ELEMENT\n",
1413 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001414 return(NULL);
1415 }
1416 break;
1417 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001418 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
1419 "Internal: ELEMENT decl corrupted invalid type\n",
1420 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001421 return(NULL);
1422 }
1423
1424 /*
1425 * check if name is a QName
1426 */
1427 uqname = xmlSplitQName2(name, &ns);
1428 if (uqname != NULL)
1429 name = uqname;
1430
1431 /*
1432 * Create the Element table if needed.
1433 */
1434 table = (xmlElementTablePtr) dtd->elements;
1435 if (table == NULL) {
Daniel Veillard316a5c32005-01-23 22:56:39 +00001436 xmlDictPtr dict = NULL;
1437
1438 if (dtd->doc != NULL)
1439 dict = dtd->doc->dict;
1440 table = xmlHashCreateDict(0, dict);
Owen Taylor3473f882001-02-23 17:55:21 +00001441 dtd->elements = (void *) table;
1442 }
1443 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001444 xmlVErrMemory(ctxt,
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001445 "xmlAddElementDecl: Table creation failed!\n");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001446 if (uqname != NULL)
1447 xmlFree(uqname);
1448 if (ns != NULL)
1449 xmlFree(ns);
Owen Taylor3473f882001-02-23 17:55:21 +00001450 return(NULL);
1451 }
1452
Daniel Veillarda10efa82001-04-18 13:09:01 +00001453 /*
1454 * lookup old attributes inserted on an undefined element in the
1455 * internal subset.
1456 */
1457 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1458 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1459 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1460 oldAttributes = ret->attributes;
1461 ret->attributes = NULL;
1462 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1463 xmlFreeElement(ret);
1464 }
Owen Taylor3473f882001-02-23 17:55:21 +00001465 }
Owen Taylor3473f882001-02-23 17:55:21 +00001466
1467 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001468 * The element may already be present if one of its attribute
1469 * was registered first
1470 */
1471 ret = xmlHashLookup2(table, name, ns);
1472 if (ret != NULL) {
1473 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001474#ifdef LIBXML_VALID_ENABLED
Daniel Veillarda10efa82001-04-18 13:09:01 +00001475 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001476 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001477 */
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001478 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED,
1479 "Redefinition of element %s\n",
1480 name, NULL, NULL);
Daniel Veillard4432df22003-09-28 18:58:27 +00001481#endif /* LIBXML_VALID_ENABLED */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001482 if (uqname != NULL)
1483 xmlFree(uqname);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001484 if (ns != NULL)
1485 xmlFree(ns);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001486 return(NULL);
1487 }
William M. Brackd6e347e2005-04-15 01:34:41 +00001488 if (ns != NULL) {
1489 xmlFree(ns);
1490 ns = NULL;
1491 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00001492 } else {
1493 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1494 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001495 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001496 if (uqname != NULL)
1497 xmlFree(uqname);
1498 if (ns != NULL)
1499 xmlFree(ns);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001500 return(NULL);
1501 }
1502 memset(ret, 0, sizeof(xmlElement));
1503 ret->type = XML_ELEMENT_DECL;
1504
1505 /*
1506 * fill the structure.
1507 */
1508 ret->name = xmlStrdup(name);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001509 if (ret->name == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001510 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001511 if (uqname != NULL)
1512 xmlFree(uqname);
1513 if (ns != NULL)
1514 xmlFree(ns);
1515 xmlFree(ret);
1516 return(NULL);
1517 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00001518 ret->prefix = ns;
1519
1520 /*
1521 * Validity Check:
1522 * Insertion must not fail
1523 */
1524 if (xmlHashAddEntry2(table, name, ns, ret)) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001525#ifdef LIBXML_VALID_ENABLED
Daniel Veillarda10efa82001-04-18 13:09:01 +00001526 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001527 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001528 */
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001529 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED,
1530 "Redefinition of element %s\n",
1531 name, NULL, NULL);
Daniel Veillard4432df22003-09-28 18:58:27 +00001532#endif /* LIBXML_VALID_ENABLED */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001533 xmlFreeElement(ret);
1534 if (uqname != NULL)
1535 xmlFree(uqname);
1536 return(NULL);
1537 }
William M. Brack4e52f2f2003-09-14 18:07:39 +00001538 /*
1539 * For new element, may have attributes from earlier
1540 * definition in internal subset
1541 */
1542 ret->attributes = oldAttributes;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001543 }
1544
1545 /*
1546 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001547 */
1548 ret->etype = type;
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001549 /*
1550 * Avoid a stupid copy when called by the parser
1551 * and flag it by setting a special parent value
1552 * so the parser doesn't unallocate it.
1553 */
Daniel Veillardc394f732005-01-26 00:04:52 +00001554 if ((ctxt != NULL) &&
1555 ((ctxt->finishDtd == XML_CTXT_FINISH_DTD_0) ||
1556 (ctxt->finishDtd == XML_CTXT_FINISH_DTD_1))) {
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001557 ret->content = content;
1558 if (content != NULL)
1559 content->parent = (xmlElementContentPtr) 1;
1560 } else {
1561 ret->content = xmlCopyDocElementContent(dtd->doc, content);
1562 }
Owen Taylor3473f882001-02-23 17:55:21 +00001563
1564 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001565 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001566 */
1567 ret->parent = dtd;
1568 ret->doc = dtd->doc;
1569 if (dtd->last == NULL) {
1570 dtd->children = dtd->last = (xmlNodePtr) ret;
1571 } else {
1572 dtd->last->next = (xmlNodePtr) ret;
1573 ret->prev = dtd->last;
1574 dtd->last = (xmlNodePtr) ret;
1575 }
1576 if (uqname != NULL)
1577 xmlFree(uqname);
1578 return(ret);
1579}
1580
1581/**
1582 * xmlFreeElementTable:
1583 * @table: An element table
1584 *
1585 * Deallocate the memory used by an element hash table.
1586 */
1587void
1588xmlFreeElementTable(xmlElementTablePtr table) {
1589 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1590}
1591
Daniel Veillard652327a2003-09-29 18:02:38 +00001592#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001593/**
1594 * xmlCopyElement:
1595 * @elem: An element
1596 *
1597 * Build a copy of an element.
1598 *
1599 * Returns the new xmlElementPtr or NULL in case of error.
1600 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001601static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001602xmlCopyElement(xmlElementPtr elem) {
1603 xmlElementPtr cur;
1604
1605 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1606 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001607 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001608 return(NULL);
1609 }
1610 memset(cur, 0, sizeof(xmlElement));
1611 cur->type = XML_ELEMENT_DECL;
1612 cur->etype = elem->etype;
1613 if (elem->name != NULL)
1614 cur->name = xmlStrdup(elem->name);
1615 else
1616 cur->name = NULL;
1617 if (elem->prefix != NULL)
1618 cur->prefix = xmlStrdup(elem->prefix);
1619 else
1620 cur->prefix = NULL;
1621 cur->content = xmlCopyElementContent(elem->content);
1622 /* TODO : rebuild the attribute list on the copy */
1623 cur->attributes = NULL;
1624 return(cur);
1625}
1626
1627/**
1628 * xmlCopyElementTable:
1629 * @table: An element table
1630 *
1631 * Build a copy of an element table.
1632 *
1633 * Returns the new xmlElementTablePtr or NULL in case of error.
1634 */
1635xmlElementTablePtr
1636xmlCopyElementTable(xmlElementTablePtr table) {
1637 return((xmlElementTablePtr) xmlHashCopy(table,
1638 (xmlHashCopier) xmlCopyElement));
1639}
Daniel Veillard652327a2003-09-29 18:02:38 +00001640#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001641
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001642#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001643/**
1644 * xmlDumpElementDecl:
1645 * @buf: the XML buffer output
1646 * @elem: An element table
1647 *
1648 * This will dump the content of the element declaration as an XML
1649 * DTD definition
1650 */
1651void
1652xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00001653 if ((buf == NULL) || (elem == NULL))
1654 return;
Owen Taylor3473f882001-02-23 17:55:21 +00001655 switch (elem->etype) {
1656 case XML_ELEMENT_TYPE_EMPTY:
1657 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001658 if (elem->prefix != NULL) {
1659 xmlBufferWriteCHAR(buf, elem->prefix);
1660 xmlBufferWriteChar(buf, ":");
1661 }
Owen Taylor3473f882001-02-23 17:55:21 +00001662 xmlBufferWriteCHAR(buf, elem->name);
1663 xmlBufferWriteChar(buf, " EMPTY>\n");
1664 break;
1665 case XML_ELEMENT_TYPE_ANY:
1666 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001667 if (elem->prefix != NULL) {
1668 xmlBufferWriteCHAR(buf, elem->prefix);
1669 xmlBufferWriteChar(buf, ":");
1670 }
Owen Taylor3473f882001-02-23 17:55:21 +00001671 xmlBufferWriteCHAR(buf, elem->name);
1672 xmlBufferWriteChar(buf, " ANY>\n");
1673 break;
1674 case XML_ELEMENT_TYPE_MIXED:
1675 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001676 if (elem->prefix != NULL) {
1677 xmlBufferWriteCHAR(buf, elem->prefix);
1678 xmlBufferWriteChar(buf, ":");
1679 }
Owen Taylor3473f882001-02-23 17:55:21 +00001680 xmlBufferWriteCHAR(buf, elem->name);
1681 xmlBufferWriteChar(buf, " ");
1682 xmlDumpElementContent(buf, elem->content, 1);
1683 xmlBufferWriteChar(buf, ">\n");
1684 break;
1685 case XML_ELEMENT_TYPE_ELEMENT:
1686 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001687 if (elem->prefix != NULL) {
1688 xmlBufferWriteCHAR(buf, elem->prefix);
1689 xmlBufferWriteChar(buf, ":");
1690 }
Owen Taylor3473f882001-02-23 17:55:21 +00001691 xmlBufferWriteCHAR(buf, elem->name);
1692 xmlBufferWriteChar(buf, " ");
1693 xmlDumpElementContent(buf, elem->content, 1);
1694 xmlBufferWriteChar(buf, ">\n");
1695 break;
1696 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00001697 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
1698 "Internal: ELEMENT struct corrupted invalid type\n",
1699 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001700 }
1701}
1702
1703/**
William M. Brack9e660592003-10-20 14:56:06 +00001704 * xmlDumpElementDeclScan:
1705 * @elem: An element table
1706 * @buf: the XML buffer output
1707 *
1708 * This routine is used by the hash scan function. It just reverses
1709 * the arguments.
1710 */
1711static void
1712xmlDumpElementDeclScan(xmlElementPtr elem, xmlBufferPtr buf) {
1713 xmlDumpElementDecl(buf, elem);
1714}
1715
1716/**
Owen Taylor3473f882001-02-23 17:55:21 +00001717 * xmlDumpElementTable:
1718 * @buf: the XML buffer output
1719 * @table: An element table
1720 *
1721 * This will dump the content of the element table as an XML DTD definition
1722 */
1723void
1724xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00001725 if ((buf == NULL) || (table == NULL))
1726 return;
William M. Brack9e660592003-10-20 14:56:06 +00001727 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDeclScan, buf);
Owen Taylor3473f882001-02-23 17:55:21 +00001728}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001729#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001730
1731/**
1732 * xmlCreateEnumeration:
1733 * @name: the enumeration name or NULL
1734 *
1735 * create and initialize an enumeration attribute node.
1736 *
1737 * Returns the xmlEnumerationPtr just created or NULL in case
1738 * of error.
1739 */
1740xmlEnumerationPtr
Daniel Veillard2fdbd322003-08-18 12:15:38 +00001741xmlCreateEnumeration(const xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00001742 xmlEnumerationPtr ret;
1743
1744 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1745 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00001746 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00001747 return(NULL);
1748 }
1749 memset(ret, 0, sizeof(xmlEnumeration));
1750
1751 if (name != NULL)
1752 ret->name = xmlStrdup(name);
1753 return(ret);
1754}
1755
1756/**
1757 * xmlFreeEnumeration:
1758 * @cur: the tree to free.
1759 *
1760 * free an enumeration attribute node (recursive).
1761 */
1762void
1763xmlFreeEnumeration(xmlEnumerationPtr cur) {
1764 if (cur == NULL) return;
1765
1766 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1767
1768 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001769 xmlFree(cur);
1770}
1771
Daniel Veillard652327a2003-09-29 18:02:38 +00001772#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001773/**
1774 * xmlCopyEnumeration:
1775 * @cur: the tree to copy.
1776 *
1777 * Copy an enumeration attribute node (recursive).
1778 *
1779 * Returns the xmlEnumerationPtr just created or NULL in case
1780 * of error.
1781 */
1782xmlEnumerationPtr
1783xmlCopyEnumeration(xmlEnumerationPtr cur) {
1784 xmlEnumerationPtr ret;
1785
1786 if (cur == NULL) return(NULL);
1787 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1788
1789 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1790 else ret->next = NULL;
1791
1792 return(ret);
1793}
Daniel Veillard652327a2003-09-29 18:02:38 +00001794#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001795
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001796#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001797/**
1798 * xmlDumpEnumeration:
1799 * @buf: the XML buffer output
1800 * @enum: An enumeration
1801 *
1802 * This will dump the content of the enumeration
1803 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001804static void
Owen Taylor3473f882001-02-23 17:55:21 +00001805xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00001806 if ((buf == NULL) || (cur == NULL))
1807 return;
Owen Taylor3473f882001-02-23 17:55:21 +00001808
1809 xmlBufferWriteCHAR(buf, cur->name);
1810 if (cur->next == NULL)
1811 xmlBufferWriteChar(buf, ")");
1812 else {
1813 xmlBufferWriteChar(buf, " | ");
1814 xmlDumpEnumeration(buf, cur->next);
1815 }
1816}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001817#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001818
Daniel Veillard4432df22003-09-28 18:58:27 +00001819#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001820/**
1821 * xmlScanAttributeDeclCallback:
1822 * @attr: the attribute decl
1823 * @list: the list to update
1824 *
1825 * Callback called by xmlScanAttributeDecl when a new attribute
1826 * has to be entered in the list.
1827 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001828static void
Owen Taylor3473f882001-02-23 17:55:21 +00001829xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001830 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001831 attr->nexth = *list;
1832 *list = attr;
1833}
1834
1835/**
1836 * xmlScanAttributeDecl:
1837 * @dtd: pointer to the DTD
1838 * @elem: the element name
1839 *
1840 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001841 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001842 *
1843 * Returns the pointer to the first attribute decl in the chain,
1844 * possibly NULL.
1845 */
1846xmlAttributePtr
1847xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1848 xmlAttributePtr ret = NULL;
1849 xmlAttributeTablePtr table;
1850
1851 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001852 return(NULL);
1853 }
1854 if (elem == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001855 return(NULL);
1856 }
1857 table = (xmlAttributeTablePtr) dtd->attributes;
1858 if (table == NULL)
1859 return(NULL);
1860
1861 /* WRONG !!! */
1862 xmlHashScan3(table, NULL, NULL, elem,
1863 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1864 return(ret);
1865}
1866
1867/**
1868 * xmlScanIDAttributeDecl:
1869 * @ctxt: the validation context
1870 * @elem: the element name
1871 *
1872 * Verify that the element don't have too many ID attributes
1873 * declared.
1874 *
1875 * Returns the number of ID attributes found.
1876 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001877static int
Owen Taylor3473f882001-02-23 17:55:21 +00001878xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1879 xmlAttributePtr cur;
1880 int ret = 0;
1881
1882 if (elem == NULL) return(0);
1883 cur = elem->attributes;
1884 while (cur != NULL) {
1885 if (cur->atype == XML_ATTRIBUTE_ID) {
1886 ret ++;
1887 if (ret > 1)
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001888 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_MULTIPLE_ID,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001889 "Element %s has too many ID attributes defined : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00001890 elem->name, cur->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001891 }
1892 cur = cur->nexth;
1893 }
1894 return(ret);
1895}
Daniel Veillard4432df22003-09-28 18:58:27 +00001896#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001897
1898/**
1899 * xmlFreeAttribute:
1900 * @elem: An attribute
1901 *
1902 * Deallocate the memory used by an attribute definition
1903 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001904static void
Owen Taylor3473f882001-02-23 17:55:21 +00001905xmlFreeAttribute(xmlAttributePtr attr) {
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001906 xmlDictPtr dict;
1907
Owen Taylor3473f882001-02-23 17:55:21 +00001908 if (attr == NULL) return;
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001909 if (attr->doc != NULL)
1910 dict = attr->doc->dict;
1911 else
1912 dict = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001913 xmlUnlinkNode((xmlNodePtr) attr);
1914 if (attr->tree != NULL)
1915 xmlFreeEnumeration(attr->tree);
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001916 if (dict) {
1917 if ((attr->elem != NULL) && (!xmlDictOwns(dict, attr->elem)))
1918 xmlFree((xmlChar *) attr->elem);
1919 if ((attr->name != NULL) && (!xmlDictOwns(dict, attr->name)))
1920 xmlFree((xmlChar *) attr->name);
1921 if ((attr->prefix != NULL) && (!xmlDictOwns(dict, attr->prefix)))
1922 xmlFree((xmlChar *) attr->prefix);
1923 if ((attr->defaultValue != NULL) &&
1924 (!xmlDictOwns(dict, attr->defaultValue)))
1925 xmlFree((xmlChar *) attr->defaultValue);
1926 } else {
1927 if (attr->elem != NULL)
1928 xmlFree((xmlChar *) attr->elem);
1929 if (attr->name != NULL)
1930 xmlFree((xmlChar *) attr->name);
1931 if (attr->defaultValue != NULL)
1932 xmlFree((xmlChar *) attr->defaultValue);
1933 if (attr->prefix != NULL)
1934 xmlFree((xmlChar *) attr->prefix);
1935 }
Owen Taylor3473f882001-02-23 17:55:21 +00001936 xmlFree(attr);
1937}
1938
1939
1940/**
1941 * xmlAddAttributeDecl:
1942 * @ctxt: the validation context
1943 * @dtd: pointer to the DTD
1944 * @elem: the element name
1945 * @name: the attribute name
1946 * @ns: the attribute namespace prefix
1947 * @type: the attribute type
1948 * @def: the attribute default type
1949 * @defaultValue: the attribute default value
1950 * @tree: if it's an enumeration, the associated list
1951 *
1952 * Register a new attribute declaration
1953 * Note that @tree becomes the ownership of the DTD
1954 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001955 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001956 */
1957xmlAttributePtr
William M. Brackedb65a72004-02-06 07:36:04 +00001958xmlAddAttributeDecl(xmlValidCtxtPtr ctxt,
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001959 xmlDtdPtr dtd, const xmlChar *elem,
Owen Taylor3473f882001-02-23 17:55:21 +00001960 const xmlChar *name, const xmlChar *ns,
1961 xmlAttributeType type, xmlAttributeDefault def,
1962 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1963 xmlAttributePtr ret;
1964 xmlAttributeTablePtr table;
1965 xmlElementPtr elemDef;
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001966 xmlDictPtr dict = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001967
1968 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001969 xmlFreeEnumeration(tree);
1970 return(NULL);
1971 }
1972 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001973 xmlFreeEnumeration(tree);
1974 return(NULL);
1975 }
1976 if (elem == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00001977 xmlFreeEnumeration(tree);
1978 return(NULL);
1979 }
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00001980 if (dtd->doc != NULL)
1981 dict = dtd->doc->dict;
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001982
Daniel Veillard4432df22003-09-28 18:58:27 +00001983#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001984 /*
1985 * Check the type and possibly the default value.
1986 */
1987 switch (type) {
1988 case XML_ATTRIBUTE_CDATA:
1989 break;
1990 case XML_ATTRIBUTE_ID:
1991 break;
1992 case XML_ATTRIBUTE_IDREF:
1993 break;
1994 case XML_ATTRIBUTE_IDREFS:
1995 break;
1996 case XML_ATTRIBUTE_ENTITY:
1997 break;
1998 case XML_ATTRIBUTE_ENTITIES:
1999 break;
2000 case XML_ATTRIBUTE_NMTOKEN:
2001 break;
2002 case XML_ATTRIBUTE_NMTOKENS:
2003 break;
2004 case XML_ATTRIBUTE_ENUMERATION:
2005 break;
2006 case XML_ATTRIBUTE_NOTATION:
2007 break;
2008 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002009 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
2010 "Internal: ATTRIBUTE struct corrupted invalid type\n",
2011 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002012 xmlFreeEnumeration(tree);
2013 return(NULL);
2014 }
2015 if ((defaultValue != NULL) &&
2016 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00002017 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_DEFAULT,
2018 "Attribute %s of %s: invalid default value\n",
2019 elem, name, defaultValue);
Owen Taylor3473f882001-02-23 17:55:21 +00002020 defaultValue = NULL;
Daniel Veillard42595322004-11-08 10:52:06 +00002021 if (ctxt != NULL)
2022 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00002023 }
Daniel Veillard4432df22003-09-28 18:58:27 +00002024#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002025
2026 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00002027 * Check first that an attribute defined in the external subset wasn't
2028 * already defined in the internal subset
2029 */
2030 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
2031 (dtd->doc->intSubset != NULL) &&
2032 (dtd->doc->intSubset->attributes != NULL)) {
2033 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
2034 if (ret != NULL)
2035 return(NULL);
2036 }
2037
2038 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002039 * Create the Attribute table if needed.
2040 */
2041 table = (xmlAttributeTablePtr) dtd->attributes;
2042 if (table == NULL) {
Daniel Veillard316a5c32005-01-23 22:56:39 +00002043 table = xmlHashCreateDict(0, dict);
Owen Taylor3473f882001-02-23 17:55:21 +00002044 dtd->attributes = (void *) table;
2045 }
2046 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002047 xmlVErrMemory(ctxt,
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002048 "xmlAddAttributeDecl: Table creation failed!\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002049 return(NULL);
2050 }
2051
2052
2053 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
2054 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002055 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002056 return(NULL);
2057 }
2058 memset(ret, 0, sizeof(xmlAttribute));
2059 ret->type = XML_ATTRIBUTE_DECL;
2060
2061 /*
2062 * fill the structure.
2063 */
2064 ret->atype = type;
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00002065 if (dict) {
2066 ret->name = xmlDictLookup(dict, name, -1);
2067 ret->prefix = xmlDictLookup(dict, ns, -1);
2068 ret->elem = xmlDictLookup(dict, elem, -1);
2069 } else {
2070 ret->name = xmlStrdup(name);
2071 ret->prefix = xmlStrdup(ns);
2072 ret->elem = xmlStrdup(elem);
2073 }
Owen Taylor3473f882001-02-23 17:55:21 +00002074 ret->def = def;
2075 ret->tree = tree;
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00002076 if (defaultValue != NULL) {
2077 if (dict)
2078 ret->defaultValue = xmlDictLookup(dict, defaultValue, -1);
2079 else
2080 ret->defaultValue = xmlStrdup(defaultValue);
2081 }
Owen Taylor3473f882001-02-23 17:55:21 +00002082
2083 /*
2084 * Validity Check:
2085 * Search the DTD for previous declarations of the ATTLIST
2086 */
Daniel Veillardcee2b3a2005-01-25 00:22:52 +00002087 if (xmlHashAddEntry3(table, ret->name, ret->prefix, ret->elem, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002088#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002089 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002090 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002091 */
Daniel Veillardbb5abab2003-10-03 22:21:51 +00002092 xmlErrValidWarning(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_REDEFINED,
Daniel Veillard58e44c92002-08-02 22:19:49 +00002093 "Attribute %s of element %s: already defined\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00002094 name, elem, NULL);
Daniel Veillard4432df22003-09-28 18:58:27 +00002095#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002096 xmlFreeAttribute(ret);
2097 return(NULL);
2098 }
2099
2100 /*
2101 * Validity Check:
2102 * Multiple ID per element
2103 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00002104 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00002105 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00002106
Daniel Veillard4432df22003-09-28 18:58:27 +00002107#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002108 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00002109 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00002110 xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID,
Owen Taylor3473f882001-02-23 17:55:21 +00002111 "Element %s has too may ID attributes defined : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00002112 elem, name, NULL);
Daniel Veillard42595322004-11-08 10:52:06 +00002113 if (ctxt != NULL)
2114 ctxt->valid = 0;
Daniel Veillardc7612992002-02-17 22:47:37 +00002115 }
Daniel Veillard4432df22003-09-28 18:58:27 +00002116#endif /* LIBXML_VALID_ENABLED */
Daniel Veillardc7612992002-02-17 22:47:37 +00002117
Daniel Veillard48da9102001-08-07 01:10:10 +00002118 /*
2119 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002120 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00002121 */
2122 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
2123 ((ret->prefix != NULL &&
2124 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
2125 ret->nexth = elemDef->attributes;
2126 elemDef->attributes = ret;
2127 } else {
2128 xmlAttributePtr tmp = elemDef->attributes;
2129
2130 while ((tmp != NULL) &&
2131 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
2132 ((ret->prefix != NULL &&
2133 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
2134 if (tmp->nexth == NULL)
2135 break;
2136 tmp = tmp->nexth;
2137 }
2138 if (tmp != NULL) {
2139 ret->nexth = tmp->nexth;
2140 tmp->nexth = ret;
2141 } else {
2142 ret->nexth = elemDef->attributes;
2143 elemDef->attributes = ret;
2144 }
2145 }
Owen Taylor3473f882001-02-23 17:55:21 +00002146 }
2147
2148 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002149 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00002150 */
2151 ret->parent = dtd;
2152 ret->doc = dtd->doc;
2153 if (dtd->last == NULL) {
2154 dtd->children = dtd->last = (xmlNodePtr) ret;
2155 } else {
2156 dtd->last->next = (xmlNodePtr) ret;
2157 ret->prev = dtd->last;
2158 dtd->last = (xmlNodePtr) ret;
2159 }
2160 return(ret);
2161}
2162
2163/**
2164 * xmlFreeAttributeTable:
2165 * @table: An attribute table
2166 *
2167 * Deallocate the memory used by an entities hash table.
2168 */
2169void
2170xmlFreeAttributeTable(xmlAttributeTablePtr table) {
2171 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
2172}
2173
Daniel Veillard652327a2003-09-29 18:02:38 +00002174#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002175/**
2176 * xmlCopyAttribute:
2177 * @attr: An attribute
2178 *
2179 * Build a copy of an attribute.
2180 *
2181 * Returns the new xmlAttributePtr or NULL in case of error.
2182 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002183static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002184xmlCopyAttribute(xmlAttributePtr attr) {
2185 xmlAttributePtr cur;
2186
2187 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
2188 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002189 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002190 return(NULL);
2191 }
2192 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00002193 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00002194 cur->atype = attr->atype;
2195 cur->def = attr->def;
2196 cur->tree = xmlCopyEnumeration(attr->tree);
2197 if (attr->elem != NULL)
2198 cur->elem = xmlStrdup(attr->elem);
2199 if (attr->name != NULL)
2200 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00002201 if (attr->prefix != NULL)
2202 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00002203 if (attr->defaultValue != NULL)
2204 cur->defaultValue = xmlStrdup(attr->defaultValue);
2205 return(cur);
2206}
2207
2208/**
2209 * xmlCopyAttributeTable:
2210 * @table: An attribute table
2211 *
2212 * Build a copy of an attribute table.
2213 *
2214 * Returns the new xmlAttributeTablePtr or NULL in case of error.
2215 */
2216xmlAttributeTablePtr
2217xmlCopyAttributeTable(xmlAttributeTablePtr table) {
2218 return((xmlAttributeTablePtr) xmlHashCopy(table,
2219 (xmlHashCopier) xmlCopyAttribute));
2220}
Daniel Veillard652327a2003-09-29 18:02:38 +00002221#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002222
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002223#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002224/**
2225 * xmlDumpAttributeDecl:
2226 * @buf: the XML buffer output
2227 * @attr: An attribute declaration
2228 *
2229 * This will dump the content of the attribute declaration as an XML
2230 * DTD definition
2231 */
2232void
2233xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00002234 if ((buf == NULL) || (attr == NULL))
2235 return;
Owen Taylor3473f882001-02-23 17:55:21 +00002236 xmlBufferWriteChar(buf, "<!ATTLIST ");
2237 xmlBufferWriteCHAR(buf, attr->elem);
2238 xmlBufferWriteChar(buf, " ");
2239 if (attr->prefix != NULL) {
2240 xmlBufferWriteCHAR(buf, attr->prefix);
2241 xmlBufferWriteChar(buf, ":");
2242 }
2243 xmlBufferWriteCHAR(buf, attr->name);
2244 switch (attr->atype) {
2245 case XML_ATTRIBUTE_CDATA:
2246 xmlBufferWriteChar(buf, " CDATA");
2247 break;
2248 case XML_ATTRIBUTE_ID:
2249 xmlBufferWriteChar(buf, " ID");
2250 break;
2251 case XML_ATTRIBUTE_IDREF:
2252 xmlBufferWriteChar(buf, " IDREF");
2253 break;
2254 case XML_ATTRIBUTE_IDREFS:
2255 xmlBufferWriteChar(buf, " IDREFS");
2256 break;
2257 case XML_ATTRIBUTE_ENTITY:
2258 xmlBufferWriteChar(buf, " ENTITY");
2259 break;
2260 case XML_ATTRIBUTE_ENTITIES:
2261 xmlBufferWriteChar(buf, " ENTITIES");
2262 break;
2263 case XML_ATTRIBUTE_NMTOKEN:
2264 xmlBufferWriteChar(buf, " NMTOKEN");
2265 break;
2266 case XML_ATTRIBUTE_NMTOKENS:
2267 xmlBufferWriteChar(buf, " NMTOKENS");
2268 break;
2269 case XML_ATTRIBUTE_ENUMERATION:
2270 xmlBufferWriteChar(buf, " (");
2271 xmlDumpEnumeration(buf, attr->tree);
2272 break;
2273 case XML_ATTRIBUTE_NOTATION:
2274 xmlBufferWriteChar(buf, " NOTATION (");
2275 xmlDumpEnumeration(buf, attr->tree);
2276 break;
2277 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002278 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2279 "Internal: ATTRIBUTE struct corrupted invalid type\n",
2280 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002281 }
2282 switch (attr->def) {
2283 case XML_ATTRIBUTE_NONE:
2284 break;
2285 case XML_ATTRIBUTE_REQUIRED:
2286 xmlBufferWriteChar(buf, " #REQUIRED");
2287 break;
2288 case XML_ATTRIBUTE_IMPLIED:
2289 xmlBufferWriteChar(buf, " #IMPLIED");
2290 break;
2291 case XML_ATTRIBUTE_FIXED:
2292 xmlBufferWriteChar(buf, " #FIXED");
2293 break;
2294 default:
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002295 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2296 "Internal: ATTRIBUTE struct corrupted invalid def\n",
2297 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002298 }
2299 if (attr->defaultValue != NULL) {
2300 xmlBufferWriteChar(buf, " ");
2301 xmlBufferWriteQuotedString(buf, attr->defaultValue);
2302 }
2303 xmlBufferWriteChar(buf, ">\n");
2304}
2305
2306/**
William M. Brack9e660592003-10-20 14:56:06 +00002307 * xmlDumpAttributeDeclScan:
2308 * @attr: An attribute declaration
2309 * @buf: the XML buffer output
2310 *
2311 * This is used with the hash scan function - just reverses arguments
2312 */
2313static void
2314xmlDumpAttributeDeclScan(xmlAttributePtr attr, xmlBufferPtr buf) {
2315 xmlDumpAttributeDecl(buf, attr);
2316}
2317
2318/**
Owen Taylor3473f882001-02-23 17:55:21 +00002319 * xmlDumpAttributeTable:
2320 * @buf: the XML buffer output
2321 * @table: An attribute table
2322 *
2323 * This will dump the content of the attribute table as an XML DTD definition
2324 */
2325void
2326xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00002327 if ((buf == NULL) || (table == NULL))
2328 return;
William M. Brack9e660592003-10-20 14:56:06 +00002329 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDeclScan, buf);
Owen Taylor3473f882001-02-23 17:55:21 +00002330}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002331#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002332
2333/************************************************************************
2334 * *
2335 * NOTATIONs *
2336 * *
2337 ************************************************************************/
2338/**
Owen Taylor3473f882001-02-23 17:55:21 +00002339 * xmlFreeNotation:
2340 * @not: A notation
2341 *
2342 * Deallocate the memory used by an notation definition
2343 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002344static void
Owen Taylor3473f882001-02-23 17:55:21 +00002345xmlFreeNotation(xmlNotationPtr nota) {
2346 if (nota == NULL) return;
2347 if (nota->name != NULL)
2348 xmlFree((xmlChar *) nota->name);
2349 if (nota->PublicID != NULL)
2350 xmlFree((xmlChar *) nota->PublicID);
2351 if (nota->SystemID != NULL)
2352 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002353 xmlFree(nota);
2354}
2355
2356
2357/**
2358 * xmlAddNotationDecl:
2359 * @dtd: pointer to the DTD
2360 * @ctxt: the validation context
2361 * @name: the entity name
2362 * @PublicID: the public identifier or NULL
2363 * @SystemID: the system identifier or NULL
2364 *
2365 * Register a new notation declaration
2366 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002367 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00002368 */
2369xmlNotationPtr
William M. Brackedb65a72004-02-06 07:36:04 +00002370xmlAddNotationDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002371 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00002372 const xmlChar *PublicID, const xmlChar *SystemID) {
2373 xmlNotationPtr ret;
2374 xmlNotationTablePtr table;
2375
2376 if (dtd == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002377 return(NULL);
2378 }
2379 if (name == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002380 return(NULL);
2381 }
2382 if ((PublicID == NULL) && (SystemID == NULL)) {
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002383 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002384 }
2385
2386 /*
2387 * Create the Notation table if needed.
2388 */
2389 table = (xmlNotationTablePtr) dtd->notations;
Daniel Veillard316a5c32005-01-23 22:56:39 +00002390 if (table == NULL) {
2391 xmlDictPtr dict = NULL;
2392 if (dtd->doc != NULL)
2393 dict = dtd->doc->dict;
2394
2395 dtd->notations = table = xmlHashCreateDict(0, dict);
2396 }
Owen Taylor3473f882001-02-23 17:55:21 +00002397 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002398 xmlVErrMemory(ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00002399 "xmlAddNotationDecl: Table creation failed!\n");
2400 return(NULL);
2401 }
2402
2403 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2404 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002405 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002406 return(NULL);
2407 }
2408 memset(ret, 0, sizeof(xmlNotation));
2409
2410 /*
2411 * fill the structure.
2412 */
2413 ret->name = xmlStrdup(name);
2414 if (SystemID != NULL)
2415 ret->SystemID = xmlStrdup(SystemID);
2416 if (PublicID != NULL)
2417 ret->PublicID = xmlStrdup(PublicID);
2418
2419 /*
2420 * Validity Check:
2421 * Check the DTD for previous declarations of the ATTLIST
2422 */
2423 if (xmlHashAddEntry(table, name, ret)) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002424#ifdef LIBXML_VALID_ENABLED
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002425 xmlErrValid(NULL, XML_DTD_NOTATION_REDEFINED,
2426 "xmlAddNotationDecl: %s already defined\n",
2427 (const char *) name);
Daniel Veillard4432df22003-09-28 18:58:27 +00002428#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002429 xmlFreeNotation(ret);
2430 return(NULL);
2431 }
2432 return(ret);
2433}
2434
2435/**
2436 * xmlFreeNotationTable:
2437 * @table: An notation table
2438 *
2439 * Deallocate the memory used by an entities hash table.
2440 */
2441void
2442xmlFreeNotationTable(xmlNotationTablePtr table) {
2443 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2444}
2445
Daniel Veillard652327a2003-09-29 18:02:38 +00002446#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002447/**
2448 * xmlCopyNotation:
2449 * @nota: A notation
2450 *
2451 * Build a copy of a notation.
2452 *
2453 * Returns the new xmlNotationPtr or NULL in case of error.
2454 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002455static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002456xmlCopyNotation(xmlNotationPtr nota) {
2457 xmlNotationPtr cur;
2458
2459 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2460 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002461 xmlVErrMemory(NULL, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002462 return(NULL);
2463 }
2464 if (nota->name != NULL)
2465 cur->name = xmlStrdup(nota->name);
2466 else
2467 cur->name = NULL;
2468 if (nota->PublicID != NULL)
2469 cur->PublicID = xmlStrdup(nota->PublicID);
2470 else
2471 cur->PublicID = NULL;
2472 if (nota->SystemID != NULL)
2473 cur->SystemID = xmlStrdup(nota->SystemID);
2474 else
2475 cur->SystemID = NULL;
2476 return(cur);
2477}
2478
2479/**
2480 * xmlCopyNotationTable:
2481 * @table: A notation table
2482 *
2483 * Build a copy of a notation table.
2484 *
2485 * Returns the new xmlNotationTablePtr or NULL in case of error.
2486 */
2487xmlNotationTablePtr
2488xmlCopyNotationTable(xmlNotationTablePtr table) {
2489 return((xmlNotationTablePtr) xmlHashCopy(table,
2490 (xmlHashCopier) xmlCopyNotation));
2491}
Daniel Veillard652327a2003-09-29 18:02:38 +00002492#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002493
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002494#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002495/**
2496 * xmlDumpNotationDecl:
2497 * @buf: the XML buffer output
2498 * @nota: A notation declaration
2499 *
2500 * This will dump the content the notation declaration as an XML DTD definition
2501 */
2502void
2503xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00002504 if ((buf == NULL) || (nota == NULL))
2505 return;
Owen Taylor3473f882001-02-23 17:55:21 +00002506 xmlBufferWriteChar(buf, "<!NOTATION ");
2507 xmlBufferWriteCHAR(buf, nota->name);
2508 if (nota->PublicID != NULL) {
2509 xmlBufferWriteChar(buf, " PUBLIC ");
2510 xmlBufferWriteQuotedString(buf, nota->PublicID);
2511 if (nota->SystemID != NULL) {
2512 xmlBufferWriteChar(buf, " ");
Daniel Veillard41c4a752004-09-08 20:55:38 +00002513 xmlBufferWriteQuotedString(buf, nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002514 }
2515 } else {
2516 xmlBufferWriteChar(buf, " SYSTEM ");
Daniel Veillard41c4a752004-09-08 20:55:38 +00002517 xmlBufferWriteQuotedString(buf, nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002518 }
2519 xmlBufferWriteChar(buf, " >\n");
2520}
2521
2522/**
William M. Brack9e660592003-10-20 14:56:06 +00002523 * xmlDumpNotationDeclScan:
2524 * @nota: A notation declaration
2525 * @buf: the XML buffer output
2526 *
2527 * This is called with the hash scan function, and just reverses args
2528 */
2529static void
2530xmlDumpNotationDeclScan(xmlNotationPtr nota, xmlBufferPtr buf) {
2531 xmlDumpNotationDecl(buf, nota);
2532}
2533
2534/**
Owen Taylor3473f882001-02-23 17:55:21 +00002535 * xmlDumpNotationTable:
2536 * @buf: the XML buffer output
2537 * @table: A notation table
2538 *
2539 * This will dump the content of the notation table as an XML DTD definition
2540 */
2541void
2542xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
Daniel Veillardce682bc2004-11-05 17:22:25 +00002543 if ((buf == NULL) || (table == NULL))
2544 return;
William M. Brack9e660592003-10-20 14:56:06 +00002545 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDeclScan, buf);
Owen Taylor3473f882001-02-23 17:55:21 +00002546}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002547#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002548
2549/************************************************************************
2550 * *
2551 * IDs *
2552 * *
2553 ************************************************************************/
2554/**
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002555 * DICT_FREE:
2556 * @str: a string
2557 *
2558 * Free a string if it is not owned by the "dict" dictionnary in the
2559 * current scope
2560 */
2561#define DICT_FREE(str) \
2562 if ((str) && ((!dict) || \
2563 (xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \
2564 xmlFree((char *)(str));
2565
2566/**
Owen Taylor3473f882001-02-23 17:55:21 +00002567 * xmlFreeID:
2568 * @not: A id
2569 *
2570 * Deallocate the memory used by an id definition
2571 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002572static void
Owen Taylor3473f882001-02-23 17:55:21 +00002573xmlFreeID(xmlIDPtr id) {
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002574 xmlDictPtr dict = NULL;
2575
Owen Taylor3473f882001-02-23 17:55:21 +00002576 if (id == NULL) return;
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002577
2578 if (id->doc != NULL)
2579 dict = id->doc->dict;
2580
Owen Taylor3473f882001-02-23 17:55:21 +00002581 if (id->value != NULL)
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002582 DICT_FREE(id->value)
Daniel Veillardea7751d2002-12-20 00:16:24 +00002583 if (id->name != NULL)
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002584 DICT_FREE(id->name)
Owen Taylor3473f882001-02-23 17:55:21 +00002585 xmlFree(id);
2586}
2587
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002588
Owen Taylor3473f882001-02-23 17:55:21 +00002589/**
2590 * xmlAddID:
2591 * @ctxt: the validation context
2592 * @doc: pointer to the document
2593 * @value: the value name
2594 * @attr: the attribute holding the ID
2595 *
2596 * Register a new id declaration
2597 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002598 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002599 */
2600xmlIDPtr
2601xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2602 xmlAttrPtr attr) {
2603 xmlIDPtr ret;
2604 xmlIDTablePtr table;
2605
2606 if (doc == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002607 return(NULL);
2608 }
2609 if (value == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002610 return(NULL);
2611 }
2612 if (attr == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002613 return(NULL);
2614 }
2615
2616 /*
2617 * Create the ID table if needed.
2618 */
2619 table = (xmlIDTablePtr) doc->ids;
Daniel Veillard316a5c32005-01-23 22:56:39 +00002620 if (table == NULL) {
2621 doc->ids = table = xmlHashCreateDict(0, doc->dict);
2622 }
Owen Taylor3473f882001-02-23 17:55:21 +00002623 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002624 xmlVErrMemory(ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00002625 "xmlAddID: Table creation failed!\n");
2626 return(NULL);
2627 }
2628
2629 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2630 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002631 xmlVErrMemory(ctxt, "malloc failed");
Owen Taylor3473f882001-02-23 17:55:21 +00002632 return(NULL);
2633 }
2634
2635 /*
2636 * fill the structure.
2637 */
2638 ret->value = xmlStrdup(value);
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002639 ret->doc = doc;
Daniel Veillardea7751d2002-12-20 00:16:24 +00002640 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2641 /*
2642 * Operating in streaming mode, attr is gonna disapear
2643 */
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002644 if (doc->dict != NULL)
2645 ret->name = xmlDictLookup(doc->dict, attr->name, -1);
2646 else
2647 ret->name = xmlStrdup(attr->name);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002648 ret->attr = NULL;
2649 } else {
2650 ret->attr = attr;
2651 ret->name = NULL;
2652 }
2653 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002654
2655 if (xmlHashAddEntry(table, value, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002656#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002657 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002658 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002659 */
Daniel Veillardd3669b22004-02-25 12:34:55 +00002660 if ((ctxt != NULL) && (ctxt->error != NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00002661 xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED,
2662 "ID %s already defined\n",
2663 value, NULL, NULL);
Daniel Veillard76575762002-09-05 14:21:15 +00002664 }
Daniel Veillard4432df22003-09-28 18:58:27 +00002665#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002666 xmlFreeID(ret);
2667 return(NULL);
2668 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002669 if (attr != NULL)
2670 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002671 return(ret);
2672}
2673
2674/**
2675 * xmlFreeIDTable:
2676 * @table: An id table
2677 *
2678 * Deallocate the memory used by an ID hash table.
2679 */
2680void
2681xmlFreeIDTable(xmlIDTablePtr table) {
2682 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2683}
2684
2685/**
2686 * xmlIsID:
2687 * @doc: the document
2688 * @elem: the element carrying the attribute
2689 * @attr: the attribute
2690 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002691 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002692 * then this is done if DTD loading has been requested. In the case
2693 * of HTML documents parsed with the HTML parser, then ID detection is
2694 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002695 *
2696 * Returns 0 or 1 depending on the lookup result
2697 */
2698int
2699xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2700 if (doc == NULL) return(0);
2701 if (attr == NULL) return(0);
2702 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2703 return(0);
2704 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
Daniel Veillard9ba8e382003-10-28 21:31:45 +00002705 if (((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2706 (xmlStrEqual(BAD_CAST "name", attr->name))) &&
2707 ((elem != NULL) && (!xmlStrEqual(elem->name, BAD_CAST "input"))))
Owen Taylor3473f882001-02-23 17:55:21 +00002708 return(1);
2709 return(0);
2710 } else {
2711 xmlAttributePtr attrDecl;
2712
2713 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002714 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00002715 xmlChar fn[50];
Daniel Veillard37f961d2002-07-06 17:53:56 +00002716 xmlChar *fullname;
Daniel Veillardc00cda82003-04-07 10:22:39 +00002717
2718 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002719 if (fullname == NULL)
2720 return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002721 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2722 attr->name);
2723 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2724 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2725 attr->name);
Daniel Veillardc00cda82003-04-07 10:22:39 +00002726 if ((fullname != fn) && (fullname != elem->name))
2727 xmlFree(fullname);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002728 } else {
2729 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2730 attr->name);
2731 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2732 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2733 attr->name);
2734 }
Owen Taylor3473f882001-02-23 17:55:21 +00002735
2736 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2737 return(1);
2738 }
2739 return(0);
2740}
2741
2742/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002743 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002744 * @doc: the document
2745 * @attr: the attribute
2746 *
2747 * Remove the given attribute from the ID table maintained internally.
2748 *
2749 * Returns -1 if the lookup failed and 0 otherwise
2750 */
2751int
2752xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
Owen Taylor3473f882001-02-23 17:55:21 +00002753 xmlIDTablePtr table;
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002754 xmlIDPtr id;
Owen Taylor3473f882001-02-23 17:55:21 +00002755 xmlChar *ID;
2756
2757 if (doc == NULL) return(-1);
2758 if (attr == NULL) return(-1);
2759 table = (xmlIDTablePtr) doc->ids;
2760 if (table == NULL)
2761 return(-1);
2762
2763 if (attr == NULL)
2764 return(-1);
2765 ID = xmlNodeListGetString(doc, attr->children, 1);
2766 if (ID == NULL)
2767 return(-1);
Daniel Veillard8d7b5c72003-11-15 18:24:36 +00002768 id = xmlHashLookup(table, ID);
2769 if (id == NULL || id->attr != attr) {
Owen Taylor3473f882001-02-23 17:55:21 +00002770 xmlFree(ID);
2771 return(-1);
2772 }
Daniel Veillard91b955c2004-12-10 10:26:42 +00002773 xmlHashRemoveEntry(table, ID, (xmlHashDeallocator) xmlFreeID);
Owen Taylor3473f882001-02-23 17:55:21 +00002774 xmlFree(ID);
Daniel Veillardda6f4af2005-06-20 17:17:54 +00002775 attr->atype = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00002776 return(0);
2777}
2778
2779/**
2780 * xmlGetID:
2781 * @doc: pointer to the document
2782 * @ID: the ID value
2783 *
2784 * Search the attribute declaring the given ID
2785 *
2786 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2787 */
2788xmlAttrPtr
2789xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2790 xmlIDTablePtr table;
2791 xmlIDPtr id;
2792
2793 if (doc == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002794 return(NULL);
2795 }
2796
2797 if (ID == NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00002798 return(NULL);
2799 }
2800
2801 table = (xmlIDTablePtr) doc->ids;
2802 if (table == NULL)
2803 return(NULL);
2804
2805 id = xmlHashLookup(table, ID);
2806 if (id == NULL)
2807 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002808 if (id->attr == NULL) {
2809 /*
2810 * We are operating on a stream, return a well known reference
2811 * since the attribute node doesn't exist anymore
2812 */
2813 return((xmlAttrPtr) doc);
2814 }
Owen Taylor3473f882001-02-23 17:55:21 +00002815 return(id->attr);
2816}
2817
2818/************************************************************************
2819 * *
2820 * Refs *
2821 * *
2822 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002823typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002824{
2825 xmlListPtr l;
2826 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002827} xmlRemoveMemo;
2828
2829typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2830
2831typedef struct xmlValidateMemo_t
2832{
2833 xmlValidCtxtPtr ctxt;
2834 const xmlChar *name;
2835} xmlValidateMemo;
2836
2837typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002838
2839/**
Owen Taylor3473f882001-02-23 17:55:21 +00002840 * xmlFreeRef:
2841 * @lk: A list link
2842 *
2843 * Deallocate the memory used by a ref definition
2844 */
2845static void
2846xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002847 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2848 if (ref == NULL) return;
2849 if (ref->value != NULL)
2850 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002851 if (ref->name != NULL)
2852 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002853 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002854}
2855
2856/**
2857 * xmlFreeRefList:
2858 * @list_ref: A list of references.
2859 *
2860 * Deallocate the memory used by a list of references
2861 */
2862static void
2863xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002864 if (list_ref == NULL) return;
2865 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002866}
2867
2868/**
2869 * xmlWalkRemoveRef:
2870 * @data: Contents of current link
2871 * @user: Value supplied by the user
2872 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002873 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002874 */
2875static int
2876xmlWalkRemoveRef(const void *data, const void *user)
2877{
Daniel Veillard37721922001-05-04 15:21:12 +00002878 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2879 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2880 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002881
Daniel Veillard37721922001-05-04 15:21:12 +00002882 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2883 xmlListRemoveFirst(ref_list, (void *)data);
2884 return 0;
2885 }
2886 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002887}
2888
2889/**
Daniel Veillard770075b2004-02-25 10:44:30 +00002890 * xmlDummyCompare
2891 * @data0: Value supplied by the user
2892 * @data1: Value supplied by the user
2893 *
2894 * Do nothing, return 0. Used to create unordered lists.
2895 */
2896static int
Daniel Veillardf54cd532004-02-25 11:52:31 +00002897xmlDummyCompare(const void *data0 ATTRIBUTE_UNUSED,
2898 const void *data1 ATTRIBUTE_UNUSED)
Daniel Veillard770075b2004-02-25 10:44:30 +00002899{
2900 return (0);
2901}
2902
2903/**
Owen Taylor3473f882001-02-23 17:55:21 +00002904 * xmlAddRef:
2905 * @ctxt: the validation context
2906 * @doc: pointer to the document
2907 * @value: the value name
2908 * @attr: the attribute holding the Ref
2909 *
2910 * Register a new ref declaration
2911 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002912 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002913 */
2914xmlRefPtr
William M. Brackedb65a72004-02-06 07:36:04 +00002915xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002916 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002917 xmlRefPtr ret;
2918 xmlRefTablePtr table;
2919 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002920
Daniel Veillard37721922001-05-04 15:21:12 +00002921 if (doc == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002922 return(NULL);
2923 }
2924 if (value == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002925 return(NULL);
2926 }
2927 if (attr == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00002928 return(NULL);
2929 }
Owen Taylor3473f882001-02-23 17:55:21 +00002930
Daniel Veillard37721922001-05-04 15:21:12 +00002931 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002932 * Create the Ref table if needed.
2933 */
Daniel Veillard37721922001-05-04 15:21:12 +00002934 table = (xmlRefTablePtr) doc->refs;
Daniel Veillard316a5c32005-01-23 22:56:39 +00002935 if (table == NULL) {
2936 doc->refs = table = xmlHashCreateDict(0, doc->dict);
2937 }
Daniel Veillard37721922001-05-04 15:21:12 +00002938 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002939 xmlVErrMemory(ctxt,
Daniel Veillard37721922001-05-04 15:21:12 +00002940 "xmlAddRef: Table creation failed!\n");
2941 return(NULL);
2942 }
Owen Taylor3473f882001-02-23 17:55:21 +00002943
Daniel Veillard37721922001-05-04 15:21:12 +00002944 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2945 if (ret == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00002946 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard37721922001-05-04 15:21:12 +00002947 return(NULL);
2948 }
Owen Taylor3473f882001-02-23 17:55:21 +00002949
Daniel Veillard37721922001-05-04 15:21:12 +00002950 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002951 * fill the structure.
2952 */
Daniel Veillard37721922001-05-04 15:21:12 +00002953 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002954 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2955 /*
2956 * Operating in streaming mode, attr is gonna disapear
2957 */
2958 ret->name = xmlStrdup(attr->name);
2959 ret->attr = NULL;
2960 } else {
2961 ret->name = NULL;
2962 ret->attr = attr;
2963 }
2964 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002965
Daniel Veillard37721922001-05-04 15:21:12 +00002966 /* To add a reference :-
2967 * References are maintained as a list of references,
2968 * Lookup the entry, if no entry create new nodelist
2969 * Add the owning node to the NodeList
2970 * Return the ref
2971 */
Owen Taylor3473f882001-02-23 17:55:21 +00002972
Daniel Veillard37721922001-05-04 15:21:12 +00002973 if (NULL == (ref_list = xmlHashLookup(table, value))) {
Daniel Veillard770075b2004-02-25 10:44:30 +00002974 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, xmlDummyCompare))) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002975 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2976 "xmlAddRef: Reference list creation failed!\n",
2977 NULL);
Daniel Veillard37721922001-05-04 15:21:12 +00002978 return(NULL);
2979 }
2980 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2981 xmlListDelete(ref_list);
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00002982 xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR,
2983 "xmlAddRef: Reference list insertion failed!\n",
2984 NULL);
Daniel Veillard37721922001-05-04 15:21:12 +00002985 return(NULL);
2986 }
2987 }
Daniel Veillard965983a2004-02-17 16:30:24 +00002988/* xmlListInsert(ref_list, ret); */
2989 xmlListAppend(ref_list, ret);
Daniel Veillard37721922001-05-04 15:21:12 +00002990 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002991}
2992
2993/**
2994 * xmlFreeRefTable:
2995 * @table: An ref table
2996 *
2997 * Deallocate the memory used by an Ref hash table.
2998 */
2999void
3000xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00003001 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00003002}
3003
3004/**
3005 * xmlIsRef:
3006 * @doc: the document
3007 * @elem: the element carrying the attribute
3008 * @attr: the attribute
3009 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003010 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00003011 * then this is simple, otherwise we use an heuristic: name Ref (upper
3012 * or lowercase).
3013 *
3014 * Returns 0 or 1 depending on the lookup result
3015 */
3016int
3017xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillardce244ad2004-11-05 10:03:46 +00003018 if (attr == NULL)
3019 return(0);
3020 if (doc == NULL) {
3021 doc = attr->doc;
3022 if (doc == NULL) return(0);
3023 }
3024
Daniel Veillard37721922001-05-04 15:21:12 +00003025 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
3026 return(0);
3027 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
3028 /* TODO @@@ */
3029 return(0);
3030 } else {
3031 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00003032
Daniel Veillardce244ad2004-11-05 10:03:46 +00003033 if (elem == NULL) return(0);
Daniel Veillard37721922001-05-04 15:21:12 +00003034 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
3035 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3036 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3037 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003038
Daniel Veillard37721922001-05-04 15:21:12 +00003039 if ((attrDecl != NULL) &&
3040 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
3041 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
3042 return(1);
3043 }
3044 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003045}
3046
3047/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00003048 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00003049 * @doc: the document
3050 * @attr: the attribute
3051 *
3052 * Remove the given attribute from the Ref table maintained internally.
3053 *
3054 * Returns -1 if the lookup failed and 0 otherwise
3055 */
3056int
3057xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00003058 xmlListPtr ref_list;
3059 xmlRefTablePtr table;
3060 xmlChar *ID;
3061 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00003062
Daniel Veillard37721922001-05-04 15:21:12 +00003063 if (doc == NULL) return(-1);
3064 if (attr == NULL) return(-1);
3065 table = (xmlRefTablePtr) doc->refs;
3066 if (table == NULL)
3067 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003068
Daniel Veillard37721922001-05-04 15:21:12 +00003069 if (attr == NULL)
3070 return(-1);
3071 ID = xmlNodeListGetString(doc, attr->children, 1);
3072 if (ID == NULL)
3073 return(-1);
3074 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00003075
Daniel Veillard37721922001-05-04 15:21:12 +00003076 if(ref_list == NULL) {
3077 xmlFree(ID);
3078 return (-1);
3079 }
3080 /* At this point, ref_list refers to a list of references which
3081 * have the same key as the supplied attr. Our list of references
3082 * is ordered by reference address and we don't have that information
3083 * here to use when removing. We'll have to walk the list and
3084 * check for a matching attribute, when we find one stop the walk
3085 * and remove the entry.
3086 * The list is ordered by reference, so that means we don't have the
3087 * key. Passing the list and the reference to the walker means we
3088 * will have enough data to be able to remove the entry.
3089 */
3090 target.l = ref_list;
3091 target.ap = attr;
3092
3093 /* Remove the supplied attr from our list */
3094 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00003095
Daniel Veillard37721922001-05-04 15:21:12 +00003096 /*If the list is empty then remove the list entry in the hash */
3097 if (xmlListEmpty(ref_list))
3098 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
3099 xmlFreeRefList);
3100 xmlFree(ID);
3101 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003102}
3103
3104/**
3105 * xmlGetRefs:
3106 * @doc: pointer to the document
3107 * @ID: the ID value
3108 *
3109 * Find the set of references for the supplied ID.
3110 *
3111 * Returns NULL if not found, otherwise node set for the ID.
3112 */
3113xmlListPtr
3114xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00003115 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00003116
Daniel Veillard37721922001-05-04 15:21:12 +00003117 if (doc == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00003118 return(NULL);
3119 }
Owen Taylor3473f882001-02-23 17:55:21 +00003120
Daniel Veillard37721922001-05-04 15:21:12 +00003121 if (ID == NULL) {
Daniel Veillard37721922001-05-04 15:21:12 +00003122 return(NULL);
3123 }
Owen Taylor3473f882001-02-23 17:55:21 +00003124
Daniel Veillard37721922001-05-04 15:21:12 +00003125 table = (xmlRefTablePtr) doc->refs;
3126 if (table == NULL)
3127 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003128
Daniel Veillard37721922001-05-04 15:21:12 +00003129 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00003130}
3131
3132/************************************************************************
3133 * *
3134 * Routines for validity checking *
3135 * *
3136 ************************************************************************/
3137
3138/**
3139 * xmlGetDtdElementDesc:
3140 * @dtd: a pointer to the DtD to search
3141 * @name: the element name
3142 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003143 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00003144 *
3145 * returns the xmlElementPtr if found or NULL
3146 */
3147
3148xmlElementPtr
3149xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
3150 xmlElementTablePtr table;
3151 xmlElementPtr cur;
3152 xmlChar *uqname = NULL, *prefix = NULL;
3153
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00003154 if ((dtd == NULL) || (name == NULL)) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003155 if (dtd->elements == NULL)
3156 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003157 table = (xmlElementTablePtr) dtd->elements;
3158
3159 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003160 if (uqname != NULL)
3161 name = uqname;
3162 cur = xmlHashLookup2(table, name, prefix);
3163 if (prefix != NULL) xmlFree(prefix);
3164 if (uqname != NULL) xmlFree(uqname);
3165 return(cur);
3166}
3167/**
3168 * xmlGetDtdElementDesc2:
3169 * @dtd: a pointer to the DtD to search
3170 * @name: the element name
3171 * @create: create an empty description if not found
3172 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003173 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00003174 *
3175 * returns the xmlElementPtr if found or NULL
3176 */
3177
Daniel Veillard86fd5a72001-12-13 14:55:21 +00003178static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00003179xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
3180 xmlElementTablePtr table;
3181 xmlElementPtr cur;
3182 xmlChar *uqname = NULL, *prefix = NULL;
3183
3184 if (dtd == NULL) return(NULL);
3185 if (dtd->elements == NULL) {
Daniel Veillard316a5c32005-01-23 22:56:39 +00003186 xmlDictPtr dict = NULL;
3187
3188 if (dtd->doc != NULL)
3189 dict = dtd->doc->dict;
3190
Daniel Veillarda10efa82001-04-18 13:09:01 +00003191 if (!create)
3192 return(NULL);
3193 /*
3194 * Create the Element table if needed.
3195 */
3196 table = (xmlElementTablePtr) dtd->elements;
3197 if (table == NULL) {
Daniel Veillard316a5c32005-01-23 22:56:39 +00003198 table = xmlHashCreateDict(0, dict);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003199 dtd->elements = (void *) table;
3200 }
3201 if (table == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00003202 xmlVErrMemory(NULL, "element table allocation failed");
Daniel Veillarda10efa82001-04-18 13:09:01 +00003203 return(NULL);
3204 }
3205 }
3206 table = (xmlElementTablePtr) dtd->elements;
3207
3208 uqname = xmlSplitQName2(name, &prefix);
3209 if (uqname != NULL)
3210 name = uqname;
3211 cur = xmlHashLookup2(table, name, prefix);
3212 if ((cur == NULL) && (create)) {
3213 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
3214 if (cur == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00003215 xmlVErrMemory(NULL, "malloc failed");
Daniel Veillarda10efa82001-04-18 13:09:01 +00003216 return(NULL);
3217 }
3218 memset(cur, 0, sizeof(xmlElement));
3219 cur->type = XML_ELEMENT_DECL;
3220
3221 /*
3222 * fill the structure.
3223 */
3224 cur->name = xmlStrdup(name);
3225 cur->prefix = xmlStrdup(prefix);
3226 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
3227
3228 xmlHashAddEntry2(table, name, prefix, cur);
3229 }
3230 if (prefix != NULL) xmlFree(prefix);
3231 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00003232 return(cur);
3233}
3234
3235/**
3236 * xmlGetDtdQElementDesc:
3237 * @dtd: a pointer to the DtD to search
3238 * @name: the element name
3239 * @prefix: the element namespace prefix
3240 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003241 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00003242 *
3243 * returns the xmlElementPtr if found or NULL
3244 */
3245
Daniel Veillard48da9102001-08-07 01:10:10 +00003246xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00003247xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
3248 const xmlChar *prefix) {
3249 xmlElementTablePtr table;
3250
3251 if (dtd == NULL) return(NULL);
3252 if (dtd->elements == NULL) return(NULL);
3253 table = (xmlElementTablePtr) dtd->elements;
3254
3255 return(xmlHashLookup2(table, name, prefix));
3256}
3257
3258/**
3259 * xmlGetDtdAttrDesc:
3260 * @dtd: a pointer to the DtD to search
3261 * @elem: the element name
3262 * @name: the attribute name
3263 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003264 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00003265 * this element.
3266 *
3267 * returns the xmlAttributePtr if found or NULL
3268 */
3269
3270xmlAttributePtr
3271xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
3272 xmlAttributeTablePtr table;
3273 xmlAttributePtr cur;
3274 xmlChar *uqname = NULL, *prefix = NULL;
3275
3276 if (dtd == NULL) return(NULL);
3277 if (dtd->attributes == NULL) return(NULL);
3278
3279 table = (xmlAttributeTablePtr) dtd->attributes;
3280 if (table == NULL)
3281 return(NULL);
3282
3283 uqname = xmlSplitQName2(name, &prefix);
3284
3285 if (uqname != NULL) {
3286 cur = xmlHashLookup3(table, uqname, prefix, elem);
3287 if (prefix != NULL) xmlFree(prefix);
3288 if (uqname != NULL) xmlFree(uqname);
3289 } else
3290 cur = xmlHashLookup3(table, name, NULL, elem);
3291 return(cur);
3292}
3293
3294/**
3295 * xmlGetDtdQAttrDesc:
3296 * @dtd: a pointer to the DtD to search
3297 * @elem: the element name
3298 * @name: the attribute name
3299 * @prefix: the attribute namespace prefix
3300 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003301 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00003302 * this element.
3303 *
3304 * returns the xmlAttributePtr if found or NULL
3305 */
3306
Daniel Veillard48da9102001-08-07 01:10:10 +00003307xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00003308xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
3309 const xmlChar *prefix) {
3310 xmlAttributeTablePtr table;
3311
3312 if (dtd == NULL) return(NULL);
3313 if (dtd->attributes == NULL) return(NULL);
3314 table = (xmlAttributeTablePtr) dtd->attributes;
3315
3316 return(xmlHashLookup3(table, name, prefix, elem));
3317}
3318
3319/**
3320 * xmlGetDtdNotationDesc:
3321 * @dtd: a pointer to the DtD to search
3322 * @name: the notation name
3323 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003324 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00003325 *
3326 * returns the xmlNotationPtr if found or NULL
3327 */
3328
3329xmlNotationPtr
3330xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
3331 xmlNotationTablePtr table;
3332
3333 if (dtd == NULL) return(NULL);
3334 if (dtd->notations == NULL) return(NULL);
3335 table = (xmlNotationTablePtr) dtd->notations;
3336
3337 return(xmlHashLookup(table, name));
3338}
3339
Daniel Veillardf54cd532004-02-25 11:52:31 +00003340#if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED)
Owen Taylor3473f882001-02-23 17:55:21 +00003341/**
3342 * xmlValidateNotationUse:
3343 * @ctxt: the validation context
3344 * @doc: the document
3345 * @notationName: the notation name to check
3346 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003347 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00003348 * - [ VC: Notation Declared ]
3349 *
3350 * returns 1 if valid or 0 otherwise
3351 */
3352
3353int
3354xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3355 const xmlChar *notationName) {
3356 xmlNotationPtr notaDecl;
3357 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3358
3359 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
3360 if ((notaDecl == NULL) && (doc->extSubset != NULL))
3361 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
3362
Daniel Veillarde637c4a2003-03-30 21:10:09 +00003363 if ((notaDecl == NULL) && (ctxt != NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003364 xmlErrValidNode(ctxt, (xmlNodePtr) doc, XML_DTD_UNKNOWN_NOTATION,
3365 "NOTATION %s is not declared\n",
3366 notationName, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003367 return(0);
3368 }
3369 return(1);
3370}
Daniel Veillardf54cd532004-02-25 11:52:31 +00003371#endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00003372
3373/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00003374 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00003375 * @doc: the document
3376 * @name: the element name
3377 *
3378 * Search in the DtDs whether an element accept Mixed content (or ANY)
3379 * basically if it is supposed to accept text childs
3380 *
3381 * returns 0 if no, 1 if yes, and -1 if no element description is available
3382 */
3383
3384int
3385xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
3386 xmlElementPtr elemDecl;
3387
3388 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3389
3390 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
3391 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3392 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
3393 if (elemDecl == NULL) return(-1);
3394 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003395 case XML_ELEMENT_TYPE_UNDEFINED:
3396 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003397 case XML_ELEMENT_TYPE_ELEMENT:
3398 return(0);
3399 case XML_ELEMENT_TYPE_EMPTY:
3400 /*
3401 * return 1 for EMPTY since we want VC error to pop up
3402 * on <empty> </empty> for example
3403 */
3404 case XML_ELEMENT_TYPE_ANY:
3405 case XML_ELEMENT_TYPE_MIXED:
3406 return(1);
3407 }
3408 return(1);
3409}
3410
Daniel Veillard4432df22003-09-28 18:58:27 +00003411#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00003412/**
3413 * xmlValidateNameValue:
3414 * @value: an Name value
3415 *
3416 * Validate that the given value match Name production
3417 *
3418 * returns 1 if valid or 0 otherwise
3419 */
3420
Daniel Veillard9b731d72002-04-14 12:56:08 +00003421int
Owen Taylor3473f882001-02-23 17:55:21 +00003422xmlValidateNameValue(const xmlChar *value) {
3423 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003424 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003425
3426 if (value == NULL) return(0);
3427 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003428 val = xmlStringCurrentChar(NULL, cur, &len);
3429 cur += len;
3430 if (!IS_LETTER(val) && (val != '_') &&
3431 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003432 return(0);
3433 }
3434
Daniel Veillardd8224e02002-01-13 15:43:22 +00003435 val = xmlStringCurrentChar(NULL, cur, &len);
3436 cur += len;
3437 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3438 (val == '.') || (val == '-') ||
3439 (val == '_') || (val == ':') ||
3440 (IS_COMBINING(val)) ||
3441 (IS_EXTENDER(val))) {
3442 val = xmlStringCurrentChar(NULL, cur, &len);
3443 cur += len;
3444 }
Owen Taylor3473f882001-02-23 17:55:21 +00003445
Daniel Veillardd8224e02002-01-13 15:43:22 +00003446 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003447
3448 return(1);
3449}
3450
3451/**
3452 * xmlValidateNamesValue:
3453 * @value: an Names value
3454 *
3455 * Validate that the given value match Names production
3456 *
3457 * returns 1 if valid or 0 otherwise
3458 */
3459
Daniel Veillard9b731d72002-04-14 12:56:08 +00003460int
Owen Taylor3473f882001-02-23 17:55:21 +00003461xmlValidateNamesValue(const xmlChar *value) {
3462 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003463 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003464
3465 if (value == NULL) return(0);
3466 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003467 val = xmlStringCurrentChar(NULL, cur, &len);
3468 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003469
Daniel Veillardd8224e02002-01-13 15:43:22 +00003470 if (!IS_LETTER(val) && (val != '_') &&
3471 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003472 return(0);
3473 }
3474
Daniel Veillardd8224e02002-01-13 15:43:22 +00003475 val = xmlStringCurrentChar(NULL, cur, &len);
3476 cur += len;
3477 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3478 (val == '.') || (val == '-') ||
3479 (val == '_') || (val == ':') ||
3480 (IS_COMBINING(val)) ||
3481 (IS_EXTENDER(val))) {
3482 val = xmlStringCurrentChar(NULL, cur, &len);
3483 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003484 }
3485
Daniel Veillard807b4de2004-09-26 14:42:56 +00003486 /* Should not test IS_BLANK(val) here -- see erratum E20*/
3487 while (val == 0x20) {
3488 while (val == 0x20) {
Daniel Veillardd8224e02002-01-13 15:43:22 +00003489 val = xmlStringCurrentChar(NULL, cur, &len);
3490 cur += len;
3491 }
3492
3493 if (!IS_LETTER(val) && (val != '_') &&
3494 (val != ':')) {
3495 return(0);
3496 }
3497 val = xmlStringCurrentChar(NULL, cur, &len);
3498 cur += len;
3499
3500 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3501 (val == '.') || (val == '-') ||
3502 (val == '_') || (val == ':') ||
3503 (IS_COMBINING(val)) ||
3504 (IS_EXTENDER(val))) {
3505 val = xmlStringCurrentChar(NULL, cur, &len);
3506 cur += len;
3507 }
3508 }
3509
3510 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003511
3512 return(1);
3513}
3514
3515/**
3516 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003517 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003518 *
3519 * Validate that the given value match Nmtoken production
3520 *
3521 * [ VC: Name Token ]
3522 *
3523 * returns 1 if valid or 0 otherwise
3524 */
3525
Daniel Veillard9b731d72002-04-14 12:56:08 +00003526int
Owen Taylor3473f882001-02-23 17:55:21 +00003527xmlValidateNmtokenValue(const xmlChar *value) {
3528 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003529 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003530
3531 if (value == NULL) return(0);
3532 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003533 val = xmlStringCurrentChar(NULL, cur, &len);
3534 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003535
Daniel Veillardd8224e02002-01-13 15:43:22 +00003536 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3537 (val != '.') && (val != '-') &&
3538 (val != '_') && (val != ':') &&
3539 (!IS_COMBINING(val)) &&
3540 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003541 return(0);
3542
Daniel Veillardd8224e02002-01-13 15:43:22 +00003543 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3544 (val == '.') || (val == '-') ||
3545 (val == '_') || (val == ':') ||
3546 (IS_COMBINING(val)) ||
3547 (IS_EXTENDER(val))) {
3548 val = xmlStringCurrentChar(NULL, cur, &len);
3549 cur += len;
3550 }
Owen Taylor3473f882001-02-23 17:55:21 +00003551
Daniel Veillardd8224e02002-01-13 15:43:22 +00003552 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003553
3554 return(1);
3555}
3556
3557/**
3558 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003559 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003560 *
3561 * Validate that the given value match Nmtokens production
3562 *
3563 * [ VC: Name Token ]
3564 *
3565 * returns 1 if valid or 0 otherwise
3566 */
3567
Daniel Veillard9b731d72002-04-14 12:56:08 +00003568int
Owen Taylor3473f882001-02-23 17:55:21 +00003569xmlValidateNmtokensValue(const xmlChar *value) {
3570 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003571 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003572
3573 if (value == NULL) return(0);
3574 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003575 val = xmlStringCurrentChar(NULL, cur, &len);
3576 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003577
Daniel Veillardd8224e02002-01-13 15:43:22 +00003578 while (IS_BLANK(val)) {
3579 val = xmlStringCurrentChar(NULL, cur, &len);
3580 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003581 }
3582
Daniel Veillardd8224e02002-01-13 15:43:22 +00003583 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3584 (val != '.') && (val != '-') &&
3585 (val != '_') && (val != ':') &&
3586 (!IS_COMBINING(val)) &&
3587 (!IS_EXTENDER(val)))
3588 return(0);
3589
3590 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3591 (val == '.') || (val == '-') ||
3592 (val == '_') || (val == ':') ||
3593 (IS_COMBINING(val)) ||
3594 (IS_EXTENDER(val))) {
3595 val = xmlStringCurrentChar(NULL, cur, &len);
3596 cur += len;
3597 }
3598
Daniel Veillard807b4de2004-09-26 14:42:56 +00003599 /* Should not test IS_BLANK(val) here -- see erratum E20*/
3600 while (val == 0x20) {
3601 while (val == 0x20) {
Daniel Veillardd8224e02002-01-13 15:43:22 +00003602 val = xmlStringCurrentChar(NULL, cur, &len);
3603 cur += len;
3604 }
3605 if (val == 0) return(1);
3606
3607 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3608 (val != '.') && (val != '-') &&
3609 (val != '_') && (val != ':') &&
3610 (!IS_COMBINING(val)) &&
3611 (!IS_EXTENDER(val)))
3612 return(0);
3613
3614 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3615 (val == '.') || (val == '-') ||
3616 (val == '_') || (val == ':') ||
3617 (IS_COMBINING(val)) ||
3618 (IS_EXTENDER(val))) {
3619 val = xmlStringCurrentChar(NULL, cur, &len);
3620 cur += len;
3621 }
3622 }
3623
3624 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003625
3626 return(1);
3627}
3628
3629/**
3630 * xmlValidateNotationDecl:
3631 * @ctxt: the validation context
3632 * @doc: a document instance
3633 * @nota: a notation definition
3634 *
3635 * Try to validate a single notation definition
3636 * basically it does the following checks as described by the
3637 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003638 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003639 * But this function get called anyway ...
3640 *
3641 * returns 1 if valid or 0 otherwise
3642 */
3643
3644int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003645xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3646 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003647 int ret = 1;
3648
3649 return(ret);
3650}
3651
3652/**
3653 * xmlValidateAttributeValue:
3654 * @type: an attribute type
3655 * @value: an attribute value
3656 *
3657 * Validate that the given attribute value match the proper production
3658 *
3659 * [ VC: ID ]
3660 * Values of type ID must match the Name production....
3661 *
3662 * [ VC: IDREF ]
3663 * Values of type IDREF must match the Name production, and values
3664 * of type IDREFS must match Names ...
3665 *
3666 * [ VC: Entity Name ]
3667 * Values of type ENTITY must match the Name production, values
3668 * of type ENTITIES must match Names ...
3669 *
3670 * [ VC: Name Token ]
3671 * Values of type NMTOKEN must match the Nmtoken production; values
3672 * of type NMTOKENS must match Nmtokens.
3673 *
3674 * returns 1 if valid or 0 otherwise
3675 */
3676
3677int
3678xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3679 switch (type) {
3680 case XML_ATTRIBUTE_ENTITIES:
3681 case XML_ATTRIBUTE_IDREFS:
3682 return(xmlValidateNamesValue(value));
3683 case XML_ATTRIBUTE_ENTITY:
3684 case XML_ATTRIBUTE_IDREF:
3685 case XML_ATTRIBUTE_ID:
3686 case XML_ATTRIBUTE_NOTATION:
3687 return(xmlValidateNameValue(value));
3688 case XML_ATTRIBUTE_NMTOKENS:
3689 case XML_ATTRIBUTE_ENUMERATION:
3690 return(xmlValidateNmtokensValue(value));
3691 case XML_ATTRIBUTE_NMTOKEN:
3692 return(xmlValidateNmtokenValue(value));
3693 case XML_ATTRIBUTE_CDATA:
3694 break;
3695 }
3696 return(1);
3697}
3698
3699/**
3700 * xmlValidateAttributeValue2:
3701 * @ctxt: the validation context
3702 * @doc: the document
3703 * @name: the attribute name (used for error reporting only)
3704 * @type: the attribute type
3705 * @value: the attribute value
3706 *
3707 * Validate that the given attribute value match a given type.
3708 * This typically cannot be done before having finished parsing
3709 * the subsets.
3710 *
3711 * [ VC: IDREF ]
3712 * Values of type IDREF must match one of the declared IDs
3713 * Values of type IDREFS must match a sequence of the declared IDs
3714 * each Name must match the value of an ID attribute on some element
3715 * in the XML document; i.e. IDREF values must match the value of
3716 * some ID attribute
3717 *
3718 * [ VC: Entity Name ]
3719 * Values of type ENTITY must match one declared entity
3720 * Values of type ENTITIES must match a sequence of declared entities
3721 *
3722 * [ VC: Notation Attributes ]
3723 * all notation names in the declaration must be declared.
3724 *
3725 * returns 1 if valid or 0 otherwise
3726 */
3727
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003728static int
Owen Taylor3473f882001-02-23 17:55:21 +00003729xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3730 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3731 int ret = 1;
3732 switch (type) {
3733 case XML_ATTRIBUTE_IDREFS:
3734 case XML_ATTRIBUTE_IDREF:
3735 case XML_ATTRIBUTE_ID:
3736 case XML_ATTRIBUTE_NMTOKENS:
3737 case XML_ATTRIBUTE_ENUMERATION:
3738 case XML_ATTRIBUTE_NMTOKEN:
3739 case XML_ATTRIBUTE_CDATA:
3740 break;
3741 case XML_ATTRIBUTE_ENTITY: {
3742 xmlEntityPtr ent;
3743
3744 ent = xmlGetDocEntity(doc, value);
Daniel Veillard62998c02003-09-15 12:56:36 +00003745 /* yeah it's a bit messy... */
Daniel Veillard878eab02002-02-19 13:46:09 +00003746 if ((ent == NULL) && (doc->standalone == 1)) {
3747 doc->standalone = 0;
3748 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003749 }
Owen Taylor3473f882001-02-23 17:55:21 +00003750 if (ent == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003751 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3752 XML_DTD_UNKNOWN_ENTITY,
Owen Taylor3473f882001-02-23 17:55:21 +00003753 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003754 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003755 ret = 0;
3756 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003757 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3758 XML_DTD_ENTITY_TYPE,
Owen Taylor3473f882001-02-23 17:55:21 +00003759 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003760 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003761 ret = 0;
3762 }
3763 break;
3764 }
3765 case XML_ATTRIBUTE_ENTITIES: {
3766 xmlChar *dup, *nam = NULL, *cur, save;
3767 xmlEntityPtr ent;
3768
3769 dup = xmlStrdup(value);
3770 if (dup == NULL)
3771 return(0);
3772 cur = dup;
3773 while (*cur != 0) {
3774 nam = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00003775 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00003776 save = *cur;
3777 *cur = 0;
3778 ent = xmlGetDocEntity(doc, nam);
3779 if (ent == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003780 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3781 XML_DTD_UNKNOWN_ENTITY,
Owen Taylor3473f882001-02-23 17:55:21 +00003782 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003783 name, nam, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003784 ret = 0;
3785 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003786 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3787 XML_DTD_ENTITY_TYPE,
Owen Taylor3473f882001-02-23 17:55:21 +00003788 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003789 name, nam, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003790 ret = 0;
3791 }
3792 if (save == 0)
3793 break;
3794 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00003795 while (IS_BLANK_CH(*cur)) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00003796 }
3797 xmlFree(dup);
3798 break;
3799 }
3800 case XML_ATTRIBUTE_NOTATION: {
3801 xmlNotationPtr nota;
3802
3803 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3804 if ((nota == NULL) && (doc->extSubset != NULL))
3805 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3806
3807 if (nota == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003808 xmlErrValidNode(ctxt, (xmlNodePtr) doc,
3809 XML_DTD_UNKNOWN_NOTATION,
Owen Taylor3473f882001-02-23 17:55:21 +00003810 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003811 name, value, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00003812 ret = 0;
3813 }
3814 break;
3815 }
3816 }
3817 return(ret);
3818}
3819
3820/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003821 * xmlValidCtxtNormalizeAttributeValue:
3822 * @ctxt: the validation context
3823 * @doc: the document
3824 * @elem: the parent
3825 * @name: the attribute name
3826 * @value: the attribute value
3827 * @ctxt: the validation context or NULL
3828 *
3829 * Does the validation related extra step of the normalization of attribute
3830 * values:
3831 *
3832 * If the declared value is not CDATA, then the XML processor must further
3833 * process the normalized attribute value by discarding any leading and
3834 * trailing space (#x20) characters, and by replacing sequences of space
3835 * (#x20) characters by single space (#x20) character.
3836 *
3837 * Also check VC: Standalone Document Declaration in P32, and update
3838 * ctxt->valid accordingly
3839 *
3840 * returns a new normalized string if normalization is needed, NULL otherwise
3841 * the caller must free the returned value.
3842 */
3843
3844xmlChar *
3845xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3846 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3847 xmlChar *ret, *dst;
3848 const xmlChar *src;
3849 xmlAttributePtr attrDecl = NULL;
3850 int extsubset = 0;
3851
3852 if (doc == NULL) return(NULL);
3853 if (elem == NULL) return(NULL);
3854 if (name == NULL) return(NULL);
3855 if (value == NULL) return(NULL);
3856
3857 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003858 xmlChar fn[50];
3859 xmlChar *fullname;
3860
3861 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3862 if (fullname == NULL)
3863 return(0);
3864 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003865 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003866 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003867 if (attrDecl != NULL)
3868 extsubset = 1;
3869 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003870 if ((fullname != fn) && (fullname != elem->name))
3871 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003872 }
3873 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3874 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3875 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3876 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3877 if (attrDecl != NULL)
3878 extsubset = 1;
3879 }
3880
3881 if (attrDecl == NULL)
3882 return(NULL);
3883 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3884 return(NULL);
3885
3886 ret = xmlStrdup(value);
3887 if (ret == NULL)
3888 return(NULL);
3889 src = value;
3890 dst = ret;
3891 while (*src == 0x20) src++;
3892 while (*src != 0) {
3893 if (*src == 0x20) {
3894 while (*src == 0x20) src++;
3895 if (*src != 0)
3896 *dst++ = 0x20;
3897 } else {
3898 *dst++ = *src++;
3899 }
3900 }
3901 *dst = 0;
3902 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003903 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE,
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003904"standalone: %s on %s value had to be normalized based on external subset declaration\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00003905 name, elem->name, NULL);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003906 ctxt->valid = 0;
3907 }
3908 return(ret);
3909}
3910
3911/**
Owen Taylor3473f882001-02-23 17:55:21 +00003912 * xmlValidNormalizeAttributeValue:
3913 * @doc: the document
3914 * @elem: the parent
3915 * @name: the attribute name
3916 * @value: the attribute value
3917 *
3918 * Does the validation related extra step of the normalization of attribute
3919 * values:
3920 *
3921 * If the declared value is not CDATA, then the XML processor must further
3922 * process the normalized attribute value by discarding any leading and
3923 * trailing space (#x20) characters, and by replacing sequences of space
3924 * (#x20) characters by single space (#x20) character.
3925 *
Daniel Veillard652327a2003-09-29 18:02:38 +00003926 * Returns a new normalized string if normalization is needed, NULL otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00003927 * the caller must free the returned value.
3928 */
3929
3930xmlChar *
3931xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3932 const xmlChar *name, const xmlChar *value) {
3933 xmlChar *ret, *dst;
3934 const xmlChar *src;
3935 xmlAttributePtr attrDecl = NULL;
3936
3937 if (doc == NULL) return(NULL);
3938 if (elem == NULL) return(NULL);
3939 if (name == NULL) return(NULL);
3940 if (value == NULL) return(NULL);
3941
3942 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003943 xmlChar fn[50];
3944 xmlChar *fullname;
3945
3946 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3947 if (fullname == NULL)
3948 return(0);
3949 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003950 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003951 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3952 if ((fullname != fn) && (fullname != elem->name))
3953 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003954 }
3955 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3956 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3957 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3958
3959 if (attrDecl == NULL)
3960 return(NULL);
3961 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3962 return(NULL);
3963
3964 ret = xmlStrdup(value);
3965 if (ret == NULL)
3966 return(NULL);
3967 src = value;
3968 dst = ret;
3969 while (*src == 0x20) src++;
3970 while (*src != 0) {
3971 if (*src == 0x20) {
3972 while (*src == 0x20) src++;
3973 if (*src != 0)
3974 *dst++ = 0x20;
3975 } else {
3976 *dst++ = *src++;
3977 }
3978 }
3979 *dst = 0;
3980 return(ret);
3981}
3982
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003983static void
Owen Taylor3473f882001-02-23 17:55:21 +00003984xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003985 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003986 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3987}
3988
3989/**
3990 * xmlValidateAttributeDecl:
3991 * @ctxt: the validation context
3992 * @doc: a document instance
3993 * @attr: an attribute definition
3994 *
3995 * Try to validate a single attribute definition
3996 * basically it does the following checks as described by the
3997 * XML-1.0 recommendation:
3998 * - [ VC: Attribute Default Legal ]
3999 * - [ VC: Enumeration ]
4000 * - [ VC: ID Attribute Default ]
4001 *
4002 * The ID/IDREF uniqueness and matching are done separately
4003 *
4004 * returns 1 if valid or 0 otherwise
4005 */
4006
4007int
4008xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4009 xmlAttributePtr attr) {
4010 int ret = 1;
4011 int val;
4012 CHECK_DTD;
4013 if(attr == NULL) return(1);
4014
4015 /* Attribute Default Legal */
4016 /* Enumeration */
4017 if (attr->defaultValue != NULL) {
4018 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
4019 if (val == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004020 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004021 "Syntax of default value for attribute %s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004022 attr->name, attr->elem, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004023 }
4024 ret &= val;
4025 }
4026
4027 /* ID Attribute Default */
4028 if ((attr->atype == XML_ATTRIBUTE_ID)&&
4029 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
4030 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004031 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_FIXED,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004032 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004033 attr->name, attr->elem, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004034 ret = 0;
4035 }
4036
4037 /* One ID per Element Type */
4038 if (attr->atype == XML_ATTRIBUTE_ID) {
4039 int nbId;
4040
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004041 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00004042 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
4043 attr->elem);
4044 if (elem != NULL) {
4045 nbId = xmlScanIDAttributeDecl(NULL, elem);
4046 } else {
4047 xmlAttributeTablePtr table;
4048
4049 /*
4050 * The attribute may be declared in the internal subset and the
4051 * element in the external subset.
4052 */
4053 nbId = 0;
4054 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
4055 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
4056 xmlValidateAttributeIdCallback, &nbId);
4057 }
4058 if (nbId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004059
4060 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00004061 "Element %s has %d ID attribute defined in the internal subset : %s\n",
4062 attr->elem, nbId, attr->name);
4063 } else if (doc->extSubset != NULL) {
4064 int extId = 0;
4065 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
4066 if (elem != NULL) {
4067 extId = xmlScanIDAttributeDecl(NULL, elem);
4068 }
4069 if (extId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004070 xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00004071 "Element %s has %d ID attribute defined in the external subset : %s\n",
4072 attr->elem, extId, attr->name);
4073 } else if (extId + nbId > 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004074 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET,
Owen Taylor3473f882001-02-23 17:55:21 +00004075"Element %s has ID attributes defined in the internal and external subset : %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004076 attr->elem, attr->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004077 }
4078 }
4079 }
4080
4081 /* Validity Constraint: Enumeration */
4082 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
4083 xmlEnumerationPtr tree = attr->tree;
4084 while (tree != NULL) {
4085 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
4086 tree = tree->next;
4087 }
4088 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004089 xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004090"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004091 attr->defaultValue, attr->name, attr->elem);
4092 ret = 0;
4093 }
4094 }
4095
4096 return(ret);
4097}
4098
4099/**
4100 * xmlValidateElementDecl:
4101 * @ctxt: the validation context
4102 * @doc: a document instance
4103 * @elem: an element definition
4104 *
4105 * Try to validate a single element definition
4106 * basically it does the following checks as described by the
4107 * XML-1.0 recommendation:
4108 * - [ VC: One ID per Element Type ]
4109 * - [ VC: No Duplicate Types ]
4110 * - [ VC: Unique Element Type Declaration ]
4111 *
4112 * returns 1 if valid or 0 otherwise
4113 */
4114
4115int
4116xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4117 xmlElementPtr elem) {
4118 int ret = 1;
4119 xmlElementPtr tst;
4120
4121 CHECK_DTD;
4122
4123 if (elem == NULL) return(1);
4124
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004125#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00004126#ifdef LIBXML_REGEXP_ENABLED
4127 /* Build the regexp associated to the content model */
4128 ret = xmlValidBuildContentModel(ctxt, elem);
4129#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004130#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00004131
Owen Taylor3473f882001-02-23 17:55:21 +00004132 /* No Duplicate Types */
4133 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
4134 xmlElementContentPtr cur, next;
4135 const xmlChar *name;
4136
4137 cur = elem->content;
4138 while (cur != NULL) {
4139 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
4140 if (cur->c1 == NULL) break;
4141 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4142 name = cur->c1->name;
4143 next = cur->c2;
4144 while (next != NULL) {
4145 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard7b68df92003-08-03 22:58:54 +00004146 if ((xmlStrEqual(next->name, name)) &&
4147 (xmlStrEqual(next->prefix, cur->prefix))) {
4148 if (cur->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004149 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00004150 "Definition of %s has duplicate references of %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004151 elem->name, name, NULL);
Daniel Veillard7b68df92003-08-03 22:58:54 +00004152 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004153 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00004154 "Definition of %s has duplicate references of %s:%s\n",
4155 elem->name, cur->prefix, name);
4156 }
Owen Taylor3473f882001-02-23 17:55:21 +00004157 ret = 0;
4158 }
4159 break;
4160 }
4161 if (next->c1 == NULL) break;
4162 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
Daniel Veillard7b68df92003-08-03 22:58:54 +00004163 if ((xmlStrEqual(next->c1->name, name)) &&
4164 (xmlStrEqual(next->c1->prefix, cur->prefix))) {
4165 if (cur->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004166 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00004167 "Definition of %s has duplicate references to %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004168 elem->name, name, NULL);
Daniel Veillard7b68df92003-08-03 22:58:54 +00004169 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004170 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_CONTENT_ERROR,
Daniel Veillard7b68df92003-08-03 22:58:54 +00004171 "Definition of %s has duplicate references to %s:%s\n",
4172 elem->name, cur->prefix, name);
4173 }
Owen Taylor3473f882001-02-23 17:55:21 +00004174 ret = 0;
4175 }
4176 next = next->c2;
4177 }
4178 }
4179 cur = cur->c2;
4180 }
4181 }
4182
4183 /* VC: Unique Element Type Declaration */
4184 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004185 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004186 ((tst->prefix == elem->prefix) ||
4187 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00004188 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004189 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
4190 "Redefinition of element %s\n",
4191 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004192 ret = 0;
4193 }
4194 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004195 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004196 ((tst->prefix == elem->prefix) ||
4197 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00004198 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004199 xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED,
4200 "Redefinition of element %s\n",
4201 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004202 ret = 0;
4203 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00004204 /* One ID per Element Type
4205 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00004206 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
4207 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00004208 } */
Owen Taylor3473f882001-02-23 17:55:21 +00004209 return(ret);
4210}
4211
4212/**
4213 * xmlValidateOneAttribute:
4214 * @ctxt: the validation context
4215 * @doc: a document instance
4216 * @elem: an element instance
4217 * @attr: an attribute instance
4218 * @value: the attribute value (without entities processing)
4219 *
4220 * Try to validate a single attribute for an element
4221 * basically it does the following checks as described by the
4222 * XML-1.0 recommendation:
4223 * - [ VC: Attribute Value Type ]
4224 * - [ VC: Fixed Attribute Default ]
4225 * - [ VC: Entity Name ]
4226 * - [ VC: Name Token ]
4227 * - [ VC: ID ]
4228 * - [ VC: IDREF ]
4229 * - [ VC: Entity Name ]
4230 * - [ VC: Notation Attributes ]
4231 *
4232 * The ID/IDREF uniqueness and matching are done separately
4233 *
4234 * returns 1 if valid or 0 otherwise
4235 */
4236
4237int
4238xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
Daniel Veillard07cb8222003-09-10 10:51:05 +00004239 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value)
4240{
Owen Taylor3473f882001-02-23 17:55:21 +00004241 xmlAttributePtr attrDecl = NULL;
4242 int val;
4243 int ret = 1;
4244
4245 CHECK_DTD;
4246 if ((elem == NULL) || (elem->name == NULL)) return(0);
4247 if ((attr == NULL) || (attr->name == NULL)) return(0);
4248
4249 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004250 xmlChar fn[50];
4251 xmlChar *fullname;
4252
4253 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
4254 if (fullname == NULL)
4255 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004256 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004257 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00004258 attr->name, attr->ns->prefix);
4259 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004260 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00004261 attr->name, attr->ns->prefix);
4262 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004263 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004264 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4265 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00004266 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004267 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004268 if ((fullname != fn) && (fullname != elem->name))
4269 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00004270 }
4271 if (attrDecl == NULL) {
4272 if (attr->ns != NULL) {
4273 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4274 attr->name, attr->ns->prefix);
4275 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4276 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4277 attr->name, attr->ns->prefix);
4278 } else {
4279 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4280 elem->name, attr->name);
4281 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4282 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4283 elem->name, attr->name);
4284 }
4285 }
4286
4287
4288 /* Validity Constraint: Attribute Value Type */
4289 if (attrDecl == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004290 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004291 "No declaration for attribute %s of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004292 attr->name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004293 return(0);
4294 }
4295 attr->atype = attrDecl->atype;
4296
4297 val = xmlValidateAttributeValue(attrDecl->atype, value);
4298 if (val == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004299 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004300 "Syntax of value for attribute %s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004301 attr->name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00004302 ret = 0;
4303 }
4304
4305 /* Validity constraint: Fixed Attribute Default */
4306 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4307 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004308 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004309 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004310 attr->name, elem->name, attrDecl->defaultValue);
4311 ret = 0;
4312 }
4313 }
4314
4315 /* Validity Constraint: ID uniqueness */
4316 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4317 if (xmlAddID(ctxt, doc, value, attr) == NULL)
4318 ret = 0;
4319 }
4320
4321 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4322 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4323 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
4324 ret = 0;
4325 }
4326
4327 /* Validity Constraint: Notation Attributes */
4328 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4329 xmlEnumerationPtr tree = attrDecl->tree;
4330 xmlNotationPtr nota;
4331
4332 /* First check that the given NOTATION was declared */
4333 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4334 if (nota == NULL)
4335 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4336
4337 if (nota == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004338 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004339 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004340 value, attr->name, elem->name);
4341 ret = 0;
4342 }
4343
4344 /* Second, verify that it's among the list */
4345 while (tree != NULL) {
4346 if (xmlStrEqual(tree->name, value)) break;
4347 tree = tree->next;
4348 }
4349 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004350 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004351"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004352 value, attr->name, elem->name);
4353 ret = 0;
4354 }
4355 }
4356
4357 /* Validity Constraint: Enumeration */
4358 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4359 xmlEnumerationPtr tree = attrDecl->tree;
4360 while (tree != NULL) {
4361 if (xmlStrEqual(tree->name, value)) break;
4362 tree = tree->next;
4363 }
4364 if (tree == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004365 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004366 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004367 value, attr->name, elem->name);
4368 ret = 0;
4369 }
4370 }
4371
4372 /* Fixed Attribute Default */
4373 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4374 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004375 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004376 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004377 attr->name, elem->name, attrDecl->defaultValue);
4378 ret = 0;
4379 }
4380
4381 /* Extra check for the attribute value */
4382 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
4383 attrDecl->atype, value);
4384
4385 return(ret);
4386}
4387
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004388/**
4389 * xmlValidateOneNamespace:
4390 * @ctxt: the validation context
4391 * @doc: a document instance
4392 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00004393 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004394 * @ns: an namespace declaration instance
4395 * @value: the attribute value (without entities processing)
4396 *
4397 * Try to validate a single namespace declaration for an element
4398 * basically it does the following checks as described by the
4399 * XML-1.0 recommendation:
4400 * - [ VC: Attribute Value Type ]
4401 * - [ VC: Fixed Attribute Default ]
4402 * - [ VC: Entity Name ]
4403 * - [ VC: Name Token ]
4404 * - [ VC: ID ]
4405 * - [ VC: IDREF ]
4406 * - [ VC: Entity Name ]
4407 * - [ VC: Notation Attributes ]
4408 *
4409 * The ID/IDREF uniqueness and matching are done separately
4410 *
4411 * returns 1 if valid or 0 otherwise
4412 */
4413
4414int
4415xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4416xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
4417 /* xmlElementPtr elemDecl; */
4418 xmlAttributePtr attrDecl = NULL;
4419 int val;
4420 int ret = 1;
4421
4422 CHECK_DTD;
4423 if ((elem == NULL) || (elem->name == NULL)) return(0);
4424 if ((ns == NULL) || (ns->href == NULL)) return(0);
4425
4426 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004427 xmlChar fn[50];
4428 xmlChar *fullname;
4429
4430 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
4431 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00004432 xmlVErrMemory(ctxt, "Validating namespace");
Daniel Veillardc00cda82003-04-07 10:22:39 +00004433 return(0);
4434 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004435 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004436 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004437 ns->prefix, BAD_CAST "xmlns");
4438 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004439 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004440 ns->prefix, BAD_CAST "xmlns");
4441 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004442 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004443 BAD_CAST "xmlns");
4444 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004445 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004446 BAD_CAST "xmlns");
4447 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004448 if ((fullname != fn) && (fullname != elem->name))
4449 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004450 }
4451 if (attrDecl == NULL) {
4452 if (ns->prefix != NULL) {
4453 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4454 ns->prefix, BAD_CAST "xmlns");
4455 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4456 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4457 ns->prefix, BAD_CAST "xmlns");
4458 } else {
4459 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4460 elem->name, BAD_CAST "xmlns");
4461 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4462 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4463 elem->name, BAD_CAST "xmlns");
4464 }
4465 }
4466
4467
4468 /* Validity Constraint: Attribute Value Type */
4469 if (attrDecl == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004470 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004471 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004472 "No declaration for attribute xmlns:%s of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004473 ns->prefix, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004474 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004475 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ATTRIBUTE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004476 "No declaration for attribute xmlns of element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004477 elem->name, NULL, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004478 }
4479 return(0);
4480 }
4481
4482 val = xmlValidateAttributeValue(attrDecl->atype, value);
4483 if (val == 0) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004484 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004485 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004486 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004487 ns->prefix, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004488 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004489 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004490 "Syntax of value for attribute xmlns of %s is not valid\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004491 elem->name, NULL, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004492 }
4493 ret = 0;
4494 }
4495
4496 /* Validity constraint: Fixed Attribute Default */
4497 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4498 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004499 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004500 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004501 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4502 ns->prefix, elem->name, attrDecl->defaultValue);
4503 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004504 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_DEFAULT,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004505 "Value for attribute xmlns of %s is different from default \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004506 elem->name, attrDecl->defaultValue, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004507 }
4508 ret = 0;
4509 }
4510 }
4511
4512 /* Validity Constraint: ID uniqueness */
4513 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4514 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4515 ret = 0;
4516 }
4517
4518 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4519 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4520 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4521 ret = 0;
4522 }
4523
4524 /* Validity Constraint: Notation Attributes */
4525 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4526 xmlEnumerationPtr tree = attrDecl->tree;
4527 xmlNotationPtr nota;
4528
4529 /* First check that the given NOTATION was declared */
4530 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4531 if (nota == NULL)
4532 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4533
4534 if (nota == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004535 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004536 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004537 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4538 value, ns->prefix, elem->name);
4539 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004540 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_NOTATION,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004541 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004542 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004543 }
4544 ret = 0;
4545 }
4546
4547 /* Second, verify that it's among the list */
4548 while (tree != NULL) {
4549 if (xmlStrEqual(tree->name, value)) break;
4550 tree = tree->next;
4551 }
4552 if (tree == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004553 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004554 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004555"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4556 value, ns->prefix, elem->name);
4557 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004558 xmlErrValidNode(ctxt, elem, XML_DTD_NOTATION_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004559"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004560 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004561 }
4562 ret = 0;
4563 }
4564 }
4565
4566 /* Validity Constraint: Enumeration */
4567 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4568 xmlEnumerationPtr tree = attrDecl->tree;
4569 while (tree != NULL) {
4570 if (xmlStrEqual(tree->name, value)) break;
4571 tree = tree->next;
4572 }
4573 if (tree == NULL) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004574 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004575 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004576"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4577 value, ns->prefix, elem->name);
4578 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004579 xmlErrValidNode(ctxt, elem, XML_DTD_ATTRIBUTE_VALUE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004580"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004581 value, elem->name, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004582 }
4583 ret = 0;
4584 }
4585 }
4586
4587 /* Fixed Attribute Default */
4588 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4589 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004590 if (ns->prefix != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004591 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004592 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4593 ns->prefix, elem->name, attrDecl->defaultValue);
4594 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004595 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004596 "Value for attribute xmlns of %s must be \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00004597 elem->name, attrDecl->defaultValue, NULL);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004598 }
4599 ret = 0;
4600 }
4601
4602 /* Extra check for the attribute value */
4603 if (ns->prefix != NULL) {
4604 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4605 attrDecl->atype, value);
4606 } else {
4607 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4608 attrDecl->atype, value);
4609 }
4610
4611 return(ret);
4612}
4613
Daniel Veillard118aed72002-09-24 14:13:13 +00004614#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004615/**
4616 * xmlValidateSkipIgnorable:
4617 * @ctxt: the validation context
4618 * @child: the child list
4619 *
4620 * Skip ignorable elements w.r.t. the validation process
4621 *
4622 * returns the first element to consider for validation of the content model
4623 */
4624
4625static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004626xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004627 while (child != NULL) {
4628 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004629 /* These things are ignored (skipped) during validation. */
4630 case XML_PI_NODE:
4631 case XML_COMMENT_NODE:
4632 case XML_XINCLUDE_START:
4633 case XML_XINCLUDE_END:
4634 child = child->next;
4635 break;
4636 case XML_TEXT_NODE:
4637 if (xmlIsBlankNode(child))
4638 child = child->next;
4639 else
4640 return(child);
4641 break;
4642 /* keep current node */
4643 default:
4644 return(child);
4645 }
4646 }
4647 return(child);
4648}
4649
4650/**
4651 * xmlValidateElementType:
4652 * @ctxt: the validation context
4653 *
4654 * Try to validate the content model of an element internal function
4655 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004656 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4657 * reference is found and -3 if the validation succeeded but
4658 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004659 */
4660
4661static int
4662xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004663 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004664 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004665
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004666 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004667 if ((NODE == NULL) && (CONT == NULL))
4668 return(1);
4669 if ((NODE == NULL) &&
4670 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4671 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4672 return(1);
4673 }
4674 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004675 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004676 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004677
4678 /*
4679 * We arrive here when more states need to be examined
4680 */
4681cont:
4682
4683 /*
4684 * We just recovered from a rollback generated by a possible
4685 * epsilon transition, go directly to the analysis phase
4686 */
4687 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004688 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004689 DEBUG_VALID_STATE(NODE, CONT)
4690 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004691 goto analyze;
4692 }
4693
4694 DEBUG_VALID_STATE(NODE, CONT)
4695 /*
4696 * we may have to save a backup state here. This is the equivalent
4697 * of handling epsilon transition in NFAs.
4698 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004699 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004700 ((CONT->parent == NULL) ||
4701 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004702 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004703 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004704 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004705 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004706 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4707 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004708 }
4709
4710
4711 /*
4712 * Check first if the content matches
4713 */
4714 switch (CONT->type) {
4715 case XML_ELEMENT_CONTENT_PCDATA:
4716 if (NODE == NULL) {
4717 DEBUG_VALID_MSG("pcdata failed no node");
4718 ret = 0;
4719 break;
4720 }
4721 if (NODE->type == XML_TEXT_NODE) {
4722 DEBUG_VALID_MSG("pcdata found, skip to next");
4723 /*
4724 * go to next element in the content model
4725 * skipping ignorable elems
4726 */
4727 do {
4728 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004729 NODE = xmlValidateSkipIgnorable(NODE);
4730 if ((NODE != NULL) &&
4731 (NODE->type == XML_ENTITY_REF_NODE))
4732 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004733 } while ((NODE != NULL) &&
4734 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004735 (NODE->type != XML_TEXT_NODE) &&
4736 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004737 ret = 1;
4738 break;
4739 } else {
4740 DEBUG_VALID_MSG("pcdata failed");
4741 ret = 0;
4742 break;
4743 }
4744 break;
4745 case XML_ELEMENT_CONTENT_ELEMENT:
4746 if (NODE == NULL) {
4747 DEBUG_VALID_MSG("element failed no node");
4748 ret = 0;
4749 break;
4750 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004751 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4752 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004753 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004754 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4755 ret = (CONT->prefix == NULL);
4756 } else if (CONT->prefix == NULL) {
4757 ret = 0;
4758 } else {
4759 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4760 }
4761 }
4762 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004763 DEBUG_VALID_MSG("element found, skip to next");
4764 /*
4765 * go to next element in the content model
4766 * skipping ignorable elems
4767 */
4768 do {
4769 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004770 NODE = xmlValidateSkipIgnorable(NODE);
4771 if ((NODE != NULL) &&
4772 (NODE->type == XML_ENTITY_REF_NODE))
4773 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004774 } while ((NODE != NULL) &&
4775 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004776 (NODE->type != XML_TEXT_NODE) &&
4777 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004778 } else {
4779 DEBUG_VALID_MSG("element failed");
4780 ret = 0;
4781 break;
4782 }
4783 break;
4784 case XML_ELEMENT_CONTENT_OR:
4785 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004786 * Small optimization.
4787 */
4788 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4789 if ((NODE == NULL) ||
4790 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4791 DEPTH++;
4792 CONT = CONT->c2;
4793 goto cont;
4794 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004795 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4796 ret = (CONT->c1->prefix == NULL);
4797 } else if (CONT->c1->prefix == NULL) {
4798 ret = 0;
4799 } else {
4800 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4801 }
4802 if (ret == 0) {
4803 DEPTH++;
4804 CONT = CONT->c2;
4805 goto cont;
4806 }
Daniel Veillard85349052001-04-20 13:48:21 +00004807 }
4808
4809 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004810 * save the second branch 'or' branch
4811 */
4812 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004813 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4814 OCCURS, ROLLBACK_OR) < 0)
4815 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004816 DEPTH++;
4817 CONT = CONT->c1;
4818 goto cont;
4819 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004820 /*
4821 * Small optimization.
4822 */
4823 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4824 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4825 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4826 if ((NODE == NULL) ||
4827 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4828 DEPTH++;
4829 CONT = CONT->c2;
4830 goto cont;
4831 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004832 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4833 ret = (CONT->c1->prefix == NULL);
4834 } else if (CONT->c1->prefix == NULL) {
4835 ret = 0;
4836 } else {
4837 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4838 }
4839 if (ret == 0) {
4840 DEPTH++;
4841 CONT = CONT->c2;
4842 goto cont;
4843 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004844 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004845 DEPTH++;
4846 CONT = CONT->c1;
4847 goto cont;
4848 }
4849
4850 /*
4851 * At this point handle going up in the tree
4852 */
4853 if (ret == -1) {
4854 DEBUG_VALID_MSG("error found returning");
4855 return(ret);
4856 }
4857analyze:
4858 while (CONT != NULL) {
4859 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004860 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004861 * this level.
4862 */
4863 if (ret == 0) {
4864 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004865 xmlNodePtr cur;
4866
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004867 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004868 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004869 DEBUG_VALID_MSG("Once branch failed, rollback");
4870 if (vstateVPop(ctxt) < 0 ) {
4871 DEBUG_VALID_MSG("exhaustion, failed");
4872 return(0);
4873 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004874 if (cur != ctxt->vstate->node)
4875 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004876 goto cont;
4877 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004878 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004879 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004880 DEBUG_VALID_MSG("Plus branch failed, rollback");
4881 if (vstateVPop(ctxt) < 0 ) {
4882 DEBUG_VALID_MSG("exhaustion, failed");
4883 return(0);
4884 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004885 if (cur != ctxt->vstate->node)
4886 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004887 goto cont;
4888 }
4889 DEBUG_VALID_MSG("Plus branch found");
4890 ret = 1;
4891 break;
4892 case XML_ELEMENT_CONTENT_MULT:
4893#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004894 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004895 DEBUG_VALID_MSG("Mult branch failed");
4896 } else {
4897 DEBUG_VALID_MSG("Mult branch found");
4898 }
4899#endif
4900 ret = 1;
4901 break;
4902 case XML_ELEMENT_CONTENT_OPT:
4903 DEBUG_VALID_MSG("Option branch failed");
4904 ret = 1;
4905 break;
4906 }
4907 } else {
4908 switch (CONT->ocur) {
4909 case XML_ELEMENT_CONTENT_OPT:
4910 DEBUG_VALID_MSG("Option branch succeeded");
4911 ret = 1;
4912 break;
4913 case XML_ELEMENT_CONTENT_ONCE:
4914 DEBUG_VALID_MSG("Once branch succeeded");
4915 ret = 1;
4916 break;
4917 case XML_ELEMENT_CONTENT_PLUS:
4918 if (STATE == ROLLBACK_PARENT) {
4919 DEBUG_VALID_MSG("Plus branch rollback");
4920 ret = 1;
4921 break;
4922 }
4923 if (NODE == NULL) {
4924 DEBUG_VALID_MSG("Plus branch exhausted");
4925 ret = 1;
4926 break;
4927 }
4928 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004929 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004930 goto cont;
4931 case XML_ELEMENT_CONTENT_MULT:
4932 if (STATE == ROLLBACK_PARENT) {
4933 DEBUG_VALID_MSG("Mult branch rollback");
4934 ret = 1;
4935 break;
4936 }
4937 if (NODE == NULL) {
4938 DEBUG_VALID_MSG("Mult branch exhausted");
4939 ret = 1;
4940 break;
4941 }
4942 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004943 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004944 goto cont;
4945 }
4946 }
4947 STATE = 0;
4948
4949 /*
4950 * Then act accordingly at the parent level
4951 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004952 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004953 if (CONT->parent == NULL)
4954 break;
4955
4956 switch (CONT->parent->type) {
4957 case XML_ELEMENT_CONTENT_PCDATA:
4958 DEBUG_VALID_MSG("Error: parent pcdata");
4959 return(-1);
4960 case XML_ELEMENT_CONTENT_ELEMENT:
4961 DEBUG_VALID_MSG("Error: parent element");
4962 return(-1);
4963 case XML_ELEMENT_CONTENT_OR:
4964 if (ret == 1) {
4965 DEBUG_VALID_MSG("Or succeeded");
4966 CONT = CONT->parent;
4967 DEPTH--;
4968 } else {
4969 DEBUG_VALID_MSG("Or failed");
4970 CONT = CONT->parent;
4971 DEPTH--;
4972 }
4973 break;
4974 case XML_ELEMENT_CONTENT_SEQ:
4975 if (ret == 0) {
4976 DEBUG_VALID_MSG("Sequence failed");
4977 CONT = CONT->parent;
4978 DEPTH--;
4979 } else if (CONT == CONT->parent->c1) {
4980 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4981 CONT = CONT->parent->c2;
4982 goto cont;
4983 } else {
4984 DEBUG_VALID_MSG("Sequence succeeded");
4985 CONT = CONT->parent;
4986 DEPTH--;
4987 }
4988 }
4989 }
4990 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004991 xmlNodePtr cur;
4992
4993 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004994 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4995 if (vstateVPop(ctxt) < 0 ) {
4996 DEBUG_VALID_MSG("exhaustion, failed");
4997 return(0);
4998 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004999 if (cur != ctxt->vstate->node)
5000 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005001 goto cont;
5002 }
5003 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005004 xmlNodePtr cur;
5005
5006 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005007 DEBUG_VALID_MSG("Failure, rollback");
5008 if (vstateVPop(ctxt) < 0 ) {
5009 DEBUG_VALID_MSG("exhaustion, failed");
5010 return(0);
5011 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005012 if (cur != ctxt->vstate->node)
5013 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005014 goto cont;
5015 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005016 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005017}
Daniel Veillard23e73572002-09-19 19:56:43 +00005018#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005019
5020/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00005021 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005022 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00005023 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005024 * @content: An element
5025 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
5026 *
5027 * This will dump the list of elements to the buffer
5028 * Intended just for the debug routine
5029 */
5030static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00005031xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005032 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00005033 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005034
5035 if (node == NULL) return;
5036 if (glob) strcat(buf, "(");
5037 cur = node;
5038 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00005039 len = strlen(buf);
5040 if (size - len < 50) {
5041 if ((size - len > 4) && (buf[len - 1] != '.'))
5042 strcat(buf, " ...");
5043 return;
5044 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005045 switch (cur->type) {
5046 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005047 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00005048 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005049 if ((size - len > 4) && (buf[len - 1] != '.'))
5050 strcat(buf, " ...");
5051 return;
5052 }
5053 strcat(buf, (char *) cur->ns->prefix);
5054 strcat(buf, ":");
5055 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00005056 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00005057 if ((size - len > 4) && (buf[len - 1] != '.'))
5058 strcat(buf, " ...");
5059 return;
5060 }
5061 strcat(buf, (char *) cur->name);
5062 if (cur->next != NULL)
5063 strcat(buf, " ");
5064 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005065 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00005066 if (xmlIsBlankNode(cur))
5067 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005068 case XML_CDATA_SECTION_NODE:
5069 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00005070 strcat(buf, "CDATA");
5071 if (cur->next != NULL)
5072 strcat(buf, " ");
5073 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005074 case XML_ATTRIBUTE_NODE:
5075 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00005076#ifdef LIBXML_DOCB_ENABLED
5077 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005078#endif
5079 case XML_HTML_DOCUMENT_NODE:
5080 case XML_DOCUMENT_TYPE_NODE:
5081 case XML_DOCUMENT_FRAG_NODE:
5082 case XML_NOTATION_NODE:
5083 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00005084 strcat(buf, "???");
5085 if (cur->next != NULL)
5086 strcat(buf, " ");
5087 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005088 case XML_ENTITY_NODE:
5089 case XML_PI_NODE:
5090 case XML_DTD_NODE:
5091 case XML_COMMENT_NODE:
5092 case XML_ELEMENT_DECL:
5093 case XML_ATTRIBUTE_DECL:
5094 case XML_ENTITY_DECL:
5095 case XML_XINCLUDE_START:
5096 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00005097 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005098 }
5099 cur = cur->next;
5100 }
5101 if (glob) strcat(buf, ")");
5102}
5103
5104/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005105 * xmlValidateElementContent:
5106 * @ctxt: the validation context
5107 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005108 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005109 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005110 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005111 *
5112 * Try to validate the content model of an element
5113 *
5114 * returns 1 if valid or 0 if not and -1 in case of error
5115 */
5116
5117static int
5118xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005119 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00005120 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00005121#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00005122 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00005123#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00005124 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005125 xmlElementContentPtr cont;
5126 const xmlChar *name;
5127
5128 if (elemDecl == NULL)
5129 return(-1);
5130 cont = elemDecl->content;
5131 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005132
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005133#ifdef LIBXML_REGEXP_ENABLED
5134 /* Build the regexp associated to the content model */
5135 if (elemDecl->contModel == NULL)
5136 ret = xmlValidBuildContentModel(ctxt, elemDecl);
5137 if (elemDecl->contModel == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005138 return(-1);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005139 } else {
5140 xmlRegExecCtxtPtr exec;
5141
Daniel Veillardec498e12003-02-05 11:01:50 +00005142 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
5143 return(-1);
5144 }
Daniel Veillard01992e02002-10-09 10:20:30 +00005145 ctxt->nodeMax = 0;
5146 ctxt->nodeNr = 0;
5147 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005148 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
5149 if (exec != NULL) {
5150 cur = child;
5151 while (cur != NULL) {
5152 switch (cur->type) {
5153 case XML_ENTITY_REF_NODE:
5154 /*
5155 * Push the current node to be able to roll back
5156 * and process within the entity
5157 */
5158 if ((cur->children != NULL) &&
5159 (cur->children->children != NULL)) {
5160 nodeVPush(ctxt, cur);
5161 cur = cur->children->children;
5162 continue;
5163 }
5164 break;
5165 case XML_TEXT_NODE:
5166 if (xmlIsBlankNode(cur))
5167 break;
5168 ret = 0;
5169 goto fail;
5170 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00005171 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005172 ret = 0;
5173 goto fail;
5174 case XML_ELEMENT_NODE:
5175 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005176 xmlChar fn[50];
5177 xmlChar *fullname;
5178
5179 fullname = xmlBuildQName(cur->name,
5180 cur->ns->prefix, fn, 50);
5181 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005182 ret = -1;
5183 goto fail;
5184 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005185 ret = xmlRegExecPushString(exec, fullname, NULL);
5186 if ((fullname != fn) && (fullname != cur->name))
5187 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005188 } else {
5189 ret = xmlRegExecPushString(exec, cur->name, NULL);
5190 }
5191 break;
5192 default:
5193 break;
5194 }
5195 /*
5196 * Switch to next element
5197 */
5198 cur = cur->next;
5199 while (cur == NULL) {
5200 cur = nodeVPop(ctxt);
5201 if (cur == NULL)
5202 break;
5203 cur = cur->next;
5204 }
5205 }
5206 ret = xmlRegExecPushString(exec, NULL, NULL);
5207fail:
5208 xmlRegFreeExecCtxt(exec);
5209 }
5210 }
5211#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005212 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005213 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005214 */
5215 ctxt->vstateMax = 8;
5216 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
5217 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
5218 if (ctxt->vstateTab == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00005219 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005220 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005221 }
5222 /*
5223 * The first entry in the stack is reserved to the current state
5224 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00005225 ctxt->nodeMax = 0;
5226 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00005227 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005228 ctxt->vstate = &ctxt->vstateTab[0];
5229 ctxt->vstateNr = 1;
5230 CONT = cont;
5231 NODE = child;
5232 DEPTH = 0;
5233 OCCURS = 0;
5234 STATE = 0;
5235 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005236 if ((ret == -3) && (warn)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005237 xmlErrValidWarning(ctxt, child, XML_DTD_CONTENT_NOT_DETERMINIST,
5238 "Content model for Element %s is ambiguous\n",
Daniel Veillard0cc72772003-10-13 14:00:21 +00005239 name, NULL, NULL);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005240 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005241 /*
5242 * An entities reference appeared at this level.
5243 * Buid a minimal representation of this node content
5244 * sufficient to run the validation process on it
5245 */
5246 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005247 cur = child;
5248 while (cur != NULL) {
5249 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005250 case XML_ENTITY_REF_NODE:
5251 /*
5252 * Push the current node to be able to roll back
5253 * and process within the entity
5254 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005255 if ((cur->children != NULL) &&
5256 (cur->children->children != NULL)) {
5257 nodeVPush(ctxt, cur);
5258 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005259 continue;
5260 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00005261 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005262 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005263 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005264 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005265 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00005266 case XML_CDATA_SECTION_NODE:
5267 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005268 case XML_ELEMENT_NODE:
5269 /*
5270 * Allocate a new node and minimally fills in
5271 * what's required
5272 */
5273 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
5274 if (tmp == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00005275 xmlVErrMemory(ctxt, "malloc failed");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005276 xmlFreeNodeList(repl);
5277 ret = -1;
5278 goto done;
5279 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005280 tmp->type = cur->type;
5281 tmp->name = cur->name;
5282 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005283 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00005284 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005285 if (repl == NULL)
5286 repl = last = tmp;
5287 else {
5288 last->next = tmp;
5289 last = tmp;
5290 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00005291 if (cur->type == XML_CDATA_SECTION_NODE) {
5292 /*
5293 * E59 spaces in CDATA does not match the
5294 * nonterminal S
5295 */
5296 tmp->content = xmlStrdup(BAD_CAST "CDATA");
5297 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005298 break;
5299 default:
5300 break;
5301 }
5302 /*
5303 * Switch to next element
5304 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005305 cur = cur->next;
5306 while (cur == NULL) {
5307 cur = nodeVPop(ctxt);
5308 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005309 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005310 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005311 }
5312 }
5313
5314 /*
5315 * Relaunch the validation
5316 */
5317 ctxt->vstate = &ctxt->vstateTab[0];
5318 ctxt->vstateNr = 1;
5319 CONT = cont;
5320 NODE = repl;
5321 DEPTH = 0;
5322 OCCURS = 0;
5323 STATE = 0;
5324 ret = xmlValidateElementType(ctxt);
5325 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00005326#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005327 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005328 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
5329 char expr[5000];
5330 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005331
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005332 expr[0] = 0;
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005333 xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005334 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00005335#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005336 if (repl != NULL)
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005337 xmlSnprintfElements(&list[0], 5000, repl, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005338 else
Daniel Veillard01992e02002-10-09 10:20:30 +00005339#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00005340 xmlSnprintfElements(&list[0], 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005341
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005342 if (name != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005343 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5344 "Element %s content does not follow the DTD, expecting %s, got %s\n",
5345 name, BAD_CAST expr, BAD_CAST list);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005346 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005347 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5348 "Element content does not follow the DTD, expecting %s, got %s\n",
5349 BAD_CAST expr, BAD_CAST list, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005350 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005351 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005352 if (name != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005353 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005354 "Element %s content does not follow the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005355 name, NULL, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005356 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005357 xmlErrValidNode(ctxt, parent, XML_DTD_CONTENT_MODEL,
5358 "Element content does not follow the DTD\n",
5359 NULL, NULL, NULL);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00005360 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005361 }
5362 ret = 0;
5363 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00005364 if (ret == -3)
5365 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005366
Daniel Veillard23e73572002-09-19 19:56:43 +00005367#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005368done:
5369 /*
5370 * Deallocate the copy if done, and free up the validation stack
5371 */
5372 while (repl != NULL) {
5373 tmp = repl->next;
5374 xmlFree(repl);
5375 repl = tmp;
5376 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005377 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005378 if (ctxt->vstateTab != NULL) {
5379 xmlFree(ctxt->vstateTab);
5380 ctxt->vstateTab = NULL;
5381 }
Daniel Veillard01992e02002-10-09 10:20:30 +00005382#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005383 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00005384 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005385 if (ctxt->nodeTab != NULL) {
5386 xmlFree(ctxt->nodeTab);
5387 ctxt->nodeTab = NULL;
5388 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005389 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005390
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005391}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005392
Owen Taylor3473f882001-02-23 17:55:21 +00005393/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005394 * xmlValidateCdataElement:
5395 * @ctxt: the validation context
5396 * @doc: a document instance
5397 * @elem: an element instance
5398 *
5399 * Check that an element follows #CDATA
5400 *
5401 * returns 1 if valid or 0 otherwise
5402 */
5403static int
5404xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5405 xmlNodePtr elem) {
5406 int ret = 1;
5407 xmlNodePtr cur, child;
5408
Daniel Veillardceb09b92002-10-04 11:46:37 +00005409 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005410 return(0);
5411
5412 child = elem->children;
5413
5414 cur = child;
5415 while (cur != NULL) {
5416 switch (cur->type) {
5417 case XML_ENTITY_REF_NODE:
5418 /*
5419 * Push the current node to be able to roll back
5420 * and process within the entity
5421 */
5422 if ((cur->children != NULL) &&
5423 (cur->children->children != NULL)) {
5424 nodeVPush(ctxt, cur);
5425 cur = cur->children->children;
5426 continue;
5427 }
5428 break;
5429 case XML_COMMENT_NODE:
5430 case XML_PI_NODE:
5431 case XML_TEXT_NODE:
5432 case XML_CDATA_SECTION_NODE:
5433 break;
5434 default:
5435 ret = 0;
5436 goto done;
5437 }
5438 /*
5439 * Switch to next element
5440 */
5441 cur = cur->next;
5442 while (cur == NULL) {
5443 cur = nodeVPop(ctxt);
5444 if (cur == NULL)
5445 break;
5446 cur = cur->next;
5447 }
5448 }
5449done:
5450 ctxt->nodeMax = 0;
5451 ctxt->nodeNr = 0;
5452 if (ctxt->nodeTab != NULL) {
5453 xmlFree(ctxt->nodeTab);
5454 ctxt->nodeTab = NULL;
5455 }
5456 return(ret);
5457}
5458
5459/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005460 * xmlValidateCheckMixed:
5461 * @ctxt: the validation context
5462 * @cont: the mixed content model
5463 * @qname: the qualified name as appearing in the serialization
5464 *
5465 * Check if the given node is part of the content model.
5466 *
5467 * Returns 1 if yes, 0 if no, -1 in case of error
5468 */
5469static int
William M. Brackedb65a72004-02-06 07:36:04 +00005470xmlValidateCheckMixed(xmlValidCtxtPtr ctxt,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005471 xmlElementContentPtr cont, const xmlChar *qname) {
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005472 const xmlChar *name;
5473 int plen;
5474 name = xmlSplitQName3(qname, &plen);
5475
5476 if (name == NULL) {
5477 while (cont != NULL) {
5478 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5479 if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname)))
5480 return(1);
5481 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5482 (cont->c1 != NULL) &&
5483 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5484 if ((cont->c1->prefix == NULL) &&
5485 (xmlStrEqual(cont->c1->name, qname)))
5486 return(1);
5487 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5488 (cont->c1 == NULL) ||
5489 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005490 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5491 "Internal: MIXED struct corrupted\n",
5492 NULL);
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005493 break;
5494 }
5495 cont = cont->c2;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005496 }
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005497 } else {
5498 while (cont != NULL) {
5499 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5500 if ((cont->prefix != NULL) &&
5501 (xmlStrncmp(cont->prefix, qname, plen) == 0) &&
5502 (xmlStrEqual(cont->name, name)))
5503 return(1);
5504 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5505 (cont->c1 != NULL) &&
5506 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5507 if ((cont->c1->prefix != NULL) &&
5508 (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) &&
5509 (xmlStrEqual(cont->c1->name, name)))
5510 return(1);
5511 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5512 (cont->c1 == NULL) ||
5513 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005514 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
5515 "Internal: MIXED struct corrupted\n",
5516 NULL);
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005517 break;
5518 }
5519 cont = cont->c2;
5520 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005521 }
5522 return(0);
5523}
5524
5525/**
5526 * xmlValidGetElemDecl:
5527 * @ctxt: the validation context
5528 * @doc: a document instance
5529 * @elem: an element instance
5530 * @extsubset: pointer, (out) indicate if the declaration was found
5531 * in the external subset.
5532 *
5533 * Finds a declaration associated to an element in the document.
5534 *
5535 * returns the pointer to the declaration or NULL if not found.
5536 */
5537static xmlElementPtr
5538xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5539 xmlNodePtr elem, int *extsubset) {
5540 xmlElementPtr elemDecl = NULL;
5541 const xmlChar *prefix = NULL;
5542
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005543 if ((ctxt == NULL) || (doc == NULL) ||
5544 (elem == NULL) || (elem->name == NULL))
5545 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005546 if (extsubset != NULL)
5547 *extsubset = 0;
5548
5549 /*
5550 * Fetch the declaration for the qualified name
5551 */
5552 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5553 prefix = elem->ns->prefix;
5554
5555 if (prefix != NULL) {
5556 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5557 elem->name, prefix);
5558 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5559 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5560 elem->name, prefix);
5561 if ((elemDecl != NULL) && (extsubset != NULL))
5562 *extsubset = 1;
5563 }
5564 }
5565
5566 /*
5567 * Fetch the declaration for the non qualified name
5568 * This is "non-strict" validation should be done on the
5569 * full QName but in that case being flexible makes sense.
5570 */
5571 if (elemDecl == NULL) {
5572 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5573 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5574 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5575 if ((elemDecl != NULL) && (extsubset != NULL))
5576 *extsubset = 1;
5577 }
5578 }
5579 if (elemDecl == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005580 xmlErrValidNode(ctxt, elem,
5581 XML_DTD_UNKNOWN_ELEM,
5582 "No declaration for element %s\n",
5583 elem->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005584 }
5585 return(elemDecl);
5586}
5587
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005588#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005589/**
5590 * xmlValidatePushElement:
5591 * @ctxt: the validation context
5592 * @doc: a document instance
5593 * @elem: an element instance
5594 * @qname: the qualified name as appearing in the serialization
5595 *
5596 * Push a new element start on the validation stack.
5597 *
5598 * returns 1 if no validation problem was found or 0 otherwise
5599 */
5600int
5601xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5602 xmlNodePtr elem, const xmlChar *qname) {
5603 int ret = 1;
5604 xmlElementPtr eDecl;
5605 int extsubset = 0;
5606
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005607 if (ctxt == NULL)
5608 return(0);
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005609/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005610 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5611 xmlValidStatePtr state = ctxt->vstate;
5612 xmlElementPtr elemDecl;
5613
5614 /*
5615 * Check the new element agaisnt the content model of the new elem.
5616 */
5617 if (state->elemDecl != NULL) {
5618 elemDecl = state->elemDecl;
5619
5620 switch(elemDecl->etype) {
5621 case XML_ELEMENT_TYPE_UNDEFINED:
5622 ret = 0;
5623 break;
5624 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005625 xmlErrValidNode(ctxt, state->node,
5626 XML_DTD_NOT_EMPTY,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005627 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005628 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005629 ret = 0;
5630 break;
5631 case XML_ELEMENT_TYPE_ANY:
5632 /* I don't think anything is required then */
5633 break;
5634 case XML_ELEMENT_TYPE_MIXED:
5635 /* simple case of declared as #PCDATA */
5636 if ((elemDecl->content != NULL) &&
5637 (elemDecl->content->type ==
5638 XML_ELEMENT_CONTENT_PCDATA)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005639 xmlErrValidNode(ctxt, state->node,
5640 XML_DTD_NOT_PCDATA,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005641 "Element %s was declared #PCDATA but contains non text nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005642 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005643 ret = 0;
5644 } else {
5645 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5646 qname);
5647 if (ret != 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005648 xmlErrValidNode(ctxt, state->node,
5649 XML_DTD_INVALID_CHILD,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005650 "Element %s is not declared in %s list of possible children\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005651 qname, state->node->name, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005652 }
5653 }
5654 break;
5655 case XML_ELEMENT_TYPE_ELEMENT:
5656 /*
5657 * TODO:
5658 * VC: Standalone Document Declaration
5659 * - element types with element content, if white space
5660 * occurs directly within any instance of those types.
5661 */
5662 if (state->exec != NULL) {
5663 ret = xmlRegExecPushString(state->exec, qname, NULL);
5664 if (ret < 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005665 xmlErrValidNode(ctxt, state->node,
5666 XML_DTD_CONTENT_MODEL,
5667 "Element %s content does not follow the DTD, Misplaced %s\n",
5668 state->node->name, qname, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005669 ret = 0;
5670 } else {
5671 ret = 1;
5672 }
5673 }
5674 break;
5675 }
5676 }
5677 }
5678 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5679 vstateVPush(ctxt, eDecl, elem);
5680 return(ret);
5681}
5682
5683/**
5684 * xmlValidatePushCData:
5685 * @ctxt: the validation context
5686 * @data: some character data read
5687 * @len: the lenght of the data
5688 *
5689 * check the CData parsed for validation in the current stack
5690 *
5691 * returns 1 if no validation problem was found or 0 otherwise
5692 */
5693int
5694xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5695 int ret = 1;
5696
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005697/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005698 if (ctxt == NULL)
5699 return(0);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005700 if (len <= 0)
5701 return(ret);
5702 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5703 xmlValidStatePtr state = ctxt->vstate;
5704 xmlElementPtr elemDecl;
5705
5706 /*
5707 * Check the new element agaisnt the content model of the new elem.
5708 */
5709 if (state->elemDecl != NULL) {
5710 elemDecl = state->elemDecl;
5711
5712 switch(elemDecl->etype) {
5713 case XML_ELEMENT_TYPE_UNDEFINED:
5714 ret = 0;
5715 break;
5716 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005717 xmlErrValidNode(ctxt, state->node,
5718 XML_DTD_NOT_EMPTY,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005719 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005720 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005721 ret = 0;
5722 break;
5723 case XML_ELEMENT_TYPE_ANY:
5724 break;
5725 case XML_ELEMENT_TYPE_MIXED:
5726 break;
5727 case XML_ELEMENT_TYPE_ELEMENT:
5728 if (len > 0) {
5729 int i;
5730
5731 for (i = 0;i < len;i++) {
William M. Brack76e95df2003-10-18 16:20:14 +00005732 if (!IS_BLANK_CH(data[i])) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005733 xmlErrValidNode(ctxt, state->node,
5734 XML_DTD_CONTENT_MODEL,
5735 "Element %s content does not follow the DTD, Text not allowed\n",
5736 state->node->name, NULL, NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005737 ret = 0;
5738 goto done;
5739 }
5740 }
5741 /*
5742 * TODO:
5743 * VC: Standalone Document Declaration
5744 * element types with element content, if white space
5745 * occurs directly within any instance of those types.
5746 */
5747 }
5748 break;
5749 }
5750 }
5751 }
5752done:
5753 return(ret);
5754}
5755
5756/**
5757 * xmlValidatePopElement:
5758 * @ctxt: the validation context
5759 * @doc: a document instance
5760 * @elem: an element instance
5761 * @qname: the qualified name as appearing in the serialization
5762 *
5763 * Pop the element end from the validation stack.
5764 *
5765 * returns 1 if no validation problem was found or 0 otherwise
5766 */
5767int
5768xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005769 xmlNodePtr elem ATTRIBUTE_UNUSED,
5770 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005771 int ret = 1;
5772
Daniel Veillardc0be74b2004-11-03 19:16:55 +00005773 if (ctxt == NULL)
5774 return(0);
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005775/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005776 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5777 xmlValidStatePtr state = ctxt->vstate;
5778 xmlElementPtr elemDecl;
5779
5780 /*
5781 * Check the new element agaisnt the content model of the new elem.
5782 */
5783 if (state->elemDecl != NULL) {
5784 elemDecl = state->elemDecl;
5785
5786 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5787 if (state->exec != NULL) {
5788 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5789 if (ret == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005790 xmlErrValidNode(ctxt, state->node,
5791 XML_DTD_CONTENT_MODEL,
5792 "Element %s content does not follow the DTD, Expecting more child\n",
5793 state->node->name, NULL,NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00005794 } else {
5795 /*
5796 * previous validation errors should not generate
5797 * a new one here
5798 */
5799 ret = 1;
5800 }
5801 }
5802 }
5803 }
5804 vstateVPop(ctxt);
5805 }
5806 return(ret);
5807}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005808#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005809
5810/**
Owen Taylor3473f882001-02-23 17:55:21 +00005811 * xmlValidateOneElement:
5812 * @ctxt: the validation context
5813 * @doc: a document instance
5814 * @elem: an element instance
5815 *
5816 * Try to validate a single element and it's attributes,
5817 * basically it does the following checks as described by the
5818 * XML-1.0 recommendation:
5819 * - [ VC: Element Valid ]
5820 * - [ VC: Required Attribute ]
5821 * Then call xmlValidateOneAttribute() for each attribute present.
5822 *
5823 * The ID/IDREF checkings are done separately
5824 *
5825 * returns 1 if valid or 0 otherwise
5826 */
5827
5828int
5829xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5830 xmlNodePtr elem) {
5831 xmlElementPtr elemDecl = NULL;
5832 xmlElementContentPtr cont;
5833 xmlAttributePtr attr;
5834 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005835 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005836 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005837 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005838
5839 CHECK_DTD;
5840
5841 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005842 switch (elem->type) {
5843 case XML_ATTRIBUTE_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005844 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5845 "Attribute element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005846 return(0);
5847 case XML_TEXT_NODE:
5848 if (elem->children != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005849 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5850 "Text element has children !\n",
5851 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005852 return(0);
5853 }
5854 if (elem->properties != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005855 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5856 "Text element has attribute !\n",
5857 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005858 return(0);
5859 }
5860 if (elem->ns != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005861 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5862 "Text element has namespace !\n",
5863 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005864 return(0);
5865 }
5866 if (elem->nsDef != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005867 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5868 "Text element has namespace !\n",
5869 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005870 return(0);
5871 }
5872 if (elem->content == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005873 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5874 "Text element has no content !\n",
5875 NULL,NULL,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005876 return(0);
5877 }
5878 return(1);
5879 case XML_XINCLUDE_START:
5880 case XML_XINCLUDE_END:
5881 return(1);
5882 case XML_CDATA_SECTION_NODE:
5883 case XML_ENTITY_REF_NODE:
5884 case XML_PI_NODE:
5885 case XML_COMMENT_NODE:
5886 return(1);
5887 case XML_ENTITY_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005888 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5889 "Entity element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005890 return(0);
5891 case XML_NOTATION_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005892 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5893 "Notation element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005894 return(0);
5895 case XML_DOCUMENT_NODE:
5896 case XML_DOCUMENT_TYPE_NODE:
5897 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005898 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5899 "Document element not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005900 return(0);
5901 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005902 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5903 "HTML Document not expected\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005904 return(0);
5905 case XML_ELEMENT_NODE:
5906 break;
5907 default:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005908 xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR,
5909 "unknown element type\n", NULL, NULL ,NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005910 return(0);
5911 }
Owen Taylor3473f882001-02-23 17:55:21 +00005912
5913 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005914 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005915 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005916 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5917 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005918 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005919
Daniel Veillardea7751d2002-12-20 00:16:24 +00005920 /*
5921 * If vstateNr is not zero that means continuous validation is
5922 * activated, do not try to check the content model at that level.
5923 */
5924 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005925 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005926 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005927 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005928 xmlErrValidNode(ctxt, elem, XML_DTD_UNKNOWN_ELEM,
5929 "No declaration for element %s\n",
5930 elem->name, NULL, NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005931 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005932 case XML_ELEMENT_TYPE_EMPTY:
5933 if (elem->children != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005934 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_EMPTY,
Owen Taylor3473f882001-02-23 17:55:21 +00005935 "Element %s was declared EMPTY this one has content\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005936 elem->name, NULL, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005937 ret = 0;
5938 }
5939 break;
5940 case XML_ELEMENT_TYPE_ANY:
5941 /* I don't think anything is required then */
5942 break;
5943 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005944
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005945 /* simple case of declared as #PCDATA */
5946 if ((elemDecl->content != NULL) &&
5947 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5948 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5949 if (!ret) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005950 xmlErrValidNode(ctxt, elem, XML_DTD_NOT_PCDATA,
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005951 "Element %s was declared #PCDATA but contains non text nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00005952 elem->name, NULL, NULL);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005953 }
5954 break;
5955 }
Owen Taylor3473f882001-02-23 17:55:21 +00005956 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005957 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005958 while (child != NULL) {
5959 if (child->type == XML_ELEMENT_NODE) {
5960 name = child->name;
5961 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005962 xmlChar fn[50];
5963 xmlChar *fullname;
5964
5965 fullname = xmlBuildQName(child->name, child->ns->prefix,
5966 fn, 50);
5967 if (fullname == NULL)
5968 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005969 cont = elemDecl->content;
5970 while (cont != NULL) {
5971 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005972 if (xmlStrEqual(cont->name, fullname))
5973 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005974 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5975 (cont->c1 != NULL) &&
5976 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005977 if (xmlStrEqual(cont->c1->name, fullname))
5978 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005979 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5980 (cont->c1 == NULL) ||
5981 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00005982 xmlErrValid(NULL, XML_DTD_MIXED_CORRUPT,
5983 "Internal: MIXED struct corrupted\n",
5984 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00005985 break;
5986 }
5987 cont = cont->c2;
5988 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005989 if ((fullname != fn) && (fullname != child->name))
5990 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005991 if (cont != NULL)
5992 goto child_ok;
5993 }
5994 cont = elemDecl->content;
5995 while (cont != NULL) {
5996 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5997 if (xmlStrEqual(cont->name, name)) break;
5998 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5999 (cont->c1 != NULL) &&
6000 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
6001 if (xmlStrEqual(cont->c1->name, name)) break;
6002 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
6003 (cont->c1 == NULL) ||
6004 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00006005 xmlErrValid(ctxt, XML_DTD_MIXED_CORRUPT,
6006 "Internal: MIXED struct corrupted\n",
6007 NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006008 break;
6009 }
6010 cont = cont->c2;
6011 }
6012 if (cont == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006013 xmlErrValidNode(ctxt, elem, XML_DTD_INVALID_CHILD,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006014 "Element %s is not declared in %s list of possible children\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006015 name, elem->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006016 ret = 0;
6017 }
6018 }
6019child_ok:
6020 child = child->next;
6021 }
6022 break;
6023 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00006024 if ((doc->standalone == 1) && (extsubset == 1)) {
6025 /*
6026 * VC: Standalone Document Declaration
6027 * - element types with element content, if white space
6028 * occurs directly within any instance of those types.
6029 */
6030 child = elem->children;
6031 while (child != NULL) {
6032 if (child->type == XML_TEXT_NODE) {
6033 const xmlChar *content = child->content;
6034
William M. Brack76e95df2003-10-18 16:20:14 +00006035 while (IS_BLANK_CH(*content))
Daniel Veillard8dc16a62002-02-19 21:08:48 +00006036 content++;
6037 if (*content == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006038 xmlErrValidNode(ctxt, elem,
6039 XML_DTD_STANDALONE_WHITE_SPACE,
Daniel Veillard8dc16a62002-02-19 21:08:48 +00006040"standalone: %s declared in the external subset contains white spaces nodes\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006041 elem->name, NULL, NULL);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00006042 ret = 0;
6043 break;
6044 }
6045 }
6046 child =child->next;
6047 }
6048 }
Owen Taylor3473f882001-02-23 17:55:21 +00006049 child = elem->children;
6050 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00006051 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00006052 if (tmp <= 0)
6053 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00006054 break;
6055 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00006056 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00006057
6058 /* [ VC: Required Attribute ] */
6059 attr = elemDecl->attributes;
6060 while (attr != NULL) {
6061 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00006062 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00006063
Daniel Veillarde4301c82002-02-13 13:32:35 +00006064 if ((attr->prefix == NULL) &&
6065 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
6066 xmlNsPtr ns;
6067
6068 ns = elem->nsDef;
6069 while (ns != NULL) {
6070 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00006071 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00006072 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00006073 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00006074 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
6075 xmlNsPtr ns;
6076
6077 ns = elem->nsDef;
6078 while (ns != NULL) {
6079 if (xmlStrEqual(attr->name, ns->prefix))
6080 goto found;
6081 ns = ns->next;
6082 }
6083 } else {
6084 xmlAttrPtr attrib;
6085
6086 attrib = elem->properties;
6087 while (attrib != NULL) {
6088 if (xmlStrEqual(attrib->name, attr->name)) {
6089 if (attr->prefix != NULL) {
6090 xmlNsPtr nameSpace = attrib->ns;
6091
6092 if (nameSpace == NULL)
6093 nameSpace = elem->ns;
6094 /*
6095 * qualified names handling is problematic, having a
6096 * different prefix should be possible but DTDs don't
6097 * allow to define the URI instead of the prefix :-(
6098 */
6099 if (nameSpace == NULL) {
6100 if (qualified < 0)
6101 qualified = 0;
6102 } else if (!xmlStrEqual(nameSpace->prefix,
6103 attr->prefix)) {
6104 if (qualified < 1)
6105 qualified = 1;
6106 } else
6107 goto found;
6108 } else {
6109 /*
6110 * We should allow applications to define namespaces
6111 * for their application even if the DTD doesn't
6112 * carry one, otherwise, basically we would always
6113 * break.
6114 */
6115 goto found;
6116 }
6117 }
6118 attrib = attrib->next;
6119 }
Owen Taylor3473f882001-02-23 17:55:21 +00006120 }
6121 if (qualified == -1) {
6122 if (attr->prefix == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006123 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006124 "Element %s does not carry attribute %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006125 elem->name, attr->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006126 ret = 0;
6127 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006128 xmlErrValidNode(ctxt, elem, XML_DTD_MISSING_ATTRIBUTE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006129 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00006130 elem->name, attr->prefix,attr->name);
6131 ret = 0;
6132 }
6133 } else if (qualified == 0) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006134 xmlErrValidWarning(ctxt, elem, XML_DTD_NO_PREFIX,
Owen Taylor3473f882001-02-23 17:55:21 +00006135 "Element %s required attribute %s:%s has no prefix\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006136 elem->name, attr->prefix, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00006137 } else if (qualified == 1) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006138 xmlErrValidWarning(ctxt, elem, XML_DTD_DIFFERENT_PREFIX,
Owen Taylor3473f882001-02-23 17:55:21 +00006139 "Element %s required attribute %s:%s has different prefix\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006140 elem->name, attr->prefix, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00006141 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00006142 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
6143 /*
6144 * Special tests checking #FIXED namespace declarations
6145 * have the right value since this is not done as an
6146 * attribute checking
6147 */
6148 if ((attr->prefix == NULL) &&
6149 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
6150 xmlNsPtr ns;
6151
6152 ns = elem->nsDef;
6153 while (ns != NULL) {
6154 if (ns->prefix == NULL) {
6155 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006156 xmlErrValidNode(ctxt, elem,
6157 XML_DTD_ELEM_DEFAULT_NAMESPACE,
Daniel Veillarde4301c82002-02-13 13:32:35 +00006158 "Element %s namespace name for default namespace does not match the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006159 elem->name, NULL, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00006160 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00006161 }
6162 goto found;
6163 }
6164 ns = ns->next;
6165 }
6166 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
6167 xmlNsPtr ns;
6168
6169 ns = elem->nsDef;
6170 while (ns != NULL) {
6171 if (xmlStrEqual(attr->name, ns->prefix)) {
6172 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006173 xmlErrValidNode(ctxt, elem, XML_DTD_ELEM_NAMESPACE,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006174 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006175 elem->name, ns->prefix, NULL);
Daniel Veillardc7612992002-02-17 22:47:37 +00006176 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00006177 }
6178 goto found;
6179 }
6180 ns = ns->next;
6181 }
6182 }
Owen Taylor3473f882001-02-23 17:55:21 +00006183 }
6184found:
6185 attr = attr->nexth;
6186 }
6187 return(ret);
6188}
6189
6190/**
6191 * xmlValidateRoot:
6192 * @ctxt: the validation context
6193 * @doc: a document instance
6194 *
6195 * Try to validate a the root element
6196 * basically it does the following check as described by the
6197 * XML-1.0 recommendation:
6198 * - [ VC: Root Element Type ]
6199 * it doesn't try to recurse or apply other check to the element
6200 *
6201 * returns 1 if valid or 0 otherwise
6202 */
6203
6204int
6205xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6206 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00006207 int ret;
6208
Owen Taylor3473f882001-02-23 17:55:21 +00006209 if (doc == NULL) return(0);
6210
6211 root = xmlDocGetRootElement(doc);
6212 if ((root == NULL) || (root->name == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006213 xmlErrValid(ctxt, XML_DTD_NO_ROOT,
6214 "no root element\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006215 return(0);
6216 }
6217
6218 /*
6219 * When doing post validation against a separate DTD, those may
6220 * no internal subset has been generated
6221 */
6222 if ((doc->intSubset != NULL) &&
6223 (doc->intSubset->name != NULL)) {
6224 /*
6225 * Check first the document root against the NQName
6226 */
6227 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
6228 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00006229 xmlChar fn[50];
6230 xmlChar *fullname;
6231
6232 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
6233 if (fullname == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00006234 xmlVErrMemory(ctxt, NULL);
Daniel Veillardc00cda82003-04-07 10:22:39 +00006235 return(0);
6236 }
6237 ret = xmlStrEqual(doc->intSubset->name, fullname);
6238 if ((fullname != fn) && (fullname != root->name))
6239 xmlFree(fullname);
6240 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00006241 goto name_ok;
6242 }
6243 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
6244 (xmlStrEqual(root->name, BAD_CAST "html")))
6245 goto name_ok;
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006246 xmlErrValidNode(ctxt, root, XML_DTD_ROOT_NAME,
6247 "root and DTD name do not match '%s' and '%s'\n",
6248 root->name, doc->intSubset->name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006249 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00006250 }
6251 }
6252name_ok:
6253 return(1);
6254}
6255
6256
6257/**
6258 * xmlValidateElement:
6259 * @ctxt: the validation context
6260 * @doc: a document instance
6261 * @elem: an element instance
6262 *
6263 * Try to validate the subtree under an element
6264 *
6265 * returns 1 if valid or 0 otherwise
6266 */
6267
6268int
6269xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
6270 xmlNodePtr child;
6271 xmlAttrPtr attr;
Daniel Veillarde133dd82003-10-30 10:42:20 +00006272 xmlNsPtr ns;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006273 const xmlChar *value;
Owen Taylor3473f882001-02-23 17:55:21 +00006274 int ret = 1;
6275
6276 if (elem == NULL) return(0);
6277
6278 /*
6279 * XInclude elements were added after parsing in the infoset,
6280 * they don't really mean anything validation wise.
6281 */
6282 if ((elem->type == XML_XINCLUDE_START) ||
6283 (elem->type == XML_XINCLUDE_END))
6284 return(1);
6285
6286 CHECK_DTD;
6287
Daniel Veillard10ea86c2001-06-20 13:55:33 +00006288 /*
6289 * Entities references have to be handled separately
6290 */
6291 if (elem->type == XML_ENTITY_REF_NODE) {
6292 return(1);
6293 }
6294
Owen Taylor3473f882001-02-23 17:55:21 +00006295 ret &= xmlValidateOneElement(ctxt, doc, elem);
6296 attr = elem->properties;
Daniel Veillarde133dd82003-10-30 10:42:20 +00006297 while (attr != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00006298 value = xmlNodeListGetString(doc, attr->children, 0);
6299 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
6300 if (value != NULL)
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006301 xmlFree((char *)value);
Owen Taylor3473f882001-02-23 17:55:21 +00006302 attr= attr->next;
6303 }
Daniel Veillarde133dd82003-10-30 10:42:20 +00006304 ns = elem->nsDef;
6305 while (ns != NULL) {
Daniel Veillard1f5c9892003-12-29 17:09:55 +00006306 if (elem->ns == NULL)
6307 ret &= xmlValidateOneNamespace(ctxt, doc, elem, NULL,
6308 ns, ns->href);
6309 else
6310 ret &= xmlValidateOneNamespace(ctxt, doc, elem, elem->ns->prefix,
6311 ns, ns->href);
Daniel Veillarde133dd82003-10-30 10:42:20 +00006312 ns = ns->next;
6313 }
Owen Taylor3473f882001-02-23 17:55:21 +00006314 child = elem->children;
6315 while (child != NULL) {
6316 ret &= xmlValidateElement(ctxt, doc, child);
6317 child = child->next;
6318 }
6319
6320 return(ret);
6321}
6322
Daniel Veillard8730c562001-02-26 10:49:57 +00006323/**
6324 * xmlValidateRef:
6325 * @ref: A reference to be validated
6326 * @ctxt: Validation context
6327 * @name: Name of ID we are searching for
6328 *
6329 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006330static void
Daniel Veillard8730c562001-02-26 10:49:57 +00006331xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00006332 const xmlChar *name) {
6333 xmlAttrPtr id;
6334 xmlAttrPtr attr;
6335
6336 if (ref == NULL)
6337 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006338 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00006339 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006340 attr = ref->attr;
6341 if (attr == NULL) {
6342 xmlChar *dup, *str = NULL, *cur, save;
6343
6344 dup = xmlStrdup(name);
6345 if (dup == NULL) {
6346 ctxt->valid = 0;
6347 return;
6348 }
6349 cur = dup;
6350 while (*cur != 0) {
6351 str = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00006352 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006353 save = *cur;
6354 *cur = 0;
6355 id = xmlGetID(ctxt->doc, str);
6356 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006357 xmlErrValidNodeNr(ctxt, NULL, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006358 "attribute %s line %d references an unknown ID \"%s\"\n",
6359 ref->name, ref->lineno, str);
6360 ctxt->valid = 0;
6361 }
6362 if (save == 0)
6363 break;
6364 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00006365 while (IS_BLANK_CH(*cur)) cur++;
Daniel Veillardea7751d2002-12-20 00:16:24 +00006366 }
6367 xmlFree(dup);
6368 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00006369 id = xmlGetID(ctxt->doc, name);
6370 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006371 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006372 "IDREF attribute %s references an unknown ID \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006373 attr->name, name, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006374 ctxt->valid = 0;
6375 }
6376 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
6377 xmlChar *dup, *str = NULL, *cur, save;
6378
6379 dup = xmlStrdup(name);
6380 if (dup == NULL) {
Daniel Veillardce9457f2003-10-05 21:33:18 +00006381 xmlVErrMemory(ctxt, "IDREFS split");
Owen Taylor3473f882001-02-23 17:55:21 +00006382 ctxt->valid = 0;
6383 return;
6384 }
6385 cur = dup;
6386 while (*cur != 0) {
6387 str = cur;
William M. Brack76e95df2003-10-18 16:20:14 +00006388 while ((*cur != 0) && (!IS_BLANK_CH(*cur))) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00006389 save = *cur;
6390 *cur = 0;
6391 id = xmlGetID(ctxt->doc, str);
6392 if (id == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006393 xmlErrValidNode(ctxt, attr->parent, XML_DTD_UNKNOWN_ID,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006394 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006395 attr->name, str, NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006396 ctxt->valid = 0;
6397 }
6398 if (save == 0)
6399 break;
6400 *cur = save;
William M. Brack76e95df2003-10-18 16:20:14 +00006401 while (IS_BLANK_CH(*cur)) cur++;
Owen Taylor3473f882001-02-23 17:55:21 +00006402 }
6403 xmlFree(dup);
6404 }
6405}
6406
6407/**
Daniel Veillard8730c562001-02-26 10:49:57 +00006408 * xmlWalkValidateList:
6409 * @data: Contents of current link
6410 * @user: Value supplied by the user
6411 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00006412 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00006413 */
6414static int
6415xmlWalkValidateList(const void *data, const void *user)
6416{
6417 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
6418 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
6419 return 1;
6420}
6421
6422/**
6423 * xmlValidateCheckRefCallback:
6424 * @ref_list: List of references
6425 * @ctxt: Validation context
6426 * @name: Name of ID we are searching for
6427 *
6428 */
6429static void
6430xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
6431 const xmlChar *name) {
6432 xmlValidateMemo memo;
6433
6434 if (ref_list == NULL)
6435 return;
6436 memo.ctxt = ctxt;
6437 memo.name = name;
6438
6439 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
6440
6441}
6442
6443/**
Owen Taylor3473f882001-02-23 17:55:21 +00006444 * xmlValidateDocumentFinal:
6445 * @ctxt: the validation context
6446 * @doc: a document instance
6447 *
6448 * Does the final step for the document validation once all the
6449 * incremental validation steps have been completed
6450 *
6451 * basically it does the following checks described by the XML Rec
6452 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006453 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00006454 *
6455 * returns 1 if valid or 0 otherwise
6456 */
6457
6458int
6459xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6460 xmlRefTablePtr table;
6461
Daniel Veillardc0be74b2004-11-03 19:16:55 +00006462 if (ctxt == NULL)
6463 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00006464 if (doc == NULL) {
Daniel Veillard2b8c4a12003-10-02 22:28:19 +00006465 xmlErrValid(ctxt, XML_DTD_NO_DOC,
6466 "xmlValidateDocumentFinal: doc == NULL\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006467 return(0);
6468 }
6469
6470 /*
6471 * Check all the NOTATION/NOTATIONS attributes
6472 */
6473 /*
6474 * Check all the ENTITY/ENTITIES attributes definition for validity
6475 */
6476 /*
6477 * Check all the IDREF/IDREFS attributes definition for validity
6478 */
6479 table = (xmlRefTablePtr) doc->refs;
6480 ctxt->doc = doc;
6481 ctxt->valid = 1;
6482 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6483 return(ctxt->valid);
6484}
6485
6486/**
6487 * xmlValidateDtd:
6488 * @ctxt: the validation context
6489 * @doc: a document instance
6490 * @dtd: a dtd instance
6491 *
6492 * Try to validate the document against the dtd instance
6493 *
William M. Brack367df6e2004-10-23 18:14:36 +00006494 * Basically it does check all the definitions in the DtD.
6495 * Note the the internal subset (if present) is de-coupled
6496 * (i.e. not used), which could give problems if ID or IDREF
6497 * is present.
Owen Taylor3473f882001-02-23 17:55:21 +00006498 *
6499 * returns 1 if valid or 0 otherwise
6500 */
6501
6502int
6503xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6504 int ret;
William M. Brack367df6e2004-10-23 18:14:36 +00006505 xmlDtdPtr oldExt, oldInt;
Owen Taylor3473f882001-02-23 17:55:21 +00006506 xmlNodePtr root;
6507
6508 if (dtd == NULL) return(0);
6509 if (doc == NULL) return(0);
6510 oldExt = doc->extSubset;
William M. Brack367df6e2004-10-23 18:14:36 +00006511 oldInt = doc->intSubset;
Owen Taylor3473f882001-02-23 17:55:21 +00006512 doc->extSubset = dtd;
William M. Brack367df6e2004-10-23 18:14:36 +00006513 doc->intSubset = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00006514 ret = xmlValidateRoot(ctxt, doc);
6515 if (ret == 0) {
6516 doc->extSubset = oldExt;
William M. Brack367df6e2004-10-23 18:14:36 +00006517 doc->intSubset = oldInt;
Owen Taylor3473f882001-02-23 17:55:21 +00006518 return(ret);
6519 }
6520 if (doc->ids != NULL) {
6521 xmlFreeIDTable(doc->ids);
6522 doc->ids = NULL;
6523 }
6524 if (doc->refs != NULL) {
6525 xmlFreeRefTable(doc->refs);
6526 doc->refs = NULL;
6527 }
6528 root = xmlDocGetRootElement(doc);
6529 ret = xmlValidateElement(ctxt, doc, root);
6530 ret &= xmlValidateDocumentFinal(ctxt, doc);
6531 doc->extSubset = oldExt;
William M. Brack367df6e2004-10-23 18:14:36 +00006532 doc->intSubset = oldInt;
Owen Taylor3473f882001-02-23 17:55:21 +00006533 return(ret);
6534}
6535
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006536static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006537xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6538 const xmlChar *name ATTRIBUTE_UNUSED) {
6539 if (cur == NULL)
6540 return;
6541 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6542 xmlChar *notation = cur->content;
6543
Daniel Veillard878eab02002-02-19 13:46:09 +00006544 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006545 int ret;
6546
6547 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6548 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006549 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006550 }
6551 }
6552 }
6553}
6554
6555static void
Owen Taylor3473f882001-02-23 17:55:21 +00006556xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006557 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006558 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006559 xmlDocPtr doc;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006560 xmlElementPtr elem = NULL;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006561
Owen Taylor3473f882001-02-23 17:55:21 +00006562 if (cur == NULL)
6563 return;
6564 switch (cur->atype) {
6565 case XML_ATTRIBUTE_CDATA:
6566 case XML_ATTRIBUTE_ID:
6567 case XML_ATTRIBUTE_IDREF :
6568 case XML_ATTRIBUTE_IDREFS:
6569 case XML_ATTRIBUTE_NMTOKEN:
6570 case XML_ATTRIBUTE_NMTOKENS:
6571 case XML_ATTRIBUTE_ENUMERATION:
6572 break;
6573 case XML_ATTRIBUTE_ENTITY:
6574 case XML_ATTRIBUTE_ENTITIES:
6575 case XML_ATTRIBUTE_NOTATION:
6576 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006577
6578 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6579 cur->atype, cur->defaultValue);
6580 if ((ret == 0) && (ctxt->valid == 1))
6581 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006582 }
6583 if (cur->tree != NULL) {
6584 xmlEnumerationPtr tree = cur->tree;
6585 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006586 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006587 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006588 if ((ret == 0) && (ctxt->valid == 1))
6589 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006590 tree = tree->next;
6591 }
6592 }
6593 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006594 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6595 doc = cur->doc;
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006596 if (cur->elem == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006597 xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR,
Daniel Veillard878eab02002-02-19 13:46:09 +00006598 "xmlValidateAttributeCallback(%s): internal error\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006599 (const char *) cur->name);
Daniel Veillard878eab02002-02-19 13:46:09 +00006600 return;
6601 }
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006602
6603 if (doc != NULL)
6604 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6605 if ((elem == NULL) && (doc != NULL))
Daniel Veillard878eab02002-02-19 13:46:09 +00006606 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
Daniel Veillarda8ff65d2003-11-03 16:20:10 +00006607 if ((elem == NULL) && (cur->parent != NULL) &&
6608 (cur->parent->type == XML_DTD_NODE))
6609 elem = xmlGetDtdElementDesc((xmlDtdPtr) cur->parent, cur->elem);
Daniel Veillard878eab02002-02-19 13:46:09 +00006610 if (elem == NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006611 xmlErrValidNode(ctxt, NULL, XML_DTD_UNKNOWN_ELEM,
Daniel Veillard878eab02002-02-19 13:46:09 +00006612 "attribute %s: could not find decl for element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006613 cur->name, cur->elem, NULL);
Daniel Veillard878eab02002-02-19 13:46:09 +00006614 return;
6615 }
6616 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006617 xmlErrValidNode(ctxt, NULL, XML_DTD_EMPTY_NOTATION,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006618 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006619 cur->name, cur->elem, NULL);
Daniel Veillard878eab02002-02-19 13:46:09 +00006620 ctxt->valid = 0;
6621 }
6622 }
Owen Taylor3473f882001-02-23 17:55:21 +00006623}
6624
6625/**
6626 * xmlValidateDtdFinal:
6627 * @ctxt: the validation context
6628 * @doc: a document instance
6629 *
6630 * Does the final step for the dtds validation once all the
6631 * subsets have been parsed
6632 *
6633 * basically it does the following checks described by the XML Rec
6634 * - check that ENTITY and ENTITIES type attributes default or
6635 * possible values matches one of the defined entities.
6636 * - check that NOTATION type attributes default or
6637 * possible values matches one of the defined notations.
6638 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006639 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006640 */
6641
6642int
6643xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006644 xmlDtdPtr dtd;
6645 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006646 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006647
6648 if (doc == NULL) return(0);
6649 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6650 return(0);
6651 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006652 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006653 dtd = doc->intSubset;
6654 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6655 table = (xmlAttributeTablePtr) dtd->attributes;
6656 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006657 }
6658 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006659 entities = (xmlEntitiesTablePtr) dtd->entities;
6660 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6661 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006662 }
6663 dtd = doc->extSubset;
6664 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6665 table = (xmlAttributeTablePtr) dtd->attributes;
6666 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006667 }
6668 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006669 entities = (xmlEntitiesTablePtr) dtd->entities;
6670 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6671 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006672 }
6673 return(ctxt->valid);
6674}
6675
6676/**
6677 * xmlValidateDocument:
6678 * @ctxt: the validation context
6679 * @doc: a document instance
6680 *
6681 * Try to validate the document instance
6682 *
6683 * basically it does the all the checks described by the XML Rec
6684 * i.e. validates the internal and external subset (if present)
6685 * and validate the document tree.
6686 *
6687 * returns 1 if valid or 0 otherwise
6688 */
6689
6690int
6691xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6692 int ret;
6693 xmlNodePtr root;
6694
Daniel Veillardc0be74b2004-11-03 19:16:55 +00006695 if (doc == NULL)
6696 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006697 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006698 xmlErrValid(ctxt, XML_DTD_NO_DTD,
6699 "no DTD found!\n", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006700 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006701 }
Owen Taylor3473f882001-02-23 17:55:21 +00006702 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6703 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
William M. Brack8c22f9f2004-08-06 16:23:27 +00006704 xmlChar *sysID;
6705 if (doc->intSubset->SystemID != NULL) {
William M. Brackbebe7302004-08-05 06:46:47 +00006706 sysID = xmlBuildURI(doc->intSubset->SystemID,
6707 doc->URL);
William M. Brack8c22f9f2004-08-06 16:23:27 +00006708 if (sysID == NULL) {
6709 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
6710 "Could not build URI for external subset \"%s\"\n",
6711 (const char *) doc->intSubset->SystemID);
6712 return 0;
6713 }
6714 } else
William M. Brackbebe7302004-08-05 06:46:47 +00006715 sysID = NULL;
William M. Brack8c22f9f2004-08-06 16:23:27 +00006716 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
William M. Brackbebe7302004-08-05 06:46:47 +00006717 (const xmlChar *)sysID);
William M. Brackbebe7302004-08-05 06:46:47 +00006718 if (sysID != NULL)
6719 xmlFree(sysID);
Owen Taylor3473f882001-02-23 17:55:21 +00006720 if (doc->extSubset == NULL) {
6721 if (doc->intSubset->SystemID != NULL) {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006722 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00006723 "Could not load the external subset \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006724 (const char *) doc->intSubset->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00006725 } else {
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006726 xmlErrValid(ctxt, XML_DTD_LOAD_ERROR,
Owen Taylor3473f882001-02-23 17:55:21 +00006727 "Could not load the external subset \"%s\"\n",
Daniel Veillardbb5abab2003-10-03 22:21:51 +00006728 (const char *) doc->intSubset->ExternalID);
Owen Taylor3473f882001-02-23 17:55:21 +00006729 }
6730 return(0);
6731 }
6732 }
6733
6734 if (doc->ids != NULL) {
6735 xmlFreeIDTable(doc->ids);
6736 doc->ids = NULL;
6737 }
6738 if (doc->refs != NULL) {
6739 xmlFreeRefTable(doc->refs);
6740 doc->refs = NULL;
6741 }
6742 ret = xmlValidateDtdFinal(ctxt, doc);
6743 if (!xmlValidateRoot(ctxt, doc)) return(0);
6744
6745 root = xmlDocGetRootElement(doc);
6746 ret &= xmlValidateElement(ctxt, doc, root);
6747 ret &= xmlValidateDocumentFinal(ctxt, doc);
6748 return(ret);
6749}
6750
Owen Taylor3473f882001-02-23 17:55:21 +00006751/************************************************************************
6752 * *
6753 * Routines for dynamic validation editing *
6754 * *
6755 ************************************************************************/
6756
6757/**
6758 * xmlValidGetPotentialChildren:
6759 * @ctree: an element content tree
6760 * @list: an array to store the list of child names
6761 * @len: a pointer to the number of element in the list
6762 * @max: the size of the array
6763 *
6764 * Build/extend a list of potential children allowed by the content tree
6765 *
6766 * returns the number of element in the list, or -1 in case of error.
6767 */
6768
6769int
6770xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6771 int *len, int max) {
6772 int i;
6773
6774 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6775 return(-1);
6776 if (*len >= max) return(*len);
6777
6778 switch (ctree->type) {
6779 case XML_ELEMENT_CONTENT_PCDATA:
6780 for (i = 0; i < *len;i++)
6781 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6782 list[(*len)++] = BAD_CAST "#PCDATA";
6783 break;
6784 case XML_ELEMENT_CONTENT_ELEMENT:
6785 for (i = 0; i < *len;i++)
6786 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6787 list[(*len)++] = ctree->name;
6788 break;
6789 case XML_ELEMENT_CONTENT_SEQ:
6790 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6791 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6792 break;
6793 case XML_ELEMENT_CONTENT_OR:
6794 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6795 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6796 break;
6797 }
6798
6799 return(*len);
6800}
6801
William M. Brack9333cc22004-06-24 08:33:40 +00006802/*
6803 * Dummy function to suppress messages while we try out valid elements
6804 */
6805static void xmlNoValidityErr(void *ctx ATTRIBUTE_UNUSED,
6806 const char *msg ATTRIBUTE_UNUSED, ...) {
6807 return;
6808}
6809
Owen Taylor3473f882001-02-23 17:55:21 +00006810/**
6811 * xmlValidGetValidElements:
6812 * @prev: an element to insert after
6813 * @next: an element to insert next
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006814 * @names: an array to store the list of child names
Owen Taylor3473f882001-02-23 17:55:21 +00006815 * @max: the size of the array
6816 *
6817 * This function returns the list of authorized children to insert
6818 * within an existing tree while respecting the validity constraints
6819 * forced by the Dtd. The insertion point is defined using @prev and
6820 * @next in the following ways:
6821 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6822 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6823 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6824 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6825 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6826 *
6827 * pointers to the element names are inserted at the beginning of the array
6828 * and do not need to be freed.
6829 *
6830 * returns the number of element in the list, or -1 in case of error. If
6831 * the function returns the value @max the caller is invited to grow the
6832 * receiving array and retry.
6833 */
6834
6835int
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006836xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **names,
Owen Taylor3473f882001-02-23 17:55:21 +00006837 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006838 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006839 int nb_valid_elements = 0;
6840 const xmlChar *elements[256];
6841 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006842 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006843
6844 xmlNode *ref_node;
6845 xmlNode *parent;
6846 xmlNode *test_node;
6847
6848 xmlNode *prev_next;
6849 xmlNode *next_prev;
6850 xmlNode *parent_childs;
6851 xmlNode *parent_last;
6852
6853 xmlElement *element_desc;
6854
6855 if (prev == NULL && next == NULL)
6856 return(-1);
6857
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006858 if (names == NULL) return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00006859 if (max <= 0) return(-1);
6860
William M. Brack9333cc22004-06-24 08:33:40 +00006861 memset(&vctxt, 0, sizeof (xmlValidCtxt));
6862 vctxt.error = xmlNoValidityErr; /* this suppresses err/warn output */
6863
Owen Taylor3473f882001-02-23 17:55:21 +00006864 nb_valid_elements = 0;
6865 ref_node = prev ? prev : next;
6866 parent = ref_node->parent;
6867
6868 /*
6869 * Retrieves the parent element declaration
6870 */
6871 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6872 parent->name);
6873 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6874 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6875 parent->name);
6876 if (element_desc == NULL) return(-1);
6877
6878 /*
6879 * Do a backup of the current tree structure
6880 */
6881 prev_next = prev ? prev->next : NULL;
6882 next_prev = next ? next->prev : NULL;
6883 parent_childs = parent->children;
6884 parent_last = parent->last;
6885
6886 /*
6887 * Creates a dummy node and insert it into the tree
6888 */
Daniel Veillard95ddcd32004-10-26 21:53:55 +00006889 test_node = xmlNewDocNode (ref_node->doc, NULL, BAD_CAST "<!dummy?>", NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00006890 test_node->parent = parent;
6891 test_node->prev = prev;
6892 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006893 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006894
6895 if (prev) prev->next = test_node;
6896 else parent->children = test_node;
6897
6898 if (next) next->prev = test_node;
6899 else parent->last = test_node;
6900
6901 /*
6902 * Insert each potential child node and check if the parent is
6903 * still valid
6904 */
6905 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6906 elements, &nb_elements, 256);
6907
6908 for (i = 0;i < nb_elements;i++) {
6909 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006910 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006911 int j;
6912
6913 for (j = 0; j < nb_valid_elements;j++)
Daniel Veillardaecc0dc2004-05-08 02:32:07 +00006914 if (xmlStrEqual(elements[i], names[j])) break;
6915 names[nb_valid_elements++] = elements[i];
Owen Taylor3473f882001-02-23 17:55:21 +00006916 if (nb_valid_elements >= max) break;
6917 }
6918 }
6919
6920 /*
6921 * Restore the tree structure
6922 */
6923 if (prev) prev->next = prev_next;
6924 if (next) next->prev = next_prev;
6925 parent->children = parent_childs;
6926 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006927
6928 /*
6929 * Free up the dummy node
6930 */
6931 test_node->name = name;
6932 xmlFreeNode(test_node);
6933
Owen Taylor3473f882001-02-23 17:55:21 +00006934 return(nb_valid_elements);
6935}
Daniel Veillard4432df22003-09-28 18:58:27 +00006936#endif /* LIBXML_VALID_ENABLED */
6937
Daniel Veillard5d4644e2005-04-01 13:11:58 +00006938#define bottom_valid
6939#include "elfgcchack.h"