blob: 36d1a35bbb66db47a613ade69c3a3a03a4ad9885 [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 }
William M. Brack4e52f2f2003-09-14 18:07:39 +00001217 /*
1218 * For new element, may have attributes from earlier
1219 * definition in internal subset
1220 */
1221 ret->attributes = oldAttributes;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001222 }
1223
1224 /*
1225 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001226 */
1227 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001228 ret->content = xmlCopyElementContent(content);
Owen Taylor3473f882001-02-23 17:55:21 +00001229
1230 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001231 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001232 */
1233 ret->parent = dtd;
1234 ret->doc = dtd->doc;
1235 if (dtd->last == NULL) {
1236 dtd->children = dtd->last = (xmlNodePtr) ret;
1237 } else {
1238 dtd->last->next = (xmlNodePtr) ret;
1239 ret->prev = dtd->last;
1240 dtd->last = (xmlNodePtr) ret;
1241 }
1242 if (uqname != NULL)
1243 xmlFree(uqname);
1244 return(ret);
1245}
1246
1247/**
1248 * xmlFreeElementTable:
1249 * @table: An element table
1250 *
1251 * Deallocate the memory used by an element hash table.
1252 */
1253void
1254xmlFreeElementTable(xmlElementTablePtr table) {
1255 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1256}
1257
1258/**
1259 * xmlCopyElement:
1260 * @elem: An element
1261 *
1262 * Build a copy of an element.
1263 *
1264 * Returns the new xmlElementPtr or NULL in case of error.
1265 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001266static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001267xmlCopyElement(xmlElementPtr elem) {
1268 xmlElementPtr cur;
1269
1270 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1271 if (cur == NULL) {
1272 xmlGenericError(xmlGenericErrorContext,
1273 "xmlCopyElement: out of memory !\n");
1274 return(NULL);
1275 }
1276 memset(cur, 0, sizeof(xmlElement));
1277 cur->type = XML_ELEMENT_DECL;
1278 cur->etype = elem->etype;
1279 if (elem->name != NULL)
1280 cur->name = xmlStrdup(elem->name);
1281 else
1282 cur->name = NULL;
1283 if (elem->prefix != NULL)
1284 cur->prefix = xmlStrdup(elem->prefix);
1285 else
1286 cur->prefix = NULL;
1287 cur->content = xmlCopyElementContent(elem->content);
1288 /* TODO : rebuild the attribute list on the copy */
1289 cur->attributes = NULL;
1290 return(cur);
1291}
1292
1293/**
1294 * xmlCopyElementTable:
1295 * @table: An element table
1296 *
1297 * Build a copy of an element table.
1298 *
1299 * Returns the new xmlElementTablePtr or NULL in case of error.
1300 */
1301xmlElementTablePtr
1302xmlCopyElementTable(xmlElementTablePtr table) {
1303 return((xmlElementTablePtr) xmlHashCopy(table,
1304 (xmlHashCopier) xmlCopyElement));
1305}
1306
1307/**
1308 * xmlDumpElementDecl:
1309 * @buf: the XML buffer output
1310 * @elem: An element table
1311 *
1312 * This will dump the content of the element declaration as an XML
1313 * DTD definition
1314 */
1315void
1316xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1317 switch (elem->etype) {
1318 case XML_ELEMENT_TYPE_EMPTY:
1319 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001320 if (elem->prefix != NULL) {
1321 xmlBufferWriteCHAR(buf, elem->prefix);
1322 xmlBufferWriteChar(buf, ":");
1323 }
Owen Taylor3473f882001-02-23 17:55:21 +00001324 xmlBufferWriteCHAR(buf, elem->name);
1325 xmlBufferWriteChar(buf, " EMPTY>\n");
1326 break;
1327 case XML_ELEMENT_TYPE_ANY:
1328 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001329 if (elem->prefix != NULL) {
1330 xmlBufferWriteCHAR(buf, elem->prefix);
1331 xmlBufferWriteChar(buf, ":");
1332 }
Owen Taylor3473f882001-02-23 17:55:21 +00001333 xmlBufferWriteCHAR(buf, elem->name);
1334 xmlBufferWriteChar(buf, " ANY>\n");
1335 break;
1336 case XML_ELEMENT_TYPE_MIXED:
1337 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001338 if (elem->prefix != NULL) {
1339 xmlBufferWriteCHAR(buf, elem->prefix);
1340 xmlBufferWriteChar(buf, ":");
1341 }
Owen Taylor3473f882001-02-23 17:55:21 +00001342 xmlBufferWriteCHAR(buf, elem->name);
1343 xmlBufferWriteChar(buf, " ");
1344 xmlDumpElementContent(buf, elem->content, 1);
1345 xmlBufferWriteChar(buf, ">\n");
1346 break;
1347 case XML_ELEMENT_TYPE_ELEMENT:
1348 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001349 if (elem->prefix != NULL) {
1350 xmlBufferWriteCHAR(buf, elem->prefix);
1351 xmlBufferWriteChar(buf, ":");
1352 }
Owen Taylor3473f882001-02-23 17:55:21 +00001353 xmlBufferWriteCHAR(buf, elem->name);
1354 xmlBufferWriteChar(buf, " ");
1355 xmlDumpElementContent(buf, elem->content, 1);
1356 xmlBufferWriteChar(buf, ">\n");
1357 break;
1358 default:
1359 xmlGenericError(xmlGenericErrorContext,
1360 "xmlDumpElementDecl: internal: unknown type %d\n",
1361 elem->etype);
1362 }
1363}
1364
1365/**
1366 * xmlDumpElementTable:
1367 * @buf: the XML buffer output
1368 * @table: An element table
1369 *
1370 * This will dump the content of the element table as an XML DTD definition
1371 */
1372void
1373xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1374 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1375}
1376
1377/**
1378 * xmlCreateEnumeration:
1379 * @name: the enumeration name or NULL
1380 *
1381 * create and initialize an enumeration attribute node.
1382 *
1383 * Returns the xmlEnumerationPtr just created or NULL in case
1384 * of error.
1385 */
1386xmlEnumerationPtr
Daniel Veillard2fdbd322003-08-18 12:15:38 +00001387xmlCreateEnumeration(const xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00001388 xmlEnumerationPtr ret;
1389
1390 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1391 if (ret == NULL) {
1392 xmlGenericError(xmlGenericErrorContext,
1393 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1394 (long)sizeof(xmlEnumeration));
1395 return(NULL);
1396 }
1397 memset(ret, 0, sizeof(xmlEnumeration));
1398
1399 if (name != NULL)
1400 ret->name = xmlStrdup(name);
1401 return(ret);
1402}
1403
1404/**
1405 * xmlFreeEnumeration:
1406 * @cur: the tree to free.
1407 *
1408 * free an enumeration attribute node (recursive).
1409 */
1410void
1411xmlFreeEnumeration(xmlEnumerationPtr cur) {
1412 if (cur == NULL) return;
1413
1414 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1415
1416 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001417 xmlFree(cur);
1418}
1419
1420/**
1421 * xmlCopyEnumeration:
1422 * @cur: the tree to copy.
1423 *
1424 * Copy an enumeration attribute node (recursive).
1425 *
1426 * Returns the xmlEnumerationPtr just created or NULL in case
1427 * of error.
1428 */
1429xmlEnumerationPtr
1430xmlCopyEnumeration(xmlEnumerationPtr cur) {
1431 xmlEnumerationPtr ret;
1432
1433 if (cur == NULL) return(NULL);
1434 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1435
1436 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1437 else ret->next = NULL;
1438
1439 return(ret);
1440}
1441
1442/**
1443 * xmlDumpEnumeration:
1444 * @buf: the XML buffer output
1445 * @enum: An enumeration
1446 *
1447 * This will dump the content of the enumeration
1448 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001449static void
Owen Taylor3473f882001-02-23 17:55:21 +00001450xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1451 if (cur == NULL) return;
1452
1453 xmlBufferWriteCHAR(buf, cur->name);
1454 if (cur->next == NULL)
1455 xmlBufferWriteChar(buf, ")");
1456 else {
1457 xmlBufferWriteChar(buf, " | ");
1458 xmlDumpEnumeration(buf, cur->next);
1459 }
1460}
1461
1462/**
1463 * xmlCreateAttributeTable:
1464 *
1465 * create and initialize an empty attribute hash table.
1466 *
1467 * Returns the xmlAttributeTablePtr just created or NULL in case
1468 * of error.
1469 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001470static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001471xmlCreateAttributeTable(void) {
1472 return(xmlHashCreate(0));
1473}
1474
1475/**
1476 * xmlScanAttributeDeclCallback:
1477 * @attr: the attribute decl
1478 * @list: the list to update
1479 *
1480 * Callback called by xmlScanAttributeDecl when a new attribute
1481 * has to be entered in the list.
1482 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001483static void
Owen Taylor3473f882001-02-23 17:55:21 +00001484xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001485 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001486 attr->nexth = *list;
1487 *list = attr;
1488}
1489
1490/**
1491 * xmlScanAttributeDecl:
1492 * @dtd: pointer to the DTD
1493 * @elem: the element name
1494 *
1495 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001496 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001497 *
1498 * Returns the pointer to the first attribute decl in the chain,
1499 * possibly NULL.
1500 */
1501xmlAttributePtr
1502xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1503 xmlAttributePtr ret = NULL;
1504 xmlAttributeTablePtr table;
1505
1506 if (dtd == NULL) {
1507 xmlGenericError(xmlGenericErrorContext,
1508 "xmlScanAttributeDecl: dtd == NULL\n");
1509 return(NULL);
1510 }
1511 if (elem == NULL) {
1512 xmlGenericError(xmlGenericErrorContext,
1513 "xmlScanAttributeDecl: elem == NULL\n");
1514 return(NULL);
1515 }
1516 table = (xmlAttributeTablePtr) dtd->attributes;
1517 if (table == NULL)
1518 return(NULL);
1519
1520 /* WRONG !!! */
1521 xmlHashScan3(table, NULL, NULL, elem,
1522 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1523 return(ret);
1524}
1525
1526/**
1527 * xmlScanIDAttributeDecl:
1528 * @ctxt: the validation context
1529 * @elem: the element name
1530 *
1531 * Verify that the element don't have too many ID attributes
1532 * declared.
1533 *
1534 * Returns the number of ID attributes found.
1535 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001536static int
Owen Taylor3473f882001-02-23 17:55:21 +00001537xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1538 xmlAttributePtr cur;
1539 int ret = 0;
1540
1541 if (elem == NULL) return(0);
1542 cur = elem->attributes;
1543 while (cur != NULL) {
1544 if (cur->atype == XML_ATTRIBUTE_ID) {
1545 ret ++;
1546 if (ret > 1)
1547 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001548 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001549 elem->name, cur->name);
1550 }
1551 cur = cur->nexth;
1552 }
1553 return(ret);
1554}
1555
1556/**
1557 * xmlFreeAttribute:
1558 * @elem: An attribute
1559 *
1560 * Deallocate the memory used by an attribute definition
1561 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001562static void
Owen Taylor3473f882001-02-23 17:55:21 +00001563xmlFreeAttribute(xmlAttributePtr attr) {
1564 if (attr == NULL) return;
1565 xmlUnlinkNode((xmlNodePtr) attr);
1566 if (attr->tree != NULL)
1567 xmlFreeEnumeration(attr->tree);
1568 if (attr->elem != NULL)
1569 xmlFree((xmlChar *) attr->elem);
1570 if (attr->name != NULL)
1571 xmlFree((xmlChar *) attr->name);
1572 if (attr->defaultValue != NULL)
1573 xmlFree((xmlChar *) attr->defaultValue);
1574 if (attr->prefix != NULL)
1575 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001576 xmlFree(attr);
1577}
1578
1579
1580/**
1581 * xmlAddAttributeDecl:
1582 * @ctxt: the validation context
1583 * @dtd: pointer to the DTD
1584 * @elem: the element name
1585 * @name: the attribute name
1586 * @ns: the attribute namespace prefix
1587 * @type: the attribute type
1588 * @def: the attribute default type
1589 * @defaultValue: the attribute default value
1590 * @tree: if it's an enumeration, the associated list
1591 *
1592 * Register a new attribute declaration
1593 * Note that @tree becomes the ownership of the DTD
1594 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001595 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001596 */
1597xmlAttributePtr
1598xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1599 const xmlChar *name, const xmlChar *ns,
1600 xmlAttributeType type, xmlAttributeDefault def,
1601 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1602 xmlAttributePtr ret;
1603 xmlAttributeTablePtr table;
1604 xmlElementPtr elemDef;
1605
1606 if (dtd == NULL) {
1607 xmlGenericError(xmlGenericErrorContext,
1608 "xmlAddAttributeDecl: dtd == NULL\n");
1609 xmlFreeEnumeration(tree);
1610 return(NULL);
1611 }
1612 if (name == NULL) {
1613 xmlGenericError(xmlGenericErrorContext,
1614 "xmlAddAttributeDecl: name == NULL\n");
1615 xmlFreeEnumeration(tree);
1616 return(NULL);
1617 }
1618 if (elem == NULL) {
1619 xmlGenericError(xmlGenericErrorContext,
1620 "xmlAddAttributeDecl: elem == NULL\n");
1621 xmlFreeEnumeration(tree);
1622 return(NULL);
1623 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001624
Owen Taylor3473f882001-02-23 17:55:21 +00001625 /*
1626 * Check the type and possibly the default value.
1627 */
1628 switch (type) {
1629 case XML_ATTRIBUTE_CDATA:
1630 break;
1631 case XML_ATTRIBUTE_ID:
1632 break;
1633 case XML_ATTRIBUTE_IDREF:
1634 break;
1635 case XML_ATTRIBUTE_IDREFS:
1636 break;
1637 case XML_ATTRIBUTE_ENTITY:
1638 break;
1639 case XML_ATTRIBUTE_ENTITIES:
1640 break;
1641 case XML_ATTRIBUTE_NMTOKEN:
1642 break;
1643 case XML_ATTRIBUTE_NMTOKENS:
1644 break;
1645 case XML_ATTRIBUTE_ENUMERATION:
1646 break;
1647 case XML_ATTRIBUTE_NOTATION:
1648 break;
1649 default:
1650 xmlGenericError(xmlGenericErrorContext,
1651 "xmlAddAttributeDecl: unknown type %d\n", type);
1652 xmlFreeEnumeration(tree);
1653 return(NULL);
1654 }
1655 if ((defaultValue != NULL) &&
1656 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001657 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001658 elem, name, defaultValue);
1659 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001660 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001661 }
1662
1663 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001664 * Check first that an attribute defined in the external subset wasn't
1665 * already defined in the internal subset
1666 */
1667 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1668 (dtd->doc->intSubset != NULL) &&
1669 (dtd->doc->intSubset->attributes != NULL)) {
1670 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1671 if (ret != NULL)
1672 return(NULL);
1673 }
1674
1675 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001676 * Create the Attribute table if needed.
1677 */
1678 table = (xmlAttributeTablePtr) dtd->attributes;
1679 if (table == NULL) {
1680 table = xmlCreateAttributeTable();
1681 dtd->attributes = (void *) table;
1682 }
1683 if (table == NULL) {
1684 xmlGenericError(xmlGenericErrorContext,
1685 "xmlAddAttributeDecl: Table creation failed!\n");
1686 return(NULL);
1687 }
1688
1689
1690 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1691 if (ret == NULL) {
1692 xmlGenericError(xmlGenericErrorContext,
1693 "xmlAddAttributeDecl: out of memory\n");
1694 return(NULL);
1695 }
1696 memset(ret, 0, sizeof(xmlAttribute));
1697 ret->type = XML_ATTRIBUTE_DECL;
1698
1699 /*
1700 * fill the structure.
1701 */
1702 ret->atype = type;
1703 ret->name = xmlStrdup(name);
1704 ret->prefix = xmlStrdup(ns);
1705 ret->elem = xmlStrdup(elem);
1706 ret->def = def;
1707 ret->tree = tree;
1708 if (defaultValue != NULL)
1709 ret->defaultValue = xmlStrdup(defaultValue);
1710
1711 /*
1712 * Validity Check:
1713 * Search the DTD for previous declarations of the ATTLIST
1714 */
1715 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1716 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001717 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001718 */
1719 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001720 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001721 name, elem);
1722 xmlFreeAttribute(ret);
1723 return(NULL);
1724 }
1725
1726 /*
1727 * Validity Check:
1728 * Multiple ID per element
1729 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001730 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001731 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001732
Owen Taylor3473f882001-02-23 17:55:21 +00001733 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001734 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001735 VERROR(ctxt->userData,
1736 "Element %s has too may ID attributes defined : %s\n",
1737 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001738 ctxt->valid = 0;
1739 }
1740
Daniel Veillard48da9102001-08-07 01:10:10 +00001741 /*
1742 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001743 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001744 */
1745 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1746 ((ret->prefix != NULL &&
1747 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1748 ret->nexth = elemDef->attributes;
1749 elemDef->attributes = ret;
1750 } else {
1751 xmlAttributePtr tmp = elemDef->attributes;
1752
1753 while ((tmp != NULL) &&
1754 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1755 ((ret->prefix != NULL &&
1756 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1757 if (tmp->nexth == NULL)
1758 break;
1759 tmp = tmp->nexth;
1760 }
1761 if (tmp != NULL) {
1762 ret->nexth = tmp->nexth;
1763 tmp->nexth = ret;
1764 } else {
1765 ret->nexth = elemDef->attributes;
1766 elemDef->attributes = ret;
1767 }
1768 }
Owen Taylor3473f882001-02-23 17:55:21 +00001769 }
1770
1771 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001772 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001773 */
1774 ret->parent = dtd;
1775 ret->doc = dtd->doc;
1776 if (dtd->last == NULL) {
1777 dtd->children = dtd->last = (xmlNodePtr) ret;
1778 } else {
1779 dtd->last->next = (xmlNodePtr) ret;
1780 ret->prev = dtd->last;
1781 dtd->last = (xmlNodePtr) ret;
1782 }
1783 return(ret);
1784}
1785
1786/**
1787 * xmlFreeAttributeTable:
1788 * @table: An attribute table
1789 *
1790 * Deallocate the memory used by an entities hash table.
1791 */
1792void
1793xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1794 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1795}
1796
1797/**
1798 * xmlCopyAttribute:
1799 * @attr: An attribute
1800 *
1801 * Build a copy of an attribute.
1802 *
1803 * Returns the new xmlAttributePtr or NULL in case of error.
1804 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001805static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001806xmlCopyAttribute(xmlAttributePtr attr) {
1807 xmlAttributePtr cur;
1808
1809 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1810 if (cur == NULL) {
1811 xmlGenericError(xmlGenericErrorContext,
1812 "xmlCopyAttribute: out of memory !\n");
1813 return(NULL);
1814 }
1815 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001816 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001817 cur->atype = attr->atype;
1818 cur->def = attr->def;
1819 cur->tree = xmlCopyEnumeration(attr->tree);
1820 if (attr->elem != NULL)
1821 cur->elem = xmlStrdup(attr->elem);
1822 if (attr->name != NULL)
1823 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001824 if (attr->prefix != NULL)
1825 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001826 if (attr->defaultValue != NULL)
1827 cur->defaultValue = xmlStrdup(attr->defaultValue);
1828 return(cur);
1829}
1830
1831/**
1832 * xmlCopyAttributeTable:
1833 * @table: An attribute table
1834 *
1835 * Build a copy of an attribute table.
1836 *
1837 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1838 */
1839xmlAttributeTablePtr
1840xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1841 return((xmlAttributeTablePtr) xmlHashCopy(table,
1842 (xmlHashCopier) xmlCopyAttribute));
1843}
1844
1845/**
1846 * xmlDumpAttributeDecl:
1847 * @buf: the XML buffer output
1848 * @attr: An attribute declaration
1849 *
1850 * This will dump the content of the attribute declaration as an XML
1851 * DTD definition
1852 */
1853void
1854xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1855 xmlBufferWriteChar(buf, "<!ATTLIST ");
1856 xmlBufferWriteCHAR(buf, attr->elem);
1857 xmlBufferWriteChar(buf, " ");
1858 if (attr->prefix != NULL) {
1859 xmlBufferWriteCHAR(buf, attr->prefix);
1860 xmlBufferWriteChar(buf, ":");
1861 }
1862 xmlBufferWriteCHAR(buf, attr->name);
1863 switch (attr->atype) {
1864 case XML_ATTRIBUTE_CDATA:
1865 xmlBufferWriteChar(buf, " CDATA");
1866 break;
1867 case XML_ATTRIBUTE_ID:
1868 xmlBufferWriteChar(buf, " ID");
1869 break;
1870 case XML_ATTRIBUTE_IDREF:
1871 xmlBufferWriteChar(buf, " IDREF");
1872 break;
1873 case XML_ATTRIBUTE_IDREFS:
1874 xmlBufferWriteChar(buf, " IDREFS");
1875 break;
1876 case XML_ATTRIBUTE_ENTITY:
1877 xmlBufferWriteChar(buf, " ENTITY");
1878 break;
1879 case XML_ATTRIBUTE_ENTITIES:
1880 xmlBufferWriteChar(buf, " ENTITIES");
1881 break;
1882 case XML_ATTRIBUTE_NMTOKEN:
1883 xmlBufferWriteChar(buf, " NMTOKEN");
1884 break;
1885 case XML_ATTRIBUTE_NMTOKENS:
1886 xmlBufferWriteChar(buf, " NMTOKENS");
1887 break;
1888 case XML_ATTRIBUTE_ENUMERATION:
1889 xmlBufferWriteChar(buf, " (");
1890 xmlDumpEnumeration(buf, attr->tree);
1891 break;
1892 case XML_ATTRIBUTE_NOTATION:
1893 xmlBufferWriteChar(buf, " NOTATION (");
1894 xmlDumpEnumeration(buf, attr->tree);
1895 break;
1896 default:
1897 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001898 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001899 attr->atype);
1900 }
1901 switch (attr->def) {
1902 case XML_ATTRIBUTE_NONE:
1903 break;
1904 case XML_ATTRIBUTE_REQUIRED:
1905 xmlBufferWriteChar(buf, " #REQUIRED");
1906 break;
1907 case XML_ATTRIBUTE_IMPLIED:
1908 xmlBufferWriteChar(buf, " #IMPLIED");
1909 break;
1910 case XML_ATTRIBUTE_FIXED:
1911 xmlBufferWriteChar(buf, " #FIXED");
1912 break;
1913 default:
1914 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001915 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001916 attr->def);
1917 }
1918 if (attr->defaultValue != NULL) {
1919 xmlBufferWriteChar(buf, " ");
1920 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1921 }
1922 xmlBufferWriteChar(buf, ">\n");
1923}
1924
1925/**
1926 * xmlDumpAttributeTable:
1927 * @buf: the XML buffer output
1928 * @table: An attribute table
1929 *
1930 * This will dump the content of the attribute table as an XML DTD definition
1931 */
1932void
1933xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1934 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1935}
1936
1937/************************************************************************
1938 * *
1939 * NOTATIONs *
1940 * *
1941 ************************************************************************/
1942/**
1943 * xmlCreateNotationTable:
1944 *
1945 * create and initialize an empty notation hash table.
1946 *
1947 * Returns the xmlNotationTablePtr just created or NULL in case
1948 * of error.
1949 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001950static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001951xmlCreateNotationTable(void) {
1952 return(xmlHashCreate(0));
1953}
1954
1955/**
1956 * xmlFreeNotation:
1957 * @not: A notation
1958 *
1959 * Deallocate the memory used by an notation definition
1960 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001961static void
Owen Taylor3473f882001-02-23 17:55:21 +00001962xmlFreeNotation(xmlNotationPtr nota) {
1963 if (nota == NULL) return;
1964 if (nota->name != NULL)
1965 xmlFree((xmlChar *) nota->name);
1966 if (nota->PublicID != NULL)
1967 xmlFree((xmlChar *) nota->PublicID);
1968 if (nota->SystemID != NULL)
1969 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001970 xmlFree(nota);
1971}
1972
1973
1974/**
1975 * xmlAddNotationDecl:
1976 * @dtd: pointer to the DTD
1977 * @ctxt: the validation context
1978 * @name: the entity name
1979 * @PublicID: the public identifier or NULL
1980 * @SystemID: the system identifier or NULL
1981 *
1982 * Register a new notation declaration
1983 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001984 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001985 */
1986xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001987xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001988 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001989 const xmlChar *PublicID, const xmlChar *SystemID) {
1990 xmlNotationPtr ret;
1991 xmlNotationTablePtr table;
1992
1993 if (dtd == NULL) {
1994 xmlGenericError(xmlGenericErrorContext,
1995 "xmlAddNotationDecl: dtd == NULL\n");
1996 return(NULL);
1997 }
1998 if (name == NULL) {
1999 xmlGenericError(xmlGenericErrorContext,
2000 "xmlAddNotationDecl: name == NULL\n");
2001 return(NULL);
2002 }
2003 if ((PublicID == NULL) && (SystemID == NULL)) {
2004 xmlGenericError(xmlGenericErrorContext,
2005 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002006 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002007 }
2008
2009 /*
2010 * Create the Notation table if needed.
2011 */
2012 table = (xmlNotationTablePtr) dtd->notations;
2013 if (table == NULL)
2014 dtd->notations = table = xmlCreateNotationTable();
2015 if (table == NULL) {
2016 xmlGenericError(xmlGenericErrorContext,
2017 "xmlAddNotationDecl: Table creation failed!\n");
2018 return(NULL);
2019 }
2020
2021 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2022 if (ret == NULL) {
2023 xmlGenericError(xmlGenericErrorContext,
2024 "xmlAddNotationDecl: out of memory\n");
2025 return(NULL);
2026 }
2027 memset(ret, 0, sizeof(xmlNotation));
2028
2029 /*
2030 * fill the structure.
2031 */
2032 ret->name = xmlStrdup(name);
2033 if (SystemID != NULL)
2034 ret->SystemID = xmlStrdup(SystemID);
2035 if (PublicID != NULL)
2036 ret->PublicID = xmlStrdup(PublicID);
2037
2038 /*
2039 * Validity Check:
2040 * Check the DTD for previous declarations of the ATTLIST
2041 */
2042 if (xmlHashAddEntry(table, name, ret)) {
2043 xmlGenericError(xmlGenericErrorContext,
2044 "xmlAddNotationDecl: %s already defined\n", name);
2045 xmlFreeNotation(ret);
2046 return(NULL);
2047 }
2048 return(ret);
2049}
2050
2051/**
2052 * xmlFreeNotationTable:
2053 * @table: An notation table
2054 *
2055 * Deallocate the memory used by an entities hash table.
2056 */
2057void
2058xmlFreeNotationTable(xmlNotationTablePtr table) {
2059 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2060}
2061
2062/**
2063 * xmlCopyNotation:
2064 * @nota: A notation
2065 *
2066 * Build a copy of a notation.
2067 *
2068 * Returns the new xmlNotationPtr or NULL in case of error.
2069 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002070static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002071xmlCopyNotation(xmlNotationPtr nota) {
2072 xmlNotationPtr cur;
2073
2074 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2075 if (cur == NULL) {
2076 xmlGenericError(xmlGenericErrorContext,
2077 "xmlCopyNotation: out of memory !\n");
2078 return(NULL);
2079 }
2080 if (nota->name != NULL)
2081 cur->name = xmlStrdup(nota->name);
2082 else
2083 cur->name = NULL;
2084 if (nota->PublicID != NULL)
2085 cur->PublicID = xmlStrdup(nota->PublicID);
2086 else
2087 cur->PublicID = NULL;
2088 if (nota->SystemID != NULL)
2089 cur->SystemID = xmlStrdup(nota->SystemID);
2090 else
2091 cur->SystemID = NULL;
2092 return(cur);
2093}
2094
2095/**
2096 * xmlCopyNotationTable:
2097 * @table: A notation table
2098 *
2099 * Build a copy of a notation table.
2100 *
2101 * Returns the new xmlNotationTablePtr or NULL in case of error.
2102 */
2103xmlNotationTablePtr
2104xmlCopyNotationTable(xmlNotationTablePtr table) {
2105 return((xmlNotationTablePtr) xmlHashCopy(table,
2106 (xmlHashCopier) xmlCopyNotation));
2107}
2108
2109/**
2110 * xmlDumpNotationDecl:
2111 * @buf: the XML buffer output
2112 * @nota: A notation declaration
2113 *
2114 * This will dump the content the notation declaration as an XML DTD definition
2115 */
2116void
2117xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2118 xmlBufferWriteChar(buf, "<!NOTATION ");
2119 xmlBufferWriteCHAR(buf, nota->name);
2120 if (nota->PublicID != NULL) {
2121 xmlBufferWriteChar(buf, " PUBLIC ");
2122 xmlBufferWriteQuotedString(buf, nota->PublicID);
2123 if (nota->SystemID != NULL) {
2124 xmlBufferWriteChar(buf, " ");
2125 xmlBufferWriteCHAR(buf, nota->SystemID);
2126 }
2127 } else {
2128 xmlBufferWriteChar(buf, " SYSTEM ");
2129 xmlBufferWriteCHAR(buf, nota->SystemID);
2130 }
2131 xmlBufferWriteChar(buf, " >\n");
2132}
2133
2134/**
2135 * xmlDumpNotationTable:
2136 * @buf: the XML buffer output
2137 * @table: A notation table
2138 *
2139 * This will dump the content of the notation table as an XML DTD definition
2140 */
2141void
2142xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2143 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2144}
2145
2146/************************************************************************
2147 * *
2148 * IDs *
2149 * *
2150 ************************************************************************/
2151/**
2152 * xmlCreateIDTable:
2153 *
2154 * create and initialize an empty id hash table.
2155 *
2156 * Returns the xmlIDTablePtr just created or NULL in case
2157 * of error.
2158 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002159static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002160xmlCreateIDTable(void) {
2161 return(xmlHashCreate(0));
2162}
2163
2164/**
2165 * xmlFreeID:
2166 * @not: A id
2167 *
2168 * Deallocate the memory used by an id definition
2169 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002170static void
Owen Taylor3473f882001-02-23 17:55:21 +00002171xmlFreeID(xmlIDPtr id) {
2172 if (id == NULL) return;
2173 if (id->value != NULL)
2174 xmlFree((xmlChar *) id->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002175 if (id->name != NULL)
2176 xmlFree((xmlChar *) id->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002177 xmlFree(id);
2178}
2179
2180/**
2181 * xmlAddID:
2182 * @ctxt: the validation context
2183 * @doc: pointer to the document
2184 * @value: the value name
2185 * @attr: the attribute holding the ID
2186 *
2187 * Register a new id declaration
2188 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002189 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002190 */
2191xmlIDPtr
2192xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2193 xmlAttrPtr attr) {
2194 xmlIDPtr ret;
2195 xmlIDTablePtr table;
2196
2197 if (doc == NULL) {
2198 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002199 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002200 return(NULL);
2201 }
2202 if (value == NULL) {
2203 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002204 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002205 return(NULL);
2206 }
2207 if (attr == NULL) {
2208 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002209 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002210 return(NULL);
2211 }
2212
2213 /*
2214 * Create the ID table if needed.
2215 */
2216 table = (xmlIDTablePtr) doc->ids;
2217 if (table == NULL)
2218 doc->ids = table = xmlCreateIDTable();
2219 if (table == NULL) {
2220 xmlGenericError(xmlGenericErrorContext,
2221 "xmlAddID: Table creation failed!\n");
2222 return(NULL);
2223 }
2224
2225 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2226 if (ret == NULL) {
2227 xmlGenericError(xmlGenericErrorContext,
2228 "xmlAddID: out of memory\n");
2229 return(NULL);
2230 }
2231
2232 /*
2233 * fill the structure.
2234 */
2235 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002236 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2237 /*
2238 * Operating in streaming mode, attr is gonna disapear
2239 */
2240 ret->name = xmlStrdup(attr->name);
2241 ret->attr = NULL;
2242 } else {
2243 ret->attr = attr;
2244 ret->name = NULL;
2245 }
2246 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002247
2248 if (xmlHashAddEntry(table, value, ret) < 0) {
2249 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002250 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002251 */
Daniel Veillard76575762002-09-05 14:21:15 +00002252 if (ctxt != NULL) {
2253 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002254 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002255 }
Owen Taylor3473f882001-02-23 17:55:21 +00002256 xmlFreeID(ret);
2257 return(NULL);
2258 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002259 if (attr != NULL)
2260 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002261 return(ret);
2262}
2263
2264/**
2265 * xmlFreeIDTable:
2266 * @table: An id table
2267 *
2268 * Deallocate the memory used by an ID hash table.
2269 */
2270void
2271xmlFreeIDTable(xmlIDTablePtr table) {
2272 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2273}
2274
2275/**
2276 * xmlIsID:
2277 * @doc: the document
2278 * @elem: the element carrying the attribute
2279 * @attr: the attribute
2280 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002281 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002282 * then this is done if DTD loading has been requested. In the case
2283 * of HTML documents parsed with the HTML parser, then ID detection is
2284 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002285 *
2286 * Returns 0 or 1 depending on the lookup result
2287 */
2288int
2289xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2290 if (doc == NULL) return(0);
2291 if (attr == NULL) return(0);
2292 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2293 return(0);
2294 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2295 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2296 (xmlStrEqual(BAD_CAST "name", attr->name)))
2297 return(1);
2298 return(0);
2299 } else {
2300 xmlAttributePtr attrDecl;
2301
2302 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002303 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00002304 xmlChar fn[50];
Daniel Veillard37f961d2002-07-06 17:53:56 +00002305 xmlChar *fullname;
Daniel Veillardc00cda82003-04-07 10:22:39 +00002306
2307 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002308 if (fullname == NULL)
2309 return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002310 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2311 attr->name);
2312 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2313 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2314 attr->name);
Daniel Veillardc00cda82003-04-07 10:22:39 +00002315 if ((fullname != fn) && (fullname != elem->name))
2316 xmlFree(fullname);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002317 } else {
2318 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2319 attr->name);
2320 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2321 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2322 attr->name);
2323 }
Owen Taylor3473f882001-02-23 17:55:21 +00002324
2325 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2326 return(1);
2327 }
2328 return(0);
2329}
2330
2331/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002332 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002333 * @doc: the document
2334 * @attr: the attribute
2335 *
2336 * Remove the given attribute from the ID table maintained internally.
2337 *
2338 * Returns -1 if the lookup failed and 0 otherwise
2339 */
2340int
2341xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2342 xmlAttrPtr cur;
2343 xmlIDTablePtr table;
2344 xmlChar *ID;
2345
2346 if (doc == NULL) return(-1);
2347 if (attr == NULL) return(-1);
2348 table = (xmlIDTablePtr) doc->ids;
2349 if (table == NULL)
2350 return(-1);
2351
2352 if (attr == NULL)
2353 return(-1);
2354 ID = xmlNodeListGetString(doc, attr->children, 1);
2355 if (ID == NULL)
2356 return(-1);
2357 cur = xmlHashLookup(table, ID);
2358 if (cur != attr) {
2359 xmlFree(ID);
2360 return(-1);
2361 }
2362 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2363 xmlFree(ID);
2364 return(0);
2365}
2366
2367/**
2368 * xmlGetID:
2369 * @doc: pointer to the document
2370 * @ID: the ID value
2371 *
2372 * Search the attribute declaring the given ID
2373 *
2374 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2375 */
2376xmlAttrPtr
2377xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2378 xmlIDTablePtr table;
2379 xmlIDPtr id;
2380
2381 if (doc == NULL) {
2382 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2383 return(NULL);
2384 }
2385
2386 if (ID == NULL) {
2387 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2388 return(NULL);
2389 }
2390
2391 table = (xmlIDTablePtr) doc->ids;
2392 if (table == NULL)
2393 return(NULL);
2394
2395 id = xmlHashLookup(table, ID);
2396 if (id == NULL)
2397 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002398 if (id->attr == NULL) {
2399 /*
2400 * We are operating on a stream, return a well known reference
2401 * since the attribute node doesn't exist anymore
2402 */
2403 return((xmlAttrPtr) doc);
2404 }
Owen Taylor3473f882001-02-23 17:55:21 +00002405 return(id->attr);
2406}
2407
2408/************************************************************************
2409 * *
2410 * Refs *
2411 * *
2412 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002413typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002414{
2415 xmlListPtr l;
2416 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002417} xmlRemoveMemo;
2418
2419typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2420
2421typedef struct xmlValidateMemo_t
2422{
2423 xmlValidCtxtPtr ctxt;
2424 const xmlChar *name;
2425} xmlValidateMemo;
2426
2427typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002428
2429/**
2430 * xmlCreateRefTable:
2431 *
2432 * create and initialize an empty ref hash table.
2433 *
2434 * Returns the xmlRefTablePtr just created or NULL in case
2435 * of error.
2436 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002437static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002438xmlCreateRefTable(void) {
2439 return(xmlHashCreate(0));
2440}
2441
2442/**
2443 * xmlFreeRef:
2444 * @lk: A list link
2445 *
2446 * Deallocate the memory used by a ref definition
2447 */
2448static void
2449xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002450 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2451 if (ref == NULL) return;
2452 if (ref->value != NULL)
2453 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002454 if (ref->name != NULL)
2455 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002456 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002457}
2458
2459/**
2460 * xmlFreeRefList:
2461 * @list_ref: A list of references.
2462 *
2463 * Deallocate the memory used by a list of references
2464 */
2465static void
2466xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002467 if (list_ref == NULL) return;
2468 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002469}
2470
2471/**
2472 * xmlWalkRemoveRef:
2473 * @data: Contents of current link
2474 * @user: Value supplied by the user
2475 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002476 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002477 */
2478static int
2479xmlWalkRemoveRef(const void *data, const void *user)
2480{
Daniel Veillard37721922001-05-04 15:21:12 +00002481 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2482 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2483 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002484
Daniel Veillard37721922001-05-04 15:21:12 +00002485 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2486 xmlListRemoveFirst(ref_list, (void *)data);
2487 return 0;
2488 }
2489 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002490}
2491
2492/**
2493 * xmlAddRef:
2494 * @ctxt: the validation context
2495 * @doc: pointer to the document
2496 * @value: the value name
2497 * @attr: the attribute holding the Ref
2498 *
2499 * Register a new ref declaration
2500 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002501 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002502 */
2503xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002504xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002505 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002506 xmlRefPtr ret;
2507 xmlRefTablePtr table;
2508 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002509
Daniel Veillard37721922001-05-04 15:21:12 +00002510 if (doc == NULL) {
2511 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002512 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002513 return(NULL);
2514 }
2515 if (value == NULL) {
2516 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002517 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002518 return(NULL);
2519 }
2520 if (attr == NULL) {
2521 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002522 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002523 return(NULL);
2524 }
Owen Taylor3473f882001-02-23 17:55:21 +00002525
Daniel Veillard37721922001-05-04 15:21:12 +00002526 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002527 * Create the Ref table if needed.
2528 */
Daniel Veillard37721922001-05-04 15:21:12 +00002529 table = (xmlRefTablePtr) doc->refs;
2530 if (table == NULL)
2531 doc->refs = table = xmlCreateRefTable();
2532 if (table == NULL) {
2533 xmlGenericError(xmlGenericErrorContext,
2534 "xmlAddRef: Table creation failed!\n");
2535 return(NULL);
2536 }
Owen Taylor3473f882001-02-23 17:55:21 +00002537
Daniel Veillard37721922001-05-04 15:21:12 +00002538 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2539 if (ret == NULL) {
2540 xmlGenericError(xmlGenericErrorContext,
2541 "xmlAddRef: out of memory\n");
2542 return(NULL);
2543 }
Owen Taylor3473f882001-02-23 17:55:21 +00002544
Daniel Veillard37721922001-05-04 15:21:12 +00002545 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002546 * fill the structure.
2547 */
Daniel Veillard37721922001-05-04 15:21:12 +00002548 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002549 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2550 /*
2551 * Operating in streaming mode, attr is gonna disapear
2552 */
2553 ret->name = xmlStrdup(attr->name);
2554 ret->attr = NULL;
2555 } else {
2556 ret->name = NULL;
2557 ret->attr = attr;
2558 }
2559 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002560
Daniel Veillard37721922001-05-04 15:21:12 +00002561 /* To add a reference :-
2562 * References are maintained as a list of references,
2563 * Lookup the entry, if no entry create new nodelist
2564 * Add the owning node to the NodeList
2565 * Return the ref
2566 */
Owen Taylor3473f882001-02-23 17:55:21 +00002567
Daniel Veillard37721922001-05-04 15:21:12 +00002568 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2569 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2570 xmlGenericError(xmlGenericErrorContext,
2571 "xmlAddRef: Reference list creation failed!\n");
2572 return(NULL);
2573 }
2574 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2575 xmlListDelete(ref_list);
2576 xmlGenericError(xmlGenericErrorContext,
2577 "xmlAddRef: Reference list insertion failed!\n");
2578 return(NULL);
2579 }
2580 }
2581 xmlListInsert(ref_list, ret);
2582 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002583}
2584
2585/**
2586 * xmlFreeRefTable:
2587 * @table: An ref table
2588 *
2589 * Deallocate the memory used by an Ref hash table.
2590 */
2591void
2592xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002593 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002594}
2595
2596/**
2597 * xmlIsRef:
2598 * @doc: the document
2599 * @elem: the element carrying the attribute
2600 * @attr: the attribute
2601 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002602 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002603 * then this is simple, otherwise we use an heuristic: name Ref (upper
2604 * or lowercase).
2605 *
2606 * Returns 0 or 1 depending on the lookup result
2607 */
2608int
2609xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002610 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2611 return(0);
2612 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2613 /* TODO @@@ */
2614 return(0);
2615 } else {
2616 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002617
Daniel Veillard37721922001-05-04 15:21:12 +00002618 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2619 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2620 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2621 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002622
Daniel Veillard37721922001-05-04 15:21:12 +00002623 if ((attrDecl != NULL) &&
2624 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2625 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2626 return(1);
2627 }
2628 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002629}
2630
2631/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002632 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002633 * @doc: the document
2634 * @attr: the attribute
2635 *
2636 * Remove the given attribute from the Ref table maintained internally.
2637 *
2638 * Returns -1 if the lookup failed and 0 otherwise
2639 */
2640int
2641xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002642 xmlListPtr ref_list;
2643 xmlRefTablePtr table;
2644 xmlChar *ID;
2645 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002646
Daniel Veillard37721922001-05-04 15:21:12 +00002647 if (doc == NULL) return(-1);
2648 if (attr == NULL) return(-1);
2649 table = (xmlRefTablePtr) doc->refs;
2650 if (table == NULL)
2651 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002652
Daniel Veillard37721922001-05-04 15:21:12 +00002653 if (attr == NULL)
2654 return(-1);
2655 ID = xmlNodeListGetString(doc, attr->children, 1);
2656 if (ID == NULL)
2657 return(-1);
2658 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002659
Daniel Veillard37721922001-05-04 15:21:12 +00002660 if(ref_list == NULL) {
2661 xmlFree(ID);
2662 return (-1);
2663 }
2664 /* At this point, ref_list refers to a list of references which
2665 * have the same key as the supplied attr. Our list of references
2666 * is ordered by reference address and we don't have that information
2667 * here to use when removing. We'll have to walk the list and
2668 * check for a matching attribute, when we find one stop the walk
2669 * and remove the entry.
2670 * The list is ordered by reference, so that means we don't have the
2671 * key. Passing the list and the reference to the walker means we
2672 * will have enough data to be able to remove the entry.
2673 */
2674 target.l = ref_list;
2675 target.ap = attr;
2676
2677 /* Remove the supplied attr from our list */
2678 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002679
Daniel Veillard37721922001-05-04 15:21:12 +00002680 /*If the list is empty then remove the list entry in the hash */
2681 if (xmlListEmpty(ref_list))
2682 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2683 xmlFreeRefList);
2684 xmlFree(ID);
2685 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002686}
2687
2688/**
2689 * xmlGetRefs:
2690 * @doc: pointer to the document
2691 * @ID: the ID value
2692 *
2693 * Find the set of references for the supplied ID.
2694 *
2695 * Returns NULL if not found, otherwise node set for the ID.
2696 */
2697xmlListPtr
2698xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002699 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002700
Daniel Veillard37721922001-05-04 15:21:12 +00002701 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002702 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002703 return(NULL);
2704 }
Owen Taylor3473f882001-02-23 17:55:21 +00002705
Daniel Veillard37721922001-05-04 15:21:12 +00002706 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002707 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002708 return(NULL);
2709 }
Owen Taylor3473f882001-02-23 17:55:21 +00002710
Daniel Veillard37721922001-05-04 15:21:12 +00002711 table = (xmlRefTablePtr) doc->refs;
2712 if (table == NULL)
2713 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002714
Daniel Veillard37721922001-05-04 15:21:12 +00002715 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002716}
2717
2718/************************************************************************
2719 * *
2720 * Routines for validity checking *
2721 * *
2722 ************************************************************************/
2723
2724/**
2725 * xmlGetDtdElementDesc:
2726 * @dtd: a pointer to the DtD to search
2727 * @name: the element name
2728 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002729 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002730 *
2731 * returns the xmlElementPtr if found or NULL
2732 */
2733
2734xmlElementPtr
2735xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2736 xmlElementTablePtr table;
2737 xmlElementPtr cur;
2738 xmlChar *uqname = NULL, *prefix = NULL;
2739
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00002740 if ((dtd == NULL) || (name == NULL)) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002741 if (dtd->elements == NULL)
2742 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002743 table = (xmlElementTablePtr) dtd->elements;
2744
2745 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002746 if (uqname != NULL)
2747 name = uqname;
2748 cur = xmlHashLookup2(table, name, prefix);
2749 if (prefix != NULL) xmlFree(prefix);
2750 if (uqname != NULL) xmlFree(uqname);
2751 return(cur);
2752}
2753/**
2754 * xmlGetDtdElementDesc2:
2755 * @dtd: a pointer to the DtD to search
2756 * @name: the element name
2757 * @create: create an empty description if not found
2758 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002759 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002760 *
2761 * returns the xmlElementPtr if found or NULL
2762 */
2763
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002764static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002765xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2766 xmlElementTablePtr table;
2767 xmlElementPtr cur;
2768 xmlChar *uqname = NULL, *prefix = NULL;
2769
2770 if (dtd == NULL) return(NULL);
2771 if (dtd->elements == NULL) {
2772 if (!create)
2773 return(NULL);
2774 /*
2775 * Create the Element table if needed.
2776 */
2777 table = (xmlElementTablePtr) dtd->elements;
2778 if (table == NULL) {
2779 table = xmlCreateElementTable();
2780 dtd->elements = (void *) table;
2781 }
2782 if (table == NULL) {
2783 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002784 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002785 return(NULL);
2786 }
2787 }
2788 table = (xmlElementTablePtr) dtd->elements;
2789
2790 uqname = xmlSplitQName2(name, &prefix);
2791 if (uqname != NULL)
2792 name = uqname;
2793 cur = xmlHashLookup2(table, name, prefix);
2794 if ((cur == NULL) && (create)) {
2795 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2796 if (cur == NULL) {
2797 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002798 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002799 return(NULL);
2800 }
2801 memset(cur, 0, sizeof(xmlElement));
2802 cur->type = XML_ELEMENT_DECL;
2803
2804 /*
2805 * fill the structure.
2806 */
2807 cur->name = xmlStrdup(name);
2808 cur->prefix = xmlStrdup(prefix);
2809 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2810
2811 xmlHashAddEntry2(table, name, prefix, cur);
2812 }
2813 if (prefix != NULL) xmlFree(prefix);
2814 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002815 return(cur);
2816}
2817
2818/**
2819 * xmlGetDtdQElementDesc:
2820 * @dtd: a pointer to the DtD to search
2821 * @name: the element name
2822 * @prefix: the element namespace prefix
2823 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002824 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002825 *
2826 * returns the xmlElementPtr if found or NULL
2827 */
2828
Daniel Veillard48da9102001-08-07 01:10:10 +00002829xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002830xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2831 const xmlChar *prefix) {
2832 xmlElementTablePtr table;
2833
2834 if (dtd == NULL) return(NULL);
2835 if (dtd->elements == NULL) return(NULL);
2836 table = (xmlElementTablePtr) dtd->elements;
2837
2838 return(xmlHashLookup2(table, name, prefix));
2839}
2840
2841/**
2842 * xmlGetDtdAttrDesc:
2843 * @dtd: a pointer to the DtD to search
2844 * @elem: the element name
2845 * @name: the attribute name
2846 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002847 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002848 * this element.
2849 *
2850 * returns the xmlAttributePtr if found or NULL
2851 */
2852
2853xmlAttributePtr
2854xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2855 xmlAttributeTablePtr table;
2856 xmlAttributePtr cur;
2857 xmlChar *uqname = NULL, *prefix = NULL;
2858
2859 if (dtd == NULL) return(NULL);
2860 if (dtd->attributes == NULL) return(NULL);
2861
2862 table = (xmlAttributeTablePtr) dtd->attributes;
2863 if (table == NULL)
2864 return(NULL);
2865
2866 uqname = xmlSplitQName2(name, &prefix);
2867
2868 if (uqname != NULL) {
2869 cur = xmlHashLookup3(table, uqname, prefix, elem);
2870 if (prefix != NULL) xmlFree(prefix);
2871 if (uqname != NULL) xmlFree(uqname);
2872 } else
2873 cur = xmlHashLookup3(table, name, NULL, elem);
2874 return(cur);
2875}
2876
2877/**
2878 * xmlGetDtdQAttrDesc:
2879 * @dtd: a pointer to the DtD to search
2880 * @elem: the element name
2881 * @name: the attribute name
2882 * @prefix: the attribute namespace prefix
2883 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002884 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002885 * this element.
2886 *
2887 * returns the xmlAttributePtr if found or NULL
2888 */
2889
Daniel Veillard48da9102001-08-07 01:10:10 +00002890xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002891xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2892 const xmlChar *prefix) {
2893 xmlAttributeTablePtr table;
2894
2895 if (dtd == NULL) return(NULL);
2896 if (dtd->attributes == NULL) return(NULL);
2897 table = (xmlAttributeTablePtr) dtd->attributes;
2898
2899 return(xmlHashLookup3(table, name, prefix, elem));
2900}
2901
2902/**
2903 * xmlGetDtdNotationDesc:
2904 * @dtd: a pointer to the DtD to search
2905 * @name: the notation name
2906 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002907 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002908 *
2909 * returns the xmlNotationPtr if found or NULL
2910 */
2911
2912xmlNotationPtr
2913xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2914 xmlNotationTablePtr table;
2915
2916 if (dtd == NULL) return(NULL);
2917 if (dtd->notations == NULL) return(NULL);
2918 table = (xmlNotationTablePtr) dtd->notations;
2919
2920 return(xmlHashLookup(table, name));
2921}
2922
2923/**
2924 * xmlValidateNotationUse:
2925 * @ctxt: the validation context
2926 * @doc: the document
2927 * @notationName: the notation name to check
2928 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002929 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002930 * - [ VC: Notation Declared ]
2931 *
2932 * returns 1 if valid or 0 otherwise
2933 */
2934
2935int
2936xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2937 const xmlChar *notationName) {
2938 xmlNotationPtr notaDecl;
2939 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2940
2941 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2942 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2943 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2944
Daniel Veillarde637c4a2003-03-30 21:10:09 +00002945 if ((notaDecl == NULL) && (ctxt != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00002946 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2947 notationName);
2948 return(0);
2949 }
2950 return(1);
2951}
2952
2953/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002954 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00002955 * @doc: the document
2956 * @name: the element name
2957 *
2958 * Search in the DtDs whether an element accept Mixed content (or ANY)
2959 * basically if it is supposed to accept text childs
2960 *
2961 * returns 0 if no, 1 if yes, and -1 if no element description is available
2962 */
2963
2964int
2965xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2966 xmlElementPtr elemDecl;
2967
2968 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2969
2970 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2971 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2972 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2973 if (elemDecl == NULL) return(-1);
2974 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002975 case XML_ELEMENT_TYPE_UNDEFINED:
2976 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002977 case XML_ELEMENT_TYPE_ELEMENT:
2978 return(0);
2979 case XML_ELEMENT_TYPE_EMPTY:
2980 /*
2981 * return 1 for EMPTY since we want VC error to pop up
2982 * on <empty> </empty> for example
2983 */
2984 case XML_ELEMENT_TYPE_ANY:
2985 case XML_ELEMENT_TYPE_MIXED:
2986 return(1);
2987 }
2988 return(1);
2989}
2990
2991/**
2992 * xmlValidateNameValue:
2993 * @value: an Name value
2994 *
2995 * Validate that the given value match Name production
2996 *
2997 * returns 1 if valid or 0 otherwise
2998 */
2999
Daniel Veillard9b731d72002-04-14 12:56:08 +00003000int
Owen Taylor3473f882001-02-23 17:55:21 +00003001xmlValidateNameValue(const xmlChar *value) {
3002 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003003 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003004
3005 if (value == NULL) return(0);
3006 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003007 val = xmlStringCurrentChar(NULL, cur, &len);
3008 cur += len;
3009 if (!IS_LETTER(val) && (val != '_') &&
3010 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003011 return(0);
3012 }
3013
Daniel Veillardd8224e02002-01-13 15:43:22 +00003014 val = xmlStringCurrentChar(NULL, cur, &len);
3015 cur += len;
3016 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3017 (val == '.') || (val == '-') ||
3018 (val == '_') || (val == ':') ||
3019 (IS_COMBINING(val)) ||
3020 (IS_EXTENDER(val))) {
3021 val = xmlStringCurrentChar(NULL, cur, &len);
3022 cur += len;
3023 }
Owen Taylor3473f882001-02-23 17:55:21 +00003024
Daniel Veillardd8224e02002-01-13 15:43:22 +00003025 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003026
3027 return(1);
3028}
3029
3030/**
3031 * xmlValidateNamesValue:
3032 * @value: an Names value
3033 *
3034 * Validate that the given value match Names production
3035 *
3036 * returns 1 if valid or 0 otherwise
3037 */
3038
Daniel Veillard9b731d72002-04-14 12:56:08 +00003039int
Owen Taylor3473f882001-02-23 17:55:21 +00003040xmlValidateNamesValue(const xmlChar *value) {
3041 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003042 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003043
3044 if (value == NULL) return(0);
3045 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003046 val = xmlStringCurrentChar(NULL, cur, &len);
3047 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003048
Daniel Veillardd8224e02002-01-13 15:43:22 +00003049 if (!IS_LETTER(val) && (val != '_') &&
3050 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003051 return(0);
3052 }
3053
Daniel Veillardd8224e02002-01-13 15:43:22 +00003054 val = xmlStringCurrentChar(NULL, cur, &len);
3055 cur += len;
3056 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3057 (val == '.') || (val == '-') ||
3058 (val == '_') || (val == ':') ||
3059 (IS_COMBINING(val)) ||
3060 (IS_EXTENDER(val))) {
3061 val = xmlStringCurrentChar(NULL, cur, &len);
3062 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003063 }
3064
Daniel Veillardd8224e02002-01-13 15:43:22 +00003065 while (IS_BLANK(val)) {
3066 while (IS_BLANK(val)) {
3067 val = xmlStringCurrentChar(NULL, cur, &len);
3068 cur += len;
3069 }
3070
3071 if (!IS_LETTER(val) && (val != '_') &&
3072 (val != ':')) {
3073 return(0);
3074 }
3075 val = xmlStringCurrentChar(NULL, cur, &len);
3076 cur += len;
3077
3078 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3079 (val == '.') || (val == '-') ||
3080 (val == '_') || (val == ':') ||
3081 (IS_COMBINING(val)) ||
3082 (IS_EXTENDER(val))) {
3083 val = xmlStringCurrentChar(NULL, cur, &len);
3084 cur += len;
3085 }
3086 }
3087
3088 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003089
3090 return(1);
3091}
3092
3093/**
3094 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003095 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003096 *
3097 * Validate that the given value match Nmtoken production
3098 *
3099 * [ VC: Name Token ]
3100 *
3101 * returns 1 if valid or 0 otherwise
3102 */
3103
Daniel Veillard9b731d72002-04-14 12:56:08 +00003104int
Owen Taylor3473f882001-02-23 17:55:21 +00003105xmlValidateNmtokenValue(const xmlChar *value) {
3106 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003107 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003108
3109 if (value == NULL) return(0);
3110 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003111 val = xmlStringCurrentChar(NULL, cur, &len);
3112 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003113
Daniel Veillardd8224e02002-01-13 15:43:22 +00003114 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3115 (val != '.') && (val != '-') &&
3116 (val != '_') && (val != ':') &&
3117 (!IS_COMBINING(val)) &&
3118 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003119 return(0);
3120
Daniel Veillardd8224e02002-01-13 15:43:22 +00003121 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3122 (val == '.') || (val == '-') ||
3123 (val == '_') || (val == ':') ||
3124 (IS_COMBINING(val)) ||
3125 (IS_EXTENDER(val))) {
3126 val = xmlStringCurrentChar(NULL, cur, &len);
3127 cur += len;
3128 }
Owen Taylor3473f882001-02-23 17:55:21 +00003129
Daniel Veillardd8224e02002-01-13 15:43:22 +00003130 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003131
3132 return(1);
3133}
3134
3135/**
3136 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003137 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003138 *
3139 * Validate that the given value match Nmtokens production
3140 *
3141 * [ VC: Name Token ]
3142 *
3143 * returns 1 if valid or 0 otherwise
3144 */
3145
Daniel Veillard9b731d72002-04-14 12:56:08 +00003146int
Owen Taylor3473f882001-02-23 17:55:21 +00003147xmlValidateNmtokensValue(const xmlChar *value) {
3148 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003149 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003150
3151 if (value == NULL) return(0);
3152 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003153 val = xmlStringCurrentChar(NULL, cur, &len);
3154 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003155
Daniel Veillardd8224e02002-01-13 15:43:22 +00003156 while (IS_BLANK(val)) {
3157 val = xmlStringCurrentChar(NULL, cur, &len);
3158 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003159 }
3160
Daniel Veillardd8224e02002-01-13 15:43:22 +00003161 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3162 (val != '.') && (val != '-') &&
3163 (val != '_') && (val != ':') &&
3164 (!IS_COMBINING(val)) &&
3165 (!IS_EXTENDER(val)))
3166 return(0);
3167
3168 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3169 (val == '.') || (val == '-') ||
3170 (val == '_') || (val == ':') ||
3171 (IS_COMBINING(val)) ||
3172 (IS_EXTENDER(val))) {
3173 val = xmlStringCurrentChar(NULL, cur, &len);
3174 cur += len;
3175 }
3176
3177 while (IS_BLANK(val)) {
3178 while (IS_BLANK(val)) {
3179 val = xmlStringCurrentChar(NULL, cur, &len);
3180 cur += len;
3181 }
3182 if (val == 0) return(1);
3183
3184 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3185 (val != '.') && (val != '-') &&
3186 (val != '_') && (val != ':') &&
3187 (!IS_COMBINING(val)) &&
3188 (!IS_EXTENDER(val)))
3189 return(0);
3190
3191 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3192 (val == '.') || (val == '-') ||
3193 (val == '_') || (val == ':') ||
3194 (IS_COMBINING(val)) ||
3195 (IS_EXTENDER(val))) {
3196 val = xmlStringCurrentChar(NULL, cur, &len);
3197 cur += len;
3198 }
3199 }
3200
3201 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003202
3203 return(1);
3204}
3205
3206/**
3207 * xmlValidateNotationDecl:
3208 * @ctxt: the validation context
3209 * @doc: a document instance
3210 * @nota: a notation definition
3211 *
3212 * Try to validate a single notation definition
3213 * basically it does the following checks as described by the
3214 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003215 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003216 * But this function get called anyway ...
3217 *
3218 * returns 1 if valid or 0 otherwise
3219 */
3220
3221int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003222xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3223 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003224 int ret = 1;
3225
3226 return(ret);
3227}
3228
3229/**
3230 * xmlValidateAttributeValue:
3231 * @type: an attribute type
3232 * @value: an attribute value
3233 *
3234 * Validate that the given attribute value match the proper production
3235 *
3236 * [ VC: ID ]
3237 * Values of type ID must match the Name production....
3238 *
3239 * [ VC: IDREF ]
3240 * Values of type IDREF must match the Name production, and values
3241 * of type IDREFS must match Names ...
3242 *
3243 * [ VC: Entity Name ]
3244 * Values of type ENTITY must match the Name production, values
3245 * of type ENTITIES must match Names ...
3246 *
3247 * [ VC: Name Token ]
3248 * Values of type NMTOKEN must match the Nmtoken production; values
3249 * of type NMTOKENS must match Nmtokens.
3250 *
3251 * returns 1 if valid or 0 otherwise
3252 */
3253
3254int
3255xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3256 switch (type) {
3257 case XML_ATTRIBUTE_ENTITIES:
3258 case XML_ATTRIBUTE_IDREFS:
3259 return(xmlValidateNamesValue(value));
3260 case XML_ATTRIBUTE_ENTITY:
3261 case XML_ATTRIBUTE_IDREF:
3262 case XML_ATTRIBUTE_ID:
3263 case XML_ATTRIBUTE_NOTATION:
3264 return(xmlValidateNameValue(value));
3265 case XML_ATTRIBUTE_NMTOKENS:
3266 case XML_ATTRIBUTE_ENUMERATION:
3267 return(xmlValidateNmtokensValue(value));
3268 case XML_ATTRIBUTE_NMTOKEN:
3269 return(xmlValidateNmtokenValue(value));
3270 case XML_ATTRIBUTE_CDATA:
3271 break;
3272 }
3273 return(1);
3274}
3275
3276/**
3277 * xmlValidateAttributeValue2:
3278 * @ctxt: the validation context
3279 * @doc: the document
3280 * @name: the attribute name (used for error reporting only)
3281 * @type: the attribute type
3282 * @value: the attribute value
3283 *
3284 * Validate that the given attribute value match a given type.
3285 * This typically cannot be done before having finished parsing
3286 * the subsets.
3287 *
3288 * [ VC: IDREF ]
3289 * Values of type IDREF must match one of the declared IDs
3290 * Values of type IDREFS must match a sequence of the declared IDs
3291 * each Name must match the value of an ID attribute on some element
3292 * in the XML document; i.e. IDREF values must match the value of
3293 * some ID attribute
3294 *
3295 * [ VC: Entity Name ]
3296 * Values of type ENTITY must match one declared entity
3297 * Values of type ENTITIES must match a sequence of declared entities
3298 *
3299 * [ VC: Notation Attributes ]
3300 * all notation names in the declaration must be declared.
3301 *
3302 * returns 1 if valid or 0 otherwise
3303 */
3304
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003305static int
Owen Taylor3473f882001-02-23 17:55:21 +00003306xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3307 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3308 int ret = 1;
3309 switch (type) {
3310 case XML_ATTRIBUTE_IDREFS:
3311 case XML_ATTRIBUTE_IDREF:
3312 case XML_ATTRIBUTE_ID:
3313 case XML_ATTRIBUTE_NMTOKENS:
3314 case XML_ATTRIBUTE_ENUMERATION:
3315 case XML_ATTRIBUTE_NMTOKEN:
3316 case XML_ATTRIBUTE_CDATA:
3317 break;
3318 case XML_ATTRIBUTE_ENTITY: {
3319 xmlEntityPtr ent;
3320
3321 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003322 if ((ent == NULL) && (doc->standalone == 1)) {
3323 doc->standalone = 0;
3324 ent = xmlGetDocEntity(doc, value);
3325 if (ent != NULL) {
3326 VERROR(ctxt->userData,
3327"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3328 name, value);
3329 /* WAIT to get answer from the Core WG on this
3330 ret = 0;
3331 */
3332 }
3333 }
Owen Taylor3473f882001-02-23 17:55:21 +00003334 if (ent == NULL) {
3335 VERROR(ctxt->userData,
3336 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3337 name, value);
3338 ret = 0;
3339 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3340 VERROR(ctxt->userData,
3341 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3342 name, value);
3343 ret = 0;
3344 }
3345 break;
3346 }
3347 case XML_ATTRIBUTE_ENTITIES: {
3348 xmlChar *dup, *nam = NULL, *cur, save;
3349 xmlEntityPtr ent;
3350
3351 dup = xmlStrdup(value);
3352 if (dup == NULL)
3353 return(0);
3354 cur = dup;
3355 while (*cur != 0) {
3356 nam = cur;
3357 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3358 save = *cur;
3359 *cur = 0;
3360 ent = xmlGetDocEntity(doc, nam);
3361 if (ent == NULL) {
3362 VERROR(ctxt->userData,
3363 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3364 name, nam);
3365 ret = 0;
3366 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3367 VERROR(ctxt->userData,
3368 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3369 name, nam);
3370 ret = 0;
3371 }
3372 if (save == 0)
3373 break;
3374 *cur = save;
3375 while (IS_BLANK(*cur)) cur++;
3376 }
3377 xmlFree(dup);
3378 break;
3379 }
3380 case XML_ATTRIBUTE_NOTATION: {
3381 xmlNotationPtr nota;
3382
3383 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3384 if ((nota == NULL) && (doc->extSubset != NULL))
3385 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3386
3387 if (nota == NULL) {
3388 VERROR(ctxt->userData,
3389 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3390 name, value);
3391 ret = 0;
3392 }
3393 break;
3394 }
3395 }
3396 return(ret);
3397}
3398
3399/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003400 * xmlValidCtxtNormalizeAttributeValue:
3401 * @ctxt: the validation context
3402 * @doc: the document
3403 * @elem: the parent
3404 * @name: the attribute name
3405 * @value: the attribute value
3406 * @ctxt: the validation context or NULL
3407 *
3408 * Does the validation related extra step of the normalization of attribute
3409 * values:
3410 *
3411 * If the declared value is not CDATA, then the XML processor must further
3412 * process the normalized attribute value by discarding any leading and
3413 * trailing space (#x20) characters, and by replacing sequences of space
3414 * (#x20) characters by single space (#x20) character.
3415 *
3416 * Also check VC: Standalone Document Declaration in P32, and update
3417 * ctxt->valid accordingly
3418 *
3419 * returns a new normalized string if normalization is needed, NULL otherwise
3420 * the caller must free the returned value.
3421 */
3422
3423xmlChar *
3424xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3425 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3426 xmlChar *ret, *dst;
3427 const xmlChar *src;
3428 xmlAttributePtr attrDecl = NULL;
3429 int extsubset = 0;
3430
3431 if (doc == NULL) return(NULL);
3432 if (elem == NULL) return(NULL);
3433 if (name == NULL) return(NULL);
3434 if (value == NULL) return(NULL);
3435
3436 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003437 xmlChar fn[50];
3438 xmlChar *fullname;
3439
3440 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3441 if (fullname == NULL)
3442 return(0);
3443 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003444 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003445 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003446 if (attrDecl != NULL)
3447 extsubset = 1;
3448 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003449 if ((fullname != fn) && (fullname != elem->name))
3450 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003451 }
3452 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3453 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3454 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3455 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3456 if (attrDecl != NULL)
3457 extsubset = 1;
3458 }
3459
3460 if (attrDecl == NULL)
3461 return(NULL);
3462 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3463 return(NULL);
3464
3465 ret = xmlStrdup(value);
3466 if (ret == NULL)
3467 return(NULL);
3468 src = value;
3469 dst = ret;
3470 while (*src == 0x20) src++;
3471 while (*src != 0) {
3472 if (*src == 0x20) {
3473 while (*src == 0x20) src++;
3474 if (*src != 0)
3475 *dst++ = 0x20;
3476 } else {
3477 *dst++ = *src++;
3478 }
3479 }
3480 *dst = 0;
3481 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3482 VERROR(ctxt->userData,
3483"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3484 name, elem->name);
3485 ctxt->valid = 0;
3486 }
3487 return(ret);
3488}
3489
3490/**
Owen Taylor3473f882001-02-23 17:55:21 +00003491 * xmlValidNormalizeAttributeValue:
3492 * @doc: the document
3493 * @elem: the parent
3494 * @name: the attribute name
3495 * @value: the attribute value
3496 *
3497 * Does the validation related extra step of the normalization of attribute
3498 * values:
3499 *
3500 * If the declared value is not CDATA, then the XML processor must further
3501 * process the normalized attribute value by discarding any leading and
3502 * trailing space (#x20) characters, and by replacing sequences of space
3503 * (#x20) characters by single space (#x20) character.
3504 *
3505 * returns a new normalized string if normalization is needed, NULL otherwise
3506 * the caller must free the returned value.
3507 */
3508
3509xmlChar *
3510xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3511 const xmlChar *name, const xmlChar *value) {
3512 xmlChar *ret, *dst;
3513 const xmlChar *src;
3514 xmlAttributePtr attrDecl = NULL;
3515
3516 if (doc == NULL) return(NULL);
3517 if (elem == NULL) return(NULL);
3518 if (name == NULL) return(NULL);
3519 if (value == NULL) return(NULL);
3520
3521 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003522 xmlChar fn[50];
3523 xmlChar *fullname;
3524
3525 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3526 if (fullname == NULL)
3527 return(0);
3528 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003529 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003530 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3531 if ((fullname != fn) && (fullname != elem->name))
3532 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003533 }
3534 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3535 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3536 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3537
3538 if (attrDecl == NULL)
3539 return(NULL);
3540 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3541 return(NULL);
3542
3543 ret = xmlStrdup(value);
3544 if (ret == NULL)
3545 return(NULL);
3546 src = value;
3547 dst = ret;
3548 while (*src == 0x20) src++;
3549 while (*src != 0) {
3550 if (*src == 0x20) {
3551 while (*src == 0x20) src++;
3552 if (*src != 0)
3553 *dst++ = 0x20;
3554 } else {
3555 *dst++ = *src++;
3556 }
3557 }
3558 *dst = 0;
3559 return(ret);
3560}
3561
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003562static void
Owen Taylor3473f882001-02-23 17:55:21 +00003563xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003564 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003565 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3566}
3567
3568/**
3569 * xmlValidateAttributeDecl:
3570 * @ctxt: the validation context
3571 * @doc: a document instance
3572 * @attr: an attribute definition
3573 *
3574 * Try to validate a single attribute definition
3575 * basically it does the following checks as described by the
3576 * XML-1.0 recommendation:
3577 * - [ VC: Attribute Default Legal ]
3578 * - [ VC: Enumeration ]
3579 * - [ VC: ID Attribute Default ]
3580 *
3581 * The ID/IDREF uniqueness and matching are done separately
3582 *
3583 * returns 1 if valid or 0 otherwise
3584 */
3585
3586int
3587xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3588 xmlAttributePtr attr) {
3589 int ret = 1;
3590 int val;
3591 CHECK_DTD;
3592 if(attr == NULL) return(1);
3593
3594 /* Attribute Default Legal */
3595 /* Enumeration */
3596 if (attr->defaultValue != NULL) {
3597 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3598 if (val == 0) {
3599 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003600 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003601 attr->name, attr->elem);
3602 }
3603 ret &= val;
3604 }
3605
3606 /* ID Attribute Default */
3607 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3608 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3609 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3610 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003611 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003612 attr->name, attr->elem);
3613 ret = 0;
3614 }
3615
3616 /* One ID per Element Type */
3617 if (attr->atype == XML_ATTRIBUTE_ID) {
3618 int nbId;
3619
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003620 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003621 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3622 attr->elem);
3623 if (elem != NULL) {
3624 nbId = xmlScanIDAttributeDecl(NULL, elem);
3625 } else {
3626 xmlAttributeTablePtr table;
3627
3628 /*
3629 * The attribute may be declared in the internal subset and the
3630 * element in the external subset.
3631 */
3632 nbId = 0;
3633 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3634 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3635 xmlValidateAttributeIdCallback, &nbId);
3636 }
3637 if (nbId > 1) {
3638 VERROR(ctxt->userData,
3639 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3640 attr->elem, nbId, attr->name);
3641 } else if (doc->extSubset != NULL) {
3642 int extId = 0;
3643 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3644 if (elem != NULL) {
3645 extId = xmlScanIDAttributeDecl(NULL, elem);
3646 }
3647 if (extId > 1) {
3648 VERROR(ctxt->userData,
3649 "Element %s has %d ID attribute defined in the external subset : %s\n",
3650 attr->elem, extId, attr->name);
3651 } else if (extId + nbId > 1) {
3652 VERROR(ctxt->userData,
3653"Element %s has ID attributes defined in the internal and external subset : %s\n",
3654 attr->elem, attr->name);
3655 }
3656 }
3657 }
3658
3659 /* Validity Constraint: Enumeration */
3660 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3661 xmlEnumerationPtr tree = attr->tree;
3662 while (tree != NULL) {
3663 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3664 tree = tree->next;
3665 }
3666 if (tree == NULL) {
3667 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003668"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003669 attr->defaultValue, attr->name, attr->elem);
3670 ret = 0;
3671 }
3672 }
3673
3674 return(ret);
3675}
3676
3677/**
3678 * xmlValidateElementDecl:
3679 * @ctxt: the validation context
3680 * @doc: a document instance
3681 * @elem: an element definition
3682 *
3683 * Try to validate a single element definition
3684 * basically it does the following checks as described by the
3685 * XML-1.0 recommendation:
3686 * - [ VC: One ID per Element Type ]
3687 * - [ VC: No Duplicate Types ]
3688 * - [ VC: Unique Element Type Declaration ]
3689 *
3690 * returns 1 if valid or 0 otherwise
3691 */
3692
3693int
3694xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3695 xmlElementPtr elem) {
3696 int ret = 1;
3697 xmlElementPtr tst;
3698
3699 CHECK_DTD;
3700
3701 if (elem == NULL) return(1);
3702
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003703#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003704#ifdef LIBXML_REGEXP_ENABLED
3705 /* Build the regexp associated to the content model */
3706 ret = xmlValidBuildContentModel(ctxt, elem);
3707#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003708#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003709
Owen Taylor3473f882001-02-23 17:55:21 +00003710 /* No Duplicate Types */
3711 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3712 xmlElementContentPtr cur, next;
3713 const xmlChar *name;
3714
3715 cur = elem->content;
3716 while (cur != NULL) {
3717 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3718 if (cur->c1 == NULL) break;
3719 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3720 name = cur->c1->name;
3721 next = cur->c2;
3722 while (next != NULL) {
3723 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard7b68df92003-08-03 22:58:54 +00003724 if ((xmlStrEqual(next->name, name)) &&
3725 (xmlStrEqual(next->prefix, cur->prefix))) {
3726 if (cur->prefix == NULL) {
3727 VERROR(ctxt->userData,
Owen Taylor3473f882001-02-23 17:55:21 +00003728 "Definition of %s has duplicate references of %s\n",
Daniel Veillard7b68df92003-08-03 22:58:54 +00003729 elem->name, name);
3730 } else {
3731 VERROR(ctxt->userData,
3732 "Definition of %s has duplicate references of %s:%s\n",
3733 elem->name, cur->prefix, name);
3734 }
Owen Taylor3473f882001-02-23 17:55:21 +00003735 ret = 0;
3736 }
3737 break;
3738 }
3739 if (next->c1 == NULL) break;
3740 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
Daniel Veillard7b68df92003-08-03 22:58:54 +00003741 if ((xmlStrEqual(next->c1->name, name)) &&
3742 (xmlStrEqual(next->c1->prefix, cur->prefix))) {
3743 if (cur->prefix == NULL) {
3744 VERROR(ctxt->userData,
3745 "Definition of %s has duplicate references to %s\n",
3746 elem->name, name);
3747 } else {
3748 VERROR(ctxt->userData,
3749 "Definition of %s has duplicate references to %s:%s\n",
3750 elem->name, cur->prefix, name);
3751 }
Owen Taylor3473f882001-02-23 17:55:21 +00003752 ret = 0;
3753 }
3754 next = next->c2;
3755 }
3756 }
3757 cur = cur->c2;
3758 }
3759 }
3760
3761 /* VC: Unique Element Type Declaration */
3762 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003763 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003764 ((tst->prefix == elem->prefix) ||
3765 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003766 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003767 VERROR(ctxt->userData, "Redefinition of element %s\n",
3768 elem->name);
3769 ret = 0;
3770 }
3771 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003772 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003773 ((tst->prefix == elem->prefix) ||
3774 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003775 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003776 VERROR(ctxt->userData, "Redefinition of element %s\n",
3777 elem->name);
3778 ret = 0;
3779 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003780 /* One ID per Element Type
3781 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003782 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3783 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003784 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003785 return(ret);
3786}
3787
3788/**
3789 * xmlValidateOneAttribute:
3790 * @ctxt: the validation context
3791 * @doc: a document instance
3792 * @elem: an element instance
3793 * @attr: an attribute instance
3794 * @value: the attribute value (without entities processing)
3795 *
3796 * Try to validate a single attribute for an element
3797 * basically it does the following checks as described by the
3798 * XML-1.0 recommendation:
3799 * - [ VC: Attribute Value Type ]
3800 * - [ VC: Fixed Attribute Default ]
3801 * - [ VC: Entity Name ]
3802 * - [ VC: Name Token ]
3803 * - [ VC: ID ]
3804 * - [ VC: IDREF ]
3805 * - [ VC: Entity Name ]
3806 * - [ VC: Notation Attributes ]
3807 *
3808 * The ID/IDREF uniqueness and matching are done separately
3809 *
3810 * returns 1 if valid or 0 otherwise
3811 */
3812
3813int
3814xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
Daniel Veillard07cb8222003-09-10 10:51:05 +00003815 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value)
3816{
Owen Taylor3473f882001-02-23 17:55:21 +00003817 xmlAttributePtr attrDecl = NULL;
3818 int val;
3819 int ret = 1;
3820
3821 CHECK_DTD;
3822 if ((elem == NULL) || (elem->name == NULL)) return(0);
3823 if ((attr == NULL) || (attr->name == NULL)) return(0);
3824
3825 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003826 xmlChar fn[50];
3827 xmlChar *fullname;
3828
3829 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3830 if (fullname == NULL)
3831 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003832 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003833 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003834 attr->name, attr->ns->prefix);
3835 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003836 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003837 attr->name, attr->ns->prefix);
3838 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003839 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003840 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3841 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00003842 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003843 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003844 if ((fullname != fn) && (fullname != elem->name))
3845 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003846 }
3847 if (attrDecl == NULL) {
3848 if (attr->ns != NULL) {
3849 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3850 attr->name, attr->ns->prefix);
3851 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3852 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3853 attr->name, attr->ns->prefix);
3854 } else {
3855 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3856 elem->name, attr->name);
3857 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3858 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3859 elem->name, attr->name);
3860 }
3861 }
3862
3863
3864 /* Validity Constraint: Attribute Value Type */
3865 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003866 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003867 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003868 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003869 attr->name, elem->name);
3870 return(0);
3871 }
3872 attr->atype = attrDecl->atype;
3873
3874 val = xmlValidateAttributeValue(attrDecl->atype, value);
3875 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003876 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003877 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003878 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003879 attr->name, elem->name);
3880 ret = 0;
3881 }
3882
3883 /* Validity constraint: Fixed Attribute Default */
3884 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3885 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003886 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003887 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003888 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003889 attr->name, elem->name, attrDecl->defaultValue);
3890 ret = 0;
3891 }
3892 }
3893
3894 /* Validity Constraint: ID uniqueness */
3895 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3896 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3897 ret = 0;
3898 }
3899
3900 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3901 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3902 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3903 ret = 0;
3904 }
3905
3906 /* Validity Constraint: Notation Attributes */
3907 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3908 xmlEnumerationPtr tree = attrDecl->tree;
3909 xmlNotationPtr nota;
3910
3911 /* First check that the given NOTATION was declared */
3912 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3913 if (nota == NULL)
3914 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3915
3916 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003917 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003918 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003919 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003920 value, attr->name, elem->name);
3921 ret = 0;
3922 }
3923
3924 /* Second, verify that it's among the list */
3925 while (tree != NULL) {
3926 if (xmlStrEqual(tree->name, value)) break;
3927 tree = tree->next;
3928 }
3929 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003930 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003931 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003932"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003933 value, attr->name, elem->name);
3934 ret = 0;
3935 }
3936 }
3937
3938 /* Validity Constraint: Enumeration */
3939 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3940 xmlEnumerationPtr tree = attrDecl->tree;
3941 while (tree != NULL) {
3942 if (xmlStrEqual(tree->name, value)) break;
3943 tree = tree->next;
3944 }
3945 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003946 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003947 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003948 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003949 value, attr->name, elem->name);
3950 ret = 0;
3951 }
3952 }
3953
3954 /* Fixed Attribute Default */
3955 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3956 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003957 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003958 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003959 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003960 attr->name, elem->name, attrDecl->defaultValue);
3961 ret = 0;
3962 }
3963
3964 /* Extra check for the attribute value */
3965 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3966 attrDecl->atype, value);
3967
3968 return(ret);
3969}
3970
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003971/**
3972 * xmlValidateOneNamespace:
3973 * @ctxt: the validation context
3974 * @doc: a document instance
3975 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00003976 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003977 * @ns: an namespace declaration instance
3978 * @value: the attribute value (without entities processing)
3979 *
3980 * Try to validate a single namespace declaration for an element
3981 * basically it does the following checks as described by the
3982 * XML-1.0 recommendation:
3983 * - [ VC: Attribute Value Type ]
3984 * - [ VC: Fixed Attribute Default ]
3985 * - [ VC: Entity Name ]
3986 * - [ VC: Name Token ]
3987 * - [ VC: ID ]
3988 * - [ VC: IDREF ]
3989 * - [ VC: Entity Name ]
3990 * - [ VC: Notation Attributes ]
3991 *
3992 * The ID/IDREF uniqueness and matching are done separately
3993 *
3994 * returns 1 if valid or 0 otherwise
3995 */
3996
3997int
3998xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3999xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
4000 /* xmlElementPtr elemDecl; */
4001 xmlAttributePtr attrDecl = NULL;
4002 int val;
4003 int ret = 1;
4004
4005 CHECK_DTD;
4006 if ((elem == NULL) || (elem->name == NULL)) return(0);
4007 if ((ns == NULL) || (ns->href == NULL)) return(0);
4008
4009 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004010 xmlChar fn[50];
4011 xmlChar *fullname;
4012
4013 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
4014 if (fullname == NULL) {
4015 VERROR(ctxt->userData, "Out of memory\n");
4016 return(0);
4017 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004018 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004019 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004020 ns->prefix, BAD_CAST "xmlns");
4021 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004022 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004023 ns->prefix, BAD_CAST "xmlns");
4024 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004025 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004026 BAD_CAST "xmlns");
4027 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004028 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004029 BAD_CAST "xmlns");
4030 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004031 if ((fullname != fn) && (fullname != elem->name))
4032 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004033 }
4034 if (attrDecl == NULL) {
4035 if (ns->prefix != NULL) {
4036 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4037 ns->prefix, BAD_CAST "xmlns");
4038 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4039 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4040 ns->prefix, BAD_CAST "xmlns");
4041 } else {
4042 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4043 elem->name, BAD_CAST "xmlns");
4044 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4045 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4046 elem->name, BAD_CAST "xmlns");
4047 }
4048 }
4049
4050
4051 /* Validity Constraint: Attribute Value Type */
4052 if (attrDecl == NULL) {
4053 VECTXT(ctxt, elem);
4054 if (ns->prefix != NULL) {
4055 VERROR(ctxt->userData,
4056 "No declaration for attribute xmlns:%s of element %s\n",
4057 ns->prefix, elem->name);
4058 } else {
4059 VERROR(ctxt->userData,
4060 "No declaration for attribute xmlns of element %s\n",
4061 elem->name);
4062 }
4063 return(0);
4064 }
4065
4066 val = xmlValidateAttributeValue(attrDecl->atype, value);
4067 if (val == 0) {
4068 VECTXT(ctxt, elem);
4069 if (ns->prefix != NULL) {
4070 VERROR(ctxt->userData,
4071 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
4072 ns->prefix, elem->name);
4073 } else {
4074 VERROR(ctxt->userData,
4075 "Syntax of value for attribute xmlns of %s is not valid\n",
4076 elem->name);
4077 }
4078 ret = 0;
4079 }
4080
4081 /* Validity constraint: Fixed Attribute Default */
4082 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4083 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4084 VECTXT(ctxt, elem);
4085 if (ns->prefix != NULL) {
4086 VERROR(ctxt->userData,
4087 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4088 ns->prefix, elem->name, attrDecl->defaultValue);
4089 } else {
4090 VERROR(ctxt->userData,
4091 "Value for attribute xmlns of %s is different from default \"%s\"\n",
4092 elem->name, attrDecl->defaultValue);
4093 }
4094 ret = 0;
4095 }
4096 }
4097
4098 /* Validity Constraint: ID uniqueness */
4099 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4100 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4101 ret = 0;
4102 }
4103
4104 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4105 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4106 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4107 ret = 0;
4108 }
4109
4110 /* Validity Constraint: Notation Attributes */
4111 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4112 xmlEnumerationPtr tree = attrDecl->tree;
4113 xmlNotationPtr nota;
4114
4115 /* First check that the given NOTATION was declared */
4116 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4117 if (nota == NULL)
4118 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4119
4120 if (nota == NULL) {
4121 VECTXT(ctxt, elem);
4122 if (ns->prefix != NULL) {
4123 VERROR(ctxt->userData,
4124 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4125 value, ns->prefix, elem->name);
4126 } else {
4127 VERROR(ctxt->userData,
4128 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
4129 value, elem->name);
4130 }
4131 ret = 0;
4132 }
4133
4134 /* Second, verify that it's among the list */
4135 while (tree != NULL) {
4136 if (xmlStrEqual(tree->name, value)) break;
4137 tree = tree->next;
4138 }
4139 if (tree == NULL) {
4140 VECTXT(ctxt, elem);
4141 if (ns->prefix != NULL) {
4142 VERROR(ctxt->userData,
4143"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4144 value, ns->prefix, elem->name);
4145 } else {
4146 VERROR(ctxt->userData,
4147"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
4148 value, elem->name);
4149 }
4150 ret = 0;
4151 }
4152 }
4153
4154 /* Validity Constraint: Enumeration */
4155 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4156 xmlEnumerationPtr tree = attrDecl->tree;
4157 while (tree != NULL) {
4158 if (xmlStrEqual(tree->name, value)) break;
4159 tree = tree->next;
4160 }
4161 if (tree == NULL) {
4162 VECTXT(ctxt, elem);
4163 if (ns->prefix != NULL) {
4164 VERROR(ctxt->userData,
4165"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4166 value, ns->prefix, elem->name);
4167 } else {
4168 VERROR(ctxt->userData,
4169"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4170 value, elem->name);
4171 }
4172 ret = 0;
4173 }
4174 }
4175
4176 /* Fixed Attribute Default */
4177 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4178 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4179 VECTXT(ctxt, elem);
4180 if (ns->prefix != NULL) {
4181 VERROR(ctxt->userData,
4182 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4183 ns->prefix, elem->name, attrDecl->defaultValue);
4184 } else {
4185 VERROR(ctxt->userData,
4186 "Value for attribute xmlns of %s must be \"%s\"\n",
4187 elem->name, attrDecl->defaultValue);
4188 }
4189 ret = 0;
4190 }
4191
4192 /* Extra check for the attribute value */
4193 if (ns->prefix != NULL) {
4194 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4195 attrDecl->atype, value);
4196 } else {
4197 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4198 attrDecl->atype, value);
4199 }
4200
4201 return(ret);
4202}
4203
Daniel Veillard118aed72002-09-24 14:13:13 +00004204#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004205/**
4206 * xmlValidateSkipIgnorable:
4207 * @ctxt: the validation context
4208 * @child: the child list
4209 *
4210 * Skip ignorable elements w.r.t. the validation process
4211 *
4212 * returns the first element to consider for validation of the content model
4213 */
4214
4215static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004216xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004217 while (child != NULL) {
4218 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004219 /* These things are ignored (skipped) during validation. */
4220 case XML_PI_NODE:
4221 case XML_COMMENT_NODE:
4222 case XML_XINCLUDE_START:
4223 case XML_XINCLUDE_END:
4224 child = child->next;
4225 break;
4226 case XML_TEXT_NODE:
4227 if (xmlIsBlankNode(child))
4228 child = child->next;
4229 else
4230 return(child);
4231 break;
4232 /* keep current node */
4233 default:
4234 return(child);
4235 }
4236 }
4237 return(child);
4238}
4239
4240/**
4241 * xmlValidateElementType:
4242 * @ctxt: the validation context
4243 *
4244 * Try to validate the content model of an element internal function
4245 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004246 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4247 * reference is found and -3 if the validation succeeded but
4248 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004249 */
4250
4251static int
4252xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004253 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004254 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004255
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004256 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004257 if ((NODE == NULL) && (CONT == NULL))
4258 return(1);
4259 if ((NODE == NULL) &&
4260 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4261 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4262 return(1);
4263 }
4264 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004265 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004266 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004267
4268 /*
4269 * We arrive here when more states need to be examined
4270 */
4271cont:
4272
4273 /*
4274 * We just recovered from a rollback generated by a possible
4275 * epsilon transition, go directly to the analysis phase
4276 */
4277 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004278 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004279 DEBUG_VALID_STATE(NODE, CONT)
4280 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004281 goto analyze;
4282 }
4283
4284 DEBUG_VALID_STATE(NODE, CONT)
4285 /*
4286 * we may have to save a backup state here. This is the equivalent
4287 * of handling epsilon transition in NFAs.
4288 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004289 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004290 ((CONT->parent == NULL) ||
4291 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004292 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004293 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004294 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004295 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004296 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4297 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004298 }
4299
4300
4301 /*
4302 * Check first if the content matches
4303 */
4304 switch (CONT->type) {
4305 case XML_ELEMENT_CONTENT_PCDATA:
4306 if (NODE == NULL) {
4307 DEBUG_VALID_MSG("pcdata failed no node");
4308 ret = 0;
4309 break;
4310 }
4311 if (NODE->type == XML_TEXT_NODE) {
4312 DEBUG_VALID_MSG("pcdata found, skip to next");
4313 /*
4314 * go to next element in the content model
4315 * skipping ignorable elems
4316 */
4317 do {
4318 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004319 NODE = xmlValidateSkipIgnorable(NODE);
4320 if ((NODE != NULL) &&
4321 (NODE->type == XML_ENTITY_REF_NODE))
4322 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004323 } while ((NODE != NULL) &&
4324 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004325 (NODE->type != XML_TEXT_NODE) &&
4326 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004327 ret = 1;
4328 break;
4329 } else {
4330 DEBUG_VALID_MSG("pcdata failed");
4331 ret = 0;
4332 break;
4333 }
4334 break;
4335 case XML_ELEMENT_CONTENT_ELEMENT:
4336 if (NODE == NULL) {
4337 DEBUG_VALID_MSG("element failed no node");
4338 ret = 0;
4339 break;
4340 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004341 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4342 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004343 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004344 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4345 ret = (CONT->prefix == NULL);
4346 } else if (CONT->prefix == NULL) {
4347 ret = 0;
4348 } else {
4349 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4350 }
4351 }
4352 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004353 DEBUG_VALID_MSG("element found, skip to next");
4354 /*
4355 * go to next element in the content model
4356 * skipping ignorable elems
4357 */
4358 do {
4359 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004360 NODE = xmlValidateSkipIgnorable(NODE);
4361 if ((NODE != NULL) &&
4362 (NODE->type == XML_ENTITY_REF_NODE))
4363 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004364 } while ((NODE != NULL) &&
4365 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004366 (NODE->type != XML_TEXT_NODE) &&
4367 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004368 } else {
4369 DEBUG_VALID_MSG("element failed");
4370 ret = 0;
4371 break;
4372 }
4373 break;
4374 case XML_ELEMENT_CONTENT_OR:
4375 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004376 * Small optimization.
4377 */
4378 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4379 if ((NODE == NULL) ||
4380 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4381 DEPTH++;
4382 CONT = CONT->c2;
4383 goto cont;
4384 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004385 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4386 ret = (CONT->c1->prefix == NULL);
4387 } else if (CONT->c1->prefix == NULL) {
4388 ret = 0;
4389 } else {
4390 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4391 }
4392 if (ret == 0) {
4393 DEPTH++;
4394 CONT = CONT->c2;
4395 goto cont;
4396 }
Daniel Veillard85349052001-04-20 13:48:21 +00004397 }
4398
4399 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004400 * save the second branch 'or' branch
4401 */
4402 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004403 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4404 OCCURS, ROLLBACK_OR) < 0)
4405 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004406 DEPTH++;
4407 CONT = CONT->c1;
4408 goto cont;
4409 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004410 /*
4411 * Small optimization.
4412 */
4413 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4414 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4415 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4416 if ((NODE == NULL) ||
4417 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4418 DEPTH++;
4419 CONT = CONT->c2;
4420 goto cont;
4421 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004422 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4423 ret = (CONT->c1->prefix == NULL);
4424 } else if (CONT->c1->prefix == NULL) {
4425 ret = 0;
4426 } else {
4427 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4428 }
4429 if (ret == 0) {
4430 DEPTH++;
4431 CONT = CONT->c2;
4432 goto cont;
4433 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004434 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004435 DEPTH++;
4436 CONT = CONT->c1;
4437 goto cont;
4438 }
4439
4440 /*
4441 * At this point handle going up in the tree
4442 */
4443 if (ret == -1) {
4444 DEBUG_VALID_MSG("error found returning");
4445 return(ret);
4446 }
4447analyze:
4448 while (CONT != NULL) {
4449 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004450 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004451 * this level.
4452 */
4453 if (ret == 0) {
4454 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004455 xmlNodePtr cur;
4456
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004457 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004458 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004459 DEBUG_VALID_MSG("Once branch failed, rollback");
4460 if (vstateVPop(ctxt) < 0 ) {
4461 DEBUG_VALID_MSG("exhaustion, failed");
4462 return(0);
4463 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004464 if (cur != ctxt->vstate->node)
4465 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004466 goto cont;
4467 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004468 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004469 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004470 DEBUG_VALID_MSG("Plus branch failed, rollback");
4471 if (vstateVPop(ctxt) < 0 ) {
4472 DEBUG_VALID_MSG("exhaustion, failed");
4473 return(0);
4474 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004475 if (cur != ctxt->vstate->node)
4476 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004477 goto cont;
4478 }
4479 DEBUG_VALID_MSG("Plus branch found");
4480 ret = 1;
4481 break;
4482 case XML_ELEMENT_CONTENT_MULT:
4483#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004484 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004485 DEBUG_VALID_MSG("Mult branch failed");
4486 } else {
4487 DEBUG_VALID_MSG("Mult branch found");
4488 }
4489#endif
4490 ret = 1;
4491 break;
4492 case XML_ELEMENT_CONTENT_OPT:
4493 DEBUG_VALID_MSG("Option branch failed");
4494 ret = 1;
4495 break;
4496 }
4497 } else {
4498 switch (CONT->ocur) {
4499 case XML_ELEMENT_CONTENT_OPT:
4500 DEBUG_VALID_MSG("Option branch succeeded");
4501 ret = 1;
4502 break;
4503 case XML_ELEMENT_CONTENT_ONCE:
4504 DEBUG_VALID_MSG("Once branch succeeded");
4505 ret = 1;
4506 break;
4507 case XML_ELEMENT_CONTENT_PLUS:
4508 if (STATE == ROLLBACK_PARENT) {
4509 DEBUG_VALID_MSG("Plus branch rollback");
4510 ret = 1;
4511 break;
4512 }
4513 if (NODE == NULL) {
4514 DEBUG_VALID_MSG("Plus branch exhausted");
4515 ret = 1;
4516 break;
4517 }
4518 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004519 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004520 goto cont;
4521 case XML_ELEMENT_CONTENT_MULT:
4522 if (STATE == ROLLBACK_PARENT) {
4523 DEBUG_VALID_MSG("Mult branch rollback");
4524 ret = 1;
4525 break;
4526 }
4527 if (NODE == NULL) {
4528 DEBUG_VALID_MSG("Mult branch exhausted");
4529 ret = 1;
4530 break;
4531 }
4532 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004533 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004534 goto cont;
4535 }
4536 }
4537 STATE = 0;
4538
4539 /*
4540 * Then act accordingly at the parent level
4541 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004542 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004543 if (CONT->parent == NULL)
4544 break;
4545
4546 switch (CONT->parent->type) {
4547 case XML_ELEMENT_CONTENT_PCDATA:
4548 DEBUG_VALID_MSG("Error: parent pcdata");
4549 return(-1);
4550 case XML_ELEMENT_CONTENT_ELEMENT:
4551 DEBUG_VALID_MSG("Error: parent element");
4552 return(-1);
4553 case XML_ELEMENT_CONTENT_OR:
4554 if (ret == 1) {
4555 DEBUG_VALID_MSG("Or succeeded");
4556 CONT = CONT->parent;
4557 DEPTH--;
4558 } else {
4559 DEBUG_VALID_MSG("Or failed");
4560 CONT = CONT->parent;
4561 DEPTH--;
4562 }
4563 break;
4564 case XML_ELEMENT_CONTENT_SEQ:
4565 if (ret == 0) {
4566 DEBUG_VALID_MSG("Sequence failed");
4567 CONT = CONT->parent;
4568 DEPTH--;
4569 } else if (CONT == CONT->parent->c1) {
4570 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4571 CONT = CONT->parent->c2;
4572 goto cont;
4573 } else {
4574 DEBUG_VALID_MSG("Sequence succeeded");
4575 CONT = CONT->parent;
4576 DEPTH--;
4577 }
4578 }
4579 }
4580 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004581 xmlNodePtr cur;
4582
4583 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004584 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4585 if (vstateVPop(ctxt) < 0 ) {
4586 DEBUG_VALID_MSG("exhaustion, failed");
4587 return(0);
4588 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004589 if (cur != ctxt->vstate->node)
4590 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004591 goto cont;
4592 }
4593 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004594 xmlNodePtr cur;
4595
4596 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004597 DEBUG_VALID_MSG("Failure, rollback");
4598 if (vstateVPop(ctxt) < 0 ) {
4599 DEBUG_VALID_MSG("exhaustion, failed");
4600 return(0);
4601 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004602 if (cur != ctxt->vstate->node)
4603 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004604 goto cont;
4605 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004606 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004607}
Daniel Veillard23e73572002-09-19 19:56:43 +00004608#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004609
4610/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004611 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004612 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004613 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004614 * @content: An element
4615 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4616 *
4617 * This will dump the list of elements to the buffer
4618 * Intended just for the debug routine
4619 */
4620static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004621xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004622 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004623 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004624
4625 if (node == NULL) return;
4626 if (glob) strcat(buf, "(");
4627 cur = node;
4628 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004629 len = strlen(buf);
4630 if (size - len < 50) {
4631 if ((size - len > 4) && (buf[len - 1] != '.'))
4632 strcat(buf, " ...");
4633 return;
4634 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004635 switch (cur->type) {
4636 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004637 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004638 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004639 if ((size - len > 4) && (buf[len - 1] != '.'))
4640 strcat(buf, " ...");
4641 return;
4642 }
4643 strcat(buf, (char *) cur->ns->prefix);
4644 strcat(buf, ":");
4645 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004646 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004647 if ((size - len > 4) && (buf[len - 1] != '.'))
4648 strcat(buf, " ...");
4649 return;
4650 }
4651 strcat(buf, (char *) cur->name);
4652 if (cur->next != NULL)
4653 strcat(buf, " ");
4654 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004655 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004656 if (xmlIsBlankNode(cur))
4657 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004658 case XML_CDATA_SECTION_NODE:
4659 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004660 strcat(buf, "CDATA");
4661 if (cur->next != NULL)
4662 strcat(buf, " ");
4663 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004664 case XML_ATTRIBUTE_NODE:
4665 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004666#ifdef LIBXML_DOCB_ENABLED
4667 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004668#endif
4669 case XML_HTML_DOCUMENT_NODE:
4670 case XML_DOCUMENT_TYPE_NODE:
4671 case XML_DOCUMENT_FRAG_NODE:
4672 case XML_NOTATION_NODE:
4673 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004674 strcat(buf, "???");
4675 if (cur->next != NULL)
4676 strcat(buf, " ");
4677 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004678 case XML_ENTITY_NODE:
4679 case XML_PI_NODE:
4680 case XML_DTD_NODE:
4681 case XML_COMMENT_NODE:
4682 case XML_ELEMENT_DECL:
4683 case XML_ATTRIBUTE_DECL:
4684 case XML_ENTITY_DECL:
4685 case XML_XINCLUDE_START:
4686 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004687 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004688 }
4689 cur = cur->next;
4690 }
4691 if (glob) strcat(buf, ")");
4692}
4693
4694/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004695 * xmlValidateElementContent:
4696 * @ctxt: the validation context
4697 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004698 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004699 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004700 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004701 *
4702 * Try to validate the content model of an element
4703 *
4704 * returns 1 if valid or 0 if not and -1 in case of error
4705 */
4706
4707static int
4708xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004709 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004710 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004711#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004712 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004713#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004714 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004715 xmlElementContentPtr cont;
4716 const xmlChar *name;
4717
4718 if (elemDecl == NULL)
4719 return(-1);
4720 cont = elemDecl->content;
4721 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004722
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004723#ifdef LIBXML_REGEXP_ENABLED
4724 /* Build the regexp associated to the content model */
4725 if (elemDecl->contModel == NULL)
4726 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4727 if (elemDecl->contModel == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004728 return(-1);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004729 } else {
4730 xmlRegExecCtxtPtr exec;
4731
Daniel Veillardec498e12003-02-05 11:01:50 +00004732 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
4733 return(-1);
4734 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004735 ctxt->nodeMax = 0;
4736 ctxt->nodeNr = 0;
4737 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004738 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4739 if (exec != NULL) {
4740 cur = child;
4741 while (cur != NULL) {
4742 switch (cur->type) {
4743 case XML_ENTITY_REF_NODE:
4744 /*
4745 * Push the current node to be able to roll back
4746 * and process within the entity
4747 */
4748 if ((cur->children != NULL) &&
4749 (cur->children->children != NULL)) {
4750 nodeVPush(ctxt, cur);
4751 cur = cur->children->children;
4752 continue;
4753 }
4754 break;
4755 case XML_TEXT_NODE:
4756 if (xmlIsBlankNode(cur))
4757 break;
4758 ret = 0;
4759 goto fail;
4760 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004761 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004762 ret = 0;
4763 goto fail;
4764 case XML_ELEMENT_NODE:
4765 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004766 xmlChar fn[50];
4767 xmlChar *fullname;
4768
4769 fullname = xmlBuildQName(cur->name,
4770 cur->ns->prefix, fn, 50);
4771 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004772 ret = -1;
4773 goto fail;
4774 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004775 ret = xmlRegExecPushString(exec, fullname, NULL);
4776 if ((fullname != fn) && (fullname != cur->name))
4777 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004778 } else {
4779 ret = xmlRegExecPushString(exec, cur->name, NULL);
4780 }
4781 break;
4782 default:
4783 break;
4784 }
4785 /*
4786 * Switch to next element
4787 */
4788 cur = cur->next;
4789 while (cur == NULL) {
4790 cur = nodeVPop(ctxt);
4791 if (cur == NULL)
4792 break;
4793 cur = cur->next;
4794 }
4795 }
4796 ret = xmlRegExecPushString(exec, NULL, NULL);
4797fail:
4798 xmlRegFreeExecCtxt(exec);
4799 }
4800 }
4801#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004802 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004803 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004804 */
4805 ctxt->vstateMax = 8;
4806 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4807 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4808 if (ctxt->vstateTab == NULL) {
4809 xmlGenericError(xmlGenericErrorContext,
4810 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004811 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004812 }
4813 /*
4814 * The first entry in the stack is reserved to the current state
4815 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004816 ctxt->nodeMax = 0;
4817 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004818 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004819 ctxt->vstate = &ctxt->vstateTab[0];
4820 ctxt->vstateNr = 1;
4821 CONT = cont;
4822 NODE = child;
4823 DEPTH = 0;
4824 OCCURS = 0;
4825 STATE = 0;
4826 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004827 if ((ret == -3) && (warn)) {
4828 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004829 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004830 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004831 /*
4832 * An entities reference appeared at this level.
4833 * Buid a minimal representation of this node content
4834 * sufficient to run the validation process on it
4835 */
4836 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004837 cur = child;
4838 while (cur != NULL) {
4839 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004840 case XML_ENTITY_REF_NODE:
4841 /*
4842 * Push the current node to be able to roll back
4843 * and process within the entity
4844 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004845 if ((cur->children != NULL) &&
4846 (cur->children->children != NULL)) {
4847 nodeVPush(ctxt, cur);
4848 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004849 continue;
4850 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004851 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004852 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004853 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004854 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004855 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004856 case XML_CDATA_SECTION_NODE:
4857 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004858 case XML_ELEMENT_NODE:
4859 /*
4860 * Allocate a new node and minimally fills in
4861 * what's required
4862 */
4863 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4864 if (tmp == NULL) {
4865 xmlGenericError(xmlGenericErrorContext,
4866 "xmlValidateElementContent : malloc failed\n");
4867 xmlFreeNodeList(repl);
4868 ret = -1;
4869 goto done;
4870 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004871 tmp->type = cur->type;
4872 tmp->name = cur->name;
4873 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004874 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004875 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004876 if (repl == NULL)
4877 repl = last = tmp;
4878 else {
4879 last->next = tmp;
4880 last = tmp;
4881 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004882 if (cur->type == XML_CDATA_SECTION_NODE) {
4883 /*
4884 * E59 spaces in CDATA does not match the
4885 * nonterminal S
4886 */
4887 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4888 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004889 break;
4890 default:
4891 break;
4892 }
4893 /*
4894 * Switch to next element
4895 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004896 cur = cur->next;
4897 while (cur == NULL) {
4898 cur = nodeVPop(ctxt);
4899 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004900 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004901 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004902 }
4903 }
4904
4905 /*
4906 * Relaunch the validation
4907 */
4908 ctxt->vstate = &ctxt->vstateTab[0];
4909 ctxt->vstateNr = 1;
4910 CONT = cont;
4911 NODE = repl;
4912 DEPTH = 0;
4913 OCCURS = 0;
4914 STATE = 0;
4915 ret = xmlValidateElementType(ctxt);
4916 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004917#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004918 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004919 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4920 char expr[5000];
4921 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004922
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004923 expr[0] = 0;
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004924 xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004925 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004926#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004927 if (repl != NULL)
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004928 xmlSnprintfElements(&list[0], 5000, repl, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004929 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004930#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004931 xmlSnprintfElements(&list[0], 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004932
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004933 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004934 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004935 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004936 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004937 name, expr, list);
4938 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004939 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004940 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004941 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004942 expr, list);
4943 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004944 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004945 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004946 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004947 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004948 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004949 name);
4950 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004951 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004952 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004953 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004954 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004955 }
4956 ret = 0;
4957 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004958 if (ret == -3)
4959 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004960
Daniel Veillard23e73572002-09-19 19:56:43 +00004961#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004962done:
4963 /*
4964 * Deallocate the copy if done, and free up the validation stack
4965 */
4966 while (repl != NULL) {
4967 tmp = repl->next;
4968 xmlFree(repl);
4969 repl = tmp;
4970 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004971 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004972 if (ctxt->vstateTab != NULL) {
4973 xmlFree(ctxt->vstateTab);
4974 ctxt->vstateTab = NULL;
4975 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004976#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004977 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004978 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004979 if (ctxt->nodeTab != NULL) {
4980 xmlFree(ctxt->nodeTab);
4981 ctxt->nodeTab = NULL;
4982 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004983 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004984
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004985}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004986
Owen Taylor3473f882001-02-23 17:55:21 +00004987/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004988 * xmlValidateCdataElement:
4989 * @ctxt: the validation context
4990 * @doc: a document instance
4991 * @elem: an element instance
4992 *
4993 * Check that an element follows #CDATA
4994 *
4995 * returns 1 if valid or 0 otherwise
4996 */
4997static int
4998xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4999 xmlNodePtr elem) {
5000 int ret = 1;
5001 xmlNodePtr cur, child;
5002
Daniel Veillardceb09b92002-10-04 11:46:37 +00005003 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005004 return(0);
5005
5006 child = elem->children;
5007
5008 cur = child;
5009 while (cur != NULL) {
5010 switch (cur->type) {
5011 case XML_ENTITY_REF_NODE:
5012 /*
5013 * Push the current node to be able to roll back
5014 * and process within the entity
5015 */
5016 if ((cur->children != NULL) &&
5017 (cur->children->children != NULL)) {
5018 nodeVPush(ctxt, cur);
5019 cur = cur->children->children;
5020 continue;
5021 }
5022 break;
5023 case XML_COMMENT_NODE:
5024 case XML_PI_NODE:
5025 case XML_TEXT_NODE:
5026 case XML_CDATA_SECTION_NODE:
5027 break;
5028 default:
5029 ret = 0;
5030 goto done;
5031 }
5032 /*
5033 * Switch to next element
5034 */
5035 cur = cur->next;
5036 while (cur == NULL) {
5037 cur = nodeVPop(ctxt);
5038 if (cur == NULL)
5039 break;
5040 cur = cur->next;
5041 }
5042 }
5043done:
5044 ctxt->nodeMax = 0;
5045 ctxt->nodeNr = 0;
5046 if (ctxt->nodeTab != NULL) {
5047 xmlFree(ctxt->nodeTab);
5048 ctxt->nodeTab = NULL;
5049 }
5050 return(ret);
5051}
5052
5053/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005054 * xmlValidateCheckMixed:
5055 * @ctxt: the validation context
5056 * @cont: the mixed content model
5057 * @qname: the qualified name as appearing in the serialization
5058 *
5059 * Check if the given node is part of the content model.
5060 *
5061 * Returns 1 if yes, 0 if no, -1 in case of error
5062 */
5063static int
5064xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5065 xmlElementContentPtr cont, const xmlChar *qname) {
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005066 const xmlChar *name;
5067 int plen;
5068 name = xmlSplitQName3(qname, &plen);
5069
5070 if (name == NULL) {
5071 while (cont != NULL) {
5072 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5073 if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname)))
5074 return(1);
5075 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5076 (cont->c1 != NULL) &&
5077 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5078 if ((cont->c1->prefix == NULL) &&
5079 (xmlStrEqual(cont->c1->name, qname)))
5080 return(1);
5081 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5082 (cont->c1 == NULL) ||
5083 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5084 /* Internal error !!! */
5085 xmlGenericError(xmlGenericErrorContext,
5086 "Internal: MIXED struct bad\n");
5087 break;
5088 }
5089 cont = cont->c2;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005090 }
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005091 } else {
5092 while (cont != NULL) {
5093 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5094 if ((cont->prefix != NULL) &&
5095 (xmlStrncmp(cont->prefix, qname, plen) == 0) &&
5096 (xmlStrEqual(cont->name, name)))
5097 return(1);
5098 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5099 (cont->c1 != NULL) &&
5100 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5101 if ((cont->c1->prefix != NULL) &&
5102 (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) &&
5103 (xmlStrEqual(cont->c1->name, name)))
5104 return(1);
5105 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5106 (cont->c1 == NULL) ||
5107 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5108 /* Internal error !!! */
5109 xmlGenericError(xmlGenericErrorContext,
5110 "Internal: MIXED struct bad\n");
5111 break;
5112 }
5113 cont = cont->c2;
5114 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005115 }
5116 return(0);
5117}
5118
5119/**
5120 * xmlValidGetElemDecl:
5121 * @ctxt: the validation context
5122 * @doc: a document instance
5123 * @elem: an element instance
5124 * @extsubset: pointer, (out) indicate if the declaration was found
5125 * in the external subset.
5126 *
5127 * Finds a declaration associated to an element in the document.
5128 *
5129 * returns the pointer to the declaration or NULL if not found.
5130 */
5131static xmlElementPtr
5132xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5133 xmlNodePtr elem, int *extsubset) {
5134 xmlElementPtr elemDecl = NULL;
5135 const xmlChar *prefix = NULL;
5136
5137 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5138 if (extsubset != NULL)
5139 *extsubset = 0;
5140
5141 /*
5142 * Fetch the declaration for the qualified name
5143 */
5144 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5145 prefix = elem->ns->prefix;
5146
5147 if (prefix != NULL) {
5148 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5149 elem->name, prefix);
5150 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5151 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5152 elem->name, prefix);
5153 if ((elemDecl != NULL) && (extsubset != NULL))
5154 *extsubset = 1;
5155 }
5156 }
5157
5158 /*
5159 * Fetch the declaration for the non qualified name
5160 * This is "non-strict" validation should be done on the
5161 * full QName but in that case being flexible makes sense.
5162 */
5163 if (elemDecl == NULL) {
5164 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5165 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5166 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5167 if ((elemDecl != NULL) && (extsubset != NULL))
5168 *extsubset = 1;
5169 }
5170 }
5171 if (elemDecl == NULL) {
5172 VECTXT(ctxt, elem);
5173 VERROR(ctxt->userData, "No declaration for element %s\n",
5174 elem->name);
5175 }
5176 return(elemDecl);
5177}
5178
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005179#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005180/**
5181 * xmlValidatePushElement:
5182 * @ctxt: the validation context
5183 * @doc: a document instance
5184 * @elem: an element instance
5185 * @qname: the qualified name as appearing in the serialization
5186 *
5187 * Push a new element start on the validation stack.
5188 *
5189 * returns 1 if no validation problem was found or 0 otherwise
5190 */
5191int
5192xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5193 xmlNodePtr elem, const xmlChar *qname) {
5194 int ret = 1;
5195 xmlElementPtr eDecl;
5196 int extsubset = 0;
5197
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005198/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005199 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5200 xmlValidStatePtr state = ctxt->vstate;
5201 xmlElementPtr elemDecl;
5202
5203 /*
5204 * Check the new element agaisnt the content model of the new elem.
5205 */
5206 if (state->elemDecl != NULL) {
5207 elemDecl = state->elemDecl;
5208
5209 switch(elemDecl->etype) {
5210 case XML_ELEMENT_TYPE_UNDEFINED:
5211 ret = 0;
5212 break;
5213 case XML_ELEMENT_TYPE_EMPTY:
5214 VECTXT(ctxt, state->node);
5215 VERROR(ctxt->userData,
5216 "Element %s was declared EMPTY this one has content\n",
5217 state->node->name);
5218 ret = 0;
5219 break;
5220 case XML_ELEMENT_TYPE_ANY:
5221 /* I don't think anything is required then */
5222 break;
5223 case XML_ELEMENT_TYPE_MIXED:
5224 /* simple case of declared as #PCDATA */
5225 if ((elemDecl->content != NULL) &&
5226 (elemDecl->content->type ==
5227 XML_ELEMENT_CONTENT_PCDATA)) {
5228 VECTXT(ctxt, state->node);
5229 VERROR(ctxt->userData,
5230 "Element %s was declared #PCDATA but contains non text nodes\n",
5231 state->node->name);
5232 ret = 0;
5233 } else {
5234 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5235 qname);
5236 if (ret != 1) {
5237 VECTXT(ctxt, state->node);
5238 VERROR(ctxt->userData,
5239 "Element %s is not declared in %s list of possible children\n",
5240 qname, state->node->name);
5241 }
5242 }
5243 break;
5244 case XML_ELEMENT_TYPE_ELEMENT:
5245 /*
5246 * TODO:
5247 * VC: Standalone Document Declaration
5248 * - element types with element content, if white space
5249 * occurs directly within any instance of those types.
5250 */
5251 if (state->exec != NULL) {
5252 ret = xmlRegExecPushString(state->exec, qname, NULL);
5253 if (ret < 0) {
5254 VECTXT(ctxt, state->node);
5255 VERROR(ctxt->userData,
5256 "Element %s content does not follow the DTD\nMisplaced %s\n",
5257 state->node->name, qname);
5258 ret = 0;
5259 } else {
5260 ret = 1;
5261 }
5262 }
5263 break;
5264 }
5265 }
5266 }
5267 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5268 vstateVPush(ctxt, eDecl, elem);
5269 return(ret);
5270}
5271
5272/**
5273 * xmlValidatePushCData:
5274 * @ctxt: the validation context
5275 * @data: some character data read
5276 * @len: the lenght of the data
5277 *
5278 * check the CData parsed for validation in the current stack
5279 *
5280 * returns 1 if no validation problem was found or 0 otherwise
5281 */
5282int
5283xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5284 int ret = 1;
5285
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005286/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005287 if (len <= 0)
5288 return(ret);
5289 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5290 xmlValidStatePtr state = ctxt->vstate;
5291 xmlElementPtr elemDecl;
5292
5293 /*
5294 * Check the new element agaisnt the content model of the new elem.
5295 */
5296 if (state->elemDecl != NULL) {
5297 elemDecl = state->elemDecl;
5298
5299 switch(elemDecl->etype) {
5300 case XML_ELEMENT_TYPE_UNDEFINED:
5301 ret = 0;
5302 break;
5303 case XML_ELEMENT_TYPE_EMPTY:
5304 VECTXT(ctxt, state->node);
5305 VERROR(ctxt->userData,
5306 "Element %s was declared EMPTY this one has content\n",
5307 state->node->name);
5308 ret = 0;
5309 break;
5310 case XML_ELEMENT_TYPE_ANY:
5311 break;
5312 case XML_ELEMENT_TYPE_MIXED:
5313 break;
5314 case XML_ELEMENT_TYPE_ELEMENT:
5315 if (len > 0) {
5316 int i;
5317
5318 for (i = 0;i < len;i++) {
5319 if (!IS_BLANK(data[i])) {
5320 VECTXT(ctxt, state->node);
5321 VERROR(ctxt->userData,
5322 "Element %s content does not follow the DTD\nText not allowed\n",
5323 state->node->name);
5324 ret = 0;
5325 goto done;
5326 }
5327 }
5328 /*
5329 * TODO:
5330 * VC: Standalone Document Declaration
5331 * element types with element content, if white space
5332 * occurs directly within any instance of those types.
5333 */
5334 }
5335 break;
5336 }
5337 }
5338 }
5339done:
5340 return(ret);
5341}
5342
5343/**
5344 * xmlValidatePopElement:
5345 * @ctxt: the validation context
5346 * @doc: a document instance
5347 * @elem: an element instance
5348 * @qname: the qualified name as appearing in the serialization
5349 *
5350 * Pop the element end from the validation stack.
5351 *
5352 * returns 1 if no validation problem was found or 0 otherwise
5353 */
5354int
5355xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005356 xmlNodePtr elem ATTRIBUTE_UNUSED,
5357 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005358 int ret = 1;
5359
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005360/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005361 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5362 xmlValidStatePtr state = ctxt->vstate;
5363 xmlElementPtr elemDecl;
5364
5365 /*
5366 * Check the new element agaisnt the content model of the new elem.
5367 */
5368 if (state->elemDecl != NULL) {
5369 elemDecl = state->elemDecl;
5370
5371 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5372 if (state->exec != NULL) {
5373 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5374 if (ret == 0) {
5375 VECTXT(ctxt, state->node);
5376 VERROR(ctxt->userData,
5377 "Element %s content does not follow the DTD\nExpecting more child\n",
5378 state->node->name);
5379 } else {
5380 /*
5381 * previous validation errors should not generate
5382 * a new one here
5383 */
5384 ret = 1;
5385 }
5386 }
5387 }
5388 }
5389 vstateVPop(ctxt);
5390 }
5391 return(ret);
5392}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005393#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005394
5395/**
Owen Taylor3473f882001-02-23 17:55:21 +00005396 * xmlValidateOneElement:
5397 * @ctxt: the validation context
5398 * @doc: a document instance
5399 * @elem: an element instance
5400 *
5401 * Try to validate a single element and it's attributes,
5402 * basically it does the following checks as described by the
5403 * XML-1.0 recommendation:
5404 * - [ VC: Element Valid ]
5405 * - [ VC: Required Attribute ]
5406 * Then call xmlValidateOneAttribute() for each attribute present.
5407 *
5408 * The ID/IDREF checkings are done separately
5409 *
5410 * returns 1 if valid or 0 otherwise
5411 */
5412
5413int
5414xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5415 xmlNodePtr elem) {
5416 xmlElementPtr elemDecl = NULL;
5417 xmlElementContentPtr cont;
5418 xmlAttributePtr attr;
5419 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005420 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005421 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005422 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005423
5424 CHECK_DTD;
5425
5426 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005427 switch (elem->type) {
5428 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005429 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005430 VERROR(ctxt->userData,
5431 "Attribute element not expected here\n");
5432 return(0);
5433 case XML_TEXT_NODE:
5434 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005435 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005436 VERROR(ctxt->userData, "Text element has childs !\n");
5437 return(0);
5438 }
5439 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005440 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005441 VERROR(ctxt->userData, "Text element has attributes !\n");
5442 return(0);
5443 }
5444 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005445 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005446 VERROR(ctxt->userData, "Text element has namespace !\n");
5447 return(0);
5448 }
5449 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005450 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005451 VERROR(ctxt->userData,
5452 "Text element carries namespace definitions !\n");
5453 return(0);
5454 }
5455 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005456 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005457 VERROR(ctxt->userData,
5458 "Text element has no content !\n");
5459 return(0);
5460 }
5461 return(1);
5462 case XML_XINCLUDE_START:
5463 case XML_XINCLUDE_END:
5464 return(1);
5465 case XML_CDATA_SECTION_NODE:
5466 case XML_ENTITY_REF_NODE:
5467 case XML_PI_NODE:
5468 case XML_COMMENT_NODE:
5469 return(1);
5470 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005471 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005472 VERROR(ctxt->userData,
5473 "Entity element not expected here\n");
5474 return(0);
5475 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005476 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005477 VERROR(ctxt->userData,
5478 "Notation element not expected here\n");
5479 return(0);
5480 case XML_DOCUMENT_NODE:
5481 case XML_DOCUMENT_TYPE_NODE:
5482 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005483 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005484 VERROR(ctxt->userData,
5485 "Document element not expected here\n");
5486 return(0);
5487 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005488 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005489 VERROR(ctxt->userData,
5490 "\n");
5491 return(0);
5492 case XML_ELEMENT_NODE:
5493 break;
5494 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005495 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005496 VERROR(ctxt->userData,
5497 "unknown element type %d\n", elem->type);
5498 return(0);
5499 }
Owen Taylor3473f882001-02-23 17:55:21 +00005500
5501 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005502 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005503 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005504 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5505 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005506 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005507
Daniel Veillardea7751d2002-12-20 00:16:24 +00005508 /*
5509 * If vstateNr is not zero that means continuous validation is
5510 * activated, do not try to check the content model at that level.
5511 */
5512 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005513 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005514 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005515 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005516 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005517 VERROR(ctxt->userData, "No declaration for element %s\n",
5518 elem->name);
5519 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005520 case XML_ELEMENT_TYPE_EMPTY:
5521 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005522 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005523 VERROR(ctxt->userData,
5524 "Element %s was declared EMPTY this one has content\n",
5525 elem->name);
5526 ret = 0;
5527 }
5528 break;
5529 case XML_ELEMENT_TYPE_ANY:
5530 /* I don't think anything is required then */
5531 break;
5532 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005533
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005534 /* simple case of declared as #PCDATA */
5535 if ((elemDecl->content != NULL) &&
5536 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5537 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5538 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005539 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005540 VERROR(ctxt->userData,
5541 "Element %s was declared #PCDATA but contains non text nodes\n",
5542 elem->name);
5543 }
5544 break;
5545 }
Owen Taylor3473f882001-02-23 17:55:21 +00005546 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005547 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005548 while (child != NULL) {
5549 if (child->type == XML_ELEMENT_NODE) {
5550 name = child->name;
5551 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005552 xmlChar fn[50];
5553 xmlChar *fullname;
5554
5555 fullname = xmlBuildQName(child->name, child->ns->prefix,
5556 fn, 50);
5557 if (fullname == NULL)
5558 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005559 cont = elemDecl->content;
5560 while (cont != NULL) {
5561 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005562 if (xmlStrEqual(cont->name, fullname))
5563 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005564 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5565 (cont->c1 != NULL) &&
5566 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005567 if (xmlStrEqual(cont->c1->name, fullname))
5568 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005569 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5570 (cont->c1 == NULL) ||
5571 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5572 /* Internal error !!! */
5573 xmlGenericError(xmlGenericErrorContext,
5574 "Internal: MIXED struct bad\n");
5575 break;
5576 }
5577 cont = cont->c2;
5578 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005579 if ((fullname != fn) && (fullname != child->name))
5580 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005581 if (cont != NULL)
5582 goto child_ok;
5583 }
5584 cont = elemDecl->content;
5585 while (cont != NULL) {
5586 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5587 if (xmlStrEqual(cont->name, name)) break;
5588 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5589 (cont->c1 != NULL) &&
5590 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5591 if (xmlStrEqual(cont->c1->name, name)) break;
5592 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5593 (cont->c1 == NULL) ||
5594 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5595 /* Internal error !!! */
5596 xmlGenericError(xmlGenericErrorContext,
5597 "Internal: MIXED struct bad\n");
5598 break;
5599 }
5600 cont = cont->c2;
5601 }
5602 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005603 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005604 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005605 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005606 name, elem->name);
5607 ret = 0;
5608 }
5609 }
5610child_ok:
5611 child = child->next;
5612 }
5613 break;
5614 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005615 if ((doc->standalone == 1) && (extsubset == 1)) {
5616 /*
5617 * VC: Standalone Document Declaration
5618 * - element types with element content, if white space
5619 * occurs directly within any instance of those types.
5620 */
5621 child = elem->children;
5622 while (child != NULL) {
5623 if (child->type == XML_TEXT_NODE) {
5624 const xmlChar *content = child->content;
5625
5626 while (IS_BLANK(*content))
5627 content++;
5628 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005629 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005630 VERROR(ctxt->userData,
5631"standalone: %s declared in the external subset contains white spaces nodes\n",
5632 elem->name);
5633 ret = 0;
5634 break;
5635 }
5636 }
5637 child =child->next;
5638 }
5639 }
Owen Taylor3473f882001-02-23 17:55:21 +00005640 child = elem->children;
5641 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005642 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005643 if (tmp <= 0)
5644 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005645 break;
5646 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005647 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005648
5649 /* [ VC: Required Attribute ] */
5650 attr = elemDecl->attributes;
5651 while (attr != NULL) {
5652 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005653 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005654
Daniel Veillarde4301c82002-02-13 13:32:35 +00005655 if ((attr->prefix == NULL) &&
5656 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5657 xmlNsPtr ns;
5658
5659 ns = elem->nsDef;
5660 while (ns != NULL) {
5661 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005662 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005663 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005664 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005665 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5666 xmlNsPtr ns;
5667
5668 ns = elem->nsDef;
5669 while (ns != NULL) {
5670 if (xmlStrEqual(attr->name, ns->prefix))
5671 goto found;
5672 ns = ns->next;
5673 }
5674 } else {
5675 xmlAttrPtr attrib;
5676
5677 attrib = elem->properties;
5678 while (attrib != NULL) {
5679 if (xmlStrEqual(attrib->name, attr->name)) {
5680 if (attr->prefix != NULL) {
5681 xmlNsPtr nameSpace = attrib->ns;
5682
5683 if (nameSpace == NULL)
5684 nameSpace = elem->ns;
5685 /*
5686 * qualified names handling is problematic, having a
5687 * different prefix should be possible but DTDs don't
5688 * allow to define the URI instead of the prefix :-(
5689 */
5690 if (nameSpace == NULL) {
5691 if (qualified < 0)
5692 qualified = 0;
5693 } else if (!xmlStrEqual(nameSpace->prefix,
5694 attr->prefix)) {
5695 if (qualified < 1)
5696 qualified = 1;
5697 } else
5698 goto found;
5699 } else {
5700 /*
5701 * We should allow applications to define namespaces
5702 * for their application even if the DTD doesn't
5703 * carry one, otherwise, basically we would always
5704 * break.
5705 */
5706 goto found;
5707 }
5708 }
5709 attrib = attrib->next;
5710 }
Owen Taylor3473f882001-02-23 17:55:21 +00005711 }
5712 if (qualified == -1) {
5713 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005714 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005715 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005716 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005717 elem->name, attr->name);
5718 ret = 0;
5719 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005720 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005721 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005722 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005723 elem->name, attr->prefix,attr->name);
5724 ret = 0;
5725 }
5726 } else if (qualified == 0) {
5727 VWARNING(ctxt->userData,
5728 "Element %s required attribute %s:%s has no prefix\n",
5729 elem->name, attr->prefix,attr->name);
5730 } else if (qualified == 1) {
5731 VWARNING(ctxt->userData,
5732 "Element %s required attribute %s:%s has different prefix\n",
5733 elem->name, attr->prefix,attr->name);
5734 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005735 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5736 /*
5737 * Special tests checking #FIXED namespace declarations
5738 * have the right value since this is not done as an
5739 * attribute checking
5740 */
5741 if ((attr->prefix == NULL) &&
5742 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5743 xmlNsPtr ns;
5744
5745 ns = elem->nsDef;
5746 while (ns != NULL) {
5747 if (ns->prefix == NULL) {
5748 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005749 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005750 VERROR(ctxt->userData,
5751 "Element %s namespace name for default namespace does not match the DTD\n",
5752 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005753 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005754 }
5755 goto found;
5756 }
5757 ns = ns->next;
5758 }
5759 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5760 xmlNsPtr ns;
5761
5762 ns = elem->nsDef;
5763 while (ns != NULL) {
5764 if (xmlStrEqual(attr->name, ns->prefix)) {
5765 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005766 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005767 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005768 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005769 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005770 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005771 }
5772 goto found;
5773 }
5774 ns = ns->next;
5775 }
5776 }
Owen Taylor3473f882001-02-23 17:55:21 +00005777 }
5778found:
5779 attr = attr->nexth;
5780 }
5781 return(ret);
5782}
5783
5784/**
5785 * xmlValidateRoot:
5786 * @ctxt: the validation context
5787 * @doc: a document instance
5788 *
5789 * Try to validate a the root element
5790 * basically it does the following check as described by the
5791 * XML-1.0 recommendation:
5792 * - [ VC: Root Element Type ]
5793 * it doesn't try to recurse or apply other check to the element
5794 *
5795 * returns 1 if valid or 0 otherwise
5796 */
5797
5798int
5799xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5800 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00005801 int ret;
5802
Owen Taylor3473f882001-02-23 17:55:21 +00005803 if (doc == NULL) return(0);
5804
5805 root = xmlDocGetRootElement(doc);
5806 if ((root == NULL) || (root->name == NULL)) {
5807 VERROR(ctxt->userData, "Not valid: no root element\n");
5808 return(0);
5809 }
5810
5811 /*
5812 * When doing post validation against a separate DTD, those may
5813 * no internal subset has been generated
5814 */
5815 if ((doc->intSubset != NULL) &&
5816 (doc->intSubset->name != NULL)) {
5817 /*
5818 * Check first the document root against the NQName
5819 */
5820 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5821 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005822 xmlChar fn[50];
5823 xmlChar *fullname;
5824
5825 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
5826 if (fullname == NULL) {
5827 VERROR(ctxt->userData, "Out of memory\n");
5828 return(0);
5829 }
5830 ret = xmlStrEqual(doc->intSubset->name, fullname);
5831 if ((fullname != fn) && (fullname != root->name))
5832 xmlFree(fullname);
5833 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00005834 goto name_ok;
5835 }
5836 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5837 (xmlStrEqual(root->name, BAD_CAST "html")))
5838 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005839 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005840 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005841 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005842 root->name, doc->intSubset->name);
5843 return(0);
5844
5845 }
5846 }
5847name_ok:
5848 return(1);
5849}
5850
5851
5852/**
5853 * xmlValidateElement:
5854 * @ctxt: the validation context
5855 * @doc: a document instance
5856 * @elem: an element instance
5857 *
5858 * Try to validate the subtree under an element
5859 *
5860 * returns 1 if valid or 0 otherwise
5861 */
5862
5863int
5864xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5865 xmlNodePtr child;
5866 xmlAttrPtr attr;
5867 xmlChar *value;
5868 int ret = 1;
5869
5870 if (elem == NULL) return(0);
5871
5872 /*
5873 * XInclude elements were added after parsing in the infoset,
5874 * they don't really mean anything validation wise.
5875 */
5876 if ((elem->type == XML_XINCLUDE_START) ||
5877 (elem->type == XML_XINCLUDE_END))
5878 return(1);
5879
5880 CHECK_DTD;
5881
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005882 /*
5883 * Entities references have to be handled separately
5884 */
5885 if (elem->type == XML_ENTITY_REF_NODE) {
5886 return(1);
5887 }
5888
Owen Taylor3473f882001-02-23 17:55:21 +00005889 ret &= xmlValidateOneElement(ctxt, doc, elem);
5890 attr = elem->properties;
5891 while(attr != NULL) {
5892 value = xmlNodeListGetString(doc, attr->children, 0);
5893 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5894 if (value != NULL)
5895 xmlFree(value);
5896 attr= attr->next;
5897 }
5898 child = elem->children;
5899 while (child != NULL) {
5900 ret &= xmlValidateElement(ctxt, doc, child);
5901 child = child->next;
5902 }
5903
5904 return(ret);
5905}
5906
Daniel Veillard8730c562001-02-26 10:49:57 +00005907/**
5908 * xmlValidateRef:
5909 * @ref: A reference to be validated
5910 * @ctxt: Validation context
5911 * @name: Name of ID we are searching for
5912 *
5913 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005914static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005915xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005916 const xmlChar *name) {
5917 xmlAttrPtr id;
5918 xmlAttrPtr attr;
5919
5920 if (ref == NULL)
5921 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005922 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00005923 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005924 attr = ref->attr;
5925 if (attr == NULL) {
5926 xmlChar *dup, *str = NULL, *cur, save;
5927
5928 dup = xmlStrdup(name);
5929 if (dup == NULL) {
5930 ctxt->valid = 0;
5931 return;
5932 }
5933 cur = dup;
5934 while (*cur != 0) {
5935 str = cur;
5936 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5937 save = *cur;
5938 *cur = 0;
5939 id = xmlGetID(ctxt->doc, str);
5940 if (id == NULL) {
5941 VERROR(ctxt->userData,
5942 "attribute %s line %d references an unknown ID \"%s\"\n",
5943 ref->name, ref->lineno, str);
5944 ctxt->valid = 0;
5945 }
5946 if (save == 0)
5947 break;
5948 *cur = save;
5949 while (IS_BLANK(*cur)) cur++;
5950 }
5951 xmlFree(dup);
5952 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00005953 id = xmlGetID(ctxt->doc, name);
5954 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005955 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005956 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005957 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005958 attr->name, name);
5959 ctxt->valid = 0;
5960 }
5961 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5962 xmlChar *dup, *str = NULL, *cur, save;
5963
5964 dup = xmlStrdup(name);
5965 if (dup == NULL) {
5966 ctxt->valid = 0;
5967 return;
5968 }
5969 cur = dup;
5970 while (*cur != 0) {
5971 str = cur;
5972 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5973 save = *cur;
5974 *cur = 0;
5975 id = xmlGetID(ctxt->doc, str);
5976 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005977 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005978 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005979 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005980 attr->name, str);
5981 ctxt->valid = 0;
5982 }
5983 if (save == 0)
5984 break;
5985 *cur = save;
5986 while (IS_BLANK(*cur)) cur++;
5987 }
5988 xmlFree(dup);
5989 }
5990}
5991
5992/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005993 * xmlWalkValidateList:
5994 * @data: Contents of current link
5995 * @user: Value supplied by the user
5996 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005997 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005998 */
5999static int
6000xmlWalkValidateList(const void *data, const void *user)
6001{
6002 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
6003 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
6004 return 1;
6005}
6006
6007/**
6008 * xmlValidateCheckRefCallback:
6009 * @ref_list: List of references
6010 * @ctxt: Validation context
6011 * @name: Name of ID we are searching for
6012 *
6013 */
6014static void
6015xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
6016 const xmlChar *name) {
6017 xmlValidateMemo memo;
6018
6019 if (ref_list == NULL)
6020 return;
6021 memo.ctxt = ctxt;
6022 memo.name = name;
6023
6024 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
6025
6026}
6027
6028/**
Owen Taylor3473f882001-02-23 17:55:21 +00006029 * xmlValidateDocumentFinal:
6030 * @ctxt: the validation context
6031 * @doc: a document instance
6032 *
6033 * Does the final step for the document validation once all the
6034 * incremental validation steps have been completed
6035 *
6036 * basically it does the following checks described by the XML Rec
6037 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006038 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00006039 *
6040 * returns 1 if valid or 0 otherwise
6041 */
6042
6043int
6044xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6045 xmlRefTablePtr table;
6046
6047 if (doc == NULL) {
6048 xmlGenericError(xmlGenericErrorContext,
6049 "xmlValidateDocumentFinal: doc == NULL\n");
6050 return(0);
6051 }
6052
6053 /*
6054 * Check all the NOTATION/NOTATIONS attributes
6055 */
6056 /*
6057 * Check all the ENTITY/ENTITIES attributes definition for validity
6058 */
6059 /*
6060 * Check all the IDREF/IDREFS attributes definition for validity
6061 */
6062 table = (xmlRefTablePtr) doc->refs;
6063 ctxt->doc = doc;
6064 ctxt->valid = 1;
6065 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6066 return(ctxt->valid);
6067}
6068
6069/**
6070 * xmlValidateDtd:
6071 * @ctxt: the validation context
6072 * @doc: a document instance
6073 * @dtd: a dtd instance
6074 *
6075 * Try to validate the document against the dtd instance
6076 *
6077 * basically it does check all the definitions in the DtD.
6078 *
6079 * returns 1 if valid or 0 otherwise
6080 */
6081
6082int
6083xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6084 int ret;
6085 xmlDtdPtr oldExt;
6086 xmlNodePtr root;
6087
6088 if (dtd == NULL) return(0);
6089 if (doc == NULL) return(0);
6090 oldExt = doc->extSubset;
6091 doc->extSubset = dtd;
6092 ret = xmlValidateRoot(ctxt, doc);
6093 if (ret == 0) {
6094 doc->extSubset = oldExt;
6095 return(ret);
6096 }
6097 if (doc->ids != NULL) {
6098 xmlFreeIDTable(doc->ids);
6099 doc->ids = NULL;
6100 }
6101 if (doc->refs != NULL) {
6102 xmlFreeRefTable(doc->refs);
6103 doc->refs = NULL;
6104 }
6105 root = xmlDocGetRootElement(doc);
6106 ret = xmlValidateElement(ctxt, doc, root);
6107 ret &= xmlValidateDocumentFinal(ctxt, doc);
6108 doc->extSubset = oldExt;
6109 return(ret);
6110}
6111
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006112static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006113xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6114 const xmlChar *name ATTRIBUTE_UNUSED) {
6115 if (cur == NULL)
6116 return;
6117 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6118 xmlChar *notation = cur->content;
6119
Daniel Veillard878eab02002-02-19 13:46:09 +00006120 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006121 int ret;
6122
6123 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6124 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006125 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006126 }
6127 }
6128 }
6129}
6130
6131static void
Owen Taylor3473f882001-02-23 17:55:21 +00006132xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006133 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006134 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006135 xmlDocPtr doc;
6136 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006137
Owen Taylor3473f882001-02-23 17:55:21 +00006138 if (cur == NULL)
6139 return;
6140 switch (cur->atype) {
6141 case XML_ATTRIBUTE_CDATA:
6142 case XML_ATTRIBUTE_ID:
6143 case XML_ATTRIBUTE_IDREF :
6144 case XML_ATTRIBUTE_IDREFS:
6145 case XML_ATTRIBUTE_NMTOKEN:
6146 case XML_ATTRIBUTE_NMTOKENS:
6147 case XML_ATTRIBUTE_ENUMERATION:
6148 break;
6149 case XML_ATTRIBUTE_ENTITY:
6150 case XML_ATTRIBUTE_ENTITIES:
6151 case XML_ATTRIBUTE_NOTATION:
6152 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006153
6154 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6155 cur->atype, cur->defaultValue);
6156 if ((ret == 0) && (ctxt->valid == 1))
6157 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006158 }
6159 if (cur->tree != NULL) {
6160 xmlEnumerationPtr tree = cur->tree;
6161 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006162 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006163 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006164 if ((ret == 0) && (ctxt->valid == 1))
6165 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006166 tree = tree->next;
6167 }
6168 }
6169 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006170 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6171 doc = cur->doc;
6172 if ((doc == NULL) || (cur->elem == NULL)) {
6173 VERROR(ctxt->userData,
6174 "xmlValidateAttributeCallback(%s): internal error\n",
6175 cur->name);
6176 return;
6177 }
6178 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6179 if (elem == NULL)
6180 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6181 if (elem == NULL) {
6182 VERROR(ctxt->userData,
6183 "attribute %s: could not find decl for element %s\n",
6184 cur->name, cur->elem);
6185 return;
6186 }
6187 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6188 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006189 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00006190 cur->name, cur->elem);
6191 ctxt->valid = 0;
6192 }
6193 }
Owen Taylor3473f882001-02-23 17:55:21 +00006194}
6195
6196/**
6197 * xmlValidateDtdFinal:
6198 * @ctxt: the validation context
6199 * @doc: a document instance
6200 *
6201 * Does the final step for the dtds validation once all the
6202 * subsets have been parsed
6203 *
6204 * basically it does the following checks described by the XML Rec
6205 * - check that ENTITY and ENTITIES type attributes default or
6206 * possible values matches one of the defined entities.
6207 * - check that NOTATION type attributes default or
6208 * possible values matches one of the defined notations.
6209 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006210 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006211 */
6212
6213int
6214xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006215 xmlDtdPtr dtd;
6216 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006217 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006218
6219 if (doc == NULL) return(0);
6220 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6221 return(0);
6222 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006223 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006224 dtd = doc->intSubset;
6225 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6226 table = (xmlAttributeTablePtr) dtd->attributes;
6227 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006228 }
6229 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006230 entities = (xmlEntitiesTablePtr) dtd->entities;
6231 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6232 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006233 }
6234 dtd = doc->extSubset;
6235 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6236 table = (xmlAttributeTablePtr) dtd->attributes;
6237 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006238 }
6239 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006240 entities = (xmlEntitiesTablePtr) dtd->entities;
6241 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6242 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006243 }
6244 return(ctxt->valid);
6245}
6246
6247/**
6248 * xmlValidateDocument:
6249 * @ctxt: the validation context
6250 * @doc: a document instance
6251 *
6252 * Try to validate the document instance
6253 *
6254 * basically it does the all the checks described by the XML Rec
6255 * i.e. validates the internal and external subset (if present)
6256 * and validate the document tree.
6257 *
6258 * returns 1 if valid or 0 otherwise
6259 */
6260
6261int
6262xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6263 int ret;
6264 xmlNodePtr root;
6265
Daniel Veillard2fd85422002-10-16 14:32:41 +00006266 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6267 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00006268 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006269 }
Owen Taylor3473f882001-02-23 17:55:21 +00006270 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6271 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6272 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6273 doc->intSubset->SystemID);
6274 if (doc->extSubset == NULL) {
6275 if (doc->intSubset->SystemID != NULL) {
6276 VERROR(ctxt->userData,
6277 "Could not load the external subset \"%s\"\n",
6278 doc->intSubset->SystemID);
6279 } else {
6280 VERROR(ctxt->userData,
6281 "Could not load the external subset \"%s\"\n",
6282 doc->intSubset->ExternalID);
6283 }
6284 return(0);
6285 }
6286 }
6287
6288 if (doc->ids != NULL) {
6289 xmlFreeIDTable(doc->ids);
6290 doc->ids = NULL;
6291 }
6292 if (doc->refs != NULL) {
6293 xmlFreeRefTable(doc->refs);
6294 doc->refs = NULL;
6295 }
6296 ret = xmlValidateDtdFinal(ctxt, doc);
6297 if (!xmlValidateRoot(ctxt, doc)) return(0);
6298
6299 root = xmlDocGetRootElement(doc);
6300 ret &= xmlValidateElement(ctxt, doc, root);
6301 ret &= xmlValidateDocumentFinal(ctxt, doc);
6302 return(ret);
6303}
6304
6305
6306/************************************************************************
6307 * *
6308 * Routines for dynamic validation editing *
6309 * *
6310 ************************************************************************/
6311
6312/**
6313 * xmlValidGetPotentialChildren:
6314 * @ctree: an element content tree
6315 * @list: an array to store the list of child names
6316 * @len: a pointer to the number of element in the list
6317 * @max: the size of the array
6318 *
6319 * Build/extend a list of potential children allowed by the content tree
6320 *
6321 * returns the number of element in the list, or -1 in case of error.
6322 */
6323
6324int
6325xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6326 int *len, int max) {
6327 int i;
6328
6329 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6330 return(-1);
6331 if (*len >= max) return(*len);
6332
6333 switch (ctree->type) {
6334 case XML_ELEMENT_CONTENT_PCDATA:
6335 for (i = 0; i < *len;i++)
6336 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6337 list[(*len)++] = BAD_CAST "#PCDATA";
6338 break;
6339 case XML_ELEMENT_CONTENT_ELEMENT:
6340 for (i = 0; i < *len;i++)
6341 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6342 list[(*len)++] = ctree->name;
6343 break;
6344 case XML_ELEMENT_CONTENT_SEQ:
6345 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6346 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6347 break;
6348 case XML_ELEMENT_CONTENT_OR:
6349 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6350 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6351 break;
6352 }
6353
6354 return(*len);
6355}
6356
6357/**
6358 * xmlValidGetValidElements:
6359 * @prev: an element to insert after
6360 * @next: an element to insert next
6361 * @list: an array to store the list of child names
6362 * @max: the size of the array
6363 *
6364 * This function returns the list of authorized children to insert
6365 * within an existing tree while respecting the validity constraints
6366 * forced by the Dtd. The insertion point is defined using @prev and
6367 * @next in the following ways:
6368 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6369 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6370 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6371 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6372 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6373 *
6374 * pointers to the element names are inserted at the beginning of the array
6375 * and do not need to be freed.
6376 *
6377 * returns the number of element in the list, or -1 in case of error. If
6378 * the function returns the value @max the caller is invited to grow the
6379 * receiving array and retry.
6380 */
6381
6382int
6383xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6384 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006385 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006386 int nb_valid_elements = 0;
6387 const xmlChar *elements[256];
6388 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006389 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006390
6391 xmlNode *ref_node;
6392 xmlNode *parent;
6393 xmlNode *test_node;
6394
6395 xmlNode *prev_next;
6396 xmlNode *next_prev;
6397 xmlNode *parent_childs;
6398 xmlNode *parent_last;
6399
6400 xmlElement *element_desc;
6401
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006402 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006403
Owen Taylor3473f882001-02-23 17:55:21 +00006404 if (prev == NULL && next == NULL)
6405 return(-1);
6406
6407 if (list == NULL) return(-1);
6408 if (max <= 0) return(-1);
6409
6410 nb_valid_elements = 0;
6411 ref_node = prev ? prev : next;
6412 parent = ref_node->parent;
6413
6414 /*
6415 * Retrieves the parent element declaration
6416 */
6417 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6418 parent->name);
6419 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6420 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6421 parent->name);
6422 if (element_desc == NULL) return(-1);
6423
6424 /*
6425 * Do a backup of the current tree structure
6426 */
6427 prev_next = prev ? prev->next : NULL;
6428 next_prev = next ? next->prev : NULL;
6429 parent_childs = parent->children;
6430 parent_last = parent->last;
6431
6432 /*
6433 * Creates a dummy node and insert it into the tree
6434 */
6435 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6436 test_node->doc = ref_node->doc;
6437 test_node->parent = parent;
6438 test_node->prev = prev;
6439 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006440 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006441
6442 if (prev) prev->next = test_node;
6443 else parent->children = test_node;
6444
6445 if (next) next->prev = test_node;
6446 else parent->last = test_node;
6447
6448 /*
6449 * Insert each potential child node and check if the parent is
6450 * still valid
6451 */
6452 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6453 elements, &nb_elements, 256);
6454
6455 for (i = 0;i < nb_elements;i++) {
6456 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006457 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006458 int j;
6459
6460 for (j = 0; j < nb_valid_elements;j++)
6461 if (xmlStrEqual(elements[i], list[j])) break;
6462 list[nb_valid_elements++] = elements[i];
6463 if (nb_valid_elements >= max) break;
6464 }
6465 }
6466
6467 /*
6468 * Restore the tree structure
6469 */
6470 if (prev) prev->next = prev_next;
6471 if (next) next->prev = next_prev;
6472 parent->children = parent_childs;
6473 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006474
6475 /*
6476 * Free up the dummy node
6477 */
6478 test_node->name = name;
6479 xmlFreeNode(test_node);
6480
Owen Taylor3473f882001-02-23 17:55:21 +00006481 return(nb_valid_elements);
6482}