blob: ace4b28ed6e83ea8a4bbf038c0acf96c618dec24 [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
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000722xmlNewElementContent(const xmlChar *name, xmlElementContentType type) {
Owen Taylor3473f882001-02-23 17:55:21 +0000723 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
Daniel Veillard2fdbd322003-08-18 12:15:38 +00001383xmlCreateEnumeration(const xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00001384 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) {
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005062 const xmlChar *name;
5063 int plen;
5064 name = xmlSplitQName3(qname, &plen);
5065
5066 if (name == NULL) {
5067 while (cont != NULL) {
5068 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5069 if ((cont->prefix == NULL) && (xmlStrEqual(cont->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_ELEMENT)){
5074 if ((cont->c1->prefix == NULL) &&
5075 (xmlStrEqual(cont->c1->name, qname)))
5076 return(1);
5077 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5078 (cont->c1 == NULL) ||
5079 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5080 /* Internal error !!! */
5081 xmlGenericError(xmlGenericErrorContext,
5082 "Internal: MIXED struct bad\n");
5083 break;
5084 }
5085 cont = cont->c2;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005086 }
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005087 } else {
5088 while (cont != NULL) {
5089 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5090 if ((cont->prefix != NULL) &&
5091 (xmlStrncmp(cont->prefix, qname, plen) == 0) &&
5092 (xmlStrEqual(cont->name, name)))
5093 return(1);
5094 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5095 (cont->c1 != NULL) &&
5096 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5097 if ((cont->c1->prefix != NULL) &&
5098 (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) &&
5099 (xmlStrEqual(cont->c1->name, name)))
5100 return(1);
5101 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5102 (cont->c1 == NULL) ||
5103 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5104 /* Internal error !!! */
5105 xmlGenericError(xmlGenericErrorContext,
5106 "Internal: MIXED struct bad\n");
5107 break;
5108 }
5109 cont = cont->c2;
5110 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005111 }
5112 return(0);
5113}
5114
5115/**
5116 * xmlValidGetElemDecl:
5117 * @ctxt: the validation context
5118 * @doc: a document instance
5119 * @elem: an element instance
5120 * @extsubset: pointer, (out) indicate if the declaration was found
5121 * in the external subset.
5122 *
5123 * Finds a declaration associated to an element in the document.
5124 *
5125 * returns the pointer to the declaration or NULL if not found.
5126 */
5127static xmlElementPtr
5128xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5129 xmlNodePtr elem, int *extsubset) {
5130 xmlElementPtr elemDecl = NULL;
5131 const xmlChar *prefix = NULL;
5132
5133 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5134 if (extsubset != NULL)
5135 *extsubset = 0;
5136
5137 /*
5138 * Fetch the declaration for the qualified name
5139 */
5140 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5141 prefix = elem->ns->prefix;
5142
5143 if (prefix != NULL) {
5144 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5145 elem->name, prefix);
5146 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5147 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5148 elem->name, prefix);
5149 if ((elemDecl != NULL) && (extsubset != NULL))
5150 *extsubset = 1;
5151 }
5152 }
5153
5154 /*
5155 * Fetch the declaration for the non qualified name
5156 * This is "non-strict" validation should be done on the
5157 * full QName but in that case being flexible makes sense.
5158 */
5159 if (elemDecl == NULL) {
5160 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5161 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5162 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5163 if ((elemDecl != NULL) && (extsubset != NULL))
5164 *extsubset = 1;
5165 }
5166 }
5167 if (elemDecl == NULL) {
5168 VECTXT(ctxt, elem);
5169 VERROR(ctxt->userData, "No declaration for element %s\n",
5170 elem->name);
5171 }
5172 return(elemDecl);
5173}
5174
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005175#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005176/**
5177 * xmlValidatePushElement:
5178 * @ctxt: the validation context
5179 * @doc: a document instance
5180 * @elem: an element instance
5181 * @qname: the qualified name as appearing in the serialization
5182 *
5183 * Push a new element start on the validation stack.
5184 *
5185 * returns 1 if no validation problem was found or 0 otherwise
5186 */
5187int
5188xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5189 xmlNodePtr elem, const xmlChar *qname) {
5190 int ret = 1;
5191 xmlElementPtr eDecl;
5192 int extsubset = 0;
5193
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005194/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005195 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5196 xmlValidStatePtr state = ctxt->vstate;
5197 xmlElementPtr elemDecl;
5198
5199 /*
5200 * Check the new element agaisnt the content model of the new elem.
5201 */
5202 if (state->elemDecl != NULL) {
5203 elemDecl = state->elemDecl;
5204
5205 switch(elemDecl->etype) {
5206 case XML_ELEMENT_TYPE_UNDEFINED:
5207 ret = 0;
5208 break;
5209 case XML_ELEMENT_TYPE_EMPTY:
5210 VECTXT(ctxt, state->node);
5211 VERROR(ctxt->userData,
5212 "Element %s was declared EMPTY this one has content\n",
5213 state->node->name);
5214 ret = 0;
5215 break;
5216 case XML_ELEMENT_TYPE_ANY:
5217 /* I don't think anything is required then */
5218 break;
5219 case XML_ELEMENT_TYPE_MIXED:
5220 /* simple case of declared as #PCDATA */
5221 if ((elemDecl->content != NULL) &&
5222 (elemDecl->content->type ==
5223 XML_ELEMENT_CONTENT_PCDATA)) {
5224 VECTXT(ctxt, state->node);
5225 VERROR(ctxt->userData,
5226 "Element %s was declared #PCDATA but contains non text nodes\n",
5227 state->node->name);
5228 ret = 0;
5229 } else {
5230 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5231 qname);
5232 if (ret != 1) {
5233 VECTXT(ctxt, state->node);
5234 VERROR(ctxt->userData,
5235 "Element %s is not declared in %s list of possible children\n",
5236 qname, state->node->name);
5237 }
5238 }
5239 break;
5240 case XML_ELEMENT_TYPE_ELEMENT:
5241 /*
5242 * TODO:
5243 * VC: Standalone Document Declaration
5244 * - element types with element content, if white space
5245 * occurs directly within any instance of those types.
5246 */
5247 if (state->exec != NULL) {
5248 ret = xmlRegExecPushString(state->exec, qname, NULL);
5249 if (ret < 0) {
5250 VECTXT(ctxt, state->node);
5251 VERROR(ctxt->userData,
5252 "Element %s content does not follow the DTD\nMisplaced %s\n",
5253 state->node->name, qname);
5254 ret = 0;
5255 } else {
5256 ret = 1;
5257 }
5258 }
5259 break;
5260 }
5261 }
5262 }
5263 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5264 vstateVPush(ctxt, eDecl, elem);
5265 return(ret);
5266}
5267
5268/**
5269 * xmlValidatePushCData:
5270 * @ctxt: the validation context
5271 * @data: some character data read
5272 * @len: the lenght of the data
5273 *
5274 * check the CData parsed for validation in the current stack
5275 *
5276 * returns 1 if no validation problem was found or 0 otherwise
5277 */
5278int
5279xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5280 int ret = 1;
5281
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005282/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005283 if (len <= 0)
5284 return(ret);
5285 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5286 xmlValidStatePtr state = ctxt->vstate;
5287 xmlElementPtr elemDecl;
5288
5289 /*
5290 * Check the new element agaisnt the content model of the new elem.
5291 */
5292 if (state->elemDecl != NULL) {
5293 elemDecl = state->elemDecl;
5294
5295 switch(elemDecl->etype) {
5296 case XML_ELEMENT_TYPE_UNDEFINED:
5297 ret = 0;
5298 break;
5299 case XML_ELEMENT_TYPE_EMPTY:
5300 VECTXT(ctxt, state->node);
5301 VERROR(ctxt->userData,
5302 "Element %s was declared EMPTY this one has content\n",
5303 state->node->name);
5304 ret = 0;
5305 break;
5306 case XML_ELEMENT_TYPE_ANY:
5307 break;
5308 case XML_ELEMENT_TYPE_MIXED:
5309 break;
5310 case XML_ELEMENT_TYPE_ELEMENT:
5311 if (len > 0) {
5312 int i;
5313
5314 for (i = 0;i < len;i++) {
5315 if (!IS_BLANK(data[i])) {
5316 VECTXT(ctxt, state->node);
5317 VERROR(ctxt->userData,
5318 "Element %s content does not follow the DTD\nText not allowed\n",
5319 state->node->name);
5320 ret = 0;
5321 goto done;
5322 }
5323 }
5324 /*
5325 * TODO:
5326 * VC: Standalone Document Declaration
5327 * element types with element content, if white space
5328 * occurs directly within any instance of those types.
5329 */
5330 }
5331 break;
5332 }
5333 }
5334 }
5335done:
5336 return(ret);
5337}
5338
5339/**
5340 * xmlValidatePopElement:
5341 * @ctxt: the validation context
5342 * @doc: a document instance
5343 * @elem: an element instance
5344 * @qname: the qualified name as appearing in the serialization
5345 *
5346 * Pop the element end from the validation stack.
5347 *
5348 * returns 1 if no validation problem was found or 0 otherwise
5349 */
5350int
5351xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005352 xmlNodePtr elem ATTRIBUTE_UNUSED,
5353 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005354 int ret = 1;
5355
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005356/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005357 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5358 xmlValidStatePtr state = ctxt->vstate;
5359 xmlElementPtr elemDecl;
5360
5361 /*
5362 * Check the new element agaisnt the content model of the new elem.
5363 */
5364 if (state->elemDecl != NULL) {
5365 elemDecl = state->elemDecl;
5366
5367 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5368 if (state->exec != NULL) {
5369 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5370 if (ret == 0) {
5371 VECTXT(ctxt, state->node);
5372 VERROR(ctxt->userData,
5373 "Element %s content does not follow the DTD\nExpecting more child\n",
5374 state->node->name);
5375 } else {
5376 /*
5377 * previous validation errors should not generate
5378 * a new one here
5379 */
5380 ret = 1;
5381 }
5382 }
5383 }
5384 }
5385 vstateVPop(ctxt);
5386 }
5387 return(ret);
5388}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005389#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005390
5391/**
Owen Taylor3473f882001-02-23 17:55:21 +00005392 * xmlValidateOneElement:
5393 * @ctxt: the validation context
5394 * @doc: a document instance
5395 * @elem: an element instance
5396 *
5397 * Try to validate a single element and it's attributes,
5398 * basically it does the following checks as described by the
5399 * XML-1.0 recommendation:
5400 * - [ VC: Element Valid ]
5401 * - [ VC: Required Attribute ]
5402 * Then call xmlValidateOneAttribute() for each attribute present.
5403 *
5404 * The ID/IDREF checkings are done separately
5405 *
5406 * returns 1 if valid or 0 otherwise
5407 */
5408
5409int
5410xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5411 xmlNodePtr elem) {
5412 xmlElementPtr elemDecl = NULL;
5413 xmlElementContentPtr cont;
5414 xmlAttributePtr attr;
5415 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005416 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005417 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005418 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005419
5420 CHECK_DTD;
5421
5422 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005423 switch (elem->type) {
5424 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005425 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005426 VERROR(ctxt->userData,
5427 "Attribute element not expected here\n");
5428 return(0);
5429 case XML_TEXT_NODE:
5430 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005431 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005432 VERROR(ctxt->userData, "Text element has childs !\n");
5433 return(0);
5434 }
5435 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005436 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005437 VERROR(ctxt->userData, "Text element has attributes !\n");
5438 return(0);
5439 }
5440 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005441 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005442 VERROR(ctxt->userData, "Text element has namespace !\n");
5443 return(0);
5444 }
5445 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005446 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005447 VERROR(ctxt->userData,
5448 "Text element carries namespace definitions !\n");
5449 return(0);
5450 }
5451 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005452 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005453 VERROR(ctxt->userData,
5454 "Text element has no content !\n");
5455 return(0);
5456 }
5457 return(1);
5458 case XML_XINCLUDE_START:
5459 case XML_XINCLUDE_END:
5460 return(1);
5461 case XML_CDATA_SECTION_NODE:
5462 case XML_ENTITY_REF_NODE:
5463 case XML_PI_NODE:
5464 case XML_COMMENT_NODE:
5465 return(1);
5466 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005467 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005468 VERROR(ctxt->userData,
5469 "Entity element not expected here\n");
5470 return(0);
5471 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005472 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005473 VERROR(ctxt->userData,
5474 "Notation element not expected here\n");
5475 return(0);
5476 case XML_DOCUMENT_NODE:
5477 case XML_DOCUMENT_TYPE_NODE:
5478 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005479 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005480 VERROR(ctxt->userData,
5481 "Document element not expected here\n");
5482 return(0);
5483 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005484 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005485 VERROR(ctxt->userData,
5486 "\n");
5487 return(0);
5488 case XML_ELEMENT_NODE:
5489 break;
5490 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005491 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005492 VERROR(ctxt->userData,
5493 "unknown element type %d\n", elem->type);
5494 return(0);
5495 }
Owen Taylor3473f882001-02-23 17:55:21 +00005496
5497 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005498 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005499 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005500 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5501 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005502 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005503
Daniel Veillardea7751d2002-12-20 00:16:24 +00005504 /*
5505 * If vstateNr is not zero that means continuous validation is
5506 * activated, do not try to check the content model at that level.
5507 */
5508 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005509 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005510 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005511 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005512 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005513 VERROR(ctxt->userData, "No declaration for element %s\n",
5514 elem->name);
5515 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005516 case XML_ELEMENT_TYPE_EMPTY:
5517 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005518 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005519 VERROR(ctxt->userData,
5520 "Element %s was declared EMPTY this one has content\n",
5521 elem->name);
5522 ret = 0;
5523 }
5524 break;
5525 case XML_ELEMENT_TYPE_ANY:
5526 /* I don't think anything is required then */
5527 break;
5528 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005529
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005530 /* simple case of declared as #PCDATA */
5531 if ((elemDecl->content != NULL) &&
5532 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5533 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5534 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005535 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005536 VERROR(ctxt->userData,
5537 "Element %s was declared #PCDATA but contains non text nodes\n",
5538 elem->name);
5539 }
5540 break;
5541 }
Owen Taylor3473f882001-02-23 17:55:21 +00005542 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005543 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005544 while (child != NULL) {
5545 if (child->type == XML_ELEMENT_NODE) {
5546 name = child->name;
5547 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005548 xmlChar fn[50];
5549 xmlChar *fullname;
5550
5551 fullname = xmlBuildQName(child->name, child->ns->prefix,
5552 fn, 50);
5553 if (fullname == NULL)
5554 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005555 cont = elemDecl->content;
5556 while (cont != NULL) {
5557 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005558 if (xmlStrEqual(cont->name, fullname))
5559 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005560 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5561 (cont->c1 != NULL) &&
5562 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005563 if (xmlStrEqual(cont->c1->name, fullname))
5564 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005565 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5566 (cont->c1 == NULL) ||
5567 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5568 /* Internal error !!! */
5569 xmlGenericError(xmlGenericErrorContext,
5570 "Internal: MIXED struct bad\n");
5571 break;
5572 }
5573 cont = cont->c2;
5574 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005575 if ((fullname != fn) && (fullname != child->name))
5576 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005577 if (cont != NULL)
5578 goto child_ok;
5579 }
5580 cont = elemDecl->content;
5581 while (cont != NULL) {
5582 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5583 if (xmlStrEqual(cont->name, name)) break;
5584 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5585 (cont->c1 != NULL) &&
5586 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5587 if (xmlStrEqual(cont->c1->name, name)) break;
5588 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5589 (cont->c1 == NULL) ||
5590 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5591 /* Internal error !!! */
5592 xmlGenericError(xmlGenericErrorContext,
5593 "Internal: MIXED struct bad\n");
5594 break;
5595 }
5596 cont = cont->c2;
5597 }
5598 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005599 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005600 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005601 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005602 name, elem->name);
5603 ret = 0;
5604 }
5605 }
5606child_ok:
5607 child = child->next;
5608 }
5609 break;
5610 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005611 if ((doc->standalone == 1) && (extsubset == 1)) {
5612 /*
5613 * VC: Standalone Document Declaration
5614 * - element types with element content, if white space
5615 * occurs directly within any instance of those types.
5616 */
5617 child = elem->children;
5618 while (child != NULL) {
5619 if (child->type == XML_TEXT_NODE) {
5620 const xmlChar *content = child->content;
5621
5622 while (IS_BLANK(*content))
5623 content++;
5624 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005625 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005626 VERROR(ctxt->userData,
5627"standalone: %s declared in the external subset contains white spaces nodes\n",
5628 elem->name);
5629 ret = 0;
5630 break;
5631 }
5632 }
5633 child =child->next;
5634 }
5635 }
Owen Taylor3473f882001-02-23 17:55:21 +00005636 child = elem->children;
5637 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005638 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005639 if (tmp <= 0)
5640 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005641 break;
5642 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005643 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005644
5645 /* [ VC: Required Attribute ] */
5646 attr = elemDecl->attributes;
5647 while (attr != NULL) {
5648 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005649 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005650
Daniel Veillarde4301c82002-02-13 13:32:35 +00005651 if ((attr->prefix == NULL) &&
5652 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5653 xmlNsPtr ns;
5654
5655 ns = elem->nsDef;
5656 while (ns != NULL) {
5657 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005658 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005659 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005660 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005661 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5662 xmlNsPtr ns;
5663
5664 ns = elem->nsDef;
5665 while (ns != NULL) {
5666 if (xmlStrEqual(attr->name, ns->prefix))
5667 goto found;
5668 ns = ns->next;
5669 }
5670 } else {
5671 xmlAttrPtr attrib;
5672
5673 attrib = elem->properties;
5674 while (attrib != NULL) {
5675 if (xmlStrEqual(attrib->name, attr->name)) {
5676 if (attr->prefix != NULL) {
5677 xmlNsPtr nameSpace = attrib->ns;
5678
5679 if (nameSpace == NULL)
5680 nameSpace = elem->ns;
5681 /*
5682 * qualified names handling is problematic, having a
5683 * different prefix should be possible but DTDs don't
5684 * allow to define the URI instead of the prefix :-(
5685 */
5686 if (nameSpace == NULL) {
5687 if (qualified < 0)
5688 qualified = 0;
5689 } else if (!xmlStrEqual(nameSpace->prefix,
5690 attr->prefix)) {
5691 if (qualified < 1)
5692 qualified = 1;
5693 } else
5694 goto found;
5695 } else {
5696 /*
5697 * We should allow applications to define namespaces
5698 * for their application even if the DTD doesn't
5699 * carry one, otherwise, basically we would always
5700 * break.
5701 */
5702 goto found;
5703 }
5704 }
5705 attrib = attrib->next;
5706 }
Owen Taylor3473f882001-02-23 17:55:21 +00005707 }
5708 if (qualified == -1) {
5709 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005710 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005711 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005712 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005713 elem->name, attr->name);
5714 ret = 0;
5715 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005716 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005717 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005718 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005719 elem->name, attr->prefix,attr->name);
5720 ret = 0;
5721 }
5722 } else if (qualified == 0) {
5723 VWARNING(ctxt->userData,
5724 "Element %s required attribute %s:%s has no prefix\n",
5725 elem->name, attr->prefix,attr->name);
5726 } else if (qualified == 1) {
5727 VWARNING(ctxt->userData,
5728 "Element %s required attribute %s:%s has different prefix\n",
5729 elem->name, attr->prefix,attr->name);
5730 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005731 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5732 /*
5733 * Special tests checking #FIXED namespace declarations
5734 * have the right value since this is not done as an
5735 * attribute checking
5736 */
5737 if ((attr->prefix == NULL) &&
5738 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5739 xmlNsPtr ns;
5740
5741 ns = elem->nsDef;
5742 while (ns != NULL) {
5743 if (ns->prefix == NULL) {
5744 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005745 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005746 VERROR(ctxt->userData,
5747 "Element %s namespace name for default namespace does not match the DTD\n",
5748 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005749 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005750 }
5751 goto found;
5752 }
5753 ns = ns->next;
5754 }
5755 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5756 xmlNsPtr ns;
5757
5758 ns = elem->nsDef;
5759 while (ns != NULL) {
5760 if (xmlStrEqual(attr->name, ns->prefix)) {
5761 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005762 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005763 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005764 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005765 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005766 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005767 }
5768 goto found;
5769 }
5770 ns = ns->next;
5771 }
5772 }
Owen Taylor3473f882001-02-23 17:55:21 +00005773 }
5774found:
5775 attr = attr->nexth;
5776 }
5777 return(ret);
5778}
5779
5780/**
5781 * xmlValidateRoot:
5782 * @ctxt: the validation context
5783 * @doc: a document instance
5784 *
5785 * Try to validate a the root element
5786 * basically it does the following check as described by the
5787 * XML-1.0 recommendation:
5788 * - [ VC: Root Element Type ]
5789 * it doesn't try to recurse or apply other check to the element
5790 *
5791 * returns 1 if valid or 0 otherwise
5792 */
5793
5794int
5795xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5796 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00005797 int ret;
5798
Owen Taylor3473f882001-02-23 17:55:21 +00005799 if (doc == NULL) return(0);
5800
5801 root = xmlDocGetRootElement(doc);
5802 if ((root == NULL) || (root->name == NULL)) {
5803 VERROR(ctxt->userData, "Not valid: no root element\n");
5804 return(0);
5805 }
5806
5807 /*
5808 * When doing post validation against a separate DTD, those may
5809 * no internal subset has been generated
5810 */
5811 if ((doc->intSubset != NULL) &&
5812 (doc->intSubset->name != NULL)) {
5813 /*
5814 * Check first the document root against the NQName
5815 */
5816 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5817 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005818 xmlChar fn[50];
5819 xmlChar *fullname;
5820
5821 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
5822 if (fullname == NULL) {
5823 VERROR(ctxt->userData, "Out of memory\n");
5824 return(0);
5825 }
5826 ret = xmlStrEqual(doc->intSubset->name, fullname);
5827 if ((fullname != fn) && (fullname != root->name))
5828 xmlFree(fullname);
5829 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00005830 goto name_ok;
5831 }
5832 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5833 (xmlStrEqual(root->name, BAD_CAST "html")))
5834 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005835 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005836 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005837 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005838 root->name, doc->intSubset->name);
5839 return(0);
5840
5841 }
5842 }
5843name_ok:
5844 return(1);
5845}
5846
5847
5848/**
5849 * xmlValidateElement:
5850 * @ctxt: the validation context
5851 * @doc: a document instance
5852 * @elem: an element instance
5853 *
5854 * Try to validate the subtree under an element
5855 *
5856 * returns 1 if valid or 0 otherwise
5857 */
5858
5859int
5860xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5861 xmlNodePtr child;
5862 xmlAttrPtr attr;
5863 xmlChar *value;
5864 int ret = 1;
5865
5866 if (elem == NULL) return(0);
5867
5868 /*
5869 * XInclude elements were added after parsing in the infoset,
5870 * they don't really mean anything validation wise.
5871 */
5872 if ((elem->type == XML_XINCLUDE_START) ||
5873 (elem->type == XML_XINCLUDE_END))
5874 return(1);
5875
5876 CHECK_DTD;
5877
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005878 /*
5879 * Entities references have to be handled separately
5880 */
5881 if (elem->type == XML_ENTITY_REF_NODE) {
5882 return(1);
5883 }
5884
Owen Taylor3473f882001-02-23 17:55:21 +00005885 ret &= xmlValidateOneElement(ctxt, doc, elem);
5886 attr = elem->properties;
5887 while(attr != NULL) {
5888 value = xmlNodeListGetString(doc, attr->children, 0);
5889 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5890 if (value != NULL)
5891 xmlFree(value);
5892 attr= attr->next;
5893 }
5894 child = elem->children;
5895 while (child != NULL) {
5896 ret &= xmlValidateElement(ctxt, doc, child);
5897 child = child->next;
5898 }
5899
5900 return(ret);
5901}
5902
Daniel Veillard8730c562001-02-26 10:49:57 +00005903/**
5904 * xmlValidateRef:
5905 * @ref: A reference to be validated
5906 * @ctxt: Validation context
5907 * @name: Name of ID we are searching for
5908 *
5909 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005910static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005911xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005912 const xmlChar *name) {
5913 xmlAttrPtr id;
5914 xmlAttrPtr attr;
5915
5916 if (ref == NULL)
5917 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005918 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00005919 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005920 attr = ref->attr;
5921 if (attr == NULL) {
5922 xmlChar *dup, *str = NULL, *cur, save;
5923
5924 dup = xmlStrdup(name);
5925 if (dup == NULL) {
5926 ctxt->valid = 0;
5927 return;
5928 }
5929 cur = dup;
5930 while (*cur != 0) {
5931 str = cur;
5932 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5933 save = *cur;
5934 *cur = 0;
5935 id = xmlGetID(ctxt->doc, str);
5936 if (id == NULL) {
5937 VERROR(ctxt->userData,
5938 "attribute %s line %d references an unknown ID \"%s\"\n",
5939 ref->name, ref->lineno, str);
5940 ctxt->valid = 0;
5941 }
5942 if (save == 0)
5943 break;
5944 *cur = save;
5945 while (IS_BLANK(*cur)) cur++;
5946 }
5947 xmlFree(dup);
5948 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00005949 id = xmlGetID(ctxt->doc, name);
5950 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005951 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005952 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005953 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005954 attr->name, name);
5955 ctxt->valid = 0;
5956 }
5957 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5958 xmlChar *dup, *str = NULL, *cur, save;
5959
5960 dup = xmlStrdup(name);
5961 if (dup == NULL) {
5962 ctxt->valid = 0;
5963 return;
5964 }
5965 cur = dup;
5966 while (*cur != 0) {
5967 str = cur;
5968 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5969 save = *cur;
5970 *cur = 0;
5971 id = xmlGetID(ctxt->doc, str);
5972 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005973 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005974 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005975 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005976 attr->name, str);
5977 ctxt->valid = 0;
5978 }
5979 if (save == 0)
5980 break;
5981 *cur = save;
5982 while (IS_BLANK(*cur)) cur++;
5983 }
5984 xmlFree(dup);
5985 }
5986}
5987
5988/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005989 * xmlWalkValidateList:
5990 * @data: Contents of current link
5991 * @user: Value supplied by the user
5992 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005993 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005994 */
5995static int
5996xmlWalkValidateList(const void *data, const void *user)
5997{
5998 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5999 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
6000 return 1;
6001}
6002
6003/**
6004 * xmlValidateCheckRefCallback:
6005 * @ref_list: List of references
6006 * @ctxt: Validation context
6007 * @name: Name of ID we are searching for
6008 *
6009 */
6010static void
6011xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
6012 const xmlChar *name) {
6013 xmlValidateMemo memo;
6014
6015 if (ref_list == NULL)
6016 return;
6017 memo.ctxt = ctxt;
6018 memo.name = name;
6019
6020 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
6021
6022}
6023
6024/**
Owen Taylor3473f882001-02-23 17:55:21 +00006025 * xmlValidateDocumentFinal:
6026 * @ctxt: the validation context
6027 * @doc: a document instance
6028 *
6029 * Does the final step for the document validation once all the
6030 * incremental validation steps have been completed
6031 *
6032 * basically it does the following checks described by the XML Rec
6033 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006034 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00006035 *
6036 * returns 1 if valid or 0 otherwise
6037 */
6038
6039int
6040xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6041 xmlRefTablePtr table;
6042
6043 if (doc == NULL) {
6044 xmlGenericError(xmlGenericErrorContext,
6045 "xmlValidateDocumentFinal: doc == NULL\n");
6046 return(0);
6047 }
6048
6049 /*
6050 * Check all the NOTATION/NOTATIONS attributes
6051 */
6052 /*
6053 * Check all the ENTITY/ENTITIES attributes definition for validity
6054 */
6055 /*
6056 * Check all the IDREF/IDREFS attributes definition for validity
6057 */
6058 table = (xmlRefTablePtr) doc->refs;
6059 ctxt->doc = doc;
6060 ctxt->valid = 1;
6061 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6062 return(ctxt->valid);
6063}
6064
6065/**
6066 * xmlValidateDtd:
6067 * @ctxt: the validation context
6068 * @doc: a document instance
6069 * @dtd: a dtd instance
6070 *
6071 * Try to validate the document against the dtd instance
6072 *
6073 * basically it does check all the definitions in the DtD.
6074 *
6075 * returns 1 if valid or 0 otherwise
6076 */
6077
6078int
6079xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6080 int ret;
6081 xmlDtdPtr oldExt;
6082 xmlNodePtr root;
6083
6084 if (dtd == NULL) return(0);
6085 if (doc == NULL) return(0);
6086 oldExt = doc->extSubset;
6087 doc->extSubset = dtd;
6088 ret = xmlValidateRoot(ctxt, doc);
6089 if (ret == 0) {
6090 doc->extSubset = oldExt;
6091 return(ret);
6092 }
6093 if (doc->ids != NULL) {
6094 xmlFreeIDTable(doc->ids);
6095 doc->ids = NULL;
6096 }
6097 if (doc->refs != NULL) {
6098 xmlFreeRefTable(doc->refs);
6099 doc->refs = NULL;
6100 }
6101 root = xmlDocGetRootElement(doc);
6102 ret = xmlValidateElement(ctxt, doc, root);
6103 ret &= xmlValidateDocumentFinal(ctxt, doc);
6104 doc->extSubset = oldExt;
6105 return(ret);
6106}
6107
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006108static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006109xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6110 const xmlChar *name ATTRIBUTE_UNUSED) {
6111 if (cur == NULL)
6112 return;
6113 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6114 xmlChar *notation = cur->content;
6115
Daniel Veillard878eab02002-02-19 13:46:09 +00006116 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006117 int ret;
6118
6119 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6120 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006121 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006122 }
6123 }
6124 }
6125}
6126
6127static void
Owen Taylor3473f882001-02-23 17:55:21 +00006128xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006129 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006130 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006131 xmlDocPtr doc;
6132 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006133
Owen Taylor3473f882001-02-23 17:55:21 +00006134 if (cur == NULL)
6135 return;
6136 switch (cur->atype) {
6137 case XML_ATTRIBUTE_CDATA:
6138 case XML_ATTRIBUTE_ID:
6139 case XML_ATTRIBUTE_IDREF :
6140 case XML_ATTRIBUTE_IDREFS:
6141 case XML_ATTRIBUTE_NMTOKEN:
6142 case XML_ATTRIBUTE_NMTOKENS:
6143 case XML_ATTRIBUTE_ENUMERATION:
6144 break;
6145 case XML_ATTRIBUTE_ENTITY:
6146 case XML_ATTRIBUTE_ENTITIES:
6147 case XML_ATTRIBUTE_NOTATION:
6148 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006149
6150 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6151 cur->atype, cur->defaultValue);
6152 if ((ret == 0) && (ctxt->valid == 1))
6153 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006154 }
6155 if (cur->tree != NULL) {
6156 xmlEnumerationPtr tree = cur->tree;
6157 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006158 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006159 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006160 if ((ret == 0) && (ctxt->valid == 1))
6161 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006162 tree = tree->next;
6163 }
6164 }
6165 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006166 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6167 doc = cur->doc;
6168 if ((doc == NULL) || (cur->elem == NULL)) {
6169 VERROR(ctxt->userData,
6170 "xmlValidateAttributeCallback(%s): internal error\n",
6171 cur->name);
6172 return;
6173 }
6174 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6175 if (elem == NULL)
6176 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6177 if (elem == NULL) {
6178 VERROR(ctxt->userData,
6179 "attribute %s: could not find decl for element %s\n",
6180 cur->name, cur->elem);
6181 return;
6182 }
6183 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6184 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006185 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00006186 cur->name, cur->elem);
6187 ctxt->valid = 0;
6188 }
6189 }
Owen Taylor3473f882001-02-23 17:55:21 +00006190}
6191
6192/**
6193 * xmlValidateDtdFinal:
6194 * @ctxt: the validation context
6195 * @doc: a document instance
6196 *
6197 * Does the final step for the dtds validation once all the
6198 * subsets have been parsed
6199 *
6200 * basically it does the following checks described by the XML Rec
6201 * - check that ENTITY and ENTITIES type attributes default or
6202 * possible values matches one of the defined entities.
6203 * - check that NOTATION type attributes default or
6204 * possible values matches one of the defined notations.
6205 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006206 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006207 */
6208
6209int
6210xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006211 xmlDtdPtr dtd;
6212 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006213 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006214
6215 if (doc == NULL) return(0);
6216 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6217 return(0);
6218 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006219 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006220 dtd = doc->intSubset;
6221 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6222 table = (xmlAttributeTablePtr) dtd->attributes;
6223 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006224 }
6225 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006226 entities = (xmlEntitiesTablePtr) dtd->entities;
6227 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6228 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006229 }
6230 dtd = doc->extSubset;
6231 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6232 table = (xmlAttributeTablePtr) dtd->attributes;
6233 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006234 }
6235 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006236 entities = (xmlEntitiesTablePtr) dtd->entities;
6237 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6238 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006239 }
6240 return(ctxt->valid);
6241}
6242
6243/**
6244 * xmlValidateDocument:
6245 * @ctxt: the validation context
6246 * @doc: a document instance
6247 *
6248 * Try to validate the document instance
6249 *
6250 * basically it does the all the checks described by the XML Rec
6251 * i.e. validates the internal and external subset (if present)
6252 * and validate the document tree.
6253 *
6254 * returns 1 if valid or 0 otherwise
6255 */
6256
6257int
6258xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6259 int ret;
6260 xmlNodePtr root;
6261
Daniel Veillard2fd85422002-10-16 14:32:41 +00006262 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6263 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00006264 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006265 }
Owen Taylor3473f882001-02-23 17:55:21 +00006266 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6267 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6268 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6269 doc->intSubset->SystemID);
6270 if (doc->extSubset == NULL) {
6271 if (doc->intSubset->SystemID != NULL) {
6272 VERROR(ctxt->userData,
6273 "Could not load the external subset \"%s\"\n",
6274 doc->intSubset->SystemID);
6275 } else {
6276 VERROR(ctxt->userData,
6277 "Could not load the external subset \"%s\"\n",
6278 doc->intSubset->ExternalID);
6279 }
6280 return(0);
6281 }
6282 }
6283
6284 if (doc->ids != NULL) {
6285 xmlFreeIDTable(doc->ids);
6286 doc->ids = NULL;
6287 }
6288 if (doc->refs != NULL) {
6289 xmlFreeRefTable(doc->refs);
6290 doc->refs = NULL;
6291 }
6292 ret = xmlValidateDtdFinal(ctxt, doc);
6293 if (!xmlValidateRoot(ctxt, doc)) return(0);
6294
6295 root = xmlDocGetRootElement(doc);
6296 ret &= xmlValidateElement(ctxt, doc, root);
6297 ret &= xmlValidateDocumentFinal(ctxt, doc);
6298 return(ret);
6299}
6300
6301
6302/************************************************************************
6303 * *
6304 * Routines for dynamic validation editing *
6305 * *
6306 ************************************************************************/
6307
6308/**
6309 * xmlValidGetPotentialChildren:
6310 * @ctree: an element content tree
6311 * @list: an array to store the list of child names
6312 * @len: a pointer to the number of element in the list
6313 * @max: the size of the array
6314 *
6315 * Build/extend a list of potential children allowed by the content tree
6316 *
6317 * returns the number of element in the list, or -1 in case of error.
6318 */
6319
6320int
6321xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6322 int *len, int max) {
6323 int i;
6324
6325 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6326 return(-1);
6327 if (*len >= max) return(*len);
6328
6329 switch (ctree->type) {
6330 case XML_ELEMENT_CONTENT_PCDATA:
6331 for (i = 0; i < *len;i++)
6332 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6333 list[(*len)++] = BAD_CAST "#PCDATA";
6334 break;
6335 case XML_ELEMENT_CONTENT_ELEMENT:
6336 for (i = 0; i < *len;i++)
6337 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6338 list[(*len)++] = ctree->name;
6339 break;
6340 case XML_ELEMENT_CONTENT_SEQ:
6341 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6342 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6343 break;
6344 case XML_ELEMENT_CONTENT_OR:
6345 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6346 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6347 break;
6348 }
6349
6350 return(*len);
6351}
6352
6353/**
6354 * xmlValidGetValidElements:
6355 * @prev: an element to insert after
6356 * @next: an element to insert next
6357 * @list: an array to store the list of child names
6358 * @max: the size of the array
6359 *
6360 * This function returns the list of authorized children to insert
6361 * within an existing tree while respecting the validity constraints
6362 * forced by the Dtd. The insertion point is defined using @prev and
6363 * @next in the following ways:
6364 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6365 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6366 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6367 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6368 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6369 *
6370 * pointers to the element names are inserted at the beginning of the array
6371 * and do not need to be freed.
6372 *
6373 * returns the number of element in the list, or -1 in case of error. If
6374 * the function returns the value @max the caller is invited to grow the
6375 * receiving array and retry.
6376 */
6377
6378int
6379xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6380 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006381 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006382 int nb_valid_elements = 0;
6383 const xmlChar *elements[256];
6384 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006385 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006386
6387 xmlNode *ref_node;
6388 xmlNode *parent;
6389 xmlNode *test_node;
6390
6391 xmlNode *prev_next;
6392 xmlNode *next_prev;
6393 xmlNode *parent_childs;
6394 xmlNode *parent_last;
6395
6396 xmlElement *element_desc;
6397
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006398 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006399
Owen Taylor3473f882001-02-23 17:55:21 +00006400 if (prev == NULL && next == NULL)
6401 return(-1);
6402
6403 if (list == NULL) return(-1);
6404 if (max <= 0) return(-1);
6405
6406 nb_valid_elements = 0;
6407 ref_node = prev ? prev : next;
6408 parent = ref_node->parent;
6409
6410 /*
6411 * Retrieves the parent element declaration
6412 */
6413 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6414 parent->name);
6415 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6416 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6417 parent->name);
6418 if (element_desc == NULL) return(-1);
6419
6420 /*
6421 * Do a backup of the current tree structure
6422 */
6423 prev_next = prev ? prev->next : NULL;
6424 next_prev = next ? next->prev : NULL;
6425 parent_childs = parent->children;
6426 parent_last = parent->last;
6427
6428 /*
6429 * Creates a dummy node and insert it into the tree
6430 */
6431 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6432 test_node->doc = ref_node->doc;
6433 test_node->parent = parent;
6434 test_node->prev = prev;
6435 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006436 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006437
6438 if (prev) prev->next = test_node;
6439 else parent->children = test_node;
6440
6441 if (next) next->prev = test_node;
6442 else parent->last = test_node;
6443
6444 /*
6445 * Insert each potential child node and check if the parent is
6446 * still valid
6447 */
6448 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6449 elements, &nb_elements, 256);
6450
6451 for (i = 0;i < nb_elements;i++) {
6452 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006453 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006454 int j;
6455
6456 for (j = 0; j < nb_valid_elements;j++)
6457 if (xmlStrEqual(elements[i], list[j])) break;
6458 list[nb_valid_elements++] = elements[i];
6459 if (nb_valid_elements >= max) break;
6460 }
6461 }
6462
6463 /*
6464 * Restore the tree structure
6465 */
6466 if (prev) prev->next = prev_next;
6467 if (next) next->prev = next_prev;
6468 parent->children = parent_childs;
6469 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006470
6471 /*
6472 * Free up the dummy node
6473 */
6474 test_node->name = name;
6475 xmlFreeNode(test_node);
6476
Owen Taylor3473f882001-02-23 17:55:21 +00006477 return(nb_valid_elements);
6478}