blob: 9dee4bbc9dffb89d2a081d3075a6576b94955954 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * valid.c : part of the code use to do the DTD handling and the validity
3 * checking
4 *
5 * See Copyright for the status of this software.
6 *
Daniel Veillardc5d64342001-06-24 12:13:24 +00007 * daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +00008 */
9
Daniel Veillard34ce8be2002-03-18 19:37:11 +000010#define IN_LIBXML
Bjorn Reese70a9da52001-04-21 16:57:29 +000011#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000012
Owen Taylor3473f882001-02-23 17:55:21 +000013#include <string.h>
14
15#ifdef HAVE_STDLIB_H
16#include <stdlib.h>
17#endif
18
19#include <libxml/xmlmemory.h>
20#include <libxml/hash.h>
21#include <libxml/valid.h>
22#include <libxml/parser.h>
23#include <libxml/parserInternals.h>
24#include <libxml/xmlerror.h>
25#include <libxml/list.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000026#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000027
Daniel Veillarde62d36c2001-05-15 08:53:16 +000028/* #define DEBUG_VALID_ALGO */
Daniel Veillard5acfd6b2002-09-18 16:29:02 +000029/* #define DEBUG_REGEXP_ALGO */
Daniel Veillarde62d36c2001-05-15 08:53:16 +000030
Daniel Veillarda646cfd2002-09-17 21:50:03 +000031#define TODO \
32 xmlGenericError(xmlGenericErrorContext, \
33 "Unimplemented block at %s:%d\n", \
34 __FILE__, __LINE__);
Owen Taylor3473f882001-02-23 17:55:21 +000035
Daniel Veillardea7751d2002-12-20 00:16:24 +000036#define VERROR \
37 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
Owen Taylor3473f882001-02-23 17:55:21 +000038
Daniel Veillardea7751d2002-12-20 00:16:24 +000039#define VWARNING \
40 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
41
42
43#ifdef LIBXML_REGEXP_ENABLED
44/*
45 * If regexp are enabled we can do continuous validation without the
46 * need of a tree to validate the content model. this is done in each
47 * callbacks.
48 * Each xmlValidState represent the validation state associated to the
49 * set of nodes currently open from the document root to the current element.
50 */
51
52
53typedef struct _xmlValidState {
54 xmlElementPtr elemDecl; /* pointer to the content model */
55 xmlNodePtr node; /* pointer to the current node */
56 xmlRegExecCtxtPtr exec; /* regexp runtime */
57} _xmlValidState;
58
59
60static int
61vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +000062 if ((ctxt->vstateMax == 0) || (ctxt->vstateTab == NULL)) {
Daniel Veillardea7751d2002-12-20 00:16:24 +000063 ctxt->vstateMax = 10;
64 ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax *
65 sizeof(ctxt->vstateTab[0]));
66 if (ctxt->vstateTab == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +000067 VERROR(ctxt->userData, "malloc failed !n");
Daniel Veillardea7751d2002-12-20 00:16:24 +000068 return(-1);
69 }
70 }
71
72 if (ctxt->vstateNr >= ctxt->vstateMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +000073 xmlValidState *tmp;
74
75 tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
76 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
77 if (tmp == NULL) {
Daniel Veillardea7751d2002-12-20 00:16:24 +000078 VERROR(ctxt->userData, "realloc failed !n");
79 return(-1);
80 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +000081 ctxt->vstateMax *= 2;
82 ctxt->vstateTab = tmp;
Daniel Veillardea7751d2002-12-20 00:16:24 +000083 }
84 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr];
85 ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl;
86 ctxt->vstateTab[ctxt->vstateNr].node = node;
87 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
88 if (elemDecl->contModel == NULL)
89 xmlValidBuildContentModel(ctxt, elemDecl);
90 if (elemDecl->contModel != NULL) {
91 ctxt->vstateTab[ctxt->vstateNr].exec =
92 xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
93 } else {
94 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
95 VERROR(ctxt->userData,
96 "Failed to build content model regexp for %s", node->name);
97 }
98 }
99 return(ctxt->vstateNr++);
100}
101
102static int
103vstateVPop(xmlValidCtxtPtr ctxt) {
104 xmlElementPtr elemDecl;
105
Daniel Veillard336fc7d2002-12-27 19:37:04 +0000106 if (ctxt->vstateNr < 1) return(-1);
Daniel Veillardea7751d2002-12-20 00:16:24 +0000107 ctxt->vstateNr--;
108 elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
109 ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
110 ctxt->vstateTab[ctxt->vstateNr].node = NULL;
111 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
112 xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
113 }
114 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
115 if (ctxt->vstateNr >= 1)
116 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
117 else
118 ctxt->vstate = NULL;
119 return(ctxt->vstateNr);
120}
121
122#else /* not LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000123/*
Daniel Veillard1c732d22002-11-30 11:22:59 +0000124 * If regexp are not enabled, it uses a home made algorithm less
125 * complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000126 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000127 * only restriction is on the deepness of the tree limited by the
128 * size of the occurs bitfield
129 *
130 * this is the content of a saved state for rollbacks
131 */
132
133#define ROLLBACK_OR 0
134#define ROLLBACK_PARENT 1
135
Daniel Veillardb44025c2001-10-11 22:55:55 +0000136typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000137 xmlElementContentPtr cont; /* pointer to the content model subtree */
138 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000139 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000140 unsigned char depth; /* current depth in the overall tree */
141 unsigned char state; /* ROLLBACK_XXX */
142} _xmlValidState;
143
Daniel Veillardfc57b412002-04-29 15:50:14 +0000144#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000145#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
146#define CONT ctxt->vstate->cont
147#define NODE ctxt->vstate->node
148#define DEPTH ctxt->vstate->depth
149#define OCCURS ctxt->vstate->occurs
150#define STATE ctxt->vstate->state
151
Daniel Veillard5344c602001-12-31 16:37:34 +0000152#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
153#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000154
Daniel Veillard5344c602001-12-31 16:37:34 +0000155#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
156#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000157
158static int
159vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
160 xmlNodePtr node, unsigned char depth, long occurs,
161 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000162 int i = ctxt->vstateNr - 1;
163
Daniel Veillard940492d2002-04-15 10:15:25 +0000164 if (ctxt->vstateNr > MAX_RECURSE) {
165 return(-1);
166 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000167 if (ctxt->vstateTab == NULL) {
168 ctxt->vstateMax = 8;
169 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
170 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
171 if (ctxt->vstateTab == NULL) {
172 xmlGenericError(xmlGenericErrorContext,
173 "malloc failed !n");
174 return(-1);
175 }
176 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000177 if (ctxt->vstateNr >= ctxt->vstateMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000178 xmlValidState *tmp;
179
180 tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
181 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
182 if (tmp == NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000183 xmlGenericError(xmlGenericErrorContext,
184 "realloc failed !n");
Daniel Veillard940492d2002-04-15 10:15:25 +0000185 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000186 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000187 ctxt->vstateMax *= 2;
188 ctxt->vstateTab = tmp;
Daniel Veillard06803992001-04-22 10:35:56 +0000189 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000190 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000191 /*
192 * Don't push on the stack a state already here
193 */
194 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
195 (ctxt->vstateTab[i].node == node) &&
196 (ctxt->vstateTab[i].depth == depth) &&
197 (ctxt->vstateTab[i].occurs == occurs) &&
198 (ctxt->vstateTab[i].state == state))
199 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000200 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
201 ctxt->vstateTab[ctxt->vstateNr].node = node;
202 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
203 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
204 ctxt->vstateTab[ctxt->vstateNr].state = state;
205 return(ctxt->vstateNr++);
206}
207
208static int
209vstateVPop(xmlValidCtxtPtr ctxt) {
210 if (ctxt->vstateNr <= 1) return(-1);
211 ctxt->vstateNr--;
212 ctxt->vstate = &ctxt->vstateTab[0];
213 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
214 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
215 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
216 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
217 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
218 return(ctxt->vstateNr);
219}
220
Daniel Veillard118aed72002-09-24 14:13:13 +0000221#endif /* LIBXML_REGEXP_ENABLED */
222
Daniel Veillard1c732d22002-11-30 11:22:59 +0000223static int
224nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value)
225{
226 if (ctxt->nodeMax <= 0) {
227 ctxt->nodeMax = 4;
228 ctxt->nodeTab =
229 (xmlNodePtr *) xmlMalloc(ctxt->nodeMax *
230 sizeof(ctxt->nodeTab[0]));
231 if (ctxt->nodeTab == NULL) {
232 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
233 ctxt->nodeMax = 0;
234 return (0);
235 }
236 }
237 if (ctxt->nodeNr >= ctxt->nodeMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000238 xmlNodePtr *tmp;
239 tmp = (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
240 ctxt->nodeMax * 2 * sizeof(ctxt->nodeTab[0]));
241 if (tmp == NULL) {
Daniel Veillard1c732d22002-11-30 11:22:59 +0000242 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
243 return (0);
244 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000245 ctxt->nodeMax *= 2;
246 ctxt->nodeTab = tmp;
Daniel Veillard1c732d22002-11-30 11:22:59 +0000247 }
248 ctxt->nodeTab[ctxt->nodeNr] = value;
249 ctxt->node = value;
250 return (ctxt->nodeNr++);
251}
252static xmlNodePtr
253nodeVPop(xmlValidCtxtPtr ctxt)
254{
255 xmlNodePtr ret;
256
257 if (ctxt->nodeNr <= 0)
258 return (0);
259 ctxt->nodeNr--;
260 if (ctxt->nodeNr > 0)
261 ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1];
262 else
263 ctxt->node = NULL;
264 ret = ctxt->nodeTab[ctxt->nodeNr];
265 ctxt->nodeTab[ctxt->nodeNr] = 0;
266 return (ret);
267}
Owen Taylor3473f882001-02-23 17:55:21 +0000268
Owen Taylor3473f882001-02-23 17:55:21 +0000269#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000270static void
271xmlValidPrintNode(xmlNodePtr cur) {
272 if (cur == NULL) {
273 xmlGenericError(xmlGenericErrorContext, "null");
274 return;
275 }
276 switch (cur->type) {
277 case XML_ELEMENT_NODE:
278 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
279 break;
280 case XML_TEXT_NODE:
281 xmlGenericError(xmlGenericErrorContext, "text ");
282 break;
283 case XML_CDATA_SECTION_NODE:
284 xmlGenericError(xmlGenericErrorContext, "cdata ");
285 break;
286 case XML_ENTITY_REF_NODE:
287 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
288 break;
289 case XML_PI_NODE:
290 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
291 break;
292 case XML_COMMENT_NODE:
293 xmlGenericError(xmlGenericErrorContext, "comment ");
294 break;
295 case XML_ATTRIBUTE_NODE:
296 xmlGenericError(xmlGenericErrorContext, "?attr? ");
297 break;
298 case XML_ENTITY_NODE:
299 xmlGenericError(xmlGenericErrorContext, "?ent? ");
300 break;
301 case XML_DOCUMENT_NODE:
302 xmlGenericError(xmlGenericErrorContext, "?doc? ");
303 break;
304 case XML_DOCUMENT_TYPE_NODE:
305 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
306 break;
307 case XML_DOCUMENT_FRAG_NODE:
308 xmlGenericError(xmlGenericErrorContext, "?frag? ");
309 break;
310 case XML_NOTATION_NODE:
311 xmlGenericError(xmlGenericErrorContext, "?nota? ");
312 break;
313 case XML_HTML_DOCUMENT_NODE:
314 xmlGenericError(xmlGenericErrorContext, "?html? ");
315 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000316#ifdef LIBXML_DOCB_ENABLED
317 case XML_DOCB_DOCUMENT_NODE:
318 xmlGenericError(xmlGenericErrorContext, "?docb? ");
319 break;
320#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000321 case XML_DTD_NODE:
322 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
323 break;
324 case XML_ELEMENT_DECL:
325 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
326 break;
327 case XML_ATTRIBUTE_DECL:
328 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
329 break;
330 case XML_ENTITY_DECL:
331 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
332 break;
333 case XML_NAMESPACE_DECL:
334 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
335 break;
336 case XML_XINCLUDE_START:
337 xmlGenericError(xmlGenericErrorContext, "incstart ");
338 break;
339 case XML_XINCLUDE_END:
340 xmlGenericError(xmlGenericErrorContext, "incend ");
341 break;
342 }
343}
344
345static void
346xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000347 if (cur == NULL)
348 xmlGenericError(xmlGenericErrorContext, "null ");
349 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000350 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000351 cur = cur->next;
352 }
353}
354
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000355static void
356xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Daniel Veillard83391282003-03-06 21:37:30 +0000357 char expr[5000];
Owen Taylor3473f882001-02-23 17:55:21 +0000358
359 expr[0] = 0;
360 xmlGenericError(xmlGenericErrorContext, "valid: ");
361 xmlValidPrintNodeList(cur);
362 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000363 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000364 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
365}
366
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000367static void
368xmlValidDebugState(xmlValidStatePtr state) {
369 xmlGenericError(xmlGenericErrorContext, "(");
370 if (state->cont == NULL)
371 xmlGenericError(xmlGenericErrorContext, "null,");
372 else
373 switch (state->cont->type) {
374 case XML_ELEMENT_CONTENT_PCDATA:
375 xmlGenericError(xmlGenericErrorContext, "pcdata,");
376 break;
377 case XML_ELEMENT_CONTENT_ELEMENT:
378 xmlGenericError(xmlGenericErrorContext, "%s,",
379 state->cont->name);
380 break;
381 case XML_ELEMENT_CONTENT_SEQ:
382 xmlGenericError(xmlGenericErrorContext, "seq,");
383 break;
384 case XML_ELEMENT_CONTENT_OR:
385 xmlGenericError(xmlGenericErrorContext, "or,");
386 break;
387 }
388 xmlValidPrintNode(state->node);
389 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
390 state->depth, state->occurs, state->state);
391}
392
393static void
394xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
395 int i, j;
396
397 xmlGenericError(xmlGenericErrorContext, "state: ");
398 xmlValidDebugState(ctxt->vstate);
399 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
400 ctxt->vstateNr - 1);
401 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
402 xmlValidDebugState(&ctxt->vstateTab[j]);
403 xmlGenericError(xmlGenericErrorContext, "\n");
404}
405
406/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000407#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000408 *****/
409
410#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000411#define DEBUG_VALID_MSG(m) \
412 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
413
Owen Taylor3473f882001-02-23 17:55:21 +0000414#else
415#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000416#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000417#endif
418
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000419/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000420
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000421#define VECTXT(ctxt, node) \
422 if ((ctxt != NULL) && (ctxt->error != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000423 (node != NULL)) { \
424 xmlChar *base = xmlNodeGetBase(NULL,node); \
425 if (base != NULL) { \
426 ctxt->error(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000427 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000428 xmlFree(base); \
429 } else \
430 ctxt->error(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000431 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000432 }
433
434#define VWCTXT(ctxt, node) \
435 if ((ctxt != NULL) && (ctxt->warning != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000436 (node != NULL)) { \
437 xmlChar *base = xmlNodeGetBase(NULL,node); \
438 if (base != NULL) { \
439 ctxt->warning(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000440 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000441 xmlFree(base); \
442 } else \
443 ctxt->warning(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000444 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000445 }
446
Owen Taylor3473f882001-02-23 17:55:21 +0000447#define CHECK_DTD \
448 if (doc == NULL) return(0); \
449 else if ((doc->intSubset == NULL) && \
450 (doc->extSubset == NULL)) return(0)
451
Daniel Veillarda10efa82001-04-18 13:09:01 +0000452static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
453 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000454xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
455
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000456#ifdef LIBXML_REGEXP_ENABLED
457
458/************************************************************************
459 * *
460 * Content model validation based on the regexps *
461 * *
462 ************************************************************************/
463
464/**
465 * xmlValidBuildAContentModel:
466 * @content: the content model
467 * @ctxt: the schema parser context
468 * @name: the element name whose content is being built
469 *
470 * Generate the automata sequence needed for that type
471 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000472 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000473 */
474static int
475xmlValidBuildAContentModel(xmlElementContentPtr content,
476 xmlValidCtxtPtr ctxt,
477 const xmlChar *name) {
478 if (content == NULL) {
479 VERROR(ctxt->userData,
480 "Found unexpected type = NULL in %s content model\n", name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000481 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000482 }
483 switch (content->type) {
484 case XML_ELEMENT_CONTENT_PCDATA:
485 VERROR(ctxt->userData, "ContentModel found PCDATA for element %s\n",
486 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000487 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000488 break;
489 case XML_ELEMENT_CONTENT_ELEMENT: {
490 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillardc00cda82003-04-07 10:22:39 +0000491 xmlChar fn[50];
492 xmlChar *fullname;
493
494 fullname = xmlBuildQName(content->name, content->prefix, fn, 50);
495 if (fullname == NULL) {
496 VERROR(ctxt->userData, "Out of memory\n");
497 return(0);
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000498 }
499
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000500 switch (content->ocur) {
501 case XML_ELEMENT_CONTENT_ONCE:
502 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000503 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000504 break;
505 case XML_ELEMENT_CONTENT_OPT:
506 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000507 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000508 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
509 break;
510 case XML_ELEMENT_CONTENT_PLUS:
511 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000512 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000513 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000514 ctxt->state, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000515 break;
516 case XML_ELEMENT_CONTENT_MULT:
517 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000518 ctxt->state, fullname, NULL);
Daniel Veillard57e79b32003-02-04 15:33:12 +0000519 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, ctxt->state,
520 NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000521 break;
522 }
Daniel Veillardc00cda82003-04-07 10:22:39 +0000523 if ((fullname != fn) && (fullname != content->name))
524 xmlFree(fullname);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000525 break;
526 }
527 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000528 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000529 xmlElementContentOccur ocur;
530
531 /*
532 * Simply iterate over the content
533 */
534 oldstate = ctxt->state;
535 ocur = content->ocur;
Daniel Veillardf4be0182003-02-24 19:54:33 +0000536 if (ocur != XML_ELEMENT_CONTENT_ONCE) {
537 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
538 oldstate = ctxt->state;
539 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000540 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000541 xmlValidBuildAContentModel(content->c1, ctxt, name);
542 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000543 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
544 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
545 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000546 oldend = ctxt->state;
547 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000548 switch (ocur) {
549 case XML_ELEMENT_CONTENT_ONCE:
550 break;
551 case XML_ELEMENT_CONTENT_OPT:
552 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
553 break;
554 case XML_ELEMENT_CONTENT_MULT:
555 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000556 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000557 break;
558 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000559 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000560 break;
561 }
562 break;
563 }
564 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000565 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000566 xmlElementContentOccur ocur;
567
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000568 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000569 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
570 (ocur == XML_ELEMENT_CONTENT_MULT)) {
571 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
572 ctxt->state, NULL);
573 }
574 oldstate = ctxt->state;
575 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000576
577 /*
578 * iterate over the subtypes and remerge the end with an
579 * epsilon transition
580 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000581 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000582 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000583 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000584 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000585 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000586 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
587 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000588 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000589 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000590 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
591 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000592 switch (ocur) {
593 case XML_ELEMENT_CONTENT_ONCE:
594 break;
595 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000596 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000597 break;
598 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000599 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
600 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000601 break;
602 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000603 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000604 break;
605 }
606 break;
607 }
608 default:
609 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
610 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000611 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000612 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000613 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000614}
615/**
616 * xmlValidBuildContentModel:
617 * @ctxt: a validation context
618 * @elem: an element declaration node
619 *
620 * (Re)Build the automata associated to the content model of this
621 * element
622 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000623 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000624 */
625int
626xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000627
628 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000629 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000630 if (elem->type != XML_ELEMENT_DECL)
631 return(0);
632 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
633 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000634 /* TODO: should we rebuild in this case ? */
Daniel Veillardec498e12003-02-05 11:01:50 +0000635 if (elem->contModel != NULL) {
636 if (!xmlRegexpIsDeterminist(elem->contModel)) {
637 ctxt->valid = 0;
638 return(0);
639 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000640 return(1);
Daniel Veillardec498e12003-02-05 11:01:50 +0000641 }
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000642
643 ctxt->am = xmlNewAutomata();
644 if (ctxt->am == NULL) {
645 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
646 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000647 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000648 }
William M. Brack78637da2003-07-31 14:47:38 +0000649 ctxt->state = xmlAutomataGetInitState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000650 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
651 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000652 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000653 if (xmlRegexpIsDeterminist(elem->contModel) != 1) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000654 char expr[5000];
655 expr[0] = 0;
656 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
657 VERROR(ctxt->userData, "Content model of %s is not determinist: %s\n",
658 elem->name, expr);
659#ifdef DEBUG_REGEXP_ALGO
660 xmlRegexpPrint(stderr, elem->contModel);
661#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000662 ctxt->valid = 0;
Daniel Veillardec498e12003-02-05 11:01:50 +0000663 ctxt->state = NULL;
664 xmlFreeAutomata(ctxt->am);
665 ctxt->am = NULL;
666 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000667 }
668 ctxt->state = NULL;
669 xmlFreeAutomata(ctxt->am);
670 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000671 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000672}
673
674#endif /* LIBXML_REGEXP_ENABLED */
675
Owen Taylor3473f882001-02-23 17:55:21 +0000676/****************************************************************
677 * *
678 * Util functions for data allocation/deallocation *
679 * *
680 ****************************************************************/
681
682/**
Daniel Veillarda37aab82003-06-09 09:10:36 +0000683 * xmlNewValidCtxt:
684 *
685 * Allocate a validation context structure.
686 *
687 * Returns NULL if not, otherwise the new validation context structure
688 */
689xmlValidCtxtPtr
690xmlNewValidCtxt(void) {
691 xmlValidCtxtPtr ret;
692
693 if ((ret = xmlMalloc(sizeof (xmlValidCtxt))) == NULL)
694 return (NULL);
695
696 (void) memset(ret, 0, sizeof (xmlValidCtxt));
697
698 return (ret);
699}
700
701/**
702 * xmlFreeValidCtxt:
703 * @cur: the validation context to free
704 *
705 * Free a validation context structure.
706 */
707void
708xmlFreeValidCtxt(xmlValidCtxtPtr cur) {
709 xmlFree(cur);
710}
711
712/**
Owen Taylor3473f882001-02-23 17:55:21 +0000713 * xmlNewElementContent:
714 * @name: the subelement name or NULL
715 * @type: the type of element content decl
716 *
717 * Allocate an element content structure.
718 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000719 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000720 */
721xmlElementContentPtr
722xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
723 xmlElementContentPtr ret;
724
725 switch(type) {
726 case XML_ELEMENT_CONTENT_ELEMENT:
727 if (name == NULL) {
728 xmlGenericError(xmlGenericErrorContext,
729 "xmlNewElementContent : name == NULL !\n");
730 }
731 break;
732 case XML_ELEMENT_CONTENT_PCDATA:
733 case XML_ELEMENT_CONTENT_SEQ:
734 case XML_ELEMENT_CONTENT_OR:
735 if (name != NULL) {
736 xmlGenericError(xmlGenericErrorContext,
737 "xmlNewElementContent : name != NULL !\n");
738 }
739 break;
740 default:
741 xmlGenericError(xmlGenericErrorContext,
742 "xmlNewElementContent: unknown type %d\n", type);
743 return(NULL);
744 }
745 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
746 if (ret == NULL) {
747 xmlGenericError(xmlGenericErrorContext,
748 "xmlNewElementContent : out of memory!\n");
749 return(NULL);
750 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000751 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000752 ret->type = type;
753 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000754 if (name != NULL) {
755 xmlChar *prefix = NULL;
756 ret->name = xmlSplitQName2(name, &prefix);
757 if (ret->name == NULL)
758 ret->name = xmlStrdup(name);
759 ret->prefix = prefix;
760 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000761 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000762 ret->prefix = NULL;
763 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000764 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000765 return(ret);
766}
767
768/**
769 * xmlCopyElementContent:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000770 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +0000771 *
772 * Build a copy of an element content description.
773 *
774 * Returns the new xmlElementContentPtr or NULL in case of error.
775 */
776xmlElementContentPtr
777xmlCopyElementContent(xmlElementContentPtr cur) {
778 xmlElementContentPtr ret;
779
780 if (cur == NULL) return(NULL);
781 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
782 if (ret == NULL) {
783 xmlGenericError(xmlGenericErrorContext,
784 "xmlCopyElementContent : out of memory\n");
785 return(NULL);
786 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000787 if (cur->prefix != NULL)
788 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000789 ret->ocur = cur->ocur;
790 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000791 if (ret->c1 != NULL)
792 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000793 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000794 if (ret->c2 != NULL)
795 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000796 return(ret);
797}
798
799/**
800 * xmlFreeElementContent:
801 * @cur: the element content tree to free
802 *
803 * Free an element content structure. This is a recursive call !
804 */
805void
806xmlFreeElementContent(xmlElementContentPtr cur) {
807 if (cur == NULL) return;
808 switch (cur->type) {
809 case XML_ELEMENT_CONTENT_PCDATA:
810 case XML_ELEMENT_CONTENT_ELEMENT:
811 case XML_ELEMENT_CONTENT_SEQ:
812 case XML_ELEMENT_CONTENT_OR:
813 break;
814 default:
815 xmlGenericError(xmlGenericErrorContext,
816 "xmlFreeElementContent : type %d\n", cur->type);
817 return;
818 }
819 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
820 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
821 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000822 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000823 xmlFree(cur);
824}
825
826/**
827 * xmlDumpElementContent:
828 * @buf: An XML buffer
829 * @content: An element table
830 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
831 *
832 * This will dump the content of the element table as an XML DTD definition
833 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000834static void
Owen Taylor3473f882001-02-23 17:55:21 +0000835xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
836 if (content == NULL) return;
837
838 if (glob) xmlBufferWriteChar(buf, "(");
839 switch (content->type) {
840 case XML_ELEMENT_CONTENT_PCDATA:
841 xmlBufferWriteChar(buf, "#PCDATA");
842 break;
843 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000844 if (content->prefix != NULL) {
845 xmlBufferWriteCHAR(buf, content->prefix);
846 xmlBufferWriteChar(buf, ":");
847 }
Owen Taylor3473f882001-02-23 17:55:21 +0000848 xmlBufferWriteCHAR(buf, content->name);
849 break;
850 case XML_ELEMENT_CONTENT_SEQ:
851 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
852 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
853 xmlDumpElementContent(buf, content->c1, 1);
854 else
855 xmlDumpElementContent(buf, content->c1, 0);
856 xmlBufferWriteChar(buf, " , ");
857 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
858 xmlDumpElementContent(buf, content->c2, 1);
859 else
860 xmlDumpElementContent(buf, content->c2, 0);
861 break;
862 case XML_ELEMENT_CONTENT_OR:
863 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
864 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
865 xmlDumpElementContent(buf, content->c1, 1);
866 else
867 xmlDumpElementContent(buf, content->c1, 0);
868 xmlBufferWriteChar(buf, " | ");
869 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
870 xmlDumpElementContent(buf, content->c2, 1);
871 else
872 xmlDumpElementContent(buf, content->c2, 0);
873 break;
874 default:
875 xmlGenericError(xmlGenericErrorContext,
876 "xmlDumpElementContent: unknown type %d\n",
877 content->type);
878 }
879 if (glob)
880 xmlBufferWriteChar(buf, ")");
881 switch (content->ocur) {
882 case XML_ELEMENT_CONTENT_ONCE:
883 break;
884 case XML_ELEMENT_CONTENT_OPT:
885 xmlBufferWriteChar(buf, "?");
886 break;
887 case XML_ELEMENT_CONTENT_MULT:
888 xmlBufferWriteChar(buf, "*");
889 break;
890 case XML_ELEMENT_CONTENT_PLUS:
891 xmlBufferWriteChar(buf, "+");
892 break;
893 }
894}
895
896/**
897 * xmlSprintfElementContent:
898 * @buf: an output buffer
899 * @content: An element table
900 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
901 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000902 * Deprecated, unsafe, use xmlSnprintfElementContent
903 */
904void
905xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
906 xmlElementContentPtr content ATTRIBUTE_UNUSED,
907 int glob ATTRIBUTE_UNUSED) {
908}
909
910/**
911 * xmlSnprintfElementContent:
912 * @buf: an output buffer
913 * @size: the buffer size
914 * @content: An element table
915 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
916 *
Owen Taylor3473f882001-02-23 17:55:21 +0000917 * This will dump the content of the element content definition
918 * Intended just for the debug routine
919 */
920void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000921xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
922 int len;
923
Owen Taylor3473f882001-02-23 17:55:21 +0000924 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000925 len = strlen(buf);
926 if (size - len < 50) {
927 if ((size - len > 4) && (buf[len - 1] != '.'))
928 strcat(buf, " ...");
929 return;
930 }
Owen Taylor3473f882001-02-23 17:55:21 +0000931 if (glob) strcat(buf, "(");
932 switch (content->type) {
933 case XML_ELEMENT_CONTENT_PCDATA:
934 strcat(buf, "#PCDATA");
935 break;
936 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000937 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000938 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000939 strcat(buf, " ...");
940 return;
941 }
942 strcat(buf, (char *) content->prefix);
943 strcat(buf, ":");
944 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000945 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000946 strcat(buf, " ...");
947 return;
948 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000949 if (content->name != NULL)
950 strcat(buf, (char *) content->name);
Owen Taylor3473f882001-02-23 17:55:21 +0000951 break;
952 case XML_ELEMENT_CONTENT_SEQ:
953 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
954 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000955 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000956 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000957 xmlSnprintfElementContent(buf, size, content->c1, 0);
958 len = strlen(buf);
959 if (size - len < 50) {
960 if ((size - len > 4) && (buf[len - 1] != '.'))
961 strcat(buf, " ...");
962 return;
963 }
Owen Taylor3473f882001-02-23 17:55:21 +0000964 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000965 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
966 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
967 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000968 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000969 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000970 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000971 break;
972 case XML_ELEMENT_CONTENT_OR:
973 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
974 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000975 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000976 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000977 xmlSnprintfElementContent(buf, size, content->c1, 0);
978 len = strlen(buf);
979 if (size - len < 50) {
980 if ((size - len > 4) && (buf[len - 1] != '.'))
981 strcat(buf, " ...");
982 return;
983 }
Owen Taylor3473f882001-02-23 17:55:21 +0000984 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000985 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
986 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
987 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000988 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000989 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000990 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000991 break;
992 }
993 if (glob)
994 strcat(buf, ")");
995 switch (content->ocur) {
996 case XML_ELEMENT_CONTENT_ONCE:
997 break;
998 case XML_ELEMENT_CONTENT_OPT:
999 strcat(buf, "?");
1000 break;
1001 case XML_ELEMENT_CONTENT_MULT:
1002 strcat(buf, "*");
1003 break;
1004 case XML_ELEMENT_CONTENT_PLUS:
1005 strcat(buf, "+");
1006 break;
1007 }
1008}
1009
1010/****************************************************************
1011 * *
1012 * Registration of DTD declarations *
1013 * *
1014 ****************************************************************/
1015
1016/**
1017 * xmlCreateElementTable:
1018 *
1019 * create and initialize an empty element hash table.
1020 *
1021 * Returns the xmlElementTablePtr just created or NULL in case of error.
1022 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001023static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001024xmlCreateElementTable(void) {
1025 return(xmlHashCreate(0));
1026}
1027
1028/**
1029 * xmlFreeElement:
1030 * @elem: An element
1031 *
1032 * Deallocate the memory used by an element definition
1033 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001034static void
Owen Taylor3473f882001-02-23 17:55:21 +00001035xmlFreeElement(xmlElementPtr elem) {
1036 if (elem == NULL) return;
1037 xmlUnlinkNode((xmlNodePtr) elem);
1038 xmlFreeElementContent(elem->content);
1039 if (elem->name != NULL)
1040 xmlFree((xmlChar *) elem->name);
1041 if (elem->prefix != NULL)
1042 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001043#ifdef LIBXML_REGEXP_ENABLED
1044 if (elem->contModel != NULL)
1045 xmlRegFreeRegexp(elem->contModel);
1046#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001047 xmlFree(elem);
1048}
1049
1050
1051/**
1052 * xmlAddElementDecl:
1053 * @ctxt: the validation context
1054 * @dtd: pointer to the DTD
1055 * @name: the entity name
1056 * @type: the element type
1057 * @content: the element content tree or NULL
1058 *
1059 * Register a new element declaration
1060 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001061 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001062 */
1063xmlElementPtr
1064xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
1065 xmlElementTypeVal type,
1066 xmlElementContentPtr content) {
1067 xmlElementPtr ret;
1068 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001069 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001070 xmlChar *ns, *uqname;
1071
1072 if (dtd == NULL) {
1073 xmlGenericError(xmlGenericErrorContext,
1074 "xmlAddElementDecl: dtd == NULL\n");
1075 return(NULL);
1076 }
1077 if (name == NULL) {
1078 xmlGenericError(xmlGenericErrorContext,
1079 "xmlAddElementDecl: name == NULL\n");
1080 return(NULL);
1081 }
1082 switch (type) {
1083 case XML_ELEMENT_TYPE_EMPTY:
1084 if (content != NULL) {
1085 xmlGenericError(xmlGenericErrorContext,
1086 "xmlAddElementDecl: content != NULL for EMPTY\n");
1087 return(NULL);
1088 }
1089 break;
1090 case XML_ELEMENT_TYPE_ANY:
1091 if (content != NULL) {
1092 xmlGenericError(xmlGenericErrorContext,
1093 "xmlAddElementDecl: content != NULL for ANY\n");
1094 return(NULL);
1095 }
1096 break;
1097 case XML_ELEMENT_TYPE_MIXED:
1098 if (content == NULL) {
1099 xmlGenericError(xmlGenericErrorContext,
1100 "xmlAddElementDecl: content == NULL for MIXED\n");
1101 return(NULL);
1102 }
1103 break;
1104 case XML_ELEMENT_TYPE_ELEMENT:
1105 if (content == NULL) {
1106 xmlGenericError(xmlGenericErrorContext,
1107 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1108 return(NULL);
1109 }
1110 break;
1111 default:
1112 xmlGenericError(xmlGenericErrorContext,
1113 "xmlAddElementDecl: unknown type %d\n", type);
1114 return(NULL);
1115 }
1116
1117 /*
1118 * check if name is a QName
1119 */
1120 uqname = xmlSplitQName2(name, &ns);
1121 if (uqname != NULL)
1122 name = uqname;
1123
1124 /*
1125 * Create the Element table if needed.
1126 */
1127 table = (xmlElementTablePtr) dtd->elements;
1128 if (table == NULL) {
1129 table = xmlCreateElementTable();
1130 dtd->elements = (void *) table;
1131 }
1132 if (table == NULL) {
1133 xmlGenericError(xmlGenericErrorContext,
1134 "xmlAddElementDecl: Table creation failed!\n");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001135 if (uqname != NULL)
1136 xmlFree(uqname);
1137 if (ns != NULL)
1138 xmlFree(ns);
Owen Taylor3473f882001-02-23 17:55:21 +00001139 return(NULL);
1140 }
1141
Daniel Veillarda10efa82001-04-18 13:09:01 +00001142 /*
1143 * lookup old attributes inserted on an undefined element in the
1144 * internal subset.
1145 */
1146 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1147 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1148 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1149 oldAttributes = ret->attributes;
1150 ret->attributes = NULL;
1151 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1152 xmlFreeElement(ret);
1153 }
Owen Taylor3473f882001-02-23 17:55:21 +00001154 }
Owen Taylor3473f882001-02-23 17:55:21 +00001155
1156 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001157 * The element may already be present if one of its attribute
1158 * was registered first
1159 */
1160 ret = xmlHashLookup2(table, name, ns);
1161 if (ret != NULL) {
1162 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1163 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001164 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001165 */
1166 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1167 if (uqname != NULL)
1168 xmlFree(uqname);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001169 if (ns != NULL)
1170 xmlFree(ns);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001171 return(NULL);
1172 }
1173 } else {
1174 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1175 if (ret == NULL) {
1176 xmlGenericError(xmlGenericErrorContext,
1177 "xmlAddElementDecl: out of memory\n");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001178 if (uqname != NULL)
1179 xmlFree(uqname);
1180 if (ns != NULL)
1181 xmlFree(ns);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001182 return(NULL);
1183 }
1184 memset(ret, 0, sizeof(xmlElement));
1185 ret->type = XML_ELEMENT_DECL;
1186
1187 /*
1188 * fill the structure.
1189 */
1190 ret->name = xmlStrdup(name);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001191 if (ret->name == NULL) {
1192 xmlGenericError(xmlGenericErrorContext,
1193 "xmlAddElementDecl: out of memory\n");
1194 if (uqname != NULL)
1195 xmlFree(uqname);
1196 if (ns != NULL)
1197 xmlFree(ns);
1198 xmlFree(ret);
1199 return(NULL);
1200 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00001201 ret->prefix = ns;
1202
1203 /*
1204 * Validity Check:
1205 * Insertion must not fail
1206 */
1207 if (xmlHashAddEntry2(table, name, ns, ret)) {
1208 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001209 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001210 */
1211 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1212 xmlFreeElement(ret);
1213 if (uqname != NULL)
1214 xmlFree(uqname);
1215 return(NULL);
1216 }
1217 }
1218
1219 /*
1220 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001221 */
1222 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001223 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001224 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001225
1226 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001227 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001228 */
1229 ret->parent = dtd;
1230 ret->doc = dtd->doc;
1231 if (dtd->last == NULL) {
1232 dtd->children = dtd->last = (xmlNodePtr) ret;
1233 } else {
1234 dtd->last->next = (xmlNodePtr) ret;
1235 ret->prev = dtd->last;
1236 dtd->last = (xmlNodePtr) ret;
1237 }
1238 if (uqname != NULL)
1239 xmlFree(uqname);
1240 return(ret);
1241}
1242
1243/**
1244 * xmlFreeElementTable:
1245 * @table: An element table
1246 *
1247 * Deallocate the memory used by an element hash table.
1248 */
1249void
1250xmlFreeElementTable(xmlElementTablePtr table) {
1251 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1252}
1253
1254/**
1255 * xmlCopyElement:
1256 * @elem: An element
1257 *
1258 * Build a copy of an element.
1259 *
1260 * Returns the new xmlElementPtr or NULL in case of error.
1261 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001262static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001263xmlCopyElement(xmlElementPtr elem) {
1264 xmlElementPtr cur;
1265
1266 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1267 if (cur == NULL) {
1268 xmlGenericError(xmlGenericErrorContext,
1269 "xmlCopyElement: out of memory !\n");
1270 return(NULL);
1271 }
1272 memset(cur, 0, sizeof(xmlElement));
1273 cur->type = XML_ELEMENT_DECL;
1274 cur->etype = elem->etype;
1275 if (elem->name != NULL)
1276 cur->name = xmlStrdup(elem->name);
1277 else
1278 cur->name = NULL;
1279 if (elem->prefix != NULL)
1280 cur->prefix = xmlStrdup(elem->prefix);
1281 else
1282 cur->prefix = NULL;
1283 cur->content = xmlCopyElementContent(elem->content);
1284 /* TODO : rebuild the attribute list on the copy */
1285 cur->attributes = NULL;
1286 return(cur);
1287}
1288
1289/**
1290 * xmlCopyElementTable:
1291 * @table: An element table
1292 *
1293 * Build a copy of an element table.
1294 *
1295 * Returns the new xmlElementTablePtr or NULL in case of error.
1296 */
1297xmlElementTablePtr
1298xmlCopyElementTable(xmlElementTablePtr table) {
1299 return((xmlElementTablePtr) xmlHashCopy(table,
1300 (xmlHashCopier) xmlCopyElement));
1301}
1302
1303/**
1304 * xmlDumpElementDecl:
1305 * @buf: the XML buffer output
1306 * @elem: An element table
1307 *
1308 * This will dump the content of the element declaration as an XML
1309 * DTD definition
1310 */
1311void
1312xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1313 switch (elem->etype) {
1314 case XML_ELEMENT_TYPE_EMPTY:
1315 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001316 if (elem->prefix != NULL) {
1317 xmlBufferWriteCHAR(buf, elem->prefix);
1318 xmlBufferWriteChar(buf, ":");
1319 }
Owen Taylor3473f882001-02-23 17:55:21 +00001320 xmlBufferWriteCHAR(buf, elem->name);
1321 xmlBufferWriteChar(buf, " EMPTY>\n");
1322 break;
1323 case XML_ELEMENT_TYPE_ANY:
1324 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001325 if (elem->prefix != NULL) {
1326 xmlBufferWriteCHAR(buf, elem->prefix);
1327 xmlBufferWriteChar(buf, ":");
1328 }
Owen Taylor3473f882001-02-23 17:55:21 +00001329 xmlBufferWriteCHAR(buf, elem->name);
1330 xmlBufferWriteChar(buf, " ANY>\n");
1331 break;
1332 case XML_ELEMENT_TYPE_MIXED:
1333 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001334 if (elem->prefix != NULL) {
1335 xmlBufferWriteCHAR(buf, elem->prefix);
1336 xmlBufferWriteChar(buf, ":");
1337 }
Owen Taylor3473f882001-02-23 17:55:21 +00001338 xmlBufferWriteCHAR(buf, elem->name);
1339 xmlBufferWriteChar(buf, " ");
1340 xmlDumpElementContent(buf, elem->content, 1);
1341 xmlBufferWriteChar(buf, ">\n");
1342 break;
1343 case XML_ELEMENT_TYPE_ELEMENT:
1344 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001345 if (elem->prefix != NULL) {
1346 xmlBufferWriteCHAR(buf, elem->prefix);
1347 xmlBufferWriteChar(buf, ":");
1348 }
Owen Taylor3473f882001-02-23 17:55:21 +00001349 xmlBufferWriteCHAR(buf, elem->name);
1350 xmlBufferWriteChar(buf, " ");
1351 xmlDumpElementContent(buf, elem->content, 1);
1352 xmlBufferWriteChar(buf, ">\n");
1353 break;
1354 default:
1355 xmlGenericError(xmlGenericErrorContext,
1356 "xmlDumpElementDecl: internal: unknown type %d\n",
1357 elem->etype);
1358 }
1359}
1360
1361/**
1362 * xmlDumpElementTable:
1363 * @buf: the XML buffer output
1364 * @table: An element table
1365 *
1366 * This will dump the content of the element table as an XML DTD definition
1367 */
1368void
1369xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1370 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1371}
1372
1373/**
1374 * xmlCreateEnumeration:
1375 * @name: the enumeration name or NULL
1376 *
1377 * create and initialize an enumeration attribute node.
1378 *
1379 * Returns the xmlEnumerationPtr just created or NULL in case
1380 * of error.
1381 */
1382xmlEnumerationPtr
1383xmlCreateEnumeration(xmlChar *name) {
1384 xmlEnumerationPtr ret;
1385
1386 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1387 if (ret == NULL) {
1388 xmlGenericError(xmlGenericErrorContext,
1389 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1390 (long)sizeof(xmlEnumeration));
1391 return(NULL);
1392 }
1393 memset(ret, 0, sizeof(xmlEnumeration));
1394
1395 if (name != NULL)
1396 ret->name = xmlStrdup(name);
1397 return(ret);
1398}
1399
1400/**
1401 * xmlFreeEnumeration:
1402 * @cur: the tree to free.
1403 *
1404 * free an enumeration attribute node (recursive).
1405 */
1406void
1407xmlFreeEnumeration(xmlEnumerationPtr cur) {
1408 if (cur == NULL) return;
1409
1410 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1411
1412 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001413 xmlFree(cur);
1414}
1415
1416/**
1417 * xmlCopyEnumeration:
1418 * @cur: the tree to copy.
1419 *
1420 * Copy an enumeration attribute node (recursive).
1421 *
1422 * Returns the xmlEnumerationPtr just created or NULL in case
1423 * of error.
1424 */
1425xmlEnumerationPtr
1426xmlCopyEnumeration(xmlEnumerationPtr cur) {
1427 xmlEnumerationPtr ret;
1428
1429 if (cur == NULL) return(NULL);
1430 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1431
1432 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1433 else ret->next = NULL;
1434
1435 return(ret);
1436}
1437
1438/**
1439 * xmlDumpEnumeration:
1440 * @buf: the XML buffer output
1441 * @enum: An enumeration
1442 *
1443 * This will dump the content of the enumeration
1444 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001445static void
Owen Taylor3473f882001-02-23 17:55:21 +00001446xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1447 if (cur == NULL) return;
1448
1449 xmlBufferWriteCHAR(buf, cur->name);
1450 if (cur->next == NULL)
1451 xmlBufferWriteChar(buf, ")");
1452 else {
1453 xmlBufferWriteChar(buf, " | ");
1454 xmlDumpEnumeration(buf, cur->next);
1455 }
1456}
1457
1458/**
1459 * xmlCreateAttributeTable:
1460 *
1461 * create and initialize an empty attribute hash table.
1462 *
1463 * Returns the xmlAttributeTablePtr just created or NULL in case
1464 * of error.
1465 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001466static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001467xmlCreateAttributeTable(void) {
1468 return(xmlHashCreate(0));
1469}
1470
1471/**
1472 * xmlScanAttributeDeclCallback:
1473 * @attr: the attribute decl
1474 * @list: the list to update
1475 *
1476 * Callback called by xmlScanAttributeDecl when a new attribute
1477 * has to be entered in the list.
1478 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001479static void
Owen Taylor3473f882001-02-23 17:55:21 +00001480xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001481 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001482 attr->nexth = *list;
1483 *list = attr;
1484}
1485
1486/**
1487 * xmlScanAttributeDecl:
1488 * @dtd: pointer to the DTD
1489 * @elem: the element name
1490 *
1491 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001492 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001493 *
1494 * Returns the pointer to the first attribute decl in the chain,
1495 * possibly NULL.
1496 */
1497xmlAttributePtr
1498xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1499 xmlAttributePtr ret = NULL;
1500 xmlAttributeTablePtr table;
1501
1502 if (dtd == NULL) {
1503 xmlGenericError(xmlGenericErrorContext,
1504 "xmlScanAttributeDecl: dtd == NULL\n");
1505 return(NULL);
1506 }
1507 if (elem == NULL) {
1508 xmlGenericError(xmlGenericErrorContext,
1509 "xmlScanAttributeDecl: elem == NULL\n");
1510 return(NULL);
1511 }
1512 table = (xmlAttributeTablePtr) dtd->attributes;
1513 if (table == NULL)
1514 return(NULL);
1515
1516 /* WRONG !!! */
1517 xmlHashScan3(table, NULL, NULL, elem,
1518 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1519 return(ret);
1520}
1521
1522/**
1523 * xmlScanIDAttributeDecl:
1524 * @ctxt: the validation context
1525 * @elem: the element name
1526 *
1527 * Verify that the element don't have too many ID attributes
1528 * declared.
1529 *
1530 * Returns the number of ID attributes found.
1531 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001532static int
Owen Taylor3473f882001-02-23 17:55:21 +00001533xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1534 xmlAttributePtr cur;
1535 int ret = 0;
1536
1537 if (elem == NULL) return(0);
1538 cur = elem->attributes;
1539 while (cur != NULL) {
1540 if (cur->atype == XML_ATTRIBUTE_ID) {
1541 ret ++;
1542 if (ret > 1)
1543 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001544 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001545 elem->name, cur->name);
1546 }
1547 cur = cur->nexth;
1548 }
1549 return(ret);
1550}
1551
1552/**
1553 * xmlFreeAttribute:
1554 * @elem: An attribute
1555 *
1556 * Deallocate the memory used by an attribute definition
1557 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001558static void
Owen Taylor3473f882001-02-23 17:55:21 +00001559xmlFreeAttribute(xmlAttributePtr attr) {
1560 if (attr == NULL) return;
1561 xmlUnlinkNode((xmlNodePtr) attr);
1562 if (attr->tree != NULL)
1563 xmlFreeEnumeration(attr->tree);
1564 if (attr->elem != NULL)
1565 xmlFree((xmlChar *) attr->elem);
1566 if (attr->name != NULL)
1567 xmlFree((xmlChar *) attr->name);
1568 if (attr->defaultValue != NULL)
1569 xmlFree((xmlChar *) attr->defaultValue);
1570 if (attr->prefix != NULL)
1571 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001572 xmlFree(attr);
1573}
1574
1575
1576/**
1577 * xmlAddAttributeDecl:
1578 * @ctxt: the validation context
1579 * @dtd: pointer to the DTD
1580 * @elem: the element name
1581 * @name: the attribute name
1582 * @ns: the attribute namespace prefix
1583 * @type: the attribute type
1584 * @def: the attribute default type
1585 * @defaultValue: the attribute default value
1586 * @tree: if it's an enumeration, the associated list
1587 *
1588 * Register a new attribute declaration
1589 * Note that @tree becomes the ownership of the DTD
1590 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001591 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001592 */
1593xmlAttributePtr
1594xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1595 const xmlChar *name, const xmlChar *ns,
1596 xmlAttributeType type, xmlAttributeDefault def,
1597 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1598 xmlAttributePtr ret;
1599 xmlAttributeTablePtr table;
1600 xmlElementPtr elemDef;
1601
1602 if (dtd == NULL) {
1603 xmlGenericError(xmlGenericErrorContext,
1604 "xmlAddAttributeDecl: dtd == NULL\n");
1605 xmlFreeEnumeration(tree);
1606 return(NULL);
1607 }
1608 if (name == NULL) {
1609 xmlGenericError(xmlGenericErrorContext,
1610 "xmlAddAttributeDecl: name == NULL\n");
1611 xmlFreeEnumeration(tree);
1612 return(NULL);
1613 }
1614 if (elem == NULL) {
1615 xmlGenericError(xmlGenericErrorContext,
1616 "xmlAddAttributeDecl: elem == NULL\n");
1617 xmlFreeEnumeration(tree);
1618 return(NULL);
1619 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001620
Owen Taylor3473f882001-02-23 17:55:21 +00001621 /*
1622 * Check the type and possibly the default value.
1623 */
1624 switch (type) {
1625 case XML_ATTRIBUTE_CDATA:
1626 break;
1627 case XML_ATTRIBUTE_ID:
1628 break;
1629 case XML_ATTRIBUTE_IDREF:
1630 break;
1631 case XML_ATTRIBUTE_IDREFS:
1632 break;
1633 case XML_ATTRIBUTE_ENTITY:
1634 break;
1635 case XML_ATTRIBUTE_ENTITIES:
1636 break;
1637 case XML_ATTRIBUTE_NMTOKEN:
1638 break;
1639 case XML_ATTRIBUTE_NMTOKENS:
1640 break;
1641 case XML_ATTRIBUTE_ENUMERATION:
1642 break;
1643 case XML_ATTRIBUTE_NOTATION:
1644 break;
1645 default:
1646 xmlGenericError(xmlGenericErrorContext,
1647 "xmlAddAttributeDecl: unknown type %d\n", type);
1648 xmlFreeEnumeration(tree);
1649 return(NULL);
1650 }
1651 if ((defaultValue != NULL) &&
1652 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001653 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001654 elem, name, defaultValue);
1655 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001656 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001657 }
1658
1659 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001660 * Check first that an attribute defined in the external subset wasn't
1661 * already defined in the internal subset
1662 */
1663 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1664 (dtd->doc->intSubset != NULL) &&
1665 (dtd->doc->intSubset->attributes != NULL)) {
1666 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1667 if (ret != NULL)
1668 return(NULL);
1669 }
1670
1671 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001672 * Create the Attribute table if needed.
1673 */
1674 table = (xmlAttributeTablePtr) dtd->attributes;
1675 if (table == NULL) {
1676 table = xmlCreateAttributeTable();
1677 dtd->attributes = (void *) table;
1678 }
1679 if (table == NULL) {
1680 xmlGenericError(xmlGenericErrorContext,
1681 "xmlAddAttributeDecl: Table creation failed!\n");
1682 return(NULL);
1683 }
1684
1685
1686 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1687 if (ret == NULL) {
1688 xmlGenericError(xmlGenericErrorContext,
1689 "xmlAddAttributeDecl: out of memory\n");
1690 return(NULL);
1691 }
1692 memset(ret, 0, sizeof(xmlAttribute));
1693 ret->type = XML_ATTRIBUTE_DECL;
1694
1695 /*
1696 * fill the structure.
1697 */
1698 ret->atype = type;
1699 ret->name = xmlStrdup(name);
1700 ret->prefix = xmlStrdup(ns);
1701 ret->elem = xmlStrdup(elem);
1702 ret->def = def;
1703 ret->tree = tree;
1704 if (defaultValue != NULL)
1705 ret->defaultValue = xmlStrdup(defaultValue);
1706
1707 /*
1708 * Validity Check:
1709 * Search the DTD for previous declarations of the ATTLIST
1710 */
1711 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1712 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001713 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001714 */
1715 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001716 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001717 name, elem);
1718 xmlFreeAttribute(ret);
1719 return(NULL);
1720 }
1721
1722 /*
1723 * Validity Check:
1724 * Multiple ID per element
1725 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001726 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001727 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001728
Owen Taylor3473f882001-02-23 17:55:21 +00001729 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001730 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001731 VERROR(ctxt->userData,
1732 "Element %s has too may ID attributes defined : %s\n",
1733 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001734 ctxt->valid = 0;
1735 }
1736
Daniel Veillard48da9102001-08-07 01:10:10 +00001737 /*
1738 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001739 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001740 */
1741 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1742 ((ret->prefix != NULL &&
1743 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1744 ret->nexth = elemDef->attributes;
1745 elemDef->attributes = ret;
1746 } else {
1747 xmlAttributePtr tmp = elemDef->attributes;
1748
1749 while ((tmp != NULL) &&
1750 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1751 ((ret->prefix != NULL &&
1752 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1753 if (tmp->nexth == NULL)
1754 break;
1755 tmp = tmp->nexth;
1756 }
1757 if (tmp != NULL) {
1758 ret->nexth = tmp->nexth;
1759 tmp->nexth = ret;
1760 } else {
1761 ret->nexth = elemDef->attributes;
1762 elemDef->attributes = ret;
1763 }
1764 }
Owen Taylor3473f882001-02-23 17:55:21 +00001765 }
1766
1767 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001768 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001769 */
1770 ret->parent = dtd;
1771 ret->doc = dtd->doc;
1772 if (dtd->last == NULL) {
1773 dtd->children = dtd->last = (xmlNodePtr) ret;
1774 } else {
1775 dtd->last->next = (xmlNodePtr) ret;
1776 ret->prev = dtd->last;
1777 dtd->last = (xmlNodePtr) ret;
1778 }
1779 return(ret);
1780}
1781
1782/**
1783 * xmlFreeAttributeTable:
1784 * @table: An attribute table
1785 *
1786 * Deallocate the memory used by an entities hash table.
1787 */
1788void
1789xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1790 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1791}
1792
1793/**
1794 * xmlCopyAttribute:
1795 * @attr: An attribute
1796 *
1797 * Build a copy of an attribute.
1798 *
1799 * Returns the new xmlAttributePtr or NULL in case of error.
1800 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001801static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001802xmlCopyAttribute(xmlAttributePtr attr) {
1803 xmlAttributePtr cur;
1804
1805 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1806 if (cur == NULL) {
1807 xmlGenericError(xmlGenericErrorContext,
1808 "xmlCopyAttribute: out of memory !\n");
1809 return(NULL);
1810 }
1811 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001812 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001813 cur->atype = attr->atype;
1814 cur->def = attr->def;
1815 cur->tree = xmlCopyEnumeration(attr->tree);
1816 if (attr->elem != NULL)
1817 cur->elem = xmlStrdup(attr->elem);
1818 if (attr->name != NULL)
1819 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001820 if (attr->prefix != NULL)
1821 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001822 if (attr->defaultValue != NULL)
1823 cur->defaultValue = xmlStrdup(attr->defaultValue);
1824 return(cur);
1825}
1826
1827/**
1828 * xmlCopyAttributeTable:
1829 * @table: An attribute table
1830 *
1831 * Build a copy of an attribute table.
1832 *
1833 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1834 */
1835xmlAttributeTablePtr
1836xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1837 return((xmlAttributeTablePtr) xmlHashCopy(table,
1838 (xmlHashCopier) xmlCopyAttribute));
1839}
1840
1841/**
1842 * xmlDumpAttributeDecl:
1843 * @buf: the XML buffer output
1844 * @attr: An attribute declaration
1845 *
1846 * This will dump the content of the attribute declaration as an XML
1847 * DTD definition
1848 */
1849void
1850xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1851 xmlBufferWriteChar(buf, "<!ATTLIST ");
1852 xmlBufferWriteCHAR(buf, attr->elem);
1853 xmlBufferWriteChar(buf, " ");
1854 if (attr->prefix != NULL) {
1855 xmlBufferWriteCHAR(buf, attr->prefix);
1856 xmlBufferWriteChar(buf, ":");
1857 }
1858 xmlBufferWriteCHAR(buf, attr->name);
1859 switch (attr->atype) {
1860 case XML_ATTRIBUTE_CDATA:
1861 xmlBufferWriteChar(buf, " CDATA");
1862 break;
1863 case XML_ATTRIBUTE_ID:
1864 xmlBufferWriteChar(buf, " ID");
1865 break;
1866 case XML_ATTRIBUTE_IDREF:
1867 xmlBufferWriteChar(buf, " IDREF");
1868 break;
1869 case XML_ATTRIBUTE_IDREFS:
1870 xmlBufferWriteChar(buf, " IDREFS");
1871 break;
1872 case XML_ATTRIBUTE_ENTITY:
1873 xmlBufferWriteChar(buf, " ENTITY");
1874 break;
1875 case XML_ATTRIBUTE_ENTITIES:
1876 xmlBufferWriteChar(buf, " ENTITIES");
1877 break;
1878 case XML_ATTRIBUTE_NMTOKEN:
1879 xmlBufferWriteChar(buf, " NMTOKEN");
1880 break;
1881 case XML_ATTRIBUTE_NMTOKENS:
1882 xmlBufferWriteChar(buf, " NMTOKENS");
1883 break;
1884 case XML_ATTRIBUTE_ENUMERATION:
1885 xmlBufferWriteChar(buf, " (");
1886 xmlDumpEnumeration(buf, attr->tree);
1887 break;
1888 case XML_ATTRIBUTE_NOTATION:
1889 xmlBufferWriteChar(buf, " NOTATION (");
1890 xmlDumpEnumeration(buf, attr->tree);
1891 break;
1892 default:
1893 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001894 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001895 attr->atype);
1896 }
1897 switch (attr->def) {
1898 case XML_ATTRIBUTE_NONE:
1899 break;
1900 case XML_ATTRIBUTE_REQUIRED:
1901 xmlBufferWriteChar(buf, " #REQUIRED");
1902 break;
1903 case XML_ATTRIBUTE_IMPLIED:
1904 xmlBufferWriteChar(buf, " #IMPLIED");
1905 break;
1906 case XML_ATTRIBUTE_FIXED:
1907 xmlBufferWriteChar(buf, " #FIXED");
1908 break;
1909 default:
1910 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001911 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001912 attr->def);
1913 }
1914 if (attr->defaultValue != NULL) {
1915 xmlBufferWriteChar(buf, " ");
1916 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1917 }
1918 xmlBufferWriteChar(buf, ">\n");
1919}
1920
1921/**
1922 * xmlDumpAttributeTable:
1923 * @buf: the XML buffer output
1924 * @table: An attribute table
1925 *
1926 * This will dump the content of the attribute table as an XML DTD definition
1927 */
1928void
1929xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1930 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1931}
1932
1933/************************************************************************
1934 * *
1935 * NOTATIONs *
1936 * *
1937 ************************************************************************/
1938/**
1939 * xmlCreateNotationTable:
1940 *
1941 * create and initialize an empty notation hash table.
1942 *
1943 * Returns the xmlNotationTablePtr just created or NULL in case
1944 * of error.
1945 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001946static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001947xmlCreateNotationTable(void) {
1948 return(xmlHashCreate(0));
1949}
1950
1951/**
1952 * xmlFreeNotation:
1953 * @not: A notation
1954 *
1955 * Deallocate the memory used by an notation definition
1956 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001957static void
Owen Taylor3473f882001-02-23 17:55:21 +00001958xmlFreeNotation(xmlNotationPtr nota) {
1959 if (nota == NULL) return;
1960 if (nota->name != NULL)
1961 xmlFree((xmlChar *) nota->name);
1962 if (nota->PublicID != NULL)
1963 xmlFree((xmlChar *) nota->PublicID);
1964 if (nota->SystemID != NULL)
1965 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001966 xmlFree(nota);
1967}
1968
1969
1970/**
1971 * xmlAddNotationDecl:
1972 * @dtd: pointer to the DTD
1973 * @ctxt: the validation context
1974 * @name: the entity name
1975 * @PublicID: the public identifier or NULL
1976 * @SystemID: the system identifier or NULL
1977 *
1978 * Register a new notation declaration
1979 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001980 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001981 */
1982xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001983xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001984 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001985 const xmlChar *PublicID, const xmlChar *SystemID) {
1986 xmlNotationPtr ret;
1987 xmlNotationTablePtr table;
1988
1989 if (dtd == NULL) {
1990 xmlGenericError(xmlGenericErrorContext,
1991 "xmlAddNotationDecl: dtd == NULL\n");
1992 return(NULL);
1993 }
1994 if (name == NULL) {
1995 xmlGenericError(xmlGenericErrorContext,
1996 "xmlAddNotationDecl: name == NULL\n");
1997 return(NULL);
1998 }
1999 if ((PublicID == NULL) && (SystemID == NULL)) {
2000 xmlGenericError(xmlGenericErrorContext,
2001 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002002 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002003 }
2004
2005 /*
2006 * Create the Notation table if needed.
2007 */
2008 table = (xmlNotationTablePtr) dtd->notations;
2009 if (table == NULL)
2010 dtd->notations = table = xmlCreateNotationTable();
2011 if (table == NULL) {
2012 xmlGenericError(xmlGenericErrorContext,
2013 "xmlAddNotationDecl: Table creation failed!\n");
2014 return(NULL);
2015 }
2016
2017 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2018 if (ret == NULL) {
2019 xmlGenericError(xmlGenericErrorContext,
2020 "xmlAddNotationDecl: out of memory\n");
2021 return(NULL);
2022 }
2023 memset(ret, 0, sizeof(xmlNotation));
2024
2025 /*
2026 * fill the structure.
2027 */
2028 ret->name = xmlStrdup(name);
2029 if (SystemID != NULL)
2030 ret->SystemID = xmlStrdup(SystemID);
2031 if (PublicID != NULL)
2032 ret->PublicID = xmlStrdup(PublicID);
2033
2034 /*
2035 * Validity Check:
2036 * Check the DTD for previous declarations of the ATTLIST
2037 */
2038 if (xmlHashAddEntry(table, name, ret)) {
2039 xmlGenericError(xmlGenericErrorContext,
2040 "xmlAddNotationDecl: %s already defined\n", name);
2041 xmlFreeNotation(ret);
2042 return(NULL);
2043 }
2044 return(ret);
2045}
2046
2047/**
2048 * xmlFreeNotationTable:
2049 * @table: An notation table
2050 *
2051 * Deallocate the memory used by an entities hash table.
2052 */
2053void
2054xmlFreeNotationTable(xmlNotationTablePtr table) {
2055 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2056}
2057
2058/**
2059 * xmlCopyNotation:
2060 * @nota: A notation
2061 *
2062 * Build a copy of a notation.
2063 *
2064 * Returns the new xmlNotationPtr or NULL in case of error.
2065 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002066static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002067xmlCopyNotation(xmlNotationPtr nota) {
2068 xmlNotationPtr cur;
2069
2070 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2071 if (cur == NULL) {
2072 xmlGenericError(xmlGenericErrorContext,
2073 "xmlCopyNotation: out of memory !\n");
2074 return(NULL);
2075 }
2076 if (nota->name != NULL)
2077 cur->name = xmlStrdup(nota->name);
2078 else
2079 cur->name = NULL;
2080 if (nota->PublicID != NULL)
2081 cur->PublicID = xmlStrdup(nota->PublicID);
2082 else
2083 cur->PublicID = NULL;
2084 if (nota->SystemID != NULL)
2085 cur->SystemID = xmlStrdup(nota->SystemID);
2086 else
2087 cur->SystemID = NULL;
2088 return(cur);
2089}
2090
2091/**
2092 * xmlCopyNotationTable:
2093 * @table: A notation table
2094 *
2095 * Build a copy of a notation table.
2096 *
2097 * Returns the new xmlNotationTablePtr or NULL in case of error.
2098 */
2099xmlNotationTablePtr
2100xmlCopyNotationTable(xmlNotationTablePtr table) {
2101 return((xmlNotationTablePtr) xmlHashCopy(table,
2102 (xmlHashCopier) xmlCopyNotation));
2103}
2104
2105/**
2106 * xmlDumpNotationDecl:
2107 * @buf: the XML buffer output
2108 * @nota: A notation declaration
2109 *
2110 * This will dump the content the notation declaration as an XML DTD definition
2111 */
2112void
2113xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2114 xmlBufferWriteChar(buf, "<!NOTATION ");
2115 xmlBufferWriteCHAR(buf, nota->name);
2116 if (nota->PublicID != NULL) {
2117 xmlBufferWriteChar(buf, " PUBLIC ");
2118 xmlBufferWriteQuotedString(buf, nota->PublicID);
2119 if (nota->SystemID != NULL) {
2120 xmlBufferWriteChar(buf, " ");
2121 xmlBufferWriteCHAR(buf, nota->SystemID);
2122 }
2123 } else {
2124 xmlBufferWriteChar(buf, " SYSTEM ");
2125 xmlBufferWriteCHAR(buf, nota->SystemID);
2126 }
2127 xmlBufferWriteChar(buf, " >\n");
2128}
2129
2130/**
2131 * xmlDumpNotationTable:
2132 * @buf: the XML buffer output
2133 * @table: A notation table
2134 *
2135 * This will dump the content of the notation table as an XML DTD definition
2136 */
2137void
2138xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2139 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2140}
2141
2142/************************************************************************
2143 * *
2144 * IDs *
2145 * *
2146 ************************************************************************/
2147/**
2148 * xmlCreateIDTable:
2149 *
2150 * create and initialize an empty id hash table.
2151 *
2152 * Returns the xmlIDTablePtr just created or NULL in case
2153 * of error.
2154 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002155static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002156xmlCreateIDTable(void) {
2157 return(xmlHashCreate(0));
2158}
2159
2160/**
2161 * xmlFreeID:
2162 * @not: A id
2163 *
2164 * Deallocate the memory used by an id definition
2165 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002166static void
Owen Taylor3473f882001-02-23 17:55:21 +00002167xmlFreeID(xmlIDPtr id) {
2168 if (id == NULL) return;
2169 if (id->value != NULL)
2170 xmlFree((xmlChar *) id->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002171 if (id->name != NULL)
2172 xmlFree((xmlChar *) id->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002173 xmlFree(id);
2174}
2175
2176/**
2177 * xmlAddID:
2178 * @ctxt: the validation context
2179 * @doc: pointer to the document
2180 * @value: the value name
2181 * @attr: the attribute holding the ID
2182 *
2183 * Register a new id declaration
2184 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002185 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002186 */
2187xmlIDPtr
2188xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2189 xmlAttrPtr attr) {
2190 xmlIDPtr ret;
2191 xmlIDTablePtr table;
2192
2193 if (doc == NULL) {
2194 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002195 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002196 return(NULL);
2197 }
2198 if (value == NULL) {
2199 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002200 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002201 return(NULL);
2202 }
2203 if (attr == NULL) {
2204 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002205 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002206 return(NULL);
2207 }
2208
2209 /*
2210 * Create the ID table if needed.
2211 */
2212 table = (xmlIDTablePtr) doc->ids;
2213 if (table == NULL)
2214 doc->ids = table = xmlCreateIDTable();
2215 if (table == NULL) {
2216 xmlGenericError(xmlGenericErrorContext,
2217 "xmlAddID: Table creation failed!\n");
2218 return(NULL);
2219 }
2220
2221 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2222 if (ret == NULL) {
2223 xmlGenericError(xmlGenericErrorContext,
2224 "xmlAddID: out of memory\n");
2225 return(NULL);
2226 }
2227
2228 /*
2229 * fill the structure.
2230 */
2231 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002232 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2233 /*
2234 * Operating in streaming mode, attr is gonna disapear
2235 */
2236 ret->name = xmlStrdup(attr->name);
2237 ret->attr = NULL;
2238 } else {
2239 ret->attr = attr;
2240 ret->name = NULL;
2241 }
2242 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002243
2244 if (xmlHashAddEntry(table, value, ret) < 0) {
2245 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002246 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002247 */
Daniel Veillard76575762002-09-05 14:21:15 +00002248 if (ctxt != NULL) {
2249 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002250 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002251 }
Owen Taylor3473f882001-02-23 17:55:21 +00002252 xmlFreeID(ret);
2253 return(NULL);
2254 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002255 if (attr != NULL)
2256 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002257 return(ret);
2258}
2259
2260/**
2261 * xmlFreeIDTable:
2262 * @table: An id table
2263 *
2264 * Deallocate the memory used by an ID hash table.
2265 */
2266void
2267xmlFreeIDTable(xmlIDTablePtr table) {
2268 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2269}
2270
2271/**
2272 * xmlIsID:
2273 * @doc: the document
2274 * @elem: the element carrying the attribute
2275 * @attr: the attribute
2276 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002277 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002278 * then this is done if DTD loading has been requested. In the case
2279 * of HTML documents parsed with the HTML parser, then ID detection is
2280 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002281 *
2282 * Returns 0 or 1 depending on the lookup result
2283 */
2284int
2285xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2286 if (doc == NULL) return(0);
2287 if (attr == NULL) return(0);
2288 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2289 return(0);
2290 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2291 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2292 (xmlStrEqual(BAD_CAST "name", attr->name)))
2293 return(1);
2294 return(0);
2295 } else {
2296 xmlAttributePtr attrDecl;
2297
2298 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002299 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00002300 xmlChar fn[50];
Daniel Veillard37f961d2002-07-06 17:53:56 +00002301 xmlChar *fullname;
Daniel Veillardc00cda82003-04-07 10:22:39 +00002302
2303 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002304 if (fullname == NULL)
2305 return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002306 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2307 attr->name);
2308 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2309 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2310 attr->name);
Daniel Veillardc00cda82003-04-07 10:22:39 +00002311 if ((fullname != fn) && (fullname != elem->name))
2312 xmlFree(fullname);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002313 } else {
2314 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2315 attr->name);
2316 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2317 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2318 attr->name);
2319 }
Owen Taylor3473f882001-02-23 17:55:21 +00002320
2321 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2322 return(1);
2323 }
2324 return(0);
2325}
2326
2327/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002328 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002329 * @doc: the document
2330 * @attr: the attribute
2331 *
2332 * Remove the given attribute from the ID table maintained internally.
2333 *
2334 * Returns -1 if the lookup failed and 0 otherwise
2335 */
2336int
2337xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2338 xmlAttrPtr cur;
2339 xmlIDTablePtr table;
2340 xmlChar *ID;
2341
2342 if (doc == NULL) return(-1);
2343 if (attr == NULL) return(-1);
2344 table = (xmlIDTablePtr) doc->ids;
2345 if (table == NULL)
2346 return(-1);
2347
2348 if (attr == NULL)
2349 return(-1);
2350 ID = xmlNodeListGetString(doc, attr->children, 1);
2351 if (ID == NULL)
2352 return(-1);
2353 cur = xmlHashLookup(table, ID);
2354 if (cur != attr) {
2355 xmlFree(ID);
2356 return(-1);
2357 }
2358 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2359 xmlFree(ID);
2360 return(0);
2361}
2362
2363/**
2364 * xmlGetID:
2365 * @doc: pointer to the document
2366 * @ID: the ID value
2367 *
2368 * Search the attribute declaring the given ID
2369 *
2370 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2371 */
2372xmlAttrPtr
2373xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2374 xmlIDTablePtr table;
2375 xmlIDPtr id;
2376
2377 if (doc == NULL) {
2378 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2379 return(NULL);
2380 }
2381
2382 if (ID == NULL) {
2383 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2384 return(NULL);
2385 }
2386
2387 table = (xmlIDTablePtr) doc->ids;
2388 if (table == NULL)
2389 return(NULL);
2390
2391 id = xmlHashLookup(table, ID);
2392 if (id == NULL)
2393 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002394 if (id->attr == NULL) {
2395 /*
2396 * We are operating on a stream, return a well known reference
2397 * since the attribute node doesn't exist anymore
2398 */
2399 return((xmlAttrPtr) doc);
2400 }
Owen Taylor3473f882001-02-23 17:55:21 +00002401 return(id->attr);
2402}
2403
2404/************************************************************************
2405 * *
2406 * Refs *
2407 * *
2408 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002409typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002410{
2411 xmlListPtr l;
2412 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002413} xmlRemoveMemo;
2414
2415typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2416
2417typedef struct xmlValidateMemo_t
2418{
2419 xmlValidCtxtPtr ctxt;
2420 const xmlChar *name;
2421} xmlValidateMemo;
2422
2423typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002424
2425/**
2426 * xmlCreateRefTable:
2427 *
2428 * create and initialize an empty ref hash table.
2429 *
2430 * Returns the xmlRefTablePtr just created or NULL in case
2431 * of error.
2432 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002433static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002434xmlCreateRefTable(void) {
2435 return(xmlHashCreate(0));
2436}
2437
2438/**
2439 * xmlFreeRef:
2440 * @lk: A list link
2441 *
2442 * Deallocate the memory used by a ref definition
2443 */
2444static void
2445xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002446 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2447 if (ref == NULL) return;
2448 if (ref->value != NULL)
2449 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002450 if (ref->name != NULL)
2451 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002452 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002453}
2454
2455/**
2456 * xmlFreeRefList:
2457 * @list_ref: A list of references.
2458 *
2459 * Deallocate the memory used by a list of references
2460 */
2461static void
2462xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002463 if (list_ref == NULL) return;
2464 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002465}
2466
2467/**
2468 * xmlWalkRemoveRef:
2469 * @data: Contents of current link
2470 * @user: Value supplied by the user
2471 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002472 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002473 */
2474static int
2475xmlWalkRemoveRef(const void *data, const void *user)
2476{
Daniel Veillard37721922001-05-04 15:21:12 +00002477 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2478 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2479 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002480
Daniel Veillard37721922001-05-04 15:21:12 +00002481 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2482 xmlListRemoveFirst(ref_list, (void *)data);
2483 return 0;
2484 }
2485 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002486}
2487
2488/**
2489 * xmlAddRef:
2490 * @ctxt: the validation context
2491 * @doc: pointer to the document
2492 * @value: the value name
2493 * @attr: the attribute holding the Ref
2494 *
2495 * Register a new ref declaration
2496 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002497 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002498 */
2499xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002500xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002501 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002502 xmlRefPtr ret;
2503 xmlRefTablePtr table;
2504 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002505
Daniel Veillard37721922001-05-04 15:21:12 +00002506 if (doc == NULL) {
2507 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002508 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002509 return(NULL);
2510 }
2511 if (value == NULL) {
2512 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002513 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002514 return(NULL);
2515 }
2516 if (attr == NULL) {
2517 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002518 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002519 return(NULL);
2520 }
Owen Taylor3473f882001-02-23 17:55:21 +00002521
Daniel Veillard37721922001-05-04 15:21:12 +00002522 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002523 * Create the Ref table if needed.
2524 */
Daniel Veillard37721922001-05-04 15:21:12 +00002525 table = (xmlRefTablePtr) doc->refs;
2526 if (table == NULL)
2527 doc->refs = table = xmlCreateRefTable();
2528 if (table == NULL) {
2529 xmlGenericError(xmlGenericErrorContext,
2530 "xmlAddRef: Table creation failed!\n");
2531 return(NULL);
2532 }
Owen Taylor3473f882001-02-23 17:55:21 +00002533
Daniel Veillard37721922001-05-04 15:21:12 +00002534 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2535 if (ret == NULL) {
2536 xmlGenericError(xmlGenericErrorContext,
2537 "xmlAddRef: out of memory\n");
2538 return(NULL);
2539 }
Owen Taylor3473f882001-02-23 17:55:21 +00002540
Daniel Veillard37721922001-05-04 15:21:12 +00002541 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002542 * fill the structure.
2543 */
Daniel Veillard37721922001-05-04 15:21:12 +00002544 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002545 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2546 /*
2547 * Operating in streaming mode, attr is gonna disapear
2548 */
2549 ret->name = xmlStrdup(attr->name);
2550 ret->attr = NULL;
2551 } else {
2552 ret->name = NULL;
2553 ret->attr = attr;
2554 }
2555 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002556
Daniel Veillard37721922001-05-04 15:21:12 +00002557 /* To add a reference :-
2558 * References are maintained as a list of references,
2559 * Lookup the entry, if no entry create new nodelist
2560 * Add the owning node to the NodeList
2561 * Return the ref
2562 */
Owen Taylor3473f882001-02-23 17:55:21 +00002563
Daniel Veillard37721922001-05-04 15:21:12 +00002564 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2565 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2566 xmlGenericError(xmlGenericErrorContext,
2567 "xmlAddRef: Reference list creation failed!\n");
2568 return(NULL);
2569 }
2570 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2571 xmlListDelete(ref_list);
2572 xmlGenericError(xmlGenericErrorContext,
2573 "xmlAddRef: Reference list insertion failed!\n");
2574 return(NULL);
2575 }
2576 }
2577 xmlListInsert(ref_list, ret);
2578 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002579}
2580
2581/**
2582 * xmlFreeRefTable:
2583 * @table: An ref table
2584 *
2585 * Deallocate the memory used by an Ref hash table.
2586 */
2587void
2588xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002589 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002590}
2591
2592/**
2593 * xmlIsRef:
2594 * @doc: the document
2595 * @elem: the element carrying the attribute
2596 * @attr: the attribute
2597 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002598 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002599 * then this is simple, otherwise we use an heuristic: name Ref (upper
2600 * or lowercase).
2601 *
2602 * Returns 0 or 1 depending on the lookup result
2603 */
2604int
2605xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002606 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2607 return(0);
2608 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2609 /* TODO @@@ */
2610 return(0);
2611 } else {
2612 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002613
Daniel Veillard37721922001-05-04 15:21:12 +00002614 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2615 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2616 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2617 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002618
Daniel Veillard37721922001-05-04 15:21:12 +00002619 if ((attrDecl != NULL) &&
2620 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2621 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2622 return(1);
2623 }
2624 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002625}
2626
2627/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002628 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002629 * @doc: the document
2630 * @attr: the attribute
2631 *
2632 * Remove the given attribute from the Ref table maintained internally.
2633 *
2634 * Returns -1 if the lookup failed and 0 otherwise
2635 */
2636int
2637xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002638 xmlListPtr ref_list;
2639 xmlRefTablePtr table;
2640 xmlChar *ID;
2641 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002642
Daniel Veillard37721922001-05-04 15:21:12 +00002643 if (doc == NULL) return(-1);
2644 if (attr == NULL) return(-1);
2645 table = (xmlRefTablePtr) doc->refs;
2646 if (table == NULL)
2647 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002648
Daniel Veillard37721922001-05-04 15:21:12 +00002649 if (attr == NULL)
2650 return(-1);
2651 ID = xmlNodeListGetString(doc, attr->children, 1);
2652 if (ID == NULL)
2653 return(-1);
2654 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002655
Daniel Veillard37721922001-05-04 15:21:12 +00002656 if(ref_list == NULL) {
2657 xmlFree(ID);
2658 return (-1);
2659 }
2660 /* At this point, ref_list refers to a list of references which
2661 * have the same key as the supplied attr. Our list of references
2662 * is ordered by reference address and we don't have that information
2663 * here to use when removing. We'll have to walk the list and
2664 * check for a matching attribute, when we find one stop the walk
2665 * and remove the entry.
2666 * The list is ordered by reference, so that means we don't have the
2667 * key. Passing the list and the reference to the walker means we
2668 * will have enough data to be able to remove the entry.
2669 */
2670 target.l = ref_list;
2671 target.ap = attr;
2672
2673 /* Remove the supplied attr from our list */
2674 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002675
Daniel Veillard37721922001-05-04 15:21:12 +00002676 /*If the list is empty then remove the list entry in the hash */
2677 if (xmlListEmpty(ref_list))
2678 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2679 xmlFreeRefList);
2680 xmlFree(ID);
2681 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002682}
2683
2684/**
2685 * xmlGetRefs:
2686 * @doc: pointer to the document
2687 * @ID: the ID value
2688 *
2689 * Find the set of references for the supplied ID.
2690 *
2691 * Returns NULL if not found, otherwise node set for the ID.
2692 */
2693xmlListPtr
2694xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002695 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002696
Daniel Veillard37721922001-05-04 15:21:12 +00002697 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002698 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002699 return(NULL);
2700 }
Owen Taylor3473f882001-02-23 17:55:21 +00002701
Daniel Veillard37721922001-05-04 15:21:12 +00002702 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002703 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002704 return(NULL);
2705 }
Owen Taylor3473f882001-02-23 17:55:21 +00002706
Daniel Veillard37721922001-05-04 15:21:12 +00002707 table = (xmlRefTablePtr) doc->refs;
2708 if (table == NULL)
2709 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002710
Daniel Veillard37721922001-05-04 15:21:12 +00002711 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002712}
2713
2714/************************************************************************
2715 * *
2716 * Routines for validity checking *
2717 * *
2718 ************************************************************************/
2719
2720/**
2721 * xmlGetDtdElementDesc:
2722 * @dtd: a pointer to the DtD to search
2723 * @name: the element name
2724 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002725 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002726 *
2727 * returns the xmlElementPtr if found or NULL
2728 */
2729
2730xmlElementPtr
2731xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2732 xmlElementTablePtr table;
2733 xmlElementPtr cur;
2734 xmlChar *uqname = NULL, *prefix = NULL;
2735
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00002736 if ((dtd == NULL) || (name == NULL)) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002737 if (dtd->elements == NULL)
2738 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002739 table = (xmlElementTablePtr) dtd->elements;
2740
2741 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002742 if (uqname != NULL)
2743 name = uqname;
2744 cur = xmlHashLookup2(table, name, prefix);
2745 if (prefix != NULL) xmlFree(prefix);
2746 if (uqname != NULL) xmlFree(uqname);
2747 return(cur);
2748}
2749/**
2750 * xmlGetDtdElementDesc2:
2751 * @dtd: a pointer to the DtD to search
2752 * @name: the element name
2753 * @create: create an empty description if not found
2754 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002755 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002756 *
2757 * returns the xmlElementPtr if found or NULL
2758 */
2759
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002760static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002761xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2762 xmlElementTablePtr table;
2763 xmlElementPtr cur;
2764 xmlChar *uqname = NULL, *prefix = NULL;
2765
2766 if (dtd == NULL) return(NULL);
2767 if (dtd->elements == NULL) {
2768 if (!create)
2769 return(NULL);
2770 /*
2771 * Create the Element table if needed.
2772 */
2773 table = (xmlElementTablePtr) dtd->elements;
2774 if (table == NULL) {
2775 table = xmlCreateElementTable();
2776 dtd->elements = (void *) table;
2777 }
2778 if (table == NULL) {
2779 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002780 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002781 return(NULL);
2782 }
2783 }
2784 table = (xmlElementTablePtr) dtd->elements;
2785
2786 uqname = xmlSplitQName2(name, &prefix);
2787 if (uqname != NULL)
2788 name = uqname;
2789 cur = xmlHashLookup2(table, name, prefix);
2790 if ((cur == NULL) && (create)) {
2791 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2792 if (cur == NULL) {
2793 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002794 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002795 return(NULL);
2796 }
2797 memset(cur, 0, sizeof(xmlElement));
2798 cur->type = XML_ELEMENT_DECL;
2799
2800 /*
2801 * fill the structure.
2802 */
2803 cur->name = xmlStrdup(name);
2804 cur->prefix = xmlStrdup(prefix);
2805 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2806
2807 xmlHashAddEntry2(table, name, prefix, cur);
2808 }
2809 if (prefix != NULL) xmlFree(prefix);
2810 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002811 return(cur);
2812}
2813
2814/**
2815 * xmlGetDtdQElementDesc:
2816 * @dtd: a pointer to the DtD to search
2817 * @name: the element name
2818 * @prefix: the element namespace prefix
2819 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002820 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002821 *
2822 * returns the xmlElementPtr if found or NULL
2823 */
2824
Daniel Veillard48da9102001-08-07 01:10:10 +00002825xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002826xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2827 const xmlChar *prefix) {
2828 xmlElementTablePtr table;
2829
2830 if (dtd == NULL) return(NULL);
2831 if (dtd->elements == NULL) return(NULL);
2832 table = (xmlElementTablePtr) dtd->elements;
2833
2834 return(xmlHashLookup2(table, name, prefix));
2835}
2836
2837/**
2838 * xmlGetDtdAttrDesc:
2839 * @dtd: a pointer to the DtD to search
2840 * @elem: the element name
2841 * @name: the attribute name
2842 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002843 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002844 * this element.
2845 *
2846 * returns the xmlAttributePtr if found or NULL
2847 */
2848
2849xmlAttributePtr
2850xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2851 xmlAttributeTablePtr table;
2852 xmlAttributePtr cur;
2853 xmlChar *uqname = NULL, *prefix = NULL;
2854
2855 if (dtd == NULL) return(NULL);
2856 if (dtd->attributes == NULL) return(NULL);
2857
2858 table = (xmlAttributeTablePtr) dtd->attributes;
2859 if (table == NULL)
2860 return(NULL);
2861
2862 uqname = xmlSplitQName2(name, &prefix);
2863
2864 if (uqname != NULL) {
2865 cur = xmlHashLookup3(table, uqname, prefix, elem);
2866 if (prefix != NULL) xmlFree(prefix);
2867 if (uqname != NULL) xmlFree(uqname);
2868 } else
2869 cur = xmlHashLookup3(table, name, NULL, elem);
2870 return(cur);
2871}
2872
2873/**
2874 * xmlGetDtdQAttrDesc:
2875 * @dtd: a pointer to the DtD to search
2876 * @elem: the element name
2877 * @name: the attribute name
2878 * @prefix: the attribute namespace prefix
2879 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002880 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002881 * this element.
2882 *
2883 * returns the xmlAttributePtr if found or NULL
2884 */
2885
Daniel Veillard48da9102001-08-07 01:10:10 +00002886xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002887xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2888 const xmlChar *prefix) {
2889 xmlAttributeTablePtr table;
2890
2891 if (dtd == NULL) return(NULL);
2892 if (dtd->attributes == NULL) return(NULL);
2893 table = (xmlAttributeTablePtr) dtd->attributes;
2894
2895 return(xmlHashLookup3(table, name, prefix, elem));
2896}
2897
2898/**
2899 * xmlGetDtdNotationDesc:
2900 * @dtd: a pointer to the DtD to search
2901 * @name: the notation name
2902 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002903 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002904 *
2905 * returns the xmlNotationPtr if found or NULL
2906 */
2907
2908xmlNotationPtr
2909xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2910 xmlNotationTablePtr table;
2911
2912 if (dtd == NULL) return(NULL);
2913 if (dtd->notations == NULL) return(NULL);
2914 table = (xmlNotationTablePtr) dtd->notations;
2915
2916 return(xmlHashLookup(table, name));
2917}
2918
2919/**
2920 * xmlValidateNotationUse:
2921 * @ctxt: the validation context
2922 * @doc: the document
2923 * @notationName: the notation name to check
2924 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002925 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002926 * - [ VC: Notation Declared ]
2927 *
2928 * returns 1 if valid or 0 otherwise
2929 */
2930
2931int
2932xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2933 const xmlChar *notationName) {
2934 xmlNotationPtr notaDecl;
2935 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2936
2937 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2938 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2939 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2940
Daniel Veillarde637c4a2003-03-30 21:10:09 +00002941 if ((notaDecl == NULL) && (ctxt != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00002942 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2943 notationName);
2944 return(0);
2945 }
2946 return(1);
2947}
2948
2949/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002950 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00002951 * @doc: the document
2952 * @name: the element name
2953 *
2954 * Search in the DtDs whether an element accept Mixed content (or ANY)
2955 * basically if it is supposed to accept text childs
2956 *
2957 * returns 0 if no, 1 if yes, and -1 if no element description is available
2958 */
2959
2960int
2961xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2962 xmlElementPtr elemDecl;
2963
2964 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2965
2966 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2967 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2968 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2969 if (elemDecl == NULL) return(-1);
2970 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002971 case XML_ELEMENT_TYPE_UNDEFINED:
2972 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002973 case XML_ELEMENT_TYPE_ELEMENT:
2974 return(0);
2975 case XML_ELEMENT_TYPE_EMPTY:
2976 /*
2977 * return 1 for EMPTY since we want VC error to pop up
2978 * on <empty> </empty> for example
2979 */
2980 case XML_ELEMENT_TYPE_ANY:
2981 case XML_ELEMENT_TYPE_MIXED:
2982 return(1);
2983 }
2984 return(1);
2985}
2986
2987/**
2988 * xmlValidateNameValue:
2989 * @value: an Name value
2990 *
2991 * Validate that the given value match Name production
2992 *
2993 * returns 1 if valid or 0 otherwise
2994 */
2995
Daniel Veillard9b731d72002-04-14 12:56:08 +00002996int
Owen Taylor3473f882001-02-23 17:55:21 +00002997xmlValidateNameValue(const xmlChar *value) {
2998 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002999 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003000
3001 if (value == NULL) return(0);
3002 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003003 val = xmlStringCurrentChar(NULL, cur, &len);
3004 cur += len;
3005 if (!IS_LETTER(val) && (val != '_') &&
3006 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003007 return(0);
3008 }
3009
Daniel Veillardd8224e02002-01-13 15:43:22 +00003010 val = xmlStringCurrentChar(NULL, cur, &len);
3011 cur += len;
3012 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3013 (val == '.') || (val == '-') ||
3014 (val == '_') || (val == ':') ||
3015 (IS_COMBINING(val)) ||
3016 (IS_EXTENDER(val))) {
3017 val = xmlStringCurrentChar(NULL, cur, &len);
3018 cur += len;
3019 }
Owen Taylor3473f882001-02-23 17:55:21 +00003020
Daniel Veillardd8224e02002-01-13 15:43:22 +00003021 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003022
3023 return(1);
3024}
3025
3026/**
3027 * xmlValidateNamesValue:
3028 * @value: an Names value
3029 *
3030 * Validate that the given value match Names production
3031 *
3032 * returns 1 if valid or 0 otherwise
3033 */
3034
Daniel Veillard9b731d72002-04-14 12:56:08 +00003035int
Owen Taylor3473f882001-02-23 17:55:21 +00003036xmlValidateNamesValue(const xmlChar *value) {
3037 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003038 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003039
3040 if (value == NULL) return(0);
3041 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003042 val = xmlStringCurrentChar(NULL, cur, &len);
3043 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003044
Daniel Veillardd8224e02002-01-13 15:43:22 +00003045 if (!IS_LETTER(val) && (val != '_') &&
3046 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003047 return(0);
3048 }
3049
Daniel Veillardd8224e02002-01-13 15:43:22 +00003050 val = xmlStringCurrentChar(NULL, cur, &len);
3051 cur += len;
3052 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3053 (val == '.') || (val == '-') ||
3054 (val == '_') || (val == ':') ||
3055 (IS_COMBINING(val)) ||
3056 (IS_EXTENDER(val))) {
3057 val = xmlStringCurrentChar(NULL, cur, &len);
3058 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003059 }
3060
Daniel Veillardd8224e02002-01-13 15:43:22 +00003061 while (IS_BLANK(val)) {
3062 while (IS_BLANK(val)) {
3063 val = xmlStringCurrentChar(NULL, cur, &len);
3064 cur += len;
3065 }
3066
3067 if (!IS_LETTER(val) && (val != '_') &&
3068 (val != ':')) {
3069 return(0);
3070 }
3071 val = xmlStringCurrentChar(NULL, cur, &len);
3072 cur += len;
3073
3074 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3075 (val == '.') || (val == '-') ||
3076 (val == '_') || (val == ':') ||
3077 (IS_COMBINING(val)) ||
3078 (IS_EXTENDER(val))) {
3079 val = xmlStringCurrentChar(NULL, cur, &len);
3080 cur += len;
3081 }
3082 }
3083
3084 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003085
3086 return(1);
3087}
3088
3089/**
3090 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003091 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003092 *
3093 * Validate that the given value match Nmtoken production
3094 *
3095 * [ VC: Name Token ]
3096 *
3097 * returns 1 if valid or 0 otherwise
3098 */
3099
Daniel Veillard9b731d72002-04-14 12:56:08 +00003100int
Owen Taylor3473f882001-02-23 17:55:21 +00003101xmlValidateNmtokenValue(const xmlChar *value) {
3102 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003103 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003104
3105 if (value == NULL) return(0);
3106 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003107 val = xmlStringCurrentChar(NULL, cur, &len);
3108 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003109
Daniel Veillardd8224e02002-01-13 15:43:22 +00003110 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3111 (val != '.') && (val != '-') &&
3112 (val != '_') && (val != ':') &&
3113 (!IS_COMBINING(val)) &&
3114 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003115 return(0);
3116
Daniel Veillardd8224e02002-01-13 15:43:22 +00003117 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3118 (val == '.') || (val == '-') ||
3119 (val == '_') || (val == ':') ||
3120 (IS_COMBINING(val)) ||
3121 (IS_EXTENDER(val))) {
3122 val = xmlStringCurrentChar(NULL, cur, &len);
3123 cur += len;
3124 }
Owen Taylor3473f882001-02-23 17:55:21 +00003125
Daniel Veillardd8224e02002-01-13 15:43:22 +00003126 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003127
3128 return(1);
3129}
3130
3131/**
3132 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003133 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003134 *
3135 * Validate that the given value match Nmtokens production
3136 *
3137 * [ VC: Name Token ]
3138 *
3139 * returns 1 if valid or 0 otherwise
3140 */
3141
Daniel Veillard9b731d72002-04-14 12:56:08 +00003142int
Owen Taylor3473f882001-02-23 17:55:21 +00003143xmlValidateNmtokensValue(const xmlChar *value) {
3144 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003145 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003146
3147 if (value == NULL) return(0);
3148 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003149 val = xmlStringCurrentChar(NULL, cur, &len);
3150 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003151
Daniel Veillardd8224e02002-01-13 15:43:22 +00003152 while (IS_BLANK(val)) {
3153 val = xmlStringCurrentChar(NULL, cur, &len);
3154 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003155 }
3156
Daniel Veillardd8224e02002-01-13 15:43:22 +00003157 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3158 (val != '.') && (val != '-') &&
3159 (val != '_') && (val != ':') &&
3160 (!IS_COMBINING(val)) &&
3161 (!IS_EXTENDER(val)))
3162 return(0);
3163
3164 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3165 (val == '.') || (val == '-') ||
3166 (val == '_') || (val == ':') ||
3167 (IS_COMBINING(val)) ||
3168 (IS_EXTENDER(val))) {
3169 val = xmlStringCurrentChar(NULL, cur, &len);
3170 cur += len;
3171 }
3172
3173 while (IS_BLANK(val)) {
3174 while (IS_BLANK(val)) {
3175 val = xmlStringCurrentChar(NULL, cur, &len);
3176 cur += len;
3177 }
3178 if (val == 0) return(1);
3179
3180 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3181 (val != '.') && (val != '-') &&
3182 (val != '_') && (val != ':') &&
3183 (!IS_COMBINING(val)) &&
3184 (!IS_EXTENDER(val)))
3185 return(0);
3186
3187 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3188 (val == '.') || (val == '-') ||
3189 (val == '_') || (val == ':') ||
3190 (IS_COMBINING(val)) ||
3191 (IS_EXTENDER(val))) {
3192 val = xmlStringCurrentChar(NULL, cur, &len);
3193 cur += len;
3194 }
3195 }
3196
3197 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003198
3199 return(1);
3200}
3201
3202/**
3203 * xmlValidateNotationDecl:
3204 * @ctxt: the validation context
3205 * @doc: a document instance
3206 * @nota: a notation definition
3207 *
3208 * Try to validate a single notation definition
3209 * basically it does the following checks as described by the
3210 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003211 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003212 * But this function get called anyway ...
3213 *
3214 * returns 1 if valid or 0 otherwise
3215 */
3216
3217int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003218xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3219 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003220 int ret = 1;
3221
3222 return(ret);
3223}
3224
3225/**
3226 * xmlValidateAttributeValue:
3227 * @type: an attribute type
3228 * @value: an attribute value
3229 *
3230 * Validate that the given attribute value match the proper production
3231 *
3232 * [ VC: ID ]
3233 * Values of type ID must match the Name production....
3234 *
3235 * [ VC: IDREF ]
3236 * Values of type IDREF must match the Name production, and values
3237 * of type IDREFS must match Names ...
3238 *
3239 * [ VC: Entity Name ]
3240 * Values of type ENTITY must match the Name production, values
3241 * of type ENTITIES must match Names ...
3242 *
3243 * [ VC: Name Token ]
3244 * Values of type NMTOKEN must match the Nmtoken production; values
3245 * of type NMTOKENS must match Nmtokens.
3246 *
3247 * returns 1 if valid or 0 otherwise
3248 */
3249
3250int
3251xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3252 switch (type) {
3253 case XML_ATTRIBUTE_ENTITIES:
3254 case XML_ATTRIBUTE_IDREFS:
3255 return(xmlValidateNamesValue(value));
3256 case XML_ATTRIBUTE_ENTITY:
3257 case XML_ATTRIBUTE_IDREF:
3258 case XML_ATTRIBUTE_ID:
3259 case XML_ATTRIBUTE_NOTATION:
3260 return(xmlValidateNameValue(value));
3261 case XML_ATTRIBUTE_NMTOKENS:
3262 case XML_ATTRIBUTE_ENUMERATION:
3263 return(xmlValidateNmtokensValue(value));
3264 case XML_ATTRIBUTE_NMTOKEN:
3265 return(xmlValidateNmtokenValue(value));
3266 case XML_ATTRIBUTE_CDATA:
3267 break;
3268 }
3269 return(1);
3270}
3271
3272/**
3273 * xmlValidateAttributeValue2:
3274 * @ctxt: the validation context
3275 * @doc: the document
3276 * @name: the attribute name (used for error reporting only)
3277 * @type: the attribute type
3278 * @value: the attribute value
3279 *
3280 * Validate that the given attribute value match a given type.
3281 * This typically cannot be done before having finished parsing
3282 * the subsets.
3283 *
3284 * [ VC: IDREF ]
3285 * Values of type IDREF must match one of the declared IDs
3286 * Values of type IDREFS must match a sequence of the declared IDs
3287 * each Name must match the value of an ID attribute on some element
3288 * in the XML document; i.e. IDREF values must match the value of
3289 * some ID attribute
3290 *
3291 * [ VC: Entity Name ]
3292 * Values of type ENTITY must match one declared entity
3293 * Values of type ENTITIES must match a sequence of declared entities
3294 *
3295 * [ VC: Notation Attributes ]
3296 * all notation names in the declaration must be declared.
3297 *
3298 * returns 1 if valid or 0 otherwise
3299 */
3300
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003301static int
Owen Taylor3473f882001-02-23 17:55:21 +00003302xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3303 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3304 int ret = 1;
3305 switch (type) {
3306 case XML_ATTRIBUTE_IDREFS:
3307 case XML_ATTRIBUTE_IDREF:
3308 case XML_ATTRIBUTE_ID:
3309 case XML_ATTRIBUTE_NMTOKENS:
3310 case XML_ATTRIBUTE_ENUMERATION:
3311 case XML_ATTRIBUTE_NMTOKEN:
3312 case XML_ATTRIBUTE_CDATA:
3313 break;
3314 case XML_ATTRIBUTE_ENTITY: {
3315 xmlEntityPtr ent;
3316
3317 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003318 if ((ent == NULL) && (doc->standalone == 1)) {
3319 doc->standalone = 0;
3320 ent = xmlGetDocEntity(doc, value);
3321 if (ent != NULL) {
3322 VERROR(ctxt->userData,
3323"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3324 name, value);
3325 /* WAIT to get answer from the Core WG on this
3326 ret = 0;
3327 */
3328 }
3329 }
Owen Taylor3473f882001-02-23 17:55:21 +00003330 if (ent == NULL) {
3331 VERROR(ctxt->userData,
3332 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3333 name, value);
3334 ret = 0;
3335 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3336 VERROR(ctxt->userData,
3337 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3338 name, value);
3339 ret = 0;
3340 }
3341 break;
3342 }
3343 case XML_ATTRIBUTE_ENTITIES: {
3344 xmlChar *dup, *nam = NULL, *cur, save;
3345 xmlEntityPtr ent;
3346
3347 dup = xmlStrdup(value);
3348 if (dup == NULL)
3349 return(0);
3350 cur = dup;
3351 while (*cur != 0) {
3352 nam = cur;
3353 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3354 save = *cur;
3355 *cur = 0;
3356 ent = xmlGetDocEntity(doc, nam);
3357 if (ent == NULL) {
3358 VERROR(ctxt->userData,
3359 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3360 name, nam);
3361 ret = 0;
3362 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3363 VERROR(ctxt->userData,
3364 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3365 name, nam);
3366 ret = 0;
3367 }
3368 if (save == 0)
3369 break;
3370 *cur = save;
3371 while (IS_BLANK(*cur)) cur++;
3372 }
3373 xmlFree(dup);
3374 break;
3375 }
3376 case XML_ATTRIBUTE_NOTATION: {
3377 xmlNotationPtr nota;
3378
3379 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3380 if ((nota == NULL) && (doc->extSubset != NULL))
3381 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3382
3383 if (nota == NULL) {
3384 VERROR(ctxt->userData,
3385 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3386 name, value);
3387 ret = 0;
3388 }
3389 break;
3390 }
3391 }
3392 return(ret);
3393}
3394
3395/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003396 * xmlValidCtxtNormalizeAttributeValue:
3397 * @ctxt: the validation context
3398 * @doc: the document
3399 * @elem: the parent
3400 * @name: the attribute name
3401 * @value: the attribute value
3402 * @ctxt: the validation context or NULL
3403 *
3404 * Does the validation related extra step of the normalization of attribute
3405 * values:
3406 *
3407 * If the declared value is not CDATA, then the XML processor must further
3408 * process the normalized attribute value by discarding any leading and
3409 * trailing space (#x20) characters, and by replacing sequences of space
3410 * (#x20) characters by single space (#x20) character.
3411 *
3412 * Also check VC: Standalone Document Declaration in P32, and update
3413 * ctxt->valid accordingly
3414 *
3415 * returns a new normalized string if normalization is needed, NULL otherwise
3416 * the caller must free the returned value.
3417 */
3418
3419xmlChar *
3420xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3421 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3422 xmlChar *ret, *dst;
3423 const xmlChar *src;
3424 xmlAttributePtr attrDecl = NULL;
3425 int extsubset = 0;
3426
3427 if (doc == NULL) return(NULL);
3428 if (elem == NULL) return(NULL);
3429 if (name == NULL) return(NULL);
3430 if (value == NULL) return(NULL);
3431
3432 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003433 xmlChar fn[50];
3434 xmlChar *fullname;
3435
3436 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3437 if (fullname == NULL)
3438 return(0);
3439 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003440 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003441 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003442 if (attrDecl != NULL)
3443 extsubset = 1;
3444 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003445 if ((fullname != fn) && (fullname != elem->name))
3446 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003447 }
3448 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3449 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3450 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3451 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3452 if (attrDecl != NULL)
3453 extsubset = 1;
3454 }
3455
3456 if (attrDecl == NULL)
3457 return(NULL);
3458 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3459 return(NULL);
3460
3461 ret = xmlStrdup(value);
3462 if (ret == NULL)
3463 return(NULL);
3464 src = value;
3465 dst = ret;
3466 while (*src == 0x20) src++;
3467 while (*src != 0) {
3468 if (*src == 0x20) {
3469 while (*src == 0x20) src++;
3470 if (*src != 0)
3471 *dst++ = 0x20;
3472 } else {
3473 *dst++ = *src++;
3474 }
3475 }
3476 *dst = 0;
3477 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3478 VERROR(ctxt->userData,
3479"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3480 name, elem->name);
3481 ctxt->valid = 0;
3482 }
3483 return(ret);
3484}
3485
3486/**
Owen Taylor3473f882001-02-23 17:55:21 +00003487 * xmlValidNormalizeAttributeValue:
3488 * @doc: the document
3489 * @elem: the parent
3490 * @name: the attribute name
3491 * @value: the attribute value
3492 *
3493 * Does the validation related extra step of the normalization of attribute
3494 * values:
3495 *
3496 * If the declared value is not CDATA, then the XML processor must further
3497 * process the normalized attribute value by discarding any leading and
3498 * trailing space (#x20) characters, and by replacing sequences of space
3499 * (#x20) characters by single space (#x20) character.
3500 *
3501 * returns a new normalized string if normalization is needed, NULL otherwise
3502 * the caller must free the returned value.
3503 */
3504
3505xmlChar *
3506xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3507 const xmlChar *name, const xmlChar *value) {
3508 xmlChar *ret, *dst;
3509 const xmlChar *src;
3510 xmlAttributePtr attrDecl = NULL;
3511
3512 if (doc == NULL) return(NULL);
3513 if (elem == NULL) return(NULL);
3514 if (name == NULL) return(NULL);
3515 if (value == NULL) return(NULL);
3516
3517 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003518 xmlChar fn[50];
3519 xmlChar *fullname;
3520
3521 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3522 if (fullname == NULL)
3523 return(0);
3524 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003525 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003526 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3527 if ((fullname != fn) && (fullname != elem->name))
3528 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003529 }
3530 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3531 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3532 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3533
3534 if (attrDecl == NULL)
3535 return(NULL);
3536 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3537 return(NULL);
3538
3539 ret = xmlStrdup(value);
3540 if (ret == NULL)
3541 return(NULL);
3542 src = value;
3543 dst = ret;
3544 while (*src == 0x20) src++;
3545 while (*src != 0) {
3546 if (*src == 0x20) {
3547 while (*src == 0x20) src++;
3548 if (*src != 0)
3549 *dst++ = 0x20;
3550 } else {
3551 *dst++ = *src++;
3552 }
3553 }
3554 *dst = 0;
3555 return(ret);
3556}
3557
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003558static void
Owen Taylor3473f882001-02-23 17:55:21 +00003559xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003560 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003561 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3562}
3563
3564/**
3565 * xmlValidateAttributeDecl:
3566 * @ctxt: the validation context
3567 * @doc: a document instance
3568 * @attr: an attribute definition
3569 *
3570 * Try to validate a single attribute definition
3571 * basically it does the following checks as described by the
3572 * XML-1.0 recommendation:
3573 * - [ VC: Attribute Default Legal ]
3574 * - [ VC: Enumeration ]
3575 * - [ VC: ID Attribute Default ]
3576 *
3577 * The ID/IDREF uniqueness and matching are done separately
3578 *
3579 * returns 1 if valid or 0 otherwise
3580 */
3581
3582int
3583xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3584 xmlAttributePtr attr) {
3585 int ret = 1;
3586 int val;
3587 CHECK_DTD;
3588 if(attr == NULL) return(1);
3589
3590 /* Attribute Default Legal */
3591 /* Enumeration */
3592 if (attr->defaultValue != NULL) {
3593 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3594 if (val == 0) {
3595 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003596 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003597 attr->name, attr->elem);
3598 }
3599 ret &= val;
3600 }
3601
3602 /* ID Attribute Default */
3603 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3604 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3605 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3606 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003607 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003608 attr->name, attr->elem);
3609 ret = 0;
3610 }
3611
3612 /* One ID per Element Type */
3613 if (attr->atype == XML_ATTRIBUTE_ID) {
3614 int nbId;
3615
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003616 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003617 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3618 attr->elem);
3619 if (elem != NULL) {
3620 nbId = xmlScanIDAttributeDecl(NULL, elem);
3621 } else {
3622 xmlAttributeTablePtr table;
3623
3624 /*
3625 * The attribute may be declared in the internal subset and the
3626 * element in the external subset.
3627 */
3628 nbId = 0;
3629 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3630 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3631 xmlValidateAttributeIdCallback, &nbId);
3632 }
3633 if (nbId > 1) {
3634 VERROR(ctxt->userData,
3635 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3636 attr->elem, nbId, attr->name);
3637 } else if (doc->extSubset != NULL) {
3638 int extId = 0;
3639 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3640 if (elem != NULL) {
3641 extId = xmlScanIDAttributeDecl(NULL, elem);
3642 }
3643 if (extId > 1) {
3644 VERROR(ctxt->userData,
3645 "Element %s has %d ID attribute defined in the external subset : %s\n",
3646 attr->elem, extId, attr->name);
3647 } else if (extId + nbId > 1) {
3648 VERROR(ctxt->userData,
3649"Element %s has ID attributes defined in the internal and external subset : %s\n",
3650 attr->elem, attr->name);
3651 }
3652 }
3653 }
3654
3655 /* Validity Constraint: Enumeration */
3656 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3657 xmlEnumerationPtr tree = attr->tree;
3658 while (tree != NULL) {
3659 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3660 tree = tree->next;
3661 }
3662 if (tree == NULL) {
3663 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003664"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003665 attr->defaultValue, attr->name, attr->elem);
3666 ret = 0;
3667 }
3668 }
3669
3670 return(ret);
3671}
3672
3673/**
3674 * xmlValidateElementDecl:
3675 * @ctxt: the validation context
3676 * @doc: a document instance
3677 * @elem: an element definition
3678 *
3679 * Try to validate a single element definition
3680 * basically it does the following checks as described by the
3681 * XML-1.0 recommendation:
3682 * - [ VC: One ID per Element Type ]
3683 * - [ VC: No Duplicate Types ]
3684 * - [ VC: Unique Element Type Declaration ]
3685 *
3686 * returns 1 if valid or 0 otherwise
3687 */
3688
3689int
3690xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3691 xmlElementPtr elem) {
3692 int ret = 1;
3693 xmlElementPtr tst;
3694
3695 CHECK_DTD;
3696
3697 if (elem == NULL) return(1);
3698
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003699#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003700#ifdef LIBXML_REGEXP_ENABLED
3701 /* Build the regexp associated to the content model */
3702 ret = xmlValidBuildContentModel(ctxt, elem);
3703#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003704#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003705
Owen Taylor3473f882001-02-23 17:55:21 +00003706 /* No Duplicate Types */
3707 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3708 xmlElementContentPtr cur, next;
3709 const xmlChar *name;
3710
3711 cur = elem->content;
3712 while (cur != NULL) {
3713 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3714 if (cur->c1 == NULL) break;
3715 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3716 name = cur->c1->name;
3717 next = cur->c2;
3718 while (next != NULL) {
3719 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard7b68df92003-08-03 22:58:54 +00003720 if ((xmlStrEqual(next->name, name)) &&
3721 (xmlStrEqual(next->prefix, cur->prefix))) {
3722 if (cur->prefix == NULL) {
3723 VERROR(ctxt->userData,
Owen Taylor3473f882001-02-23 17:55:21 +00003724 "Definition of %s has duplicate references of %s\n",
Daniel Veillard7b68df92003-08-03 22:58:54 +00003725 elem->name, name);
3726 } else {
3727 VERROR(ctxt->userData,
3728 "Definition of %s has duplicate references of %s:%s\n",
3729 elem->name, cur->prefix, name);
3730 }
Owen Taylor3473f882001-02-23 17:55:21 +00003731 ret = 0;
3732 }
3733 break;
3734 }
3735 if (next->c1 == NULL) break;
3736 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
Daniel Veillard7b68df92003-08-03 22:58:54 +00003737 if ((xmlStrEqual(next->c1->name, name)) &&
3738 (xmlStrEqual(next->c1->prefix, cur->prefix))) {
3739 if (cur->prefix == NULL) {
3740 VERROR(ctxt->userData,
3741 "Definition of %s has duplicate references to %s\n",
3742 elem->name, name);
3743 } else {
3744 VERROR(ctxt->userData,
3745 "Definition of %s has duplicate references to %s:%s\n",
3746 elem->name, cur->prefix, name);
3747 }
Owen Taylor3473f882001-02-23 17:55:21 +00003748 ret = 0;
3749 }
3750 next = next->c2;
3751 }
3752 }
3753 cur = cur->c2;
3754 }
3755 }
3756
3757 /* VC: Unique Element Type Declaration */
3758 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003759 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003760 ((tst->prefix == elem->prefix) ||
3761 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003762 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003763 VERROR(ctxt->userData, "Redefinition of element %s\n",
3764 elem->name);
3765 ret = 0;
3766 }
3767 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003768 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003769 ((tst->prefix == elem->prefix) ||
3770 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003771 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003772 VERROR(ctxt->userData, "Redefinition of element %s\n",
3773 elem->name);
3774 ret = 0;
3775 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003776 /* One ID per Element Type
3777 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003778 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3779 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003780 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003781 return(ret);
3782}
3783
3784/**
3785 * xmlValidateOneAttribute:
3786 * @ctxt: the validation context
3787 * @doc: a document instance
3788 * @elem: an element instance
3789 * @attr: an attribute instance
3790 * @value: the attribute value (without entities processing)
3791 *
3792 * Try to validate a single attribute for an element
3793 * basically it does the following checks as described by the
3794 * XML-1.0 recommendation:
3795 * - [ VC: Attribute Value Type ]
3796 * - [ VC: Fixed Attribute Default ]
3797 * - [ VC: Entity Name ]
3798 * - [ VC: Name Token ]
3799 * - [ VC: ID ]
3800 * - [ VC: IDREF ]
3801 * - [ VC: Entity Name ]
3802 * - [ VC: Notation Attributes ]
3803 *
3804 * The ID/IDREF uniqueness and matching are done separately
3805 *
3806 * returns 1 if valid or 0 otherwise
3807 */
3808
3809int
3810xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3811 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3812 /* xmlElementPtr elemDecl; */
3813 xmlAttributePtr attrDecl = NULL;
3814 int val;
3815 int ret = 1;
3816
3817 CHECK_DTD;
3818 if ((elem == NULL) || (elem->name == NULL)) return(0);
3819 if ((attr == NULL) || (attr->name == NULL)) return(0);
3820
3821 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003822 xmlChar fn[50];
3823 xmlChar *fullname;
3824
3825 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3826 if (fullname == NULL)
3827 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003828 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003829 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003830 attr->name, attr->ns->prefix);
3831 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003832 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003833 attr->name, attr->ns->prefix);
3834 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003835 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003836 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3837 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00003838 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003839 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003840 if ((fullname != fn) && (fullname != elem->name))
3841 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003842 }
3843 if (attrDecl == NULL) {
3844 if (attr->ns != NULL) {
3845 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3846 attr->name, attr->ns->prefix);
3847 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3848 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3849 attr->name, attr->ns->prefix);
3850 } else {
3851 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3852 elem->name, attr->name);
3853 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3854 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3855 elem->name, attr->name);
3856 }
3857 }
3858
3859
3860 /* Validity Constraint: Attribute Value Type */
3861 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003862 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003863 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003864 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003865 attr->name, elem->name);
3866 return(0);
3867 }
3868 attr->atype = attrDecl->atype;
3869
3870 val = xmlValidateAttributeValue(attrDecl->atype, value);
3871 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003872 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003873 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003874 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003875 attr->name, elem->name);
3876 ret = 0;
3877 }
3878
3879 /* Validity constraint: Fixed Attribute Default */
3880 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3881 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003882 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003883 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003884 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003885 attr->name, elem->name, attrDecl->defaultValue);
3886 ret = 0;
3887 }
3888 }
3889
3890 /* Validity Constraint: ID uniqueness */
3891 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3892 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3893 ret = 0;
3894 }
3895
3896 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3897 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3898 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3899 ret = 0;
3900 }
3901
3902 /* Validity Constraint: Notation Attributes */
3903 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3904 xmlEnumerationPtr tree = attrDecl->tree;
3905 xmlNotationPtr nota;
3906
3907 /* First check that the given NOTATION was declared */
3908 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3909 if (nota == NULL)
3910 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3911
3912 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003913 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003914 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003915 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003916 value, attr->name, elem->name);
3917 ret = 0;
3918 }
3919
3920 /* Second, verify that it's among the list */
3921 while (tree != NULL) {
3922 if (xmlStrEqual(tree->name, value)) break;
3923 tree = tree->next;
3924 }
3925 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003926 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003927 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003928"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003929 value, attr->name, elem->name);
3930 ret = 0;
3931 }
3932 }
3933
3934 /* Validity Constraint: Enumeration */
3935 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3936 xmlEnumerationPtr tree = attrDecl->tree;
3937 while (tree != NULL) {
3938 if (xmlStrEqual(tree->name, value)) break;
3939 tree = tree->next;
3940 }
3941 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003942 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003943 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003944 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003945 value, attr->name, elem->name);
3946 ret = 0;
3947 }
3948 }
3949
3950 /* Fixed Attribute Default */
3951 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3952 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003953 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003954 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003955 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003956 attr->name, elem->name, attrDecl->defaultValue);
3957 ret = 0;
3958 }
3959
3960 /* Extra check for the attribute value */
3961 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3962 attrDecl->atype, value);
3963
3964 return(ret);
3965}
3966
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003967/**
3968 * xmlValidateOneNamespace:
3969 * @ctxt: the validation context
3970 * @doc: a document instance
3971 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00003972 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003973 * @ns: an namespace declaration instance
3974 * @value: the attribute value (without entities processing)
3975 *
3976 * Try to validate a single namespace declaration for an element
3977 * basically it does the following checks as described by the
3978 * XML-1.0 recommendation:
3979 * - [ VC: Attribute Value Type ]
3980 * - [ VC: Fixed Attribute Default ]
3981 * - [ VC: Entity Name ]
3982 * - [ VC: Name Token ]
3983 * - [ VC: ID ]
3984 * - [ VC: IDREF ]
3985 * - [ VC: Entity Name ]
3986 * - [ VC: Notation Attributes ]
3987 *
3988 * The ID/IDREF uniqueness and matching are done separately
3989 *
3990 * returns 1 if valid or 0 otherwise
3991 */
3992
3993int
3994xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3995xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
3996 /* xmlElementPtr elemDecl; */
3997 xmlAttributePtr attrDecl = NULL;
3998 int val;
3999 int ret = 1;
4000
4001 CHECK_DTD;
4002 if ((elem == NULL) || (elem->name == NULL)) return(0);
4003 if ((ns == NULL) || (ns->href == NULL)) return(0);
4004
4005 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004006 xmlChar fn[50];
4007 xmlChar *fullname;
4008
4009 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
4010 if (fullname == NULL) {
4011 VERROR(ctxt->userData, "Out of memory\n");
4012 return(0);
4013 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004014 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004015 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004016 ns->prefix, BAD_CAST "xmlns");
4017 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004018 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004019 ns->prefix, BAD_CAST "xmlns");
4020 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004021 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004022 BAD_CAST "xmlns");
4023 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004024 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004025 BAD_CAST "xmlns");
4026 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004027 if ((fullname != fn) && (fullname != elem->name))
4028 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004029 }
4030 if (attrDecl == NULL) {
4031 if (ns->prefix != NULL) {
4032 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4033 ns->prefix, BAD_CAST "xmlns");
4034 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4035 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4036 ns->prefix, BAD_CAST "xmlns");
4037 } else {
4038 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4039 elem->name, BAD_CAST "xmlns");
4040 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4041 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4042 elem->name, BAD_CAST "xmlns");
4043 }
4044 }
4045
4046
4047 /* Validity Constraint: Attribute Value Type */
4048 if (attrDecl == NULL) {
4049 VECTXT(ctxt, elem);
4050 if (ns->prefix != NULL) {
4051 VERROR(ctxt->userData,
4052 "No declaration for attribute xmlns:%s of element %s\n",
4053 ns->prefix, elem->name);
4054 } else {
4055 VERROR(ctxt->userData,
4056 "No declaration for attribute xmlns of element %s\n",
4057 elem->name);
4058 }
4059 return(0);
4060 }
4061
4062 val = xmlValidateAttributeValue(attrDecl->atype, value);
4063 if (val == 0) {
4064 VECTXT(ctxt, elem);
4065 if (ns->prefix != NULL) {
4066 VERROR(ctxt->userData,
4067 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
4068 ns->prefix, elem->name);
4069 } else {
4070 VERROR(ctxt->userData,
4071 "Syntax of value for attribute xmlns of %s is not valid\n",
4072 elem->name);
4073 }
4074 ret = 0;
4075 }
4076
4077 /* Validity constraint: Fixed Attribute Default */
4078 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4079 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4080 VECTXT(ctxt, elem);
4081 if (ns->prefix != NULL) {
4082 VERROR(ctxt->userData,
4083 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4084 ns->prefix, elem->name, attrDecl->defaultValue);
4085 } else {
4086 VERROR(ctxt->userData,
4087 "Value for attribute xmlns of %s is different from default \"%s\"\n",
4088 elem->name, attrDecl->defaultValue);
4089 }
4090 ret = 0;
4091 }
4092 }
4093
4094 /* Validity Constraint: ID uniqueness */
4095 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4096 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4097 ret = 0;
4098 }
4099
4100 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4101 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4102 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4103 ret = 0;
4104 }
4105
4106 /* Validity Constraint: Notation Attributes */
4107 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4108 xmlEnumerationPtr tree = attrDecl->tree;
4109 xmlNotationPtr nota;
4110
4111 /* First check that the given NOTATION was declared */
4112 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4113 if (nota == NULL)
4114 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4115
4116 if (nota == NULL) {
4117 VECTXT(ctxt, elem);
4118 if (ns->prefix != NULL) {
4119 VERROR(ctxt->userData,
4120 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4121 value, ns->prefix, elem->name);
4122 } else {
4123 VERROR(ctxt->userData,
4124 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
4125 value, elem->name);
4126 }
4127 ret = 0;
4128 }
4129
4130 /* Second, verify that it's among the list */
4131 while (tree != NULL) {
4132 if (xmlStrEqual(tree->name, value)) break;
4133 tree = tree->next;
4134 }
4135 if (tree == NULL) {
4136 VECTXT(ctxt, elem);
4137 if (ns->prefix != NULL) {
4138 VERROR(ctxt->userData,
4139"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4140 value, ns->prefix, elem->name);
4141 } else {
4142 VERROR(ctxt->userData,
4143"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
4144 value, elem->name);
4145 }
4146 ret = 0;
4147 }
4148 }
4149
4150 /* Validity Constraint: Enumeration */
4151 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4152 xmlEnumerationPtr tree = attrDecl->tree;
4153 while (tree != NULL) {
4154 if (xmlStrEqual(tree->name, value)) break;
4155 tree = tree->next;
4156 }
4157 if (tree == NULL) {
4158 VECTXT(ctxt, elem);
4159 if (ns->prefix != NULL) {
4160 VERROR(ctxt->userData,
4161"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4162 value, ns->prefix, elem->name);
4163 } else {
4164 VERROR(ctxt->userData,
4165"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4166 value, elem->name);
4167 }
4168 ret = 0;
4169 }
4170 }
4171
4172 /* Fixed Attribute Default */
4173 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4174 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4175 VECTXT(ctxt, elem);
4176 if (ns->prefix != NULL) {
4177 VERROR(ctxt->userData,
4178 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4179 ns->prefix, elem->name, attrDecl->defaultValue);
4180 } else {
4181 VERROR(ctxt->userData,
4182 "Value for attribute xmlns of %s must be \"%s\"\n",
4183 elem->name, attrDecl->defaultValue);
4184 }
4185 ret = 0;
4186 }
4187
4188 /* Extra check for the attribute value */
4189 if (ns->prefix != NULL) {
4190 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4191 attrDecl->atype, value);
4192 } else {
4193 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4194 attrDecl->atype, value);
4195 }
4196
4197 return(ret);
4198}
4199
Daniel Veillard118aed72002-09-24 14:13:13 +00004200#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004201/**
4202 * xmlValidateSkipIgnorable:
4203 * @ctxt: the validation context
4204 * @child: the child list
4205 *
4206 * Skip ignorable elements w.r.t. the validation process
4207 *
4208 * returns the first element to consider for validation of the content model
4209 */
4210
4211static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004212xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004213 while (child != NULL) {
4214 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004215 /* These things are ignored (skipped) during validation. */
4216 case XML_PI_NODE:
4217 case XML_COMMENT_NODE:
4218 case XML_XINCLUDE_START:
4219 case XML_XINCLUDE_END:
4220 child = child->next;
4221 break;
4222 case XML_TEXT_NODE:
4223 if (xmlIsBlankNode(child))
4224 child = child->next;
4225 else
4226 return(child);
4227 break;
4228 /* keep current node */
4229 default:
4230 return(child);
4231 }
4232 }
4233 return(child);
4234}
4235
4236/**
4237 * xmlValidateElementType:
4238 * @ctxt: the validation context
4239 *
4240 * Try to validate the content model of an element internal function
4241 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004242 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4243 * reference is found and -3 if the validation succeeded but
4244 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004245 */
4246
4247static int
4248xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004249 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004250 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004251
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004252 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004253 if ((NODE == NULL) && (CONT == NULL))
4254 return(1);
4255 if ((NODE == NULL) &&
4256 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4257 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4258 return(1);
4259 }
4260 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004261 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004262 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004263
4264 /*
4265 * We arrive here when more states need to be examined
4266 */
4267cont:
4268
4269 /*
4270 * We just recovered from a rollback generated by a possible
4271 * epsilon transition, go directly to the analysis phase
4272 */
4273 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004274 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004275 DEBUG_VALID_STATE(NODE, CONT)
4276 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004277 goto analyze;
4278 }
4279
4280 DEBUG_VALID_STATE(NODE, CONT)
4281 /*
4282 * we may have to save a backup state here. This is the equivalent
4283 * of handling epsilon transition in NFAs.
4284 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004285 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004286 ((CONT->parent == NULL) ||
4287 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004288 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004289 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004290 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004291 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004292 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4293 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004294 }
4295
4296
4297 /*
4298 * Check first if the content matches
4299 */
4300 switch (CONT->type) {
4301 case XML_ELEMENT_CONTENT_PCDATA:
4302 if (NODE == NULL) {
4303 DEBUG_VALID_MSG("pcdata failed no node");
4304 ret = 0;
4305 break;
4306 }
4307 if (NODE->type == XML_TEXT_NODE) {
4308 DEBUG_VALID_MSG("pcdata found, skip to next");
4309 /*
4310 * go to next element in the content model
4311 * skipping ignorable elems
4312 */
4313 do {
4314 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004315 NODE = xmlValidateSkipIgnorable(NODE);
4316 if ((NODE != NULL) &&
4317 (NODE->type == XML_ENTITY_REF_NODE))
4318 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004319 } while ((NODE != NULL) &&
4320 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004321 (NODE->type != XML_TEXT_NODE) &&
4322 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004323 ret = 1;
4324 break;
4325 } else {
4326 DEBUG_VALID_MSG("pcdata failed");
4327 ret = 0;
4328 break;
4329 }
4330 break;
4331 case XML_ELEMENT_CONTENT_ELEMENT:
4332 if (NODE == NULL) {
4333 DEBUG_VALID_MSG("element failed no node");
4334 ret = 0;
4335 break;
4336 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004337 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4338 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004339 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004340 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4341 ret = (CONT->prefix == NULL);
4342 } else if (CONT->prefix == NULL) {
4343 ret = 0;
4344 } else {
4345 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4346 }
4347 }
4348 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004349 DEBUG_VALID_MSG("element found, skip to next");
4350 /*
4351 * go to next element in the content model
4352 * skipping ignorable elems
4353 */
4354 do {
4355 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004356 NODE = xmlValidateSkipIgnorable(NODE);
4357 if ((NODE != NULL) &&
4358 (NODE->type == XML_ENTITY_REF_NODE))
4359 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004360 } while ((NODE != NULL) &&
4361 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004362 (NODE->type != XML_TEXT_NODE) &&
4363 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004364 } else {
4365 DEBUG_VALID_MSG("element failed");
4366 ret = 0;
4367 break;
4368 }
4369 break;
4370 case XML_ELEMENT_CONTENT_OR:
4371 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004372 * Small optimization.
4373 */
4374 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4375 if ((NODE == NULL) ||
4376 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4377 DEPTH++;
4378 CONT = CONT->c2;
4379 goto cont;
4380 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004381 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4382 ret = (CONT->c1->prefix == NULL);
4383 } else if (CONT->c1->prefix == NULL) {
4384 ret = 0;
4385 } else {
4386 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4387 }
4388 if (ret == 0) {
4389 DEPTH++;
4390 CONT = CONT->c2;
4391 goto cont;
4392 }
Daniel Veillard85349052001-04-20 13:48:21 +00004393 }
4394
4395 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004396 * save the second branch 'or' branch
4397 */
4398 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004399 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4400 OCCURS, ROLLBACK_OR) < 0)
4401 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004402 DEPTH++;
4403 CONT = CONT->c1;
4404 goto cont;
4405 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004406 /*
4407 * Small optimization.
4408 */
4409 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4410 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4411 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4412 if ((NODE == NULL) ||
4413 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4414 DEPTH++;
4415 CONT = CONT->c2;
4416 goto cont;
4417 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004418 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4419 ret = (CONT->c1->prefix == NULL);
4420 } else if (CONT->c1->prefix == NULL) {
4421 ret = 0;
4422 } else {
4423 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4424 }
4425 if (ret == 0) {
4426 DEPTH++;
4427 CONT = CONT->c2;
4428 goto cont;
4429 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004430 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004431 DEPTH++;
4432 CONT = CONT->c1;
4433 goto cont;
4434 }
4435
4436 /*
4437 * At this point handle going up in the tree
4438 */
4439 if (ret == -1) {
4440 DEBUG_VALID_MSG("error found returning");
4441 return(ret);
4442 }
4443analyze:
4444 while (CONT != NULL) {
4445 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004446 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004447 * this level.
4448 */
4449 if (ret == 0) {
4450 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004451 xmlNodePtr cur;
4452
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004453 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004454 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004455 DEBUG_VALID_MSG("Once branch failed, rollback");
4456 if (vstateVPop(ctxt) < 0 ) {
4457 DEBUG_VALID_MSG("exhaustion, failed");
4458 return(0);
4459 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004460 if (cur != ctxt->vstate->node)
4461 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004462 goto cont;
4463 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004464 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004465 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004466 DEBUG_VALID_MSG("Plus branch failed, rollback");
4467 if (vstateVPop(ctxt) < 0 ) {
4468 DEBUG_VALID_MSG("exhaustion, failed");
4469 return(0);
4470 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004471 if (cur != ctxt->vstate->node)
4472 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004473 goto cont;
4474 }
4475 DEBUG_VALID_MSG("Plus branch found");
4476 ret = 1;
4477 break;
4478 case XML_ELEMENT_CONTENT_MULT:
4479#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004480 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004481 DEBUG_VALID_MSG("Mult branch failed");
4482 } else {
4483 DEBUG_VALID_MSG("Mult branch found");
4484 }
4485#endif
4486 ret = 1;
4487 break;
4488 case XML_ELEMENT_CONTENT_OPT:
4489 DEBUG_VALID_MSG("Option branch failed");
4490 ret = 1;
4491 break;
4492 }
4493 } else {
4494 switch (CONT->ocur) {
4495 case XML_ELEMENT_CONTENT_OPT:
4496 DEBUG_VALID_MSG("Option branch succeeded");
4497 ret = 1;
4498 break;
4499 case XML_ELEMENT_CONTENT_ONCE:
4500 DEBUG_VALID_MSG("Once branch succeeded");
4501 ret = 1;
4502 break;
4503 case XML_ELEMENT_CONTENT_PLUS:
4504 if (STATE == ROLLBACK_PARENT) {
4505 DEBUG_VALID_MSG("Plus branch rollback");
4506 ret = 1;
4507 break;
4508 }
4509 if (NODE == NULL) {
4510 DEBUG_VALID_MSG("Plus branch exhausted");
4511 ret = 1;
4512 break;
4513 }
4514 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004515 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004516 goto cont;
4517 case XML_ELEMENT_CONTENT_MULT:
4518 if (STATE == ROLLBACK_PARENT) {
4519 DEBUG_VALID_MSG("Mult branch rollback");
4520 ret = 1;
4521 break;
4522 }
4523 if (NODE == NULL) {
4524 DEBUG_VALID_MSG("Mult branch exhausted");
4525 ret = 1;
4526 break;
4527 }
4528 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004529 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004530 goto cont;
4531 }
4532 }
4533 STATE = 0;
4534
4535 /*
4536 * Then act accordingly at the parent level
4537 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004538 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004539 if (CONT->parent == NULL)
4540 break;
4541
4542 switch (CONT->parent->type) {
4543 case XML_ELEMENT_CONTENT_PCDATA:
4544 DEBUG_VALID_MSG("Error: parent pcdata");
4545 return(-1);
4546 case XML_ELEMENT_CONTENT_ELEMENT:
4547 DEBUG_VALID_MSG("Error: parent element");
4548 return(-1);
4549 case XML_ELEMENT_CONTENT_OR:
4550 if (ret == 1) {
4551 DEBUG_VALID_MSG("Or succeeded");
4552 CONT = CONT->parent;
4553 DEPTH--;
4554 } else {
4555 DEBUG_VALID_MSG("Or failed");
4556 CONT = CONT->parent;
4557 DEPTH--;
4558 }
4559 break;
4560 case XML_ELEMENT_CONTENT_SEQ:
4561 if (ret == 0) {
4562 DEBUG_VALID_MSG("Sequence failed");
4563 CONT = CONT->parent;
4564 DEPTH--;
4565 } else if (CONT == CONT->parent->c1) {
4566 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4567 CONT = CONT->parent->c2;
4568 goto cont;
4569 } else {
4570 DEBUG_VALID_MSG("Sequence succeeded");
4571 CONT = CONT->parent;
4572 DEPTH--;
4573 }
4574 }
4575 }
4576 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004577 xmlNodePtr cur;
4578
4579 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004580 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4581 if (vstateVPop(ctxt) < 0 ) {
4582 DEBUG_VALID_MSG("exhaustion, failed");
4583 return(0);
4584 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004585 if (cur != ctxt->vstate->node)
4586 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004587 goto cont;
4588 }
4589 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004590 xmlNodePtr cur;
4591
4592 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004593 DEBUG_VALID_MSG("Failure, rollback");
4594 if (vstateVPop(ctxt) < 0 ) {
4595 DEBUG_VALID_MSG("exhaustion, failed");
4596 return(0);
4597 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004598 if (cur != ctxt->vstate->node)
4599 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004600 goto cont;
4601 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004602 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004603}
Daniel Veillard23e73572002-09-19 19:56:43 +00004604#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004605
4606/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004607 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004608 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004609 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004610 * @content: An element
4611 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4612 *
4613 * This will dump the list of elements to the buffer
4614 * Intended just for the debug routine
4615 */
4616static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004617xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004618 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004619 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004620
4621 if (node == NULL) return;
4622 if (glob) strcat(buf, "(");
4623 cur = node;
4624 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004625 len = strlen(buf);
4626 if (size - len < 50) {
4627 if ((size - len > 4) && (buf[len - 1] != '.'))
4628 strcat(buf, " ...");
4629 return;
4630 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004631 switch (cur->type) {
4632 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004633 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004634 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004635 if ((size - len > 4) && (buf[len - 1] != '.'))
4636 strcat(buf, " ...");
4637 return;
4638 }
4639 strcat(buf, (char *) cur->ns->prefix);
4640 strcat(buf, ":");
4641 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004642 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004643 if ((size - len > 4) && (buf[len - 1] != '.'))
4644 strcat(buf, " ...");
4645 return;
4646 }
4647 strcat(buf, (char *) cur->name);
4648 if (cur->next != NULL)
4649 strcat(buf, " ");
4650 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004651 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004652 if (xmlIsBlankNode(cur))
4653 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004654 case XML_CDATA_SECTION_NODE:
4655 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004656 strcat(buf, "CDATA");
4657 if (cur->next != NULL)
4658 strcat(buf, " ");
4659 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004660 case XML_ATTRIBUTE_NODE:
4661 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004662#ifdef LIBXML_DOCB_ENABLED
4663 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004664#endif
4665 case XML_HTML_DOCUMENT_NODE:
4666 case XML_DOCUMENT_TYPE_NODE:
4667 case XML_DOCUMENT_FRAG_NODE:
4668 case XML_NOTATION_NODE:
4669 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004670 strcat(buf, "???");
4671 if (cur->next != NULL)
4672 strcat(buf, " ");
4673 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004674 case XML_ENTITY_NODE:
4675 case XML_PI_NODE:
4676 case XML_DTD_NODE:
4677 case XML_COMMENT_NODE:
4678 case XML_ELEMENT_DECL:
4679 case XML_ATTRIBUTE_DECL:
4680 case XML_ENTITY_DECL:
4681 case XML_XINCLUDE_START:
4682 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004683 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004684 }
4685 cur = cur->next;
4686 }
4687 if (glob) strcat(buf, ")");
4688}
4689
4690/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004691 * xmlValidateElementContent:
4692 * @ctxt: the validation context
4693 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004694 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004695 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004696 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004697 *
4698 * Try to validate the content model of an element
4699 *
4700 * returns 1 if valid or 0 if not and -1 in case of error
4701 */
4702
4703static int
4704xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004705 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004706 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004707#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004708 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004709#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004710 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004711 xmlElementContentPtr cont;
4712 const xmlChar *name;
4713
4714 if (elemDecl == NULL)
4715 return(-1);
4716 cont = elemDecl->content;
4717 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004718
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004719#ifdef LIBXML_REGEXP_ENABLED
4720 /* Build the regexp associated to the content model */
4721 if (elemDecl->contModel == NULL)
4722 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4723 if (elemDecl->contModel == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004724 return(-1);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004725 } else {
4726 xmlRegExecCtxtPtr exec;
4727
Daniel Veillardec498e12003-02-05 11:01:50 +00004728 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
4729 return(-1);
4730 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004731 ctxt->nodeMax = 0;
4732 ctxt->nodeNr = 0;
4733 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004734 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4735 if (exec != NULL) {
4736 cur = child;
4737 while (cur != NULL) {
4738 switch (cur->type) {
4739 case XML_ENTITY_REF_NODE:
4740 /*
4741 * Push the current node to be able to roll back
4742 * and process within the entity
4743 */
4744 if ((cur->children != NULL) &&
4745 (cur->children->children != NULL)) {
4746 nodeVPush(ctxt, cur);
4747 cur = cur->children->children;
4748 continue;
4749 }
4750 break;
4751 case XML_TEXT_NODE:
4752 if (xmlIsBlankNode(cur))
4753 break;
4754 ret = 0;
4755 goto fail;
4756 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004757 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004758 ret = 0;
4759 goto fail;
4760 case XML_ELEMENT_NODE:
4761 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004762 xmlChar fn[50];
4763 xmlChar *fullname;
4764
4765 fullname = xmlBuildQName(cur->name,
4766 cur->ns->prefix, fn, 50);
4767 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004768 ret = -1;
4769 goto fail;
4770 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004771 ret = xmlRegExecPushString(exec, fullname, NULL);
4772 if ((fullname != fn) && (fullname != cur->name))
4773 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004774 } else {
4775 ret = xmlRegExecPushString(exec, cur->name, NULL);
4776 }
4777 break;
4778 default:
4779 break;
4780 }
4781 /*
4782 * Switch to next element
4783 */
4784 cur = cur->next;
4785 while (cur == NULL) {
4786 cur = nodeVPop(ctxt);
4787 if (cur == NULL)
4788 break;
4789 cur = cur->next;
4790 }
4791 }
4792 ret = xmlRegExecPushString(exec, NULL, NULL);
4793fail:
4794 xmlRegFreeExecCtxt(exec);
4795 }
4796 }
4797#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004798 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004799 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004800 */
4801 ctxt->vstateMax = 8;
4802 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4803 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4804 if (ctxt->vstateTab == NULL) {
4805 xmlGenericError(xmlGenericErrorContext,
4806 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004807 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004808 }
4809 /*
4810 * The first entry in the stack is reserved to the current state
4811 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004812 ctxt->nodeMax = 0;
4813 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004814 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004815 ctxt->vstate = &ctxt->vstateTab[0];
4816 ctxt->vstateNr = 1;
4817 CONT = cont;
4818 NODE = child;
4819 DEPTH = 0;
4820 OCCURS = 0;
4821 STATE = 0;
4822 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004823 if ((ret == -3) && (warn)) {
4824 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004825 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004826 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004827 /*
4828 * An entities reference appeared at this level.
4829 * Buid a minimal representation of this node content
4830 * sufficient to run the validation process on it
4831 */
4832 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004833 cur = child;
4834 while (cur != NULL) {
4835 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004836 case XML_ENTITY_REF_NODE:
4837 /*
4838 * Push the current node to be able to roll back
4839 * and process within the entity
4840 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004841 if ((cur->children != NULL) &&
4842 (cur->children->children != NULL)) {
4843 nodeVPush(ctxt, cur);
4844 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004845 continue;
4846 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004847 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004848 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004849 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004850 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004851 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004852 case XML_CDATA_SECTION_NODE:
4853 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004854 case XML_ELEMENT_NODE:
4855 /*
4856 * Allocate a new node and minimally fills in
4857 * what's required
4858 */
4859 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4860 if (tmp == NULL) {
4861 xmlGenericError(xmlGenericErrorContext,
4862 "xmlValidateElementContent : malloc failed\n");
4863 xmlFreeNodeList(repl);
4864 ret = -1;
4865 goto done;
4866 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004867 tmp->type = cur->type;
4868 tmp->name = cur->name;
4869 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004870 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004871 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004872 if (repl == NULL)
4873 repl = last = tmp;
4874 else {
4875 last->next = tmp;
4876 last = tmp;
4877 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004878 if (cur->type == XML_CDATA_SECTION_NODE) {
4879 /*
4880 * E59 spaces in CDATA does not match the
4881 * nonterminal S
4882 */
4883 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4884 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004885 break;
4886 default:
4887 break;
4888 }
4889 /*
4890 * Switch to next element
4891 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004892 cur = cur->next;
4893 while (cur == NULL) {
4894 cur = nodeVPop(ctxt);
4895 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004896 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004897 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004898 }
4899 }
4900
4901 /*
4902 * Relaunch the validation
4903 */
4904 ctxt->vstate = &ctxt->vstateTab[0];
4905 ctxt->vstateNr = 1;
4906 CONT = cont;
4907 NODE = repl;
4908 DEPTH = 0;
4909 OCCURS = 0;
4910 STATE = 0;
4911 ret = xmlValidateElementType(ctxt);
4912 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004913#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004914 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004915 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4916 char expr[5000];
4917 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004918
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004919 expr[0] = 0;
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004920 xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004921 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004922#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004923 if (repl != NULL)
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004924 xmlSnprintfElements(&list[0], 5000, repl, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004925 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004926#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004927 xmlSnprintfElements(&list[0], 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004928
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004929 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004930 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004931 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004932 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004933 name, expr, list);
4934 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004935 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004936 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004937 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004938 expr, list);
4939 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004940 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004941 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004942 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004943 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004944 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004945 name);
4946 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004947 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004948 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004949 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004950 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004951 }
4952 ret = 0;
4953 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004954 if (ret == -3)
4955 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004956
Daniel Veillard23e73572002-09-19 19:56:43 +00004957#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004958done:
4959 /*
4960 * Deallocate the copy if done, and free up the validation stack
4961 */
4962 while (repl != NULL) {
4963 tmp = repl->next;
4964 xmlFree(repl);
4965 repl = tmp;
4966 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004967 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004968 if (ctxt->vstateTab != NULL) {
4969 xmlFree(ctxt->vstateTab);
4970 ctxt->vstateTab = NULL;
4971 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004972#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004973 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004974 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004975 if (ctxt->nodeTab != NULL) {
4976 xmlFree(ctxt->nodeTab);
4977 ctxt->nodeTab = NULL;
4978 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004979 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004980
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004981}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004982
Owen Taylor3473f882001-02-23 17:55:21 +00004983/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004984 * xmlValidateCdataElement:
4985 * @ctxt: the validation context
4986 * @doc: a document instance
4987 * @elem: an element instance
4988 *
4989 * Check that an element follows #CDATA
4990 *
4991 * returns 1 if valid or 0 otherwise
4992 */
4993static int
4994xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4995 xmlNodePtr elem) {
4996 int ret = 1;
4997 xmlNodePtr cur, child;
4998
Daniel Veillardceb09b92002-10-04 11:46:37 +00004999 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005000 return(0);
5001
5002 child = elem->children;
5003
5004 cur = child;
5005 while (cur != NULL) {
5006 switch (cur->type) {
5007 case XML_ENTITY_REF_NODE:
5008 /*
5009 * Push the current node to be able to roll back
5010 * and process within the entity
5011 */
5012 if ((cur->children != NULL) &&
5013 (cur->children->children != NULL)) {
5014 nodeVPush(ctxt, cur);
5015 cur = cur->children->children;
5016 continue;
5017 }
5018 break;
5019 case XML_COMMENT_NODE:
5020 case XML_PI_NODE:
5021 case XML_TEXT_NODE:
5022 case XML_CDATA_SECTION_NODE:
5023 break;
5024 default:
5025 ret = 0;
5026 goto done;
5027 }
5028 /*
5029 * Switch to next element
5030 */
5031 cur = cur->next;
5032 while (cur == NULL) {
5033 cur = nodeVPop(ctxt);
5034 if (cur == NULL)
5035 break;
5036 cur = cur->next;
5037 }
5038 }
5039done:
5040 ctxt->nodeMax = 0;
5041 ctxt->nodeNr = 0;
5042 if (ctxt->nodeTab != NULL) {
5043 xmlFree(ctxt->nodeTab);
5044 ctxt->nodeTab = NULL;
5045 }
5046 return(ret);
5047}
5048
5049/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005050 * xmlValidateCheckMixed:
5051 * @ctxt: the validation context
5052 * @cont: the mixed content model
5053 * @qname: the qualified name as appearing in the serialization
5054 *
5055 * Check if the given node is part of the content model.
5056 *
5057 * Returns 1 if yes, 0 if no, -1 in case of error
5058 */
5059static int
5060xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5061 xmlElementContentPtr cont, const xmlChar *qname) {
5062 while (cont != NULL) {
5063 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5064 if (xmlStrEqual(cont->name, qname))
5065 return(1);
5066 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5067 (cont->c1 != NULL) &&
5068 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5069 if (xmlStrEqual(cont->c1->name, qname))
5070 return(1);
5071 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5072 (cont->c1 == NULL) ||
5073 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5074 /* Internal error !!! */
5075 xmlGenericError(xmlGenericErrorContext,
5076 "Internal: MIXED struct bad\n");
5077 break;
5078 }
5079 cont = cont->c2;
5080 }
5081 return(0);
5082}
5083
5084/**
5085 * xmlValidGetElemDecl:
5086 * @ctxt: the validation context
5087 * @doc: a document instance
5088 * @elem: an element instance
5089 * @extsubset: pointer, (out) indicate if the declaration was found
5090 * in the external subset.
5091 *
5092 * Finds a declaration associated to an element in the document.
5093 *
5094 * returns the pointer to the declaration or NULL if not found.
5095 */
5096static xmlElementPtr
5097xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5098 xmlNodePtr elem, int *extsubset) {
5099 xmlElementPtr elemDecl = NULL;
5100 const xmlChar *prefix = NULL;
5101
5102 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5103 if (extsubset != NULL)
5104 *extsubset = 0;
5105
5106 /*
5107 * Fetch the declaration for the qualified name
5108 */
5109 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5110 prefix = elem->ns->prefix;
5111
5112 if (prefix != NULL) {
5113 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5114 elem->name, prefix);
5115 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5116 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5117 elem->name, prefix);
5118 if ((elemDecl != NULL) && (extsubset != NULL))
5119 *extsubset = 1;
5120 }
5121 }
5122
5123 /*
5124 * Fetch the declaration for the non qualified name
5125 * This is "non-strict" validation should be done on the
5126 * full QName but in that case being flexible makes sense.
5127 */
5128 if (elemDecl == NULL) {
5129 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5130 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5131 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5132 if ((elemDecl != NULL) && (extsubset != NULL))
5133 *extsubset = 1;
5134 }
5135 }
5136 if (elemDecl == NULL) {
5137 VECTXT(ctxt, elem);
5138 VERROR(ctxt->userData, "No declaration for element %s\n",
5139 elem->name);
5140 }
5141 return(elemDecl);
5142}
5143
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005144#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005145/**
5146 * xmlValidatePushElement:
5147 * @ctxt: the validation context
5148 * @doc: a document instance
5149 * @elem: an element instance
5150 * @qname: the qualified name as appearing in the serialization
5151 *
5152 * Push a new element start on the validation stack.
5153 *
5154 * returns 1 if no validation problem was found or 0 otherwise
5155 */
5156int
5157xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5158 xmlNodePtr elem, const xmlChar *qname) {
5159 int ret = 1;
5160 xmlElementPtr eDecl;
5161 int extsubset = 0;
5162
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005163/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005164 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5165 xmlValidStatePtr state = ctxt->vstate;
5166 xmlElementPtr elemDecl;
5167
5168 /*
5169 * Check the new element agaisnt the content model of the new elem.
5170 */
5171 if (state->elemDecl != NULL) {
5172 elemDecl = state->elemDecl;
5173
5174 switch(elemDecl->etype) {
5175 case XML_ELEMENT_TYPE_UNDEFINED:
5176 ret = 0;
5177 break;
5178 case XML_ELEMENT_TYPE_EMPTY:
5179 VECTXT(ctxt, state->node);
5180 VERROR(ctxt->userData,
5181 "Element %s was declared EMPTY this one has content\n",
5182 state->node->name);
5183 ret = 0;
5184 break;
5185 case XML_ELEMENT_TYPE_ANY:
5186 /* I don't think anything is required then */
5187 break;
5188 case XML_ELEMENT_TYPE_MIXED:
5189 /* simple case of declared as #PCDATA */
5190 if ((elemDecl->content != NULL) &&
5191 (elemDecl->content->type ==
5192 XML_ELEMENT_CONTENT_PCDATA)) {
5193 VECTXT(ctxt, state->node);
5194 VERROR(ctxt->userData,
5195 "Element %s was declared #PCDATA but contains non text nodes\n",
5196 state->node->name);
5197 ret = 0;
5198 } else {
5199 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5200 qname);
5201 if (ret != 1) {
5202 VECTXT(ctxt, state->node);
5203 VERROR(ctxt->userData,
5204 "Element %s is not declared in %s list of possible children\n",
5205 qname, state->node->name);
5206 }
5207 }
5208 break;
5209 case XML_ELEMENT_TYPE_ELEMENT:
5210 /*
5211 * TODO:
5212 * VC: Standalone Document Declaration
5213 * - element types with element content, if white space
5214 * occurs directly within any instance of those types.
5215 */
5216 if (state->exec != NULL) {
5217 ret = xmlRegExecPushString(state->exec, qname, NULL);
5218 if (ret < 0) {
5219 VECTXT(ctxt, state->node);
5220 VERROR(ctxt->userData,
5221 "Element %s content does not follow the DTD\nMisplaced %s\n",
5222 state->node->name, qname);
5223 ret = 0;
5224 } else {
5225 ret = 1;
5226 }
5227 }
5228 break;
5229 }
5230 }
5231 }
5232 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5233 vstateVPush(ctxt, eDecl, elem);
5234 return(ret);
5235}
5236
5237/**
5238 * xmlValidatePushCData:
5239 * @ctxt: the validation context
5240 * @data: some character data read
5241 * @len: the lenght of the data
5242 *
5243 * check the CData parsed for validation in the current stack
5244 *
5245 * returns 1 if no validation problem was found or 0 otherwise
5246 */
5247int
5248xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5249 int ret = 1;
5250
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005251/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005252 if (len <= 0)
5253 return(ret);
5254 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5255 xmlValidStatePtr state = ctxt->vstate;
5256 xmlElementPtr elemDecl;
5257
5258 /*
5259 * Check the new element agaisnt the content model of the new elem.
5260 */
5261 if (state->elemDecl != NULL) {
5262 elemDecl = state->elemDecl;
5263
5264 switch(elemDecl->etype) {
5265 case XML_ELEMENT_TYPE_UNDEFINED:
5266 ret = 0;
5267 break;
5268 case XML_ELEMENT_TYPE_EMPTY:
5269 VECTXT(ctxt, state->node);
5270 VERROR(ctxt->userData,
5271 "Element %s was declared EMPTY this one has content\n",
5272 state->node->name);
5273 ret = 0;
5274 break;
5275 case XML_ELEMENT_TYPE_ANY:
5276 break;
5277 case XML_ELEMENT_TYPE_MIXED:
5278 break;
5279 case XML_ELEMENT_TYPE_ELEMENT:
5280 if (len > 0) {
5281 int i;
5282
5283 for (i = 0;i < len;i++) {
5284 if (!IS_BLANK(data[i])) {
5285 VECTXT(ctxt, state->node);
5286 VERROR(ctxt->userData,
5287 "Element %s content does not follow the DTD\nText not allowed\n",
5288 state->node->name);
5289 ret = 0;
5290 goto done;
5291 }
5292 }
5293 /*
5294 * TODO:
5295 * VC: Standalone Document Declaration
5296 * element types with element content, if white space
5297 * occurs directly within any instance of those types.
5298 */
5299 }
5300 break;
5301 }
5302 }
5303 }
5304done:
5305 return(ret);
5306}
5307
5308/**
5309 * xmlValidatePopElement:
5310 * @ctxt: the validation context
5311 * @doc: a document instance
5312 * @elem: an element instance
5313 * @qname: the qualified name as appearing in the serialization
5314 *
5315 * Pop the element end from the validation stack.
5316 *
5317 * returns 1 if no validation problem was found or 0 otherwise
5318 */
5319int
5320xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005321 xmlNodePtr elem ATTRIBUTE_UNUSED,
5322 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005323 int ret = 1;
5324
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005325/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005326 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5327 xmlValidStatePtr state = ctxt->vstate;
5328 xmlElementPtr elemDecl;
5329
5330 /*
5331 * Check the new element agaisnt the content model of the new elem.
5332 */
5333 if (state->elemDecl != NULL) {
5334 elemDecl = state->elemDecl;
5335
5336 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5337 if (state->exec != NULL) {
5338 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5339 if (ret == 0) {
5340 VECTXT(ctxt, state->node);
5341 VERROR(ctxt->userData,
5342 "Element %s content does not follow the DTD\nExpecting more child\n",
5343 state->node->name);
5344 } else {
5345 /*
5346 * previous validation errors should not generate
5347 * a new one here
5348 */
5349 ret = 1;
5350 }
5351 }
5352 }
5353 }
5354 vstateVPop(ctxt);
5355 }
5356 return(ret);
5357}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005358#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005359
5360/**
Owen Taylor3473f882001-02-23 17:55:21 +00005361 * xmlValidateOneElement:
5362 * @ctxt: the validation context
5363 * @doc: a document instance
5364 * @elem: an element instance
5365 *
5366 * Try to validate a single element and it's attributes,
5367 * basically it does the following checks as described by the
5368 * XML-1.0 recommendation:
5369 * - [ VC: Element Valid ]
5370 * - [ VC: Required Attribute ]
5371 * Then call xmlValidateOneAttribute() for each attribute present.
5372 *
5373 * The ID/IDREF checkings are done separately
5374 *
5375 * returns 1 if valid or 0 otherwise
5376 */
5377
5378int
5379xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5380 xmlNodePtr elem) {
5381 xmlElementPtr elemDecl = NULL;
5382 xmlElementContentPtr cont;
5383 xmlAttributePtr attr;
5384 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005385 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005386 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005387 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005388
5389 CHECK_DTD;
5390
5391 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005392 switch (elem->type) {
5393 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005394 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005395 VERROR(ctxt->userData,
5396 "Attribute element not expected here\n");
5397 return(0);
5398 case XML_TEXT_NODE:
5399 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005400 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005401 VERROR(ctxt->userData, "Text element has childs !\n");
5402 return(0);
5403 }
5404 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005405 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005406 VERROR(ctxt->userData, "Text element has attributes !\n");
5407 return(0);
5408 }
5409 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005410 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005411 VERROR(ctxt->userData, "Text element has namespace !\n");
5412 return(0);
5413 }
5414 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005415 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005416 VERROR(ctxt->userData,
5417 "Text element carries namespace definitions !\n");
5418 return(0);
5419 }
5420 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005421 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005422 VERROR(ctxt->userData,
5423 "Text element has no content !\n");
5424 return(0);
5425 }
5426 return(1);
5427 case XML_XINCLUDE_START:
5428 case XML_XINCLUDE_END:
5429 return(1);
5430 case XML_CDATA_SECTION_NODE:
5431 case XML_ENTITY_REF_NODE:
5432 case XML_PI_NODE:
5433 case XML_COMMENT_NODE:
5434 return(1);
5435 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005436 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005437 VERROR(ctxt->userData,
5438 "Entity element not expected here\n");
5439 return(0);
5440 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005441 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005442 VERROR(ctxt->userData,
5443 "Notation element not expected here\n");
5444 return(0);
5445 case XML_DOCUMENT_NODE:
5446 case XML_DOCUMENT_TYPE_NODE:
5447 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005448 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005449 VERROR(ctxt->userData,
5450 "Document element not expected here\n");
5451 return(0);
5452 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005453 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005454 VERROR(ctxt->userData,
5455 "\n");
5456 return(0);
5457 case XML_ELEMENT_NODE:
5458 break;
5459 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005460 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005461 VERROR(ctxt->userData,
5462 "unknown element type %d\n", elem->type);
5463 return(0);
5464 }
Owen Taylor3473f882001-02-23 17:55:21 +00005465
5466 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005467 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005468 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005469 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5470 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005471 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005472
Daniel Veillardea7751d2002-12-20 00:16:24 +00005473 /*
5474 * If vstateNr is not zero that means continuous validation is
5475 * activated, do not try to check the content model at that level.
5476 */
5477 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005478 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005479 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005480 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005481 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005482 VERROR(ctxt->userData, "No declaration for element %s\n",
5483 elem->name);
5484 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005485 case XML_ELEMENT_TYPE_EMPTY:
5486 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005487 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005488 VERROR(ctxt->userData,
5489 "Element %s was declared EMPTY this one has content\n",
5490 elem->name);
5491 ret = 0;
5492 }
5493 break;
5494 case XML_ELEMENT_TYPE_ANY:
5495 /* I don't think anything is required then */
5496 break;
5497 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005498
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005499 /* simple case of declared as #PCDATA */
5500 if ((elemDecl->content != NULL) &&
5501 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5502 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5503 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005504 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005505 VERROR(ctxt->userData,
5506 "Element %s was declared #PCDATA but contains non text nodes\n",
5507 elem->name);
5508 }
5509 break;
5510 }
Owen Taylor3473f882001-02-23 17:55:21 +00005511 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005512 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005513 while (child != NULL) {
5514 if (child->type == XML_ELEMENT_NODE) {
5515 name = child->name;
5516 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005517 xmlChar fn[50];
5518 xmlChar *fullname;
5519
5520 fullname = xmlBuildQName(child->name, child->ns->prefix,
5521 fn, 50);
5522 if (fullname == NULL)
5523 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005524 cont = elemDecl->content;
5525 while (cont != NULL) {
5526 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005527 if (xmlStrEqual(cont->name, fullname))
5528 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005529 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5530 (cont->c1 != NULL) &&
5531 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005532 if (xmlStrEqual(cont->c1->name, fullname))
5533 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005534 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5535 (cont->c1 == NULL) ||
5536 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5537 /* Internal error !!! */
5538 xmlGenericError(xmlGenericErrorContext,
5539 "Internal: MIXED struct bad\n");
5540 break;
5541 }
5542 cont = cont->c2;
5543 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005544 if ((fullname != fn) && (fullname != child->name))
5545 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005546 if (cont != NULL)
5547 goto child_ok;
5548 }
5549 cont = elemDecl->content;
5550 while (cont != NULL) {
5551 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5552 if (xmlStrEqual(cont->name, name)) break;
5553 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5554 (cont->c1 != NULL) &&
5555 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5556 if (xmlStrEqual(cont->c1->name, name)) break;
5557 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5558 (cont->c1 == NULL) ||
5559 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5560 /* Internal error !!! */
5561 xmlGenericError(xmlGenericErrorContext,
5562 "Internal: MIXED struct bad\n");
5563 break;
5564 }
5565 cont = cont->c2;
5566 }
5567 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005568 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005569 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005570 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005571 name, elem->name);
5572 ret = 0;
5573 }
5574 }
5575child_ok:
5576 child = child->next;
5577 }
5578 break;
5579 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005580 if ((doc->standalone == 1) && (extsubset == 1)) {
5581 /*
5582 * VC: Standalone Document Declaration
5583 * - element types with element content, if white space
5584 * occurs directly within any instance of those types.
5585 */
5586 child = elem->children;
5587 while (child != NULL) {
5588 if (child->type == XML_TEXT_NODE) {
5589 const xmlChar *content = child->content;
5590
5591 while (IS_BLANK(*content))
5592 content++;
5593 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005594 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005595 VERROR(ctxt->userData,
5596"standalone: %s declared in the external subset contains white spaces nodes\n",
5597 elem->name);
5598 ret = 0;
5599 break;
5600 }
5601 }
5602 child =child->next;
5603 }
5604 }
Owen Taylor3473f882001-02-23 17:55:21 +00005605 child = elem->children;
5606 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005607 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005608 if (tmp <= 0)
5609 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005610 break;
5611 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005612 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005613
5614 /* [ VC: Required Attribute ] */
5615 attr = elemDecl->attributes;
5616 while (attr != NULL) {
5617 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005618 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005619
Daniel Veillarde4301c82002-02-13 13:32:35 +00005620 if ((attr->prefix == NULL) &&
5621 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5622 xmlNsPtr ns;
5623
5624 ns = elem->nsDef;
5625 while (ns != NULL) {
5626 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005627 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005628 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005629 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005630 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5631 xmlNsPtr ns;
5632
5633 ns = elem->nsDef;
5634 while (ns != NULL) {
5635 if (xmlStrEqual(attr->name, ns->prefix))
5636 goto found;
5637 ns = ns->next;
5638 }
5639 } else {
5640 xmlAttrPtr attrib;
5641
5642 attrib = elem->properties;
5643 while (attrib != NULL) {
5644 if (xmlStrEqual(attrib->name, attr->name)) {
5645 if (attr->prefix != NULL) {
5646 xmlNsPtr nameSpace = attrib->ns;
5647
5648 if (nameSpace == NULL)
5649 nameSpace = elem->ns;
5650 /*
5651 * qualified names handling is problematic, having a
5652 * different prefix should be possible but DTDs don't
5653 * allow to define the URI instead of the prefix :-(
5654 */
5655 if (nameSpace == NULL) {
5656 if (qualified < 0)
5657 qualified = 0;
5658 } else if (!xmlStrEqual(nameSpace->prefix,
5659 attr->prefix)) {
5660 if (qualified < 1)
5661 qualified = 1;
5662 } else
5663 goto found;
5664 } else {
5665 /*
5666 * We should allow applications to define namespaces
5667 * for their application even if the DTD doesn't
5668 * carry one, otherwise, basically we would always
5669 * break.
5670 */
5671 goto found;
5672 }
5673 }
5674 attrib = attrib->next;
5675 }
Owen Taylor3473f882001-02-23 17:55:21 +00005676 }
5677 if (qualified == -1) {
5678 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005679 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005680 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005681 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005682 elem->name, attr->name);
5683 ret = 0;
5684 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005685 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005686 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005687 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005688 elem->name, attr->prefix,attr->name);
5689 ret = 0;
5690 }
5691 } else if (qualified == 0) {
5692 VWARNING(ctxt->userData,
5693 "Element %s required attribute %s:%s has no prefix\n",
5694 elem->name, attr->prefix,attr->name);
5695 } else if (qualified == 1) {
5696 VWARNING(ctxt->userData,
5697 "Element %s required attribute %s:%s has different prefix\n",
5698 elem->name, attr->prefix,attr->name);
5699 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005700 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5701 /*
5702 * Special tests checking #FIXED namespace declarations
5703 * have the right value since this is not done as an
5704 * attribute checking
5705 */
5706 if ((attr->prefix == NULL) &&
5707 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5708 xmlNsPtr ns;
5709
5710 ns = elem->nsDef;
5711 while (ns != NULL) {
5712 if (ns->prefix == NULL) {
5713 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005714 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005715 VERROR(ctxt->userData,
5716 "Element %s namespace name for default namespace does not match the DTD\n",
5717 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005718 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005719 }
5720 goto found;
5721 }
5722 ns = ns->next;
5723 }
5724 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5725 xmlNsPtr ns;
5726
5727 ns = elem->nsDef;
5728 while (ns != NULL) {
5729 if (xmlStrEqual(attr->name, ns->prefix)) {
5730 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005731 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005732 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005733 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005734 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005735 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005736 }
5737 goto found;
5738 }
5739 ns = ns->next;
5740 }
5741 }
Owen Taylor3473f882001-02-23 17:55:21 +00005742 }
5743found:
5744 attr = attr->nexth;
5745 }
5746 return(ret);
5747}
5748
5749/**
5750 * xmlValidateRoot:
5751 * @ctxt: the validation context
5752 * @doc: a document instance
5753 *
5754 * Try to validate a the root element
5755 * basically it does the following check as described by the
5756 * XML-1.0 recommendation:
5757 * - [ VC: Root Element Type ]
5758 * it doesn't try to recurse or apply other check to the element
5759 *
5760 * returns 1 if valid or 0 otherwise
5761 */
5762
5763int
5764xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5765 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00005766 int ret;
5767
Owen Taylor3473f882001-02-23 17:55:21 +00005768 if (doc == NULL) return(0);
5769
5770 root = xmlDocGetRootElement(doc);
5771 if ((root == NULL) || (root->name == NULL)) {
5772 VERROR(ctxt->userData, "Not valid: no root element\n");
5773 return(0);
5774 }
5775
5776 /*
5777 * When doing post validation against a separate DTD, those may
5778 * no internal subset has been generated
5779 */
5780 if ((doc->intSubset != NULL) &&
5781 (doc->intSubset->name != NULL)) {
5782 /*
5783 * Check first the document root against the NQName
5784 */
5785 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5786 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005787 xmlChar fn[50];
5788 xmlChar *fullname;
5789
5790 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
5791 if (fullname == NULL) {
5792 VERROR(ctxt->userData, "Out of memory\n");
5793 return(0);
5794 }
5795 ret = xmlStrEqual(doc->intSubset->name, fullname);
5796 if ((fullname != fn) && (fullname != root->name))
5797 xmlFree(fullname);
5798 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00005799 goto name_ok;
5800 }
5801 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5802 (xmlStrEqual(root->name, BAD_CAST "html")))
5803 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005804 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005805 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005806 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005807 root->name, doc->intSubset->name);
5808 return(0);
5809
5810 }
5811 }
5812name_ok:
5813 return(1);
5814}
5815
5816
5817/**
5818 * xmlValidateElement:
5819 * @ctxt: the validation context
5820 * @doc: a document instance
5821 * @elem: an element instance
5822 *
5823 * Try to validate the subtree under an element
5824 *
5825 * returns 1 if valid or 0 otherwise
5826 */
5827
5828int
5829xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5830 xmlNodePtr child;
5831 xmlAttrPtr attr;
5832 xmlChar *value;
5833 int ret = 1;
5834
5835 if (elem == NULL) return(0);
5836
5837 /*
5838 * XInclude elements were added after parsing in the infoset,
5839 * they don't really mean anything validation wise.
5840 */
5841 if ((elem->type == XML_XINCLUDE_START) ||
5842 (elem->type == XML_XINCLUDE_END))
5843 return(1);
5844
5845 CHECK_DTD;
5846
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005847 /*
5848 * Entities references have to be handled separately
5849 */
5850 if (elem->type == XML_ENTITY_REF_NODE) {
5851 return(1);
5852 }
5853
Owen Taylor3473f882001-02-23 17:55:21 +00005854 ret &= xmlValidateOneElement(ctxt, doc, elem);
5855 attr = elem->properties;
5856 while(attr != NULL) {
5857 value = xmlNodeListGetString(doc, attr->children, 0);
5858 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5859 if (value != NULL)
5860 xmlFree(value);
5861 attr= attr->next;
5862 }
5863 child = elem->children;
5864 while (child != NULL) {
5865 ret &= xmlValidateElement(ctxt, doc, child);
5866 child = child->next;
5867 }
5868
5869 return(ret);
5870}
5871
Daniel Veillard8730c562001-02-26 10:49:57 +00005872/**
5873 * xmlValidateRef:
5874 * @ref: A reference to be validated
5875 * @ctxt: Validation context
5876 * @name: Name of ID we are searching for
5877 *
5878 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005879static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005880xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005881 const xmlChar *name) {
5882 xmlAttrPtr id;
5883 xmlAttrPtr attr;
5884
5885 if (ref == NULL)
5886 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005887 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00005888 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005889 attr = ref->attr;
5890 if (attr == NULL) {
5891 xmlChar *dup, *str = NULL, *cur, save;
5892
5893 dup = xmlStrdup(name);
5894 if (dup == NULL) {
5895 ctxt->valid = 0;
5896 return;
5897 }
5898 cur = dup;
5899 while (*cur != 0) {
5900 str = cur;
5901 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5902 save = *cur;
5903 *cur = 0;
5904 id = xmlGetID(ctxt->doc, str);
5905 if (id == NULL) {
5906 VERROR(ctxt->userData,
5907 "attribute %s line %d references an unknown ID \"%s\"\n",
5908 ref->name, ref->lineno, str);
5909 ctxt->valid = 0;
5910 }
5911 if (save == 0)
5912 break;
5913 *cur = save;
5914 while (IS_BLANK(*cur)) cur++;
5915 }
5916 xmlFree(dup);
5917 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00005918 id = xmlGetID(ctxt->doc, name);
5919 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005920 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005921 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005922 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005923 attr->name, name);
5924 ctxt->valid = 0;
5925 }
5926 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5927 xmlChar *dup, *str = NULL, *cur, save;
5928
5929 dup = xmlStrdup(name);
5930 if (dup == NULL) {
5931 ctxt->valid = 0;
5932 return;
5933 }
5934 cur = dup;
5935 while (*cur != 0) {
5936 str = cur;
5937 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5938 save = *cur;
5939 *cur = 0;
5940 id = xmlGetID(ctxt->doc, str);
5941 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005942 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005943 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005944 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005945 attr->name, str);
5946 ctxt->valid = 0;
5947 }
5948 if (save == 0)
5949 break;
5950 *cur = save;
5951 while (IS_BLANK(*cur)) cur++;
5952 }
5953 xmlFree(dup);
5954 }
5955}
5956
5957/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005958 * xmlWalkValidateList:
5959 * @data: Contents of current link
5960 * @user: Value supplied by the user
5961 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005962 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005963 */
5964static int
5965xmlWalkValidateList(const void *data, const void *user)
5966{
5967 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5968 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5969 return 1;
5970}
5971
5972/**
5973 * xmlValidateCheckRefCallback:
5974 * @ref_list: List of references
5975 * @ctxt: Validation context
5976 * @name: Name of ID we are searching for
5977 *
5978 */
5979static void
5980xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5981 const xmlChar *name) {
5982 xmlValidateMemo memo;
5983
5984 if (ref_list == NULL)
5985 return;
5986 memo.ctxt = ctxt;
5987 memo.name = name;
5988
5989 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5990
5991}
5992
5993/**
Owen Taylor3473f882001-02-23 17:55:21 +00005994 * xmlValidateDocumentFinal:
5995 * @ctxt: the validation context
5996 * @doc: a document instance
5997 *
5998 * Does the final step for the document validation once all the
5999 * incremental validation steps have been completed
6000 *
6001 * basically it does the following checks described by the XML Rec
6002 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006003 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00006004 *
6005 * returns 1 if valid or 0 otherwise
6006 */
6007
6008int
6009xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6010 xmlRefTablePtr table;
6011
6012 if (doc == NULL) {
6013 xmlGenericError(xmlGenericErrorContext,
6014 "xmlValidateDocumentFinal: doc == NULL\n");
6015 return(0);
6016 }
6017
6018 /*
6019 * Check all the NOTATION/NOTATIONS attributes
6020 */
6021 /*
6022 * Check all the ENTITY/ENTITIES attributes definition for validity
6023 */
6024 /*
6025 * Check all the IDREF/IDREFS attributes definition for validity
6026 */
6027 table = (xmlRefTablePtr) doc->refs;
6028 ctxt->doc = doc;
6029 ctxt->valid = 1;
6030 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6031 return(ctxt->valid);
6032}
6033
6034/**
6035 * xmlValidateDtd:
6036 * @ctxt: the validation context
6037 * @doc: a document instance
6038 * @dtd: a dtd instance
6039 *
6040 * Try to validate the document against the dtd instance
6041 *
6042 * basically it does check all the definitions in the DtD.
6043 *
6044 * returns 1 if valid or 0 otherwise
6045 */
6046
6047int
6048xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6049 int ret;
6050 xmlDtdPtr oldExt;
6051 xmlNodePtr root;
6052
6053 if (dtd == NULL) return(0);
6054 if (doc == NULL) return(0);
6055 oldExt = doc->extSubset;
6056 doc->extSubset = dtd;
6057 ret = xmlValidateRoot(ctxt, doc);
6058 if (ret == 0) {
6059 doc->extSubset = oldExt;
6060 return(ret);
6061 }
6062 if (doc->ids != NULL) {
6063 xmlFreeIDTable(doc->ids);
6064 doc->ids = NULL;
6065 }
6066 if (doc->refs != NULL) {
6067 xmlFreeRefTable(doc->refs);
6068 doc->refs = NULL;
6069 }
6070 root = xmlDocGetRootElement(doc);
6071 ret = xmlValidateElement(ctxt, doc, root);
6072 ret &= xmlValidateDocumentFinal(ctxt, doc);
6073 doc->extSubset = oldExt;
6074 return(ret);
6075}
6076
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006077static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006078xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6079 const xmlChar *name ATTRIBUTE_UNUSED) {
6080 if (cur == NULL)
6081 return;
6082 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6083 xmlChar *notation = cur->content;
6084
Daniel Veillard878eab02002-02-19 13:46:09 +00006085 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006086 int ret;
6087
6088 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6089 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006090 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006091 }
6092 }
6093 }
6094}
6095
6096static void
Owen Taylor3473f882001-02-23 17:55:21 +00006097xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006098 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006099 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006100 xmlDocPtr doc;
6101 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006102
Owen Taylor3473f882001-02-23 17:55:21 +00006103 if (cur == NULL)
6104 return;
6105 switch (cur->atype) {
6106 case XML_ATTRIBUTE_CDATA:
6107 case XML_ATTRIBUTE_ID:
6108 case XML_ATTRIBUTE_IDREF :
6109 case XML_ATTRIBUTE_IDREFS:
6110 case XML_ATTRIBUTE_NMTOKEN:
6111 case XML_ATTRIBUTE_NMTOKENS:
6112 case XML_ATTRIBUTE_ENUMERATION:
6113 break;
6114 case XML_ATTRIBUTE_ENTITY:
6115 case XML_ATTRIBUTE_ENTITIES:
6116 case XML_ATTRIBUTE_NOTATION:
6117 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006118
6119 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6120 cur->atype, cur->defaultValue);
6121 if ((ret == 0) && (ctxt->valid == 1))
6122 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006123 }
6124 if (cur->tree != NULL) {
6125 xmlEnumerationPtr tree = cur->tree;
6126 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006127 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006128 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006129 if ((ret == 0) && (ctxt->valid == 1))
6130 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006131 tree = tree->next;
6132 }
6133 }
6134 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006135 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6136 doc = cur->doc;
6137 if ((doc == NULL) || (cur->elem == NULL)) {
6138 VERROR(ctxt->userData,
6139 "xmlValidateAttributeCallback(%s): internal error\n",
6140 cur->name);
6141 return;
6142 }
6143 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6144 if (elem == NULL)
6145 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6146 if (elem == NULL) {
6147 VERROR(ctxt->userData,
6148 "attribute %s: could not find decl for element %s\n",
6149 cur->name, cur->elem);
6150 return;
6151 }
6152 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6153 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006154 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00006155 cur->name, cur->elem);
6156 ctxt->valid = 0;
6157 }
6158 }
Owen Taylor3473f882001-02-23 17:55:21 +00006159}
6160
6161/**
6162 * xmlValidateDtdFinal:
6163 * @ctxt: the validation context
6164 * @doc: a document instance
6165 *
6166 * Does the final step for the dtds validation once all the
6167 * subsets have been parsed
6168 *
6169 * basically it does the following checks described by the XML Rec
6170 * - check that ENTITY and ENTITIES type attributes default or
6171 * possible values matches one of the defined entities.
6172 * - check that NOTATION type attributes default or
6173 * possible values matches one of the defined notations.
6174 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006175 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006176 */
6177
6178int
6179xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006180 xmlDtdPtr dtd;
6181 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006182 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006183
6184 if (doc == NULL) return(0);
6185 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6186 return(0);
6187 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006188 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006189 dtd = doc->intSubset;
6190 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6191 table = (xmlAttributeTablePtr) dtd->attributes;
6192 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006193 }
6194 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006195 entities = (xmlEntitiesTablePtr) dtd->entities;
6196 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6197 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006198 }
6199 dtd = doc->extSubset;
6200 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6201 table = (xmlAttributeTablePtr) dtd->attributes;
6202 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006203 }
6204 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006205 entities = (xmlEntitiesTablePtr) dtd->entities;
6206 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6207 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006208 }
6209 return(ctxt->valid);
6210}
6211
6212/**
6213 * xmlValidateDocument:
6214 * @ctxt: the validation context
6215 * @doc: a document instance
6216 *
6217 * Try to validate the document instance
6218 *
6219 * basically it does the all the checks described by the XML Rec
6220 * i.e. validates the internal and external subset (if present)
6221 * and validate the document tree.
6222 *
6223 * returns 1 if valid or 0 otherwise
6224 */
6225
6226int
6227xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6228 int ret;
6229 xmlNodePtr root;
6230
Daniel Veillard2fd85422002-10-16 14:32:41 +00006231 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6232 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00006233 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006234 }
Owen Taylor3473f882001-02-23 17:55:21 +00006235 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6236 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6237 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6238 doc->intSubset->SystemID);
6239 if (doc->extSubset == NULL) {
6240 if (doc->intSubset->SystemID != NULL) {
6241 VERROR(ctxt->userData,
6242 "Could not load the external subset \"%s\"\n",
6243 doc->intSubset->SystemID);
6244 } else {
6245 VERROR(ctxt->userData,
6246 "Could not load the external subset \"%s\"\n",
6247 doc->intSubset->ExternalID);
6248 }
6249 return(0);
6250 }
6251 }
6252
6253 if (doc->ids != NULL) {
6254 xmlFreeIDTable(doc->ids);
6255 doc->ids = NULL;
6256 }
6257 if (doc->refs != NULL) {
6258 xmlFreeRefTable(doc->refs);
6259 doc->refs = NULL;
6260 }
6261 ret = xmlValidateDtdFinal(ctxt, doc);
6262 if (!xmlValidateRoot(ctxt, doc)) return(0);
6263
6264 root = xmlDocGetRootElement(doc);
6265 ret &= xmlValidateElement(ctxt, doc, root);
6266 ret &= xmlValidateDocumentFinal(ctxt, doc);
6267 return(ret);
6268}
6269
6270
6271/************************************************************************
6272 * *
6273 * Routines for dynamic validation editing *
6274 * *
6275 ************************************************************************/
6276
6277/**
6278 * xmlValidGetPotentialChildren:
6279 * @ctree: an element content tree
6280 * @list: an array to store the list of child names
6281 * @len: a pointer to the number of element in the list
6282 * @max: the size of the array
6283 *
6284 * Build/extend a list of potential children allowed by the content tree
6285 *
6286 * returns the number of element in the list, or -1 in case of error.
6287 */
6288
6289int
6290xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6291 int *len, int max) {
6292 int i;
6293
6294 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6295 return(-1);
6296 if (*len >= max) return(*len);
6297
6298 switch (ctree->type) {
6299 case XML_ELEMENT_CONTENT_PCDATA:
6300 for (i = 0; i < *len;i++)
6301 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6302 list[(*len)++] = BAD_CAST "#PCDATA";
6303 break;
6304 case XML_ELEMENT_CONTENT_ELEMENT:
6305 for (i = 0; i < *len;i++)
6306 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6307 list[(*len)++] = ctree->name;
6308 break;
6309 case XML_ELEMENT_CONTENT_SEQ:
6310 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6311 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6312 break;
6313 case XML_ELEMENT_CONTENT_OR:
6314 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6315 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6316 break;
6317 }
6318
6319 return(*len);
6320}
6321
6322/**
6323 * xmlValidGetValidElements:
6324 * @prev: an element to insert after
6325 * @next: an element to insert next
6326 * @list: an array to store the list of child names
6327 * @max: the size of the array
6328 *
6329 * This function returns the list of authorized children to insert
6330 * within an existing tree while respecting the validity constraints
6331 * forced by the Dtd. The insertion point is defined using @prev and
6332 * @next in the following ways:
6333 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6334 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6335 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6336 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6337 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6338 *
6339 * pointers to the element names are inserted at the beginning of the array
6340 * and do not need to be freed.
6341 *
6342 * returns the number of element in the list, or -1 in case of error. If
6343 * the function returns the value @max the caller is invited to grow the
6344 * receiving array and retry.
6345 */
6346
6347int
6348xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6349 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006350 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006351 int nb_valid_elements = 0;
6352 const xmlChar *elements[256];
6353 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006354 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006355
6356 xmlNode *ref_node;
6357 xmlNode *parent;
6358 xmlNode *test_node;
6359
6360 xmlNode *prev_next;
6361 xmlNode *next_prev;
6362 xmlNode *parent_childs;
6363 xmlNode *parent_last;
6364
6365 xmlElement *element_desc;
6366
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006367 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006368
Owen Taylor3473f882001-02-23 17:55:21 +00006369 if (prev == NULL && next == NULL)
6370 return(-1);
6371
6372 if (list == NULL) return(-1);
6373 if (max <= 0) return(-1);
6374
6375 nb_valid_elements = 0;
6376 ref_node = prev ? prev : next;
6377 parent = ref_node->parent;
6378
6379 /*
6380 * Retrieves the parent element declaration
6381 */
6382 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6383 parent->name);
6384 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6385 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6386 parent->name);
6387 if (element_desc == NULL) return(-1);
6388
6389 /*
6390 * Do a backup of the current tree structure
6391 */
6392 prev_next = prev ? prev->next : NULL;
6393 next_prev = next ? next->prev : NULL;
6394 parent_childs = parent->children;
6395 parent_last = parent->last;
6396
6397 /*
6398 * Creates a dummy node and insert it into the tree
6399 */
6400 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6401 test_node->doc = ref_node->doc;
6402 test_node->parent = parent;
6403 test_node->prev = prev;
6404 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006405 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006406
6407 if (prev) prev->next = test_node;
6408 else parent->children = test_node;
6409
6410 if (next) next->prev = test_node;
6411 else parent->last = test_node;
6412
6413 /*
6414 * Insert each potential child node and check if the parent is
6415 * still valid
6416 */
6417 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6418 elements, &nb_elements, 256);
6419
6420 for (i = 0;i < nb_elements;i++) {
6421 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006422 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006423 int j;
6424
6425 for (j = 0; j < nb_valid_elements;j++)
6426 if (xmlStrEqual(elements[i], list[j])) break;
6427 list[nb_valid_elements++] = elements[i];
6428 if (nb_valid_elements >= max) break;
6429 }
6430 }
6431
6432 /*
6433 * Restore the tree structure
6434 */
6435 if (prev) prev->next = prev_next;
6436 if (next) next->prev = next_prev;
6437 parent->children = parent_childs;
6438 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006439
6440 /*
6441 * Free up the dummy node
6442 */
6443 test_node->name = name;
6444 xmlFreeNode(test_node);
6445
Owen Taylor3473f882001-02-23 17:55:21 +00006446 return(nb_valid_elements);
6447}