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