blob: b99e8c5a8107dc1a05918e29980f614320bc824b [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) {
627 xmlAutomataStatePtr start;
628
629 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000630 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000631 if (elem->type != XML_ELEMENT_DECL)
632 return(0);
633 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
634 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000635 /* TODO: should we rebuild in this case ? */
Daniel Veillardec498e12003-02-05 11:01:50 +0000636 if (elem->contModel != NULL) {
637 if (!xmlRegexpIsDeterminist(elem->contModel)) {
638 ctxt->valid = 0;
639 return(0);
640 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000641 return(1);
Daniel Veillardec498e12003-02-05 11:01:50 +0000642 }
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000643
644 ctxt->am = xmlNewAutomata();
645 if (ctxt->am == NULL) {
646 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
647 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000648 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000649 }
650 start = ctxt->state = xmlAutomataGetInitState(ctxt->am);
651 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
652 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000653 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000654 if (xmlRegexpIsDeterminist(elem->contModel) != 1) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000655 char expr[5000];
656 expr[0] = 0;
657 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
658 VERROR(ctxt->userData, "Content model of %s is not determinist: %s\n",
659 elem->name, expr);
660#ifdef DEBUG_REGEXP_ALGO
661 xmlRegexpPrint(stderr, elem->contModel);
662#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000663 ctxt->valid = 0;
Daniel Veillardec498e12003-02-05 11:01:50 +0000664 ctxt->state = NULL;
665 xmlFreeAutomata(ctxt->am);
666 ctxt->am = NULL;
667 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000668 }
669 ctxt->state = NULL;
670 xmlFreeAutomata(ctxt->am);
671 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000672 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000673}
674
675#endif /* LIBXML_REGEXP_ENABLED */
676
Owen Taylor3473f882001-02-23 17:55:21 +0000677/****************************************************************
678 * *
679 * Util functions for data allocation/deallocation *
680 * *
681 ****************************************************************/
682
683/**
Daniel Veillarda37aab82003-06-09 09:10:36 +0000684 * xmlNewValidCtxt:
685 *
686 * Allocate a validation context structure.
687 *
688 * Returns NULL if not, otherwise the new validation context structure
689 */
690xmlValidCtxtPtr
691xmlNewValidCtxt(void) {
692 xmlValidCtxtPtr ret;
693
694 if ((ret = xmlMalloc(sizeof (xmlValidCtxt))) == NULL)
695 return (NULL);
696
697 (void) memset(ret, 0, sizeof (xmlValidCtxt));
698
699 return (ret);
700}
701
702/**
703 * xmlFreeValidCtxt:
704 * @cur: the validation context to free
705 *
706 * Free a validation context structure.
707 */
708void
709xmlFreeValidCtxt(xmlValidCtxtPtr cur) {
710 xmlFree(cur);
711}
712
713/**
Owen Taylor3473f882001-02-23 17:55:21 +0000714 * xmlNewElementContent:
715 * @name: the subelement name or NULL
716 * @type: the type of element content decl
717 *
718 * Allocate an element content structure.
719 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000720 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000721 */
722xmlElementContentPtr
723xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
724 xmlElementContentPtr ret;
725
726 switch(type) {
727 case XML_ELEMENT_CONTENT_ELEMENT:
728 if (name == NULL) {
729 xmlGenericError(xmlGenericErrorContext,
730 "xmlNewElementContent : name == NULL !\n");
731 }
732 break;
733 case XML_ELEMENT_CONTENT_PCDATA:
734 case XML_ELEMENT_CONTENT_SEQ:
735 case XML_ELEMENT_CONTENT_OR:
736 if (name != NULL) {
737 xmlGenericError(xmlGenericErrorContext,
738 "xmlNewElementContent : name != NULL !\n");
739 }
740 break;
741 default:
742 xmlGenericError(xmlGenericErrorContext,
743 "xmlNewElementContent: unknown type %d\n", type);
744 return(NULL);
745 }
746 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
747 if (ret == NULL) {
748 xmlGenericError(xmlGenericErrorContext,
749 "xmlNewElementContent : out of memory!\n");
750 return(NULL);
751 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000752 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000753 ret->type = type;
754 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000755 if (name != NULL) {
756 xmlChar *prefix = NULL;
757 ret->name = xmlSplitQName2(name, &prefix);
758 if (ret->name == NULL)
759 ret->name = xmlStrdup(name);
760 ret->prefix = prefix;
761 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000762 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000763 ret->prefix = NULL;
764 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000765 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000766 return(ret);
767}
768
769/**
770 * xmlCopyElementContent:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000771 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +0000772 *
773 * Build a copy of an element content description.
774 *
775 * Returns the new xmlElementContentPtr or NULL in case of error.
776 */
777xmlElementContentPtr
778xmlCopyElementContent(xmlElementContentPtr cur) {
779 xmlElementContentPtr ret;
780
781 if (cur == NULL) return(NULL);
782 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
783 if (ret == NULL) {
784 xmlGenericError(xmlGenericErrorContext,
785 "xmlCopyElementContent : out of memory\n");
786 return(NULL);
787 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000788 if (cur->prefix != NULL)
789 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000790 ret->ocur = cur->ocur;
791 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000792 if (ret->c1 != NULL)
793 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000794 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000795 if (ret->c2 != NULL)
796 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000797 return(ret);
798}
799
800/**
801 * xmlFreeElementContent:
802 * @cur: the element content tree to free
803 *
804 * Free an element content structure. This is a recursive call !
805 */
806void
807xmlFreeElementContent(xmlElementContentPtr cur) {
808 if (cur == NULL) return;
809 switch (cur->type) {
810 case XML_ELEMENT_CONTENT_PCDATA:
811 case XML_ELEMENT_CONTENT_ELEMENT:
812 case XML_ELEMENT_CONTENT_SEQ:
813 case XML_ELEMENT_CONTENT_OR:
814 break;
815 default:
816 xmlGenericError(xmlGenericErrorContext,
817 "xmlFreeElementContent : type %d\n", cur->type);
818 return;
819 }
820 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
821 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
822 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000823 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000824 xmlFree(cur);
825}
826
827/**
828 * xmlDumpElementContent:
829 * @buf: An XML buffer
830 * @content: An element table
831 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
832 *
833 * This will dump the content of the element table as an XML DTD definition
834 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000835static void
Owen Taylor3473f882001-02-23 17:55:21 +0000836xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
837 if (content == NULL) return;
838
839 if (glob) xmlBufferWriteChar(buf, "(");
840 switch (content->type) {
841 case XML_ELEMENT_CONTENT_PCDATA:
842 xmlBufferWriteChar(buf, "#PCDATA");
843 break;
844 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000845 if (content->prefix != NULL) {
846 xmlBufferWriteCHAR(buf, content->prefix);
847 xmlBufferWriteChar(buf, ":");
848 }
Owen Taylor3473f882001-02-23 17:55:21 +0000849 xmlBufferWriteCHAR(buf, content->name);
850 break;
851 case XML_ELEMENT_CONTENT_SEQ:
852 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
853 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
854 xmlDumpElementContent(buf, content->c1, 1);
855 else
856 xmlDumpElementContent(buf, content->c1, 0);
857 xmlBufferWriteChar(buf, " , ");
858 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
859 xmlDumpElementContent(buf, content->c2, 1);
860 else
861 xmlDumpElementContent(buf, content->c2, 0);
862 break;
863 case XML_ELEMENT_CONTENT_OR:
864 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
865 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
866 xmlDumpElementContent(buf, content->c1, 1);
867 else
868 xmlDumpElementContent(buf, content->c1, 0);
869 xmlBufferWriteChar(buf, " | ");
870 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
871 xmlDumpElementContent(buf, content->c2, 1);
872 else
873 xmlDumpElementContent(buf, content->c2, 0);
874 break;
875 default:
876 xmlGenericError(xmlGenericErrorContext,
877 "xmlDumpElementContent: unknown type %d\n",
878 content->type);
879 }
880 if (glob)
881 xmlBufferWriteChar(buf, ")");
882 switch (content->ocur) {
883 case XML_ELEMENT_CONTENT_ONCE:
884 break;
885 case XML_ELEMENT_CONTENT_OPT:
886 xmlBufferWriteChar(buf, "?");
887 break;
888 case XML_ELEMENT_CONTENT_MULT:
889 xmlBufferWriteChar(buf, "*");
890 break;
891 case XML_ELEMENT_CONTENT_PLUS:
892 xmlBufferWriteChar(buf, "+");
893 break;
894 }
895}
896
897/**
898 * xmlSprintfElementContent:
899 * @buf: an output buffer
900 * @content: An element table
901 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
902 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000903 * Deprecated, unsafe, use xmlSnprintfElementContent
904 */
905void
906xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
907 xmlElementContentPtr content ATTRIBUTE_UNUSED,
908 int glob ATTRIBUTE_UNUSED) {
909}
910
911/**
912 * xmlSnprintfElementContent:
913 * @buf: an output buffer
914 * @size: the buffer size
915 * @content: An element table
916 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
917 *
Owen Taylor3473f882001-02-23 17:55:21 +0000918 * This will dump the content of the element content definition
919 * Intended just for the debug routine
920 */
921void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000922xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
923 int len;
924
Owen Taylor3473f882001-02-23 17:55:21 +0000925 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000926 len = strlen(buf);
927 if (size - len < 50) {
928 if ((size - len > 4) && (buf[len - 1] != '.'))
929 strcat(buf, " ...");
930 return;
931 }
Owen Taylor3473f882001-02-23 17:55:21 +0000932 if (glob) strcat(buf, "(");
933 switch (content->type) {
934 case XML_ELEMENT_CONTENT_PCDATA:
935 strcat(buf, "#PCDATA");
936 break;
937 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000938 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000939 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000940 strcat(buf, " ...");
941 return;
942 }
943 strcat(buf, (char *) content->prefix);
944 strcat(buf, ":");
945 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000946 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000947 strcat(buf, " ...");
948 return;
949 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000950 if (content->name != NULL)
951 strcat(buf, (char *) content->name);
Owen Taylor3473f882001-02-23 17:55:21 +0000952 break;
953 case XML_ELEMENT_CONTENT_SEQ:
954 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
955 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000956 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000957 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000958 xmlSnprintfElementContent(buf, size, content->c1, 0);
959 len = strlen(buf);
960 if (size - len < 50) {
961 if ((size - len > 4) && (buf[len - 1] != '.'))
962 strcat(buf, " ...");
963 return;
964 }
Owen Taylor3473f882001-02-23 17:55:21 +0000965 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000966 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
967 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
968 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000969 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000970 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000971 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000972 break;
973 case XML_ELEMENT_CONTENT_OR:
974 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
975 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000976 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000977 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000978 xmlSnprintfElementContent(buf, size, content->c1, 0);
979 len = strlen(buf);
980 if (size - len < 50) {
981 if ((size - len > 4) && (buf[len - 1] != '.'))
982 strcat(buf, " ...");
983 return;
984 }
Owen Taylor3473f882001-02-23 17:55:21 +0000985 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000986 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
987 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
988 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000989 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000990 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000991 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000992 break;
993 }
994 if (glob)
995 strcat(buf, ")");
996 switch (content->ocur) {
997 case XML_ELEMENT_CONTENT_ONCE:
998 break;
999 case XML_ELEMENT_CONTENT_OPT:
1000 strcat(buf, "?");
1001 break;
1002 case XML_ELEMENT_CONTENT_MULT:
1003 strcat(buf, "*");
1004 break;
1005 case XML_ELEMENT_CONTENT_PLUS:
1006 strcat(buf, "+");
1007 break;
1008 }
1009}
1010
1011/****************************************************************
1012 * *
1013 * Registration of DTD declarations *
1014 * *
1015 ****************************************************************/
1016
1017/**
1018 * xmlCreateElementTable:
1019 *
1020 * create and initialize an empty element hash table.
1021 *
1022 * Returns the xmlElementTablePtr just created or NULL in case of error.
1023 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001024static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001025xmlCreateElementTable(void) {
1026 return(xmlHashCreate(0));
1027}
1028
1029/**
1030 * xmlFreeElement:
1031 * @elem: An element
1032 *
1033 * Deallocate the memory used by an element definition
1034 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001035static void
Owen Taylor3473f882001-02-23 17:55:21 +00001036xmlFreeElement(xmlElementPtr elem) {
1037 if (elem == NULL) return;
1038 xmlUnlinkNode((xmlNodePtr) elem);
1039 xmlFreeElementContent(elem->content);
1040 if (elem->name != NULL)
1041 xmlFree((xmlChar *) elem->name);
1042 if (elem->prefix != NULL)
1043 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001044#ifdef LIBXML_REGEXP_ENABLED
1045 if (elem->contModel != NULL)
1046 xmlRegFreeRegexp(elem->contModel);
1047#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001048 xmlFree(elem);
1049}
1050
1051
1052/**
1053 * xmlAddElementDecl:
1054 * @ctxt: the validation context
1055 * @dtd: pointer to the DTD
1056 * @name: the entity name
1057 * @type: the element type
1058 * @content: the element content tree or NULL
1059 *
1060 * Register a new element declaration
1061 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001062 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001063 */
1064xmlElementPtr
1065xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
1066 xmlElementTypeVal type,
1067 xmlElementContentPtr content) {
1068 xmlElementPtr ret;
1069 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001070 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001071 xmlChar *ns, *uqname;
1072
1073 if (dtd == NULL) {
1074 xmlGenericError(xmlGenericErrorContext,
1075 "xmlAddElementDecl: dtd == NULL\n");
1076 return(NULL);
1077 }
1078 if (name == NULL) {
1079 xmlGenericError(xmlGenericErrorContext,
1080 "xmlAddElementDecl: name == NULL\n");
1081 return(NULL);
1082 }
1083 switch (type) {
1084 case XML_ELEMENT_TYPE_EMPTY:
1085 if (content != NULL) {
1086 xmlGenericError(xmlGenericErrorContext,
1087 "xmlAddElementDecl: content != NULL for EMPTY\n");
1088 return(NULL);
1089 }
1090 break;
1091 case XML_ELEMENT_TYPE_ANY:
1092 if (content != NULL) {
1093 xmlGenericError(xmlGenericErrorContext,
1094 "xmlAddElementDecl: content != NULL for ANY\n");
1095 return(NULL);
1096 }
1097 break;
1098 case XML_ELEMENT_TYPE_MIXED:
1099 if (content == NULL) {
1100 xmlGenericError(xmlGenericErrorContext,
1101 "xmlAddElementDecl: content == NULL for MIXED\n");
1102 return(NULL);
1103 }
1104 break;
1105 case XML_ELEMENT_TYPE_ELEMENT:
1106 if (content == NULL) {
1107 xmlGenericError(xmlGenericErrorContext,
1108 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1109 return(NULL);
1110 }
1111 break;
1112 default:
1113 xmlGenericError(xmlGenericErrorContext,
1114 "xmlAddElementDecl: unknown type %d\n", type);
1115 return(NULL);
1116 }
1117
1118 /*
1119 * check if name is a QName
1120 */
1121 uqname = xmlSplitQName2(name, &ns);
1122 if (uqname != NULL)
1123 name = uqname;
1124
1125 /*
1126 * Create the Element table if needed.
1127 */
1128 table = (xmlElementTablePtr) dtd->elements;
1129 if (table == NULL) {
1130 table = xmlCreateElementTable();
1131 dtd->elements = (void *) table;
1132 }
1133 if (table == NULL) {
1134 xmlGenericError(xmlGenericErrorContext,
1135 "xmlAddElementDecl: Table creation failed!\n");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001136 if (uqname != NULL)
1137 xmlFree(uqname);
1138 if (ns != NULL)
1139 xmlFree(ns);
Owen Taylor3473f882001-02-23 17:55:21 +00001140 return(NULL);
1141 }
1142
Daniel Veillarda10efa82001-04-18 13:09:01 +00001143 /*
1144 * lookup old attributes inserted on an undefined element in the
1145 * internal subset.
1146 */
1147 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1148 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1149 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1150 oldAttributes = ret->attributes;
1151 ret->attributes = NULL;
1152 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1153 xmlFreeElement(ret);
1154 }
Owen Taylor3473f882001-02-23 17:55:21 +00001155 }
Owen Taylor3473f882001-02-23 17:55:21 +00001156
1157 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001158 * The element may already be present if one of its attribute
1159 * was registered first
1160 */
1161 ret = xmlHashLookup2(table, name, ns);
1162 if (ret != NULL) {
1163 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1164 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001165 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001166 */
1167 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1168 if (uqname != NULL)
1169 xmlFree(uqname);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001170 if (ns != NULL)
1171 xmlFree(ns);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001172 return(NULL);
1173 }
1174 } else {
1175 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1176 if (ret == NULL) {
1177 xmlGenericError(xmlGenericErrorContext,
1178 "xmlAddElementDecl: out of memory\n");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001179 if (uqname != NULL)
1180 xmlFree(uqname);
1181 if (ns != NULL)
1182 xmlFree(ns);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001183 return(NULL);
1184 }
1185 memset(ret, 0, sizeof(xmlElement));
1186 ret->type = XML_ELEMENT_DECL;
1187
1188 /*
1189 * fill the structure.
1190 */
1191 ret->name = xmlStrdup(name);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001192 if (ret->name == NULL) {
1193 xmlGenericError(xmlGenericErrorContext,
1194 "xmlAddElementDecl: out of memory\n");
1195 if (uqname != NULL)
1196 xmlFree(uqname);
1197 if (ns != NULL)
1198 xmlFree(ns);
1199 xmlFree(ret);
1200 return(NULL);
1201 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00001202 ret->prefix = ns;
1203
1204 /*
1205 * Validity Check:
1206 * Insertion must not fail
1207 */
1208 if (xmlHashAddEntry2(table, name, ns, ret)) {
1209 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001210 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001211 */
1212 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1213 xmlFreeElement(ret);
1214 if (uqname != NULL)
1215 xmlFree(uqname);
1216 return(NULL);
1217 }
1218 }
1219
1220 /*
1221 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001222 */
1223 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001224 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001225 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001226
1227 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001228 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001229 */
1230 ret->parent = dtd;
1231 ret->doc = dtd->doc;
1232 if (dtd->last == NULL) {
1233 dtd->children = dtd->last = (xmlNodePtr) ret;
1234 } else {
1235 dtd->last->next = (xmlNodePtr) ret;
1236 ret->prev = dtd->last;
1237 dtd->last = (xmlNodePtr) ret;
1238 }
1239 if (uqname != NULL)
1240 xmlFree(uqname);
1241 return(ret);
1242}
1243
1244/**
1245 * xmlFreeElementTable:
1246 * @table: An element table
1247 *
1248 * Deallocate the memory used by an element hash table.
1249 */
1250void
1251xmlFreeElementTable(xmlElementTablePtr table) {
1252 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1253}
1254
1255/**
1256 * xmlCopyElement:
1257 * @elem: An element
1258 *
1259 * Build a copy of an element.
1260 *
1261 * Returns the new xmlElementPtr or NULL in case of error.
1262 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001263static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001264xmlCopyElement(xmlElementPtr elem) {
1265 xmlElementPtr cur;
1266
1267 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1268 if (cur == NULL) {
1269 xmlGenericError(xmlGenericErrorContext,
1270 "xmlCopyElement: out of memory !\n");
1271 return(NULL);
1272 }
1273 memset(cur, 0, sizeof(xmlElement));
1274 cur->type = XML_ELEMENT_DECL;
1275 cur->etype = elem->etype;
1276 if (elem->name != NULL)
1277 cur->name = xmlStrdup(elem->name);
1278 else
1279 cur->name = NULL;
1280 if (elem->prefix != NULL)
1281 cur->prefix = xmlStrdup(elem->prefix);
1282 else
1283 cur->prefix = NULL;
1284 cur->content = xmlCopyElementContent(elem->content);
1285 /* TODO : rebuild the attribute list on the copy */
1286 cur->attributes = NULL;
1287 return(cur);
1288}
1289
1290/**
1291 * xmlCopyElementTable:
1292 * @table: An element table
1293 *
1294 * Build a copy of an element table.
1295 *
1296 * Returns the new xmlElementTablePtr or NULL in case of error.
1297 */
1298xmlElementTablePtr
1299xmlCopyElementTable(xmlElementTablePtr table) {
1300 return((xmlElementTablePtr) xmlHashCopy(table,
1301 (xmlHashCopier) xmlCopyElement));
1302}
1303
1304/**
1305 * xmlDumpElementDecl:
1306 * @buf: the XML buffer output
1307 * @elem: An element table
1308 *
1309 * This will dump the content of the element declaration as an XML
1310 * DTD definition
1311 */
1312void
1313xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1314 switch (elem->etype) {
1315 case XML_ELEMENT_TYPE_EMPTY:
1316 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001317 if (elem->prefix != NULL) {
1318 xmlBufferWriteCHAR(buf, elem->prefix);
1319 xmlBufferWriteChar(buf, ":");
1320 }
Owen Taylor3473f882001-02-23 17:55:21 +00001321 xmlBufferWriteCHAR(buf, elem->name);
1322 xmlBufferWriteChar(buf, " EMPTY>\n");
1323 break;
1324 case XML_ELEMENT_TYPE_ANY:
1325 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001326 if (elem->prefix != NULL) {
1327 xmlBufferWriteCHAR(buf, elem->prefix);
1328 xmlBufferWriteChar(buf, ":");
1329 }
Owen Taylor3473f882001-02-23 17:55:21 +00001330 xmlBufferWriteCHAR(buf, elem->name);
1331 xmlBufferWriteChar(buf, " ANY>\n");
1332 break;
1333 case XML_ELEMENT_TYPE_MIXED:
1334 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001335 if (elem->prefix != NULL) {
1336 xmlBufferWriteCHAR(buf, elem->prefix);
1337 xmlBufferWriteChar(buf, ":");
1338 }
Owen Taylor3473f882001-02-23 17:55:21 +00001339 xmlBufferWriteCHAR(buf, elem->name);
1340 xmlBufferWriteChar(buf, " ");
1341 xmlDumpElementContent(buf, elem->content, 1);
1342 xmlBufferWriteChar(buf, ">\n");
1343 break;
1344 case XML_ELEMENT_TYPE_ELEMENT:
1345 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001346 if (elem->prefix != NULL) {
1347 xmlBufferWriteCHAR(buf, elem->prefix);
1348 xmlBufferWriteChar(buf, ":");
1349 }
Owen Taylor3473f882001-02-23 17:55:21 +00001350 xmlBufferWriteCHAR(buf, elem->name);
1351 xmlBufferWriteChar(buf, " ");
1352 xmlDumpElementContent(buf, elem->content, 1);
1353 xmlBufferWriteChar(buf, ">\n");
1354 break;
1355 default:
1356 xmlGenericError(xmlGenericErrorContext,
1357 "xmlDumpElementDecl: internal: unknown type %d\n",
1358 elem->etype);
1359 }
1360}
1361
1362/**
1363 * xmlDumpElementTable:
1364 * @buf: the XML buffer output
1365 * @table: An element table
1366 *
1367 * This will dump the content of the element table as an XML DTD definition
1368 */
1369void
1370xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1371 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1372}
1373
1374/**
1375 * xmlCreateEnumeration:
1376 * @name: the enumeration name or NULL
1377 *
1378 * create and initialize an enumeration attribute node.
1379 *
1380 * Returns the xmlEnumerationPtr just created or NULL in case
1381 * of error.
1382 */
1383xmlEnumerationPtr
1384xmlCreateEnumeration(xmlChar *name) {
1385 xmlEnumerationPtr ret;
1386
1387 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1388 if (ret == NULL) {
1389 xmlGenericError(xmlGenericErrorContext,
1390 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1391 (long)sizeof(xmlEnumeration));
1392 return(NULL);
1393 }
1394 memset(ret, 0, sizeof(xmlEnumeration));
1395
1396 if (name != NULL)
1397 ret->name = xmlStrdup(name);
1398 return(ret);
1399}
1400
1401/**
1402 * xmlFreeEnumeration:
1403 * @cur: the tree to free.
1404 *
1405 * free an enumeration attribute node (recursive).
1406 */
1407void
1408xmlFreeEnumeration(xmlEnumerationPtr cur) {
1409 if (cur == NULL) return;
1410
1411 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1412
1413 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001414 xmlFree(cur);
1415}
1416
1417/**
1418 * xmlCopyEnumeration:
1419 * @cur: the tree to copy.
1420 *
1421 * Copy an enumeration attribute node (recursive).
1422 *
1423 * Returns the xmlEnumerationPtr just created or NULL in case
1424 * of error.
1425 */
1426xmlEnumerationPtr
1427xmlCopyEnumeration(xmlEnumerationPtr cur) {
1428 xmlEnumerationPtr ret;
1429
1430 if (cur == NULL) return(NULL);
1431 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1432
1433 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1434 else ret->next = NULL;
1435
1436 return(ret);
1437}
1438
1439/**
1440 * xmlDumpEnumeration:
1441 * @buf: the XML buffer output
1442 * @enum: An enumeration
1443 *
1444 * This will dump the content of the enumeration
1445 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001446static void
Owen Taylor3473f882001-02-23 17:55:21 +00001447xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1448 if (cur == NULL) return;
1449
1450 xmlBufferWriteCHAR(buf, cur->name);
1451 if (cur->next == NULL)
1452 xmlBufferWriteChar(buf, ")");
1453 else {
1454 xmlBufferWriteChar(buf, " | ");
1455 xmlDumpEnumeration(buf, cur->next);
1456 }
1457}
1458
1459/**
1460 * xmlCreateAttributeTable:
1461 *
1462 * create and initialize an empty attribute hash table.
1463 *
1464 * Returns the xmlAttributeTablePtr just created or NULL in case
1465 * of error.
1466 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001467static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001468xmlCreateAttributeTable(void) {
1469 return(xmlHashCreate(0));
1470}
1471
1472/**
1473 * xmlScanAttributeDeclCallback:
1474 * @attr: the attribute decl
1475 * @list: the list to update
1476 *
1477 * Callback called by xmlScanAttributeDecl when a new attribute
1478 * has to be entered in the list.
1479 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001480static void
Owen Taylor3473f882001-02-23 17:55:21 +00001481xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001482 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001483 attr->nexth = *list;
1484 *list = attr;
1485}
1486
1487/**
1488 * xmlScanAttributeDecl:
1489 * @dtd: pointer to the DTD
1490 * @elem: the element name
1491 *
1492 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001493 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001494 *
1495 * Returns the pointer to the first attribute decl in the chain,
1496 * possibly NULL.
1497 */
1498xmlAttributePtr
1499xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1500 xmlAttributePtr ret = NULL;
1501 xmlAttributeTablePtr table;
1502
1503 if (dtd == NULL) {
1504 xmlGenericError(xmlGenericErrorContext,
1505 "xmlScanAttributeDecl: dtd == NULL\n");
1506 return(NULL);
1507 }
1508 if (elem == NULL) {
1509 xmlGenericError(xmlGenericErrorContext,
1510 "xmlScanAttributeDecl: elem == NULL\n");
1511 return(NULL);
1512 }
1513 table = (xmlAttributeTablePtr) dtd->attributes;
1514 if (table == NULL)
1515 return(NULL);
1516
1517 /* WRONG !!! */
1518 xmlHashScan3(table, NULL, NULL, elem,
1519 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1520 return(ret);
1521}
1522
1523/**
1524 * xmlScanIDAttributeDecl:
1525 * @ctxt: the validation context
1526 * @elem: the element name
1527 *
1528 * Verify that the element don't have too many ID attributes
1529 * declared.
1530 *
1531 * Returns the number of ID attributes found.
1532 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001533static int
Owen Taylor3473f882001-02-23 17:55:21 +00001534xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1535 xmlAttributePtr cur;
1536 int ret = 0;
1537
1538 if (elem == NULL) return(0);
1539 cur = elem->attributes;
1540 while (cur != NULL) {
1541 if (cur->atype == XML_ATTRIBUTE_ID) {
1542 ret ++;
1543 if (ret > 1)
1544 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001545 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001546 elem->name, cur->name);
1547 }
1548 cur = cur->nexth;
1549 }
1550 return(ret);
1551}
1552
1553/**
1554 * xmlFreeAttribute:
1555 * @elem: An attribute
1556 *
1557 * Deallocate the memory used by an attribute definition
1558 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001559static void
Owen Taylor3473f882001-02-23 17:55:21 +00001560xmlFreeAttribute(xmlAttributePtr attr) {
1561 if (attr == NULL) return;
1562 xmlUnlinkNode((xmlNodePtr) attr);
1563 if (attr->tree != NULL)
1564 xmlFreeEnumeration(attr->tree);
1565 if (attr->elem != NULL)
1566 xmlFree((xmlChar *) attr->elem);
1567 if (attr->name != NULL)
1568 xmlFree((xmlChar *) attr->name);
1569 if (attr->defaultValue != NULL)
1570 xmlFree((xmlChar *) attr->defaultValue);
1571 if (attr->prefix != NULL)
1572 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001573 xmlFree(attr);
1574}
1575
1576
1577/**
1578 * xmlAddAttributeDecl:
1579 * @ctxt: the validation context
1580 * @dtd: pointer to the DTD
1581 * @elem: the element name
1582 * @name: the attribute name
1583 * @ns: the attribute namespace prefix
1584 * @type: the attribute type
1585 * @def: the attribute default type
1586 * @defaultValue: the attribute default value
1587 * @tree: if it's an enumeration, the associated list
1588 *
1589 * Register a new attribute declaration
1590 * Note that @tree becomes the ownership of the DTD
1591 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001592 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001593 */
1594xmlAttributePtr
1595xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1596 const xmlChar *name, const xmlChar *ns,
1597 xmlAttributeType type, xmlAttributeDefault def,
1598 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1599 xmlAttributePtr ret;
1600 xmlAttributeTablePtr table;
1601 xmlElementPtr elemDef;
1602
1603 if (dtd == NULL) {
1604 xmlGenericError(xmlGenericErrorContext,
1605 "xmlAddAttributeDecl: dtd == NULL\n");
1606 xmlFreeEnumeration(tree);
1607 return(NULL);
1608 }
1609 if (name == NULL) {
1610 xmlGenericError(xmlGenericErrorContext,
1611 "xmlAddAttributeDecl: name == NULL\n");
1612 xmlFreeEnumeration(tree);
1613 return(NULL);
1614 }
1615 if (elem == NULL) {
1616 xmlGenericError(xmlGenericErrorContext,
1617 "xmlAddAttributeDecl: elem == NULL\n");
1618 xmlFreeEnumeration(tree);
1619 return(NULL);
1620 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001621
Owen Taylor3473f882001-02-23 17:55:21 +00001622 /*
1623 * Check the type and possibly the default value.
1624 */
1625 switch (type) {
1626 case XML_ATTRIBUTE_CDATA:
1627 break;
1628 case XML_ATTRIBUTE_ID:
1629 break;
1630 case XML_ATTRIBUTE_IDREF:
1631 break;
1632 case XML_ATTRIBUTE_IDREFS:
1633 break;
1634 case XML_ATTRIBUTE_ENTITY:
1635 break;
1636 case XML_ATTRIBUTE_ENTITIES:
1637 break;
1638 case XML_ATTRIBUTE_NMTOKEN:
1639 break;
1640 case XML_ATTRIBUTE_NMTOKENS:
1641 break;
1642 case XML_ATTRIBUTE_ENUMERATION:
1643 break;
1644 case XML_ATTRIBUTE_NOTATION:
1645 break;
1646 default:
1647 xmlGenericError(xmlGenericErrorContext,
1648 "xmlAddAttributeDecl: unknown type %d\n", type);
1649 xmlFreeEnumeration(tree);
1650 return(NULL);
1651 }
1652 if ((defaultValue != NULL) &&
1653 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001654 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001655 elem, name, defaultValue);
1656 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001657 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001658 }
1659
1660 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001661 * Check first that an attribute defined in the external subset wasn't
1662 * already defined in the internal subset
1663 */
1664 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1665 (dtd->doc->intSubset != NULL) &&
1666 (dtd->doc->intSubset->attributes != NULL)) {
1667 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1668 if (ret != NULL)
1669 return(NULL);
1670 }
1671
1672 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001673 * Create the Attribute table if needed.
1674 */
1675 table = (xmlAttributeTablePtr) dtd->attributes;
1676 if (table == NULL) {
1677 table = xmlCreateAttributeTable();
1678 dtd->attributes = (void *) table;
1679 }
1680 if (table == NULL) {
1681 xmlGenericError(xmlGenericErrorContext,
1682 "xmlAddAttributeDecl: Table creation failed!\n");
1683 return(NULL);
1684 }
1685
1686
1687 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1688 if (ret == NULL) {
1689 xmlGenericError(xmlGenericErrorContext,
1690 "xmlAddAttributeDecl: out of memory\n");
1691 return(NULL);
1692 }
1693 memset(ret, 0, sizeof(xmlAttribute));
1694 ret->type = XML_ATTRIBUTE_DECL;
1695
1696 /*
1697 * fill the structure.
1698 */
1699 ret->atype = type;
1700 ret->name = xmlStrdup(name);
1701 ret->prefix = xmlStrdup(ns);
1702 ret->elem = xmlStrdup(elem);
1703 ret->def = def;
1704 ret->tree = tree;
1705 if (defaultValue != NULL)
1706 ret->defaultValue = xmlStrdup(defaultValue);
1707
1708 /*
1709 * Validity Check:
1710 * Search the DTD for previous declarations of the ATTLIST
1711 */
1712 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1713 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001714 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001715 */
1716 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001717 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001718 name, elem);
1719 xmlFreeAttribute(ret);
1720 return(NULL);
1721 }
1722
1723 /*
1724 * Validity Check:
1725 * Multiple ID per element
1726 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001727 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001728 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001729
Owen Taylor3473f882001-02-23 17:55:21 +00001730 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001731 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001732 VERROR(ctxt->userData,
1733 "Element %s has too may ID attributes defined : %s\n",
1734 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001735 ctxt->valid = 0;
1736 }
1737
Daniel Veillard48da9102001-08-07 01:10:10 +00001738 /*
1739 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001740 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001741 */
1742 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1743 ((ret->prefix != NULL &&
1744 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1745 ret->nexth = elemDef->attributes;
1746 elemDef->attributes = ret;
1747 } else {
1748 xmlAttributePtr tmp = elemDef->attributes;
1749
1750 while ((tmp != NULL) &&
1751 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1752 ((ret->prefix != NULL &&
1753 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1754 if (tmp->nexth == NULL)
1755 break;
1756 tmp = tmp->nexth;
1757 }
1758 if (tmp != NULL) {
1759 ret->nexth = tmp->nexth;
1760 tmp->nexth = ret;
1761 } else {
1762 ret->nexth = elemDef->attributes;
1763 elemDef->attributes = ret;
1764 }
1765 }
Owen Taylor3473f882001-02-23 17:55:21 +00001766 }
1767
1768 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001769 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001770 */
1771 ret->parent = dtd;
1772 ret->doc = dtd->doc;
1773 if (dtd->last == NULL) {
1774 dtd->children = dtd->last = (xmlNodePtr) ret;
1775 } else {
1776 dtd->last->next = (xmlNodePtr) ret;
1777 ret->prev = dtd->last;
1778 dtd->last = (xmlNodePtr) ret;
1779 }
1780 return(ret);
1781}
1782
1783/**
1784 * xmlFreeAttributeTable:
1785 * @table: An attribute table
1786 *
1787 * Deallocate the memory used by an entities hash table.
1788 */
1789void
1790xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1791 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1792}
1793
1794/**
1795 * xmlCopyAttribute:
1796 * @attr: An attribute
1797 *
1798 * Build a copy of an attribute.
1799 *
1800 * Returns the new xmlAttributePtr or NULL in case of error.
1801 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001802static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001803xmlCopyAttribute(xmlAttributePtr attr) {
1804 xmlAttributePtr cur;
1805
1806 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1807 if (cur == NULL) {
1808 xmlGenericError(xmlGenericErrorContext,
1809 "xmlCopyAttribute: out of memory !\n");
1810 return(NULL);
1811 }
1812 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001813 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001814 cur->atype = attr->atype;
1815 cur->def = attr->def;
1816 cur->tree = xmlCopyEnumeration(attr->tree);
1817 if (attr->elem != NULL)
1818 cur->elem = xmlStrdup(attr->elem);
1819 if (attr->name != NULL)
1820 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001821 if (attr->prefix != NULL)
1822 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001823 if (attr->defaultValue != NULL)
1824 cur->defaultValue = xmlStrdup(attr->defaultValue);
1825 return(cur);
1826}
1827
1828/**
1829 * xmlCopyAttributeTable:
1830 * @table: An attribute table
1831 *
1832 * Build a copy of an attribute table.
1833 *
1834 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1835 */
1836xmlAttributeTablePtr
1837xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1838 return((xmlAttributeTablePtr) xmlHashCopy(table,
1839 (xmlHashCopier) xmlCopyAttribute));
1840}
1841
1842/**
1843 * xmlDumpAttributeDecl:
1844 * @buf: the XML buffer output
1845 * @attr: An attribute declaration
1846 *
1847 * This will dump the content of the attribute declaration as an XML
1848 * DTD definition
1849 */
1850void
1851xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1852 xmlBufferWriteChar(buf, "<!ATTLIST ");
1853 xmlBufferWriteCHAR(buf, attr->elem);
1854 xmlBufferWriteChar(buf, " ");
1855 if (attr->prefix != NULL) {
1856 xmlBufferWriteCHAR(buf, attr->prefix);
1857 xmlBufferWriteChar(buf, ":");
1858 }
1859 xmlBufferWriteCHAR(buf, attr->name);
1860 switch (attr->atype) {
1861 case XML_ATTRIBUTE_CDATA:
1862 xmlBufferWriteChar(buf, " CDATA");
1863 break;
1864 case XML_ATTRIBUTE_ID:
1865 xmlBufferWriteChar(buf, " ID");
1866 break;
1867 case XML_ATTRIBUTE_IDREF:
1868 xmlBufferWriteChar(buf, " IDREF");
1869 break;
1870 case XML_ATTRIBUTE_IDREFS:
1871 xmlBufferWriteChar(buf, " IDREFS");
1872 break;
1873 case XML_ATTRIBUTE_ENTITY:
1874 xmlBufferWriteChar(buf, " ENTITY");
1875 break;
1876 case XML_ATTRIBUTE_ENTITIES:
1877 xmlBufferWriteChar(buf, " ENTITIES");
1878 break;
1879 case XML_ATTRIBUTE_NMTOKEN:
1880 xmlBufferWriteChar(buf, " NMTOKEN");
1881 break;
1882 case XML_ATTRIBUTE_NMTOKENS:
1883 xmlBufferWriteChar(buf, " NMTOKENS");
1884 break;
1885 case XML_ATTRIBUTE_ENUMERATION:
1886 xmlBufferWriteChar(buf, " (");
1887 xmlDumpEnumeration(buf, attr->tree);
1888 break;
1889 case XML_ATTRIBUTE_NOTATION:
1890 xmlBufferWriteChar(buf, " NOTATION (");
1891 xmlDumpEnumeration(buf, attr->tree);
1892 break;
1893 default:
1894 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001895 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001896 attr->atype);
1897 }
1898 switch (attr->def) {
1899 case XML_ATTRIBUTE_NONE:
1900 break;
1901 case XML_ATTRIBUTE_REQUIRED:
1902 xmlBufferWriteChar(buf, " #REQUIRED");
1903 break;
1904 case XML_ATTRIBUTE_IMPLIED:
1905 xmlBufferWriteChar(buf, " #IMPLIED");
1906 break;
1907 case XML_ATTRIBUTE_FIXED:
1908 xmlBufferWriteChar(buf, " #FIXED");
1909 break;
1910 default:
1911 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001912 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001913 attr->def);
1914 }
1915 if (attr->defaultValue != NULL) {
1916 xmlBufferWriteChar(buf, " ");
1917 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1918 }
1919 xmlBufferWriteChar(buf, ">\n");
1920}
1921
1922/**
1923 * xmlDumpAttributeTable:
1924 * @buf: the XML buffer output
1925 * @table: An attribute table
1926 *
1927 * This will dump the content of the attribute table as an XML DTD definition
1928 */
1929void
1930xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1931 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1932}
1933
1934/************************************************************************
1935 * *
1936 * NOTATIONs *
1937 * *
1938 ************************************************************************/
1939/**
1940 * xmlCreateNotationTable:
1941 *
1942 * create and initialize an empty notation hash table.
1943 *
1944 * Returns the xmlNotationTablePtr just created or NULL in case
1945 * of error.
1946 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001947static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001948xmlCreateNotationTable(void) {
1949 return(xmlHashCreate(0));
1950}
1951
1952/**
1953 * xmlFreeNotation:
1954 * @not: A notation
1955 *
1956 * Deallocate the memory used by an notation definition
1957 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001958static void
Owen Taylor3473f882001-02-23 17:55:21 +00001959xmlFreeNotation(xmlNotationPtr nota) {
1960 if (nota == NULL) return;
1961 if (nota->name != NULL)
1962 xmlFree((xmlChar *) nota->name);
1963 if (nota->PublicID != NULL)
1964 xmlFree((xmlChar *) nota->PublicID);
1965 if (nota->SystemID != NULL)
1966 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001967 xmlFree(nota);
1968}
1969
1970
1971/**
1972 * xmlAddNotationDecl:
1973 * @dtd: pointer to the DTD
1974 * @ctxt: the validation context
1975 * @name: the entity name
1976 * @PublicID: the public identifier or NULL
1977 * @SystemID: the system identifier or NULL
1978 *
1979 * Register a new notation declaration
1980 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001981 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001982 */
1983xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001984xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001985 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001986 const xmlChar *PublicID, const xmlChar *SystemID) {
1987 xmlNotationPtr ret;
1988 xmlNotationTablePtr table;
1989
1990 if (dtd == NULL) {
1991 xmlGenericError(xmlGenericErrorContext,
1992 "xmlAddNotationDecl: dtd == NULL\n");
1993 return(NULL);
1994 }
1995 if (name == NULL) {
1996 xmlGenericError(xmlGenericErrorContext,
1997 "xmlAddNotationDecl: name == NULL\n");
1998 return(NULL);
1999 }
2000 if ((PublicID == NULL) && (SystemID == NULL)) {
2001 xmlGenericError(xmlGenericErrorContext,
2002 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002003 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002004 }
2005
2006 /*
2007 * Create the Notation table if needed.
2008 */
2009 table = (xmlNotationTablePtr) dtd->notations;
2010 if (table == NULL)
2011 dtd->notations = table = xmlCreateNotationTable();
2012 if (table == NULL) {
2013 xmlGenericError(xmlGenericErrorContext,
2014 "xmlAddNotationDecl: Table creation failed!\n");
2015 return(NULL);
2016 }
2017
2018 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2019 if (ret == NULL) {
2020 xmlGenericError(xmlGenericErrorContext,
2021 "xmlAddNotationDecl: out of memory\n");
2022 return(NULL);
2023 }
2024 memset(ret, 0, sizeof(xmlNotation));
2025
2026 /*
2027 * fill the structure.
2028 */
2029 ret->name = xmlStrdup(name);
2030 if (SystemID != NULL)
2031 ret->SystemID = xmlStrdup(SystemID);
2032 if (PublicID != NULL)
2033 ret->PublicID = xmlStrdup(PublicID);
2034
2035 /*
2036 * Validity Check:
2037 * Check the DTD for previous declarations of the ATTLIST
2038 */
2039 if (xmlHashAddEntry(table, name, ret)) {
2040 xmlGenericError(xmlGenericErrorContext,
2041 "xmlAddNotationDecl: %s already defined\n", name);
2042 xmlFreeNotation(ret);
2043 return(NULL);
2044 }
2045 return(ret);
2046}
2047
2048/**
2049 * xmlFreeNotationTable:
2050 * @table: An notation table
2051 *
2052 * Deallocate the memory used by an entities hash table.
2053 */
2054void
2055xmlFreeNotationTable(xmlNotationTablePtr table) {
2056 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2057}
2058
2059/**
2060 * xmlCopyNotation:
2061 * @nota: A notation
2062 *
2063 * Build a copy of a notation.
2064 *
2065 * Returns the new xmlNotationPtr or NULL in case of error.
2066 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002067static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002068xmlCopyNotation(xmlNotationPtr nota) {
2069 xmlNotationPtr cur;
2070
2071 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2072 if (cur == NULL) {
2073 xmlGenericError(xmlGenericErrorContext,
2074 "xmlCopyNotation: out of memory !\n");
2075 return(NULL);
2076 }
2077 if (nota->name != NULL)
2078 cur->name = xmlStrdup(nota->name);
2079 else
2080 cur->name = NULL;
2081 if (nota->PublicID != NULL)
2082 cur->PublicID = xmlStrdup(nota->PublicID);
2083 else
2084 cur->PublicID = NULL;
2085 if (nota->SystemID != NULL)
2086 cur->SystemID = xmlStrdup(nota->SystemID);
2087 else
2088 cur->SystemID = NULL;
2089 return(cur);
2090}
2091
2092/**
2093 * xmlCopyNotationTable:
2094 * @table: A notation table
2095 *
2096 * Build a copy of a notation table.
2097 *
2098 * Returns the new xmlNotationTablePtr or NULL in case of error.
2099 */
2100xmlNotationTablePtr
2101xmlCopyNotationTable(xmlNotationTablePtr table) {
2102 return((xmlNotationTablePtr) xmlHashCopy(table,
2103 (xmlHashCopier) xmlCopyNotation));
2104}
2105
2106/**
2107 * xmlDumpNotationDecl:
2108 * @buf: the XML buffer output
2109 * @nota: A notation declaration
2110 *
2111 * This will dump the content the notation declaration as an XML DTD definition
2112 */
2113void
2114xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2115 xmlBufferWriteChar(buf, "<!NOTATION ");
2116 xmlBufferWriteCHAR(buf, nota->name);
2117 if (nota->PublicID != NULL) {
2118 xmlBufferWriteChar(buf, " PUBLIC ");
2119 xmlBufferWriteQuotedString(buf, nota->PublicID);
2120 if (nota->SystemID != NULL) {
2121 xmlBufferWriteChar(buf, " ");
2122 xmlBufferWriteCHAR(buf, nota->SystemID);
2123 }
2124 } else {
2125 xmlBufferWriteChar(buf, " SYSTEM ");
2126 xmlBufferWriteCHAR(buf, nota->SystemID);
2127 }
2128 xmlBufferWriteChar(buf, " >\n");
2129}
2130
2131/**
2132 * xmlDumpNotationTable:
2133 * @buf: the XML buffer output
2134 * @table: A notation table
2135 *
2136 * This will dump the content of the notation table as an XML DTD definition
2137 */
2138void
2139xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2140 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2141}
2142
2143/************************************************************************
2144 * *
2145 * IDs *
2146 * *
2147 ************************************************************************/
2148/**
2149 * xmlCreateIDTable:
2150 *
2151 * create and initialize an empty id hash table.
2152 *
2153 * Returns the xmlIDTablePtr just created or NULL in case
2154 * of error.
2155 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002156static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002157xmlCreateIDTable(void) {
2158 return(xmlHashCreate(0));
2159}
2160
2161/**
2162 * xmlFreeID:
2163 * @not: A id
2164 *
2165 * Deallocate the memory used by an id definition
2166 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002167static void
Owen Taylor3473f882001-02-23 17:55:21 +00002168xmlFreeID(xmlIDPtr id) {
2169 if (id == NULL) return;
2170 if (id->value != NULL)
2171 xmlFree((xmlChar *) id->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002172 if (id->name != NULL)
2173 xmlFree((xmlChar *) id->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002174 xmlFree(id);
2175}
2176
2177/**
2178 * xmlAddID:
2179 * @ctxt: the validation context
2180 * @doc: pointer to the document
2181 * @value: the value name
2182 * @attr: the attribute holding the ID
2183 *
2184 * Register a new id declaration
2185 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002186 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002187 */
2188xmlIDPtr
2189xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2190 xmlAttrPtr attr) {
2191 xmlIDPtr ret;
2192 xmlIDTablePtr table;
2193
2194 if (doc == NULL) {
2195 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002196 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002197 return(NULL);
2198 }
2199 if (value == NULL) {
2200 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002201 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002202 return(NULL);
2203 }
2204 if (attr == NULL) {
2205 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002206 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002207 return(NULL);
2208 }
2209
2210 /*
2211 * Create the ID table if needed.
2212 */
2213 table = (xmlIDTablePtr) doc->ids;
2214 if (table == NULL)
2215 doc->ids = table = xmlCreateIDTable();
2216 if (table == NULL) {
2217 xmlGenericError(xmlGenericErrorContext,
2218 "xmlAddID: Table creation failed!\n");
2219 return(NULL);
2220 }
2221
2222 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2223 if (ret == NULL) {
2224 xmlGenericError(xmlGenericErrorContext,
2225 "xmlAddID: out of memory\n");
2226 return(NULL);
2227 }
2228
2229 /*
2230 * fill the structure.
2231 */
2232 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002233 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2234 /*
2235 * Operating in streaming mode, attr is gonna disapear
2236 */
2237 ret->name = xmlStrdup(attr->name);
2238 ret->attr = NULL;
2239 } else {
2240 ret->attr = attr;
2241 ret->name = NULL;
2242 }
2243 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002244
2245 if (xmlHashAddEntry(table, value, ret) < 0) {
2246 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002247 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002248 */
Daniel Veillard76575762002-09-05 14:21:15 +00002249 if (ctxt != NULL) {
2250 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002251 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002252 }
Owen Taylor3473f882001-02-23 17:55:21 +00002253 xmlFreeID(ret);
2254 return(NULL);
2255 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002256 if (attr != NULL)
2257 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002258 return(ret);
2259}
2260
2261/**
2262 * xmlFreeIDTable:
2263 * @table: An id table
2264 *
2265 * Deallocate the memory used by an ID hash table.
2266 */
2267void
2268xmlFreeIDTable(xmlIDTablePtr table) {
2269 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2270}
2271
2272/**
2273 * xmlIsID:
2274 * @doc: the document
2275 * @elem: the element carrying the attribute
2276 * @attr: the attribute
2277 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002278 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002279 * then this is done if DTD loading has been requested. In the case
2280 * of HTML documents parsed with the HTML parser, then ID detection is
2281 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002282 *
2283 * Returns 0 or 1 depending on the lookup result
2284 */
2285int
2286xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2287 if (doc == NULL) return(0);
2288 if (attr == NULL) return(0);
2289 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2290 return(0);
2291 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2292 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2293 (xmlStrEqual(BAD_CAST "name", attr->name)))
2294 return(1);
2295 return(0);
2296 } else {
2297 xmlAttributePtr attrDecl;
2298
2299 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002300 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00002301 xmlChar fn[50];
Daniel Veillard37f961d2002-07-06 17:53:56 +00002302 xmlChar *fullname;
Daniel Veillardc00cda82003-04-07 10:22:39 +00002303
2304 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002305 if (fullname == NULL)
2306 return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002307 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2308 attr->name);
2309 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2310 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2311 attr->name);
Daniel Veillardc00cda82003-04-07 10:22:39 +00002312 if ((fullname != fn) && (fullname != elem->name))
2313 xmlFree(fullname);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002314 } else {
2315 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2316 attr->name);
2317 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2318 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2319 attr->name);
2320 }
Owen Taylor3473f882001-02-23 17:55:21 +00002321
2322 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2323 return(1);
2324 }
2325 return(0);
2326}
2327
2328/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002329 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002330 * @doc: the document
2331 * @attr: the attribute
2332 *
2333 * Remove the given attribute from the ID table maintained internally.
2334 *
2335 * Returns -1 if the lookup failed and 0 otherwise
2336 */
2337int
2338xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2339 xmlAttrPtr cur;
2340 xmlIDTablePtr table;
2341 xmlChar *ID;
2342
2343 if (doc == NULL) return(-1);
2344 if (attr == NULL) return(-1);
2345 table = (xmlIDTablePtr) doc->ids;
2346 if (table == NULL)
2347 return(-1);
2348
2349 if (attr == NULL)
2350 return(-1);
2351 ID = xmlNodeListGetString(doc, attr->children, 1);
2352 if (ID == NULL)
2353 return(-1);
2354 cur = xmlHashLookup(table, ID);
2355 if (cur != attr) {
2356 xmlFree(ID);
2357 return(-1);
2358 }
2359 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2360 xmlFree(ID);
2361 return(0);
2362}
2363
2364/**
2365 * xmlGetID:
2366 * @doc: pointer to the document
2367 * @ID: the ID value
2368 *
2369 * Search the attribute declaring the given ID
2370 *
2371 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2372 */
2373xmlAttrPtr
2374xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2375 xmlIDTablePtr table;
2376 xmlIDPtr id;
2377
2378 if (doc == NULL) {
2379 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2380 return(NULL);
2381 }
2382
2383 if (ID == NULL) {
2384 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2385 return(NULL);
2386 }
2387
2388 table = (xmlIDTablePtr) doc->ids;
2389 if (table == NULL)
2390 return(NULL);
2391
2392 id = xmlHashLookup(table, ID);
2393 if (id == NULL)
2394 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002395 if (id->attr == NULL) {
2396 /*
2397 * We are operating on a stream, return a well known reference
2398 * since the attribute node doesn't exist anymore
2399 */
2400 return((xmlAttrPtr) doc);
2401 }
Owen Taylor3473f882001-02-23 17:55:21 +00002402 return(id->attr);
2403}
2404
2405/************************************************************************
2406 * *
2407 * Refs *
2408 * *
2409 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002410typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002411{
2412 xmlListPtr l;
2413 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002414} xmlRemoveMemo;
2415
2416typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2417
2418typedef struct xmlValidateMemo_t
2419{
2420 xmlValidCtxtPtr ctxt;
2421 const xmlChar *name;
2422} xmlValidateMemo;
2423
2424typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002425
2426/**
2427 * xmlCreateRefTable:
2428 *
2429 * create and initialize an empty ref hash table.
2430 *
2431 * Returns the xmlRefTablePtr just created or NULL in case
2432 * of error.
2433 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002434static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002435xmlCreateRefTable(void) {
2436 return(xmlHashCreate(0));
2437}
2438
2439/**
2440 * xmlFreeRef:
2441 * @lk: A list link
2442 *
2443 * Deallocate the memory used by a ref definition
2444 */
2445static void
2446xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002447 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2448 if (ref == NULL) return;
2449 if (ref->value != NULL)
2450 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002451 if (ref->name != NULL)
2452 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002453 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002454}
2455
2456/**
2457 * xmlFreeRefList:
2458 * @list_ref: A list of references.
2459 *
2460 * Deallocate the memory used by a list of references
2461 */
2462static void
2463xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002464 if (list_ref == NULL) return;
2465 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002466}
2467
2468/**
2469 * xmlWalkRemoveRef:
2470 * @data: Contents of current link
2471 * @user: Value supplied by the user
2472 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002473 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002474 */
2475static int
2476xmlWalkRemoveRef(const void *data, const void *user)
2477{
Daniel Veillard37721922001-05-04 15:21:12 +00002478 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2479 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2480 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002481
Daniel Veillard37721922001-05-04 15:21:12 +00002482 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2483 xmlListRemoveFirst(ref_list, (void *)data);
2484 return 0;
2485 }
2486 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002487}
2488
2489/**
2490 * xmlAddRef:
2491 * @ctxt: the validation context
2492 * @doc: pointer to the document
2493 * @value: the value name
2494 * @attr: the attribute holding the Ref
2495 *
2496 * Register a new ref declaration
2497 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002498 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002499 */
2500xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002501xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002502 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002503 xmlRefPtr ret;
2504 xmlRefTablePtr table;
2505 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002506
Daniel Veillard37721922001-05-04 15:21:12 +00002507 if (doc == NULL) {
2508 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002509 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002510 return(NULL);
2511 }
2512 if (value == NULL) {
2513 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002514 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002515 return(NULL);
2516 }
2517 if (attr == NULL) {
2518 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002519 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002520 return(NULL);
2521 }
Owen Taylor3473f882001-02-23 17:55:21 +00002522
Daniel Veillard37721922001-05-04 15:21:12 +00002523 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002524 * Create the Ref table if needed.
2525 */
Daniel Veillard37721922001-05-04 15:21:12 +00002526 table = (xmlRefTablePtr) doc->refs;
2527 if (table == NULL)
2528 doc->refs = table = xmlCreateRefTable();
2529 if (table == NULL) {
2530 xmlGenericError(xmlGenericErrorContext,
2531 "xmlAddRef: Table creation failed!\n");
2532 return(NULL);
2533 }
Owen Taylor3473f882001-02-23 17:55:21 +00002534
Daniel Veillard37721922001-05-04 15:21:12 +00002535 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2536 if (ret == NULL) {
2537 xmlGenericError(xmlGenericErrorContext,
2538 "xmlAddRef: out of memory\n");
2539 return(NULL);
2540 }
Owen Taylor3473f882001-02-23 17:55:21 +00002541
Daniel Veillard37721922001-05-04 15:21:12 +00002542 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002543 * fill the structure.
2544 */
Daniel Veillard37721922001-05-04 15:21:12 +00002545 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002546 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2547 /*
2548 * Operating in streaming mode, attr is gonna disapear
2549 */
2550 ret->name = xmlStrdup(attr->name);
2551 ret->attr = NULL;
2552 } else {
2553 ret->name = NULL;
2554 ret->attr = attr;
2555 }
2556 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002557
Daniel Veillard37721922001-05-04 15:21:12 +00002558 /* To add a reference :-
2559 * References are maintained as a list of references,
2560 * Lookup the entry, if no entry create new nodelist
2561 * Add the owning node to the NodeList
2562 * Return the ref
2563 */
Owen Taylor3473f882001-02-23 17:55:21 +00002564
Daniel Veillard37721922001-05-04 15:21:12 +00002565 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2566 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2567 xmlGenericError(xmlGenericErrorContext,
2568 "xmlAddRef: Reference list creation failed!\n");
2569 return(NULL);
2570 }
2571 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2572 xmlListDelete(ref_list);
2573 xmlGenericError(xmlGenericErrorContext,
2574 "xmlAddRef: Reference list insertion failed!\n");
2575 return(NULL);
2576 }
2577 }
2578 xmlListInsert(ref_list, ret);
2579 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002580}
2581
2582/**
2583 * xmlFreeRefTable:
2584 * @table: An ref table
2585 *
2586 * Deallocate the memory used by an Ref hash table.
2587 */
2588void
2589xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002590 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002591}
2592
2593/**
2594 * xmlIsRef:
2595 * @doc: the document
2596 * @elem: the element carrying the attribute
2597 * @attr: the attribute
2598 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002599 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002600 * then this is simple, otherwise we use an heuristic: name Ref (upper
2601 * or lowercase).
2602 *
2603 * Returns 0 or 1 depending on the lookup result
2604 */
2605int
2606xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002607 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2608 return(0);
2609 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2610 /* TODO @@@ */
2611 return(0);
2612 } else {
2613 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002614
Daniel Veillard37721922001-05-04 15:21:12 +00002615 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2616 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2617 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2618 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002619
Daniel Veillard37721922001-05-04 15:21:12 +00002620 if ((attrDecl != NULL) &&
2621 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2622 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2623 return(1);
2624 }
2625 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002626}
2627
2628/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002629 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002630 * @doc: the document
2631 * @attr: the attribute
2632 *
2633 * Remove the given attribute from the Ref table maintained internally.
2634 *
2635 * Returns -1 if the lookup failed and 0 otherwise
2636 */
2637int
2638xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002639 xmlListPtr ref_list;
2640 xmlRefTablePtr table;
2641 xmlChar *ID;
2642 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002643
Daniel Veillard37721922001-05-04 15:21:12 +00002644 if (doc == NULL) return(-1);
2645 if (attr == NULL) return(-1);
2646 table = (xmlRefTablePtr) doc->refs;
2647 if (table == NULL)
2648 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002649
Daniel Veillard37721922001-05-04 15:21:12 +00002650 if (attr == NULL)
2651 return(-1);
2652 ID = xmlNodeListGetString(doc, attr->children, 1);
2653 if (ID == NULL)
2654 return(-1);
2655 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002656
Daniel Veillard37721922001-05-04 15:21:12 +00002657 if(ref_list == NULL) {
2658 xmlFree(ID);
2659 return (-1);
2660 }
2661 /* At this point, ref_list refers to a list of references which
2662 * have the same key as the supplied attr. Our list of references
2663 * is ordered by reference address and we don't have that information
2664 * here to use when removing. We'll have to walk the list and
2665 * check for a matching attribute, when we find one stop the walk
2666 * and remove the entry.
2667 * The list is ordered by reference, so that means we don't have the
2668 * key. Passing the list and the reference to the walker means we
2669 * will have enough data to be able to remove the entry.
2670 */
2671 target.l = ref_list;
2672 target.ap = attr;
2673
2674 /* Remove the supplied attr from our list */
2675 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002676
Daniel Veillard37721922001-05-04 15:21:12 +00002677 /*If the list is empty then remove the list entry in the hash */
2678 if (xmlListEmpty(ref_list))
2679 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2680 xmlFreeRefList);
2681 xmlFree(ID);
2682 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002683}
2684
2685/**
2686 * xmlGetRefs:
2687 * @doc: pointer to the document
2688 * @ID: the ID value
2689 *
2690 * Find the set of references for the supplied ID.
2691 *
2692 * Returns NULL if not found, otherwise node set for the ID.
2693 */
2694xmlListPtr
2695xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002696 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002697
Daniel Veillard37721922001-05-04 15:21:12 +00002698 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002699 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002700 return(NULL);
2701 }
Owen Taylor3473f882001-02-23 17:55:21 +00002702
Daniel Veillard37721922001-05-04 15:21:12 +00002703 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002704 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002705 return(NULL);
2706 }
Owen Taylor3473f882001-02-23 17:55:21 +00002707
Daniel Veillard37721922001-05-04 15:21:12 +00002708 table = (xmlRefTablePtr) doc->refs;
2709 if (table == NULL)
2710 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002711
Daniel Veillard37721922001-05-04 15:21:12 +00002712 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002713}
2714
2715/************************************************************************
2716 * *
2717 * Routines for validity checking *
2718 * *
2719 ************************************************************************/
2720
2721/**
2722 * xmlGetDtdElementDesc:
2723 * @dtd: a pointer to the DtD to search
2724 * @name: the element name
2725 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002726 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002727 *
2728 * returns the xmlElementPtr if found or NULL
2729 */
2730
2731xmlElementPtr
2732xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2733 xmlElementTablePtr table;
2734 xmlElementPtr cur;
2735 xmlChar *uqname = NULL, *prefix = NULL;
2736
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00002737 if ((dtd == NULL) || (name == NULL)) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002738 if (dtd->elements == NULL)
2739 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002740 table = (xmlElementTablePtr) dtd->elements;
2741
2742 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002743 if (uqname != NULL)
2744 name = uqname;
2745 cur = xmlHashLookup2(table, name, prefix);
2746 if (prefix != NULL) xmlFree(prefix);
2747 if (uqname != NULL) xmlFree(uqname);
2748 return(cur);
2749}
2750/**
2751 * xmlGetDtdElementDesc2:
2752 * @dtd: a pointer to the DtD to search
2753 * @name: the element name
2754 * @create: create an empty description if not found
2755 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002756 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002757 *
2758 * returns the xmlElementPtr if found or NULL
2759 */
2760
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002761static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002762xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2763 xmlElementTablePtr table;
2764 xmlElementPtr cur;
2765 xmlChar *uqname = NULL, *prefix = NULL;
2766
2767 if (dtd == NULL) return(NULL);
2768 if (dtd->elements == NULL) {
2769 if (!create)
2770 return(NULL);
2771 /*
2772 * Create the Element table if needed.
2773 */
2774 table = (xmlElementTablePtr) dtd->elements;
2775 if (table == NULL) {
2776 table = xmlCreateElementTable();
2777 dtd->elements = (void *) table;
2778 }
2779 if (table == NULL) {
2780 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002781 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002782 return(NULL);
2783 }
2784 }
2785 table = (xmlElementTablePtr) dtd->elements;
2786
2787 uqname = xmlSplitQName2(name, &prefix);
2788 if (uqname != NULL)
2789 name = uqname;
2790 cur = xmlHashLookup2(table, name, prefix);
2791 if ((cur == NULL) && (create)) {
2792 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2793 if (cur == NULL) {
2794 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002795 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002796 return(NULL);
2797 }
2798 memset(cur, 0, sizeof(xmlElement));
2799 cur->type = XML_ELEMENT_DECL;
2800
2801 /*
2802 * fill the structure.
2803 */
2804 cur->name = xmlStrdup(name);
2805 cur->prefix = xmlStrdup(prefix);
2806 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2807
2808 xmlHashAddEntry2(table, name, prefix, cur);
2809 }
2810 if (prefix != NULL) xmlFree(prefix);
2811 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002812 return(cur);
2813}
2814
2815/**
2816 * xmlGetDtdQElementDesc:
2817 * @dtd: a pointer to the DtD to search
2818 * @name: the element name
2819 * @prefix: the element namespace prefix
2820 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002821 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002822 *
2823 * returns the xmlElementPtr if found or NULL
2824 */
2825
Daniel Veillard48da9102001-08-07 01:10:10 +00002826xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002827xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2828 const xmlChar *prefix) {
2829 xmlElementTablePtr table;
2830
2831 if (dtd == NULL) return(NULL);
2832 if (dtd->elements == NULL) return(NULL);
2833 table = (xmlElementTablePtr) dtd->elements;
2834
2835 return(xmlHashLookup2(table, name, prefix));
2836}
2837
2838/**
2839 * xmlGetDtdAttrDesc:
2840 * @dtd: a pointer to the DtD to search
2841 * @elem: the element name
2842 * @name: the attribute name
2843 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002844 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002845 * this element.
2846 *
2847 * returns the xmlAttributePtr if found or NULL
2848 */
2849
2850xmlAttributePtr
2851xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2852 xmlAttributeTablePtr table;
2853 xmlAttributePtr cur;
2854 xmlChar *uqname = NULL, *prefix = NULL;
2855
2856 if (dtd == NULL) return(NULL);
2857 if (dtd->attributes == NULL) return(NULL);
2858
2859 table = (xmlAttributeTablePtr) dtd->attributes;
2860 if (table == NULL)
2861 return(NULL);
2862
2863 uqname = xmlSplitQName2(name, &prefix);
2864
2865 if (uqname != NULL) {
2866 cur = xmlHashLookup3(table, uqname, prefix, elem);
2867 if (prefix != NULL) xmlFree(prefix);
2868 if (uqname != NULL) xmlFree(uqname);
2869 } else
2870 cur = xmlHashLookup3(table, name, NULL, elem);
2871 return(cur);
2872}
2873
2874/**
2875 * xmlGetDtdQAttrDesc:
2876 * @dtd: a pointer to the DtD to search
2877 * @elem: the element name
2878 * @name: the attribute name
2879 * @prefix: the attribute namespace prefix
2880 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002881 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002882 * this element.
2883 *
2884 * returns the xmlAttributePtr if found or NULL
2885 */
2886
Daniel Veillard48da9102001-08-07 01:10:10 +00002887xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002888xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2889 const xmlChar *prefix) {
2890 xmlAttributeTablePtr table;
2891
2892 if (dtd == NULL) return(NULL);
2893 if (dtd->attributes == NULL) return(NULL);
2894 table = (xmlAttributeTablePtr) dtd->attributes;
2895
2896 return(xmlHashLookup3(table, name, prefix, elem));
2897}
2898
2899/**
2900 * xmlGetDtdNotationDesc:
2901 * @dtd: a pointer to the DtD to search
2902 * @name: the notation name
2903 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002904 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002905 *
2906 * returns the xmlNotationPtr if found or NULL
2907 */
2908
2909xmlNotationPtr
2910xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2911 xmlNotationTablePtr table;
2912
2913 if (dtd == NULL) return(NULL);
2914 if (dtd->notations == NULL) return(NULL);
2915 table = (xmlNotationTablePtr) dtd->notations;
2916
2917 return(xmlHashLookup(table, name));
2918}
2919
2920/**
2921 * xmlValidateNotationUse:
2922 * @ctxt: the validation context
2923 * @doc: the document
2924 * @notationName: the notation name to check
2925 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002926 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002927 * - [ VC: Notation Declared ]
2928 *
2929 * returns 1 if valid or 0 otherwise
2930 */
2931
2932int
2933xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2934 const xmlChar *notationName) {
2935 xmlNotationPtr notaDecl;
2936 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2937
2938 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2939 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2940 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2941
Daniel Veillarde637c4a2003-03-30 21:10:09 +00002942 if ((notaDecl == NULL) && (ctxt != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00002943 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2944 notationName);
2945 return(0);
2946 }
2947 return(1);
2948}
2949
2950/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002951 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00002952 * @doc: the document
2953 * @name: the element name
2954 *
2955 * Search in the DtDs whether an element accept Mixed content (or ANY)
2956 * basically if it is supposed to accept text childs
2957 *
2958 * returns 0 if no, 1 if yes, and -1 if no element description is available
2959 */
2960
2961int
2962xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2963 xmlElementPtr elemDecl;
2964
2965 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2966
2967 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2968 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2969 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2970 if (elemDecl == NULL) return(-1);
2971 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002972 case XML_ELEMENT_TYPE_UNDEFINED:
2973 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002974 case XML_ELEMENT_TYPE_ELEMENT:
2975 return(0);
2976 case XML_ELEMENT_TYPE_EMPTY:
2977 /*
2978 * return 1 for EMPTY since we want VC error to pop up
2979 * on <empty> </empty> for example
2980 */
2981 case XML_ELEMENT_TYPE_ANY:
2982 case XML_ELEMENT_TYPE_MIXED:
2983 return(1);
2984 }
2985 return(1);
2986}
2987
2988/**
2989 * xmlValidateNameValue:
2990 * @value: an Name value
2991 *
2992 * Validate that the given value match Name production
2993 *
2994 * returns 1 if valid or 0 otherwise
2995 */
2996
Daniel Veillard9b731d72002-04-14 12:56:08 +00002997int
Owen Taylor3473f882001-02-23 17:55:21 +00002998xmlValidateNameValue(const xmlChar *value) {
2999 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003000 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003001
3002 if (value == NULL) return(0);
3003 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003004 val = xmlStringCurrentChar(NULL, cur, &len);
3005 cur += len;
3006 if (!IS_LETTER(val) && (val != '_') &&
3007 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003008 return(0);
3009 }
3010
Daniel Veillardd8224e02002-01-13 15:43:22 +00003011 val = xmlStringCurrentChar(NULL, cur, &len);
3012 cur += len;
3013 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3014 (val == '.') || (val == '-') ||
3015 (val == '_') || (val == ':') ||
3016 (IS_COMBINING(val)) ||
3017 (IS_EXTENDER(val))) {
3018 val = xmlStringCurrentChar(NULL, cur, &len);
3019 cur += len;
3020 }
Owen Taylor3473f882001-02-23 17:55:21 +00003021
Daniel Veillardd8224e02002-01-13 15:43:22 +00003022 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003023
3024 return(1);
3025}
3026
3027/**
3028 * xmlValidateNamesValue:
3029 * @value: an Names value
3030 *
3031 * Validate that the given value match Names production
3032 *
3033 * returns 1 if valid or 0 otherwise
3034 */
3035
Daniel Veillard9b731d72002-04-14 12:56:08 +00003036int
Owen Taylor3473f882001-02-23 17:55:21 +00003037xmlValidateNamesValue(const xmlChar *value) {
3038 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003039 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003040
3041 if (value == NULL) return(0);
3042 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003043 val = xmlStringCurrentChar(NULL, cur, &len);
3044 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003045
Daniel Veillardd8224e02002-01-13 15:43:22 +00003046 if (!IS_LETTER(val) && (val != '_') &&
3047 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003048 return(0);
3049 }
3050
Daniel Veillardd8224e02002-01-13 15:43:22 +00003051 val = xmlStringCurrentChar(NULL, cur, &len);
3052 cur += len;
3053 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3054 (val == '.') || (val == '-') ||
3055 (val == '_') || (val == ':') ||
3056 (IS_COMBINING(val)) ||
3057 (IS_EXTENDER(val))) {
3058 val = xmlStringCurrentChar(NULL, cur, &len);
3059 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003060 }
3061
Daniel Veillardd8224e02002-01-13 15:43:22 +00003062 while (IS_BLANK(val)) {
3063 while (IS_BLANK(val)) {
3064 val = xmlStringCurrentChar(NULL, cur, &len);
3065 cur += len;
3066 }
3067
3068 if (!IS_LETTER(val) && (val != '_') &&
3069 (val != ':')) {
3070 return(0);
3071 }
3072 val = xmlStringCurrentChar(NULL, cur, &len);
3073 cur += len;
3074
3075 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3076 (val == '.') || (val == '-') ||
3077 (val == '_') || (val == ':') ||
3078 (IS_COMBINING(val)) ||
3079 (IS_EXTENDER(val))) {
3080 val = xmlStringCurrentChar(NULL, cur, &len);
3081 cur += len;
3082 }
3083 }
3084
3085 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003086
3087 return(1);
3088}
3089
3090/**
3091 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003092 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003093 *
3094 * Validate that the given value match Nmtoken production
3095 *
3096 * [ VC: Name Token ]
3097 *
3098 * returns 1 if valid or 0 otherwise
3099 */
3100
Daniel Veillard9b731d72002-04-14 12:56:08 +00003101int
Owen Taylor3473f882001-02-23 17:55:21 +00003102xmlValidateNmtokenValue(const xmlChar *value) {
3103 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003104 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003105
3106 if (value == NULL) return(0);
3107 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003108 val = xmlStringCurrentChar(NULL, cur, &len);
3109 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003110
Daniel Veillardd8224e02002-01-13 15:43:22 +00003111 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3112 (val != '.') && (val != '-') &&
3113 (val != '_') && (val != ':') &&
3114 (!IS_COMBINING(val)) &&
3115 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003116 return(0);
3117
Daniel Veillardd8224e02002-01-13 15:43:22 +00003118 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3119 (val == '.') || (val == '-') ||
3120 (val == '_') || (val == ':') ||
3121 (IS_COMBINING(val)) ||
3122 (IS_EXTENDER(val))) {
3123 val = xmlStringCurrentChar(NULL, cur, &len);
3124 cur += len;
3125 }
Owen Taylor3473f882001-02-23 17:55:21 +00003126
Daniel Veillardd8224e02002-01-13 15:43:22 +00003127 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003128
3129 return(1);
3130}
3131
3132/**
3133 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003134 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003135 *
3136 * Validate that the given value match Nmtokens production
3137 *
3138 * [ VC: Name Token ]
3139 *
3140 * returns 1 if valid or 0 otherwise
3141 */
3142
Daniel Veillard9b731d72002-04-14 12:56:08 +00003143int
Owen Taylor3473f882001-02-23 17:55:21 +00003144xmlValidateNmtokensValue(const xmlChar *value) {
3145 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003146 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003147
3148 if (value == NULL) return(0);
3149 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003150 val = xmlStringCurrentChar(NULL, cur, &len);
3151 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003152
Daniel Veillardd8224e02002-01-13 15:43:22 +00003153 while (IS_BLANK(val)) {
3154 val = xmlStringCurrentChar(NULL, cur, &len);
3155 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003156 }
3157
Daniel Veillardd8224e02002-01-13 15:43:22 +00003158 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3159 (val != '.') && (val != '-') &&
3160 (val != '_') && (val != ':') &&
3161 (!IS_COMBINING(val)) &&
3162 (!IS_EXTENDER(val)))
3163 return(0);
3164
3165 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3166 (val == '.') || (val == '-') ||
3167 (val == '_') || (val == ':') ||
3168 (IS_COMBINING(val)) ||
3169 (IS_EXTENDER(val))) {
3170 val = xmlStringCurrentChar(NULL, cur, &len);
3171 cur += len;
3172 }
3173
3174 while (IS_BLANK(val)) {
3175 while (IS_BLANK(val)) {
3176 val = xmlStringCurrentChar(NULL, cur, &len);
3177 cur += len;
3178 }
3179 if (val == 0) return(1);
3180
3181 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3182 (val != '.') && (val != '-') &&
3183 (val != '_') && (val != ':') &&
3184 (!IS_COMBINING(val)) &&
3185 (!IS_EXTENDER(val)))
3186 return(0);
3187
3188 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3189 (val == '.') || (val == '-') ||
3190 (val == '_') || (val == ':') ||
3191 (IS_COMBINING(val)) ||
3192 (IS_EXTENDER(val))) {
3193 val = xmlStringCurrentChar(NULL, cur, &len);
3194 cur += len;
3195 }
3196 }
3197
3198 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003199
3200 return(1);
3201}
3202
3203/**
3204 * xmlValidateNotationDecl:
3205 * @ctxt: the validation context
3206 * @doc: a document instance
3207 * @nota: a notation definition
3208 *
3209 * Try to validate a single notation definition
3210 * basically it does the following checks as described by the
3211 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003212 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003213 * But this function get called anyway ...
3214 *
3215 * returns 1 if valid or 0 otherwise
3216 */
3217
3218int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003219xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3220 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003221 int ret = 1;
3222
3223 return(ret);
3224}
3225
3226/**
3227 * xmlValidateAttributeValue:
3228 * @type: an attribute type
3229 * @value: an attribute value
3230 *
3231 * Validate that the given attribute value match the proper production
3232 *
3233 * [ VC: ID ]
3234 * Values of type ID must match the Name production....
3235 *
3236 * [ VC: IDREF ]
3237 * Values of type IDREF must match the Name production, and values
3238 * of type IDREFS must match Names ...
3239 *
3240 * [ VC: Entity Name ]
3241 * Values of type ENTITY must match the Name production, values
3242 * of type ENTITIES must match Names ...
3243 *
3244 * [ VC: Name Token ]
3245 * Values of type NMTOKEN must match the Nmtoken production; values
3246 * of type NMTOKENS must match Nmtokens.
3247 *
3248 * returns 1 if valid or 0 otherwise
3249 */
3250
3251int
3252xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3253 switch (type) {
3254 case XML_ATTRIBUTE_ENTITIES:
3255 case XML_ATTRIBUTE_IDREFS:
3256 return(xmlValidateNamesValue(value));
3257 case XML_ATTRIBUTE_ENTITY:
3258 case XML_ATTRIBUTE_IDREF:
3259 case XML_ATTRIBUTE_ID:
3260 case XML_ATTRIBUTE_NOTATION:
3261 return(xmlValidateNameValue(value));
3262 case XML_ATTRIBUTE_NMTOKENS:
3263 case XML_ATTRIBUTE_ENUMERATION:
3264 return(xmlValidateNmtokensValue(value));
3265 case XML_ATTRIBUTE_NMTOKEN:
3266 return(xmlValidateNmtokenValue(value));
3267 case XML_ATTRIBUTE_CDATA:
3268 break;
3269 }
3270 return(1);
3271}
3272
3273/**
3274 * xmlValidateAttributeValue2:
3275 * @ctxt: the validation context
3276 * @doc: the document
3277 * @name: the attribute name (used for error reporting only)
3278 * @type: the attribute type
3279 * @value: the attribute value
3280 *
3281 * Validate that the given attribute value match a given type.
3282 * This typically cannot be done before having finished parsing
3283 * the subsets.
3284 *
3285 * [ VC: IDREF ]
3286 * Values of type IDREF must match one of the declared IDs
3287 * Values of type IDREFS must match a sequence of the declared IDs
3288 * each Name must match the value of an ID attribute on some element
3289 * in the XML document; i.e. IDREF values must match the value of
3290 * some ID attribute
3291 *
3292 * [ VC: Entity Name ]
3293 * Values of type ENTITY must match one declared entity
3294 * Values of type ENTITIES must match a sequence of declared entities
3295 *
3296 * [ VC: Notation Attributes ]
3297 * all notation names in the declaration must be declared.
3298 *
3299 * returns 1 if valid or 0 otherwise
3300 */
3301
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003302static int
Owen Taylor3473f882001-02-23 17:55:21 +00003303xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3304 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3305 int ret = 1;
3306 switch (type) {
3307 case XML_ATTRIBUTE_IDREFS:
3308 case XML_ATTRIBUTE_IDREF:
3309 case XML_ATTRIBUTE_ID:
3310 case XML_ATTRIBUTE_NMTOKENS:
3311 case XML_ATTRIBUTE_ENUMERATION:
3312 case XML_ATTRIBUTE_NMTOKEN:
3313 case XML_ATTRIBUTE_CDATA:
3314 break;
3315 case XML_ATTRIBUTE_ENTITY: {
3316 xmlEntityPtr ent;
3317
3318 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003319 if ((ent == NULL) && (doc->standalone == 1)) {
3320 doc->standalone = 0;
3321 ent = xmlGetDocEntity(doc, value);
3322 if (ent != NULL) {
3323 VERROR(ctxt->userData,
3324"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3325 name, value);
3326 /* WAIT to get answer from the Core WG on this
3327 ret = 0;
3328 */
3329 }
3330 }
Owen Taylor3473f882001-02-23 17:55:21 +00003331 if (ent == NULL) {
3332 VERROR(ctxt->userData,
3333 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3334 name, value);
3335 ret = 0;
3336 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3337 VERROR(ctxt->userData,
3338 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3339 name, value);
3340 ret = 0;
3341 }
3342 break;
3343 }
3344 case XML_ATTRIBUTE_ENTITIES: {
3345 xmlChar *dup, *nam = NULL, *cur, save;
3346 xmlEntityPtr ent;
3347
3348 dup = xmlStrdup(value);
3349 if (dup == NULL)
3350 return(0);
3351 cur = dup;
3352 while (*cur != 0) {
3353 nam = cur;
3354 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3355 save = *cur;
3356 *cur = 0;
3357 ent = xmlGetDocEntity(doc, nam);
3358 if (ent == NULL) {
3359 VERROR(ctxt->userData,
3360 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3361 name, nam);
3362 ret = 0;
3363 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3364 VERROR(ctxt->userData,
3365 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3366 name, nam);
3367 ret = 0;
3368 }
3369 if (save == 0)
3370 break;
3371 *cur = save;
3372 while (IS_BLANK(*cur)) cur++;
3373 }
3374 xmlFree(dup);
3375 break;
3376 }
3377 case XML_ATTRIBUTE_NOTATION: {
3378 xmlNotationPtr nota;
3379
3380 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3381 if ((nota == NULL) && (doc->extSubset != NULL))
3382 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3383
3384 if (nota == NULL) {
3385 VERROR(ctxt->userData,
3386 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3387 name, value);
3388 ret = 0;
3389 }
3390 break;
3391 }
3392 }
3393 return(ret);
3394}
3395
3396/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003397 * xmlValidCtxtNormalizeAttributeValue:
3398 * @ctxt: the validation context
3399 * @doc: the document
3400 * @elem: the parent
3401 * @name: the attribute name
3402 * @value: the attribute value
3403 * @ctxt: the validation context or NULL
3404 *
3405 * Does the validation related extra step of the normalization of attribute
3406 * values:
3407 *
3408 * If the declared value is not CDATA, then the XML processor must further
3409 * process the normalized attribute value by discarding any leading and
3410 * trailing space (#x20) characters, and by replacing sequences of space
3411 * (#x20) characters by single space (#x20) character.
3412 *
3413 * Also check VC: Standalone Document Declaration in P32, and update
3414 * ctxt->valid accordingly
3415 *
3416 * returns a new normalized string if normalization is needed, NULL otherwise
3417 * the caller must free the returned value.
3418 */
3419
3420xmlChar *
3421xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3422 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3423 xmlChar *ret, *dst;
3424 const xmlChar *src;
3425 xmlAttributePtr attrDecl = NULL;
3426 int extsubset = 0;
3427
3428 if (doc == NULL) return(NULL);
3429 if (elem == NULL) return(NULL);
3430 if (name == NULL) return(NULL);
3431 if (value == NULL) return(NULL);
3432
3433 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003434 xmlChar fn[50];
3435 xmlChar *fullname;
3436
3437 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3438 if (fullname == NULL)
3439 return(0);
3440 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003441 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003442 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003443 if (attrDecl != NULL)
3444 extsubset = 1;
3445 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003446 if ((fullname != fn) && (fullname != elem->name))
3447 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003448 }
3449 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3450 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3451 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3452 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3453 if (attrDecl != NULL)
3454 extsubset = 1;
3455 }
3456
3457 if (attrDecl == NULL)
3458 return(NULL);
3459 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3460 return(NULL);
3461
3462 ret = xmlStrdup(value);
3463 if (ret == NULL)
3464 return(NULL);
3465 src = value;
3466 dst = ret;
3467 while (*src == 0x20) src++;
3468 while (*src != 0) {
3469 if (*src == 0x20) {
3470 while (*src == 0x20) src++;
3471 if (*src != 0)
3472 *dst++ = 0x20;
3473 } else {
3474 *dst++ = *src++;
3475 }
3476 }
3477 *dst = 0;
3478 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3479 VERROR(ctxt->userData,
3480"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3481 name, elem->name);
3482 ctxt->valid = 0;
3483 }
3484 return(ret);
3485}
3486
3487/**
Owen Taylor3473f882001-02-23 17:55:21 +00003488 * xmlValidNormalizeAttributeValue:
3489 * @doc: the document
3490 * @elem: the parent
3491 * @name: the attribute name
3492 * @value: the attribute value
3493 *
3494 * Does the validation related extra step of the normalization of attribute
3495 * values:
3496 *
3497 * If the declared value is not CDATA, then the XML processor must further
3498 * process the normalized attribute value by discarding any leading and
3499 * trailing space (#x20) characters, and by replacing sequences of space
3500 * (#x20) characters by single space (#x20) character.
3501 *
3502 * returns a new normalized string if normalization is needed, NULL otherwise
3503 * the caller must free the returned value.
3504 */
3505
3506xmlChar *
3507xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3508 const xmlChar *name, const xmlChar *value) {
3509 xmlChar *ret, *dst;
3510 const xmlChar *src;
3511 xmlAttributePtr attrDecl = NULL;
3512
3513 if (doc == NULL) return(NULL);
3514 if (elem == NULL) return(NULL);
3515 if (name == NULL) return(NULL);
3516 if (value == NULL) return(NULL);
3517
3518 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003519 xmlChar fn[50];
3520 xmlChar *fullname;
3521
3522 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3523 if (fullname == NULL)
3524 return(0);
3525 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003526 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003527 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3528 if ((fullname != fn) && (fullname != elem->name))
3529 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003530 }
3531 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3532 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3533 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3534
3535 if (attrDecl == NULL)
3536 return(NULL);
3537 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3538 return(NULL);
3539
3540 ret = xmlStrdup(value);
3541 if (ret == NULL)
3542 return(NULL);
3543 src = value;
3544 dst = ret;
3545 while (*src == 0x20) src++;
3546 while (*src != 0) {
3547 if (*src == 0x20) {
3548 while (*src == 0x20) src++;
3549 if (*src != 0)
3550 *dst++ = 0x20;
3551 } else {
3552 *dst++ = *src++;
3553 }
3554 }
3555 *dst = 0;
3556 return(ret);
3557}
3558
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003559static void
Owen Taylor3473f882001-02-23 17:55:21 +00003560xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003561 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003562 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3563}
3564
3565/**
3566 * xmlValidateAttributeDecl:
3567 * @ctxt: the validation context
3568 * @doc: a document instance
3569 * @attr: an attribute definition
3570 *
3571 * Try to validate a single attribute definition
3572 * basically it does the following checks as described by the
3573 * XML-1.0 recommendation:
3574 * - [ VC: Attribute Default Legal ]
3575 * - [ VC: Enumeration ]
3576 * - [ VC: ID Attribute Default ]
3577 *
3578 * The ID/IDREF uniqueness and matching are done separately
3579 *
3580 * returns 1 if valid or 0 otherwise
3581 */
3582
3583int
3584xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3585 xmlAttributePtr attr) {
3586 int ret = 1;
3587 int val;
3588 CHECK_DTD;
3589 if(attr == NULL) return(1);
3590
3591 /* Attribute Default Legal */
3592 /* Enumeration */
3593 if (attr->defaultValue != NULL) {
3594 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3595 if (val == 0) {
3596 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003597 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003598 attr->name, attr->elem);
3599 }
3600 ret &= val;
3601 }
3602
3603 /* ID Attribute Default */
3604 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3605 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3606 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3607 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003608 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003609 attr->name, attr->elem);
3610 ret = 0;
3611 }
3612
3613 /* One ID per Element Type */
3614 if (attr->atype == XML_ATTRIBUTE_ID) {
3615 int nbId;
3616
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003617 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003618 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3619 attr->elem);
3620 if (elem != NULL) {
3621 nbId = xmlScanIDAttributeDecl(NULL, elem);
3622 } else {
3623 xmlAttributeTablePtr table;
3624
3625 /*
3626 * The attribute may be declared in the internal subset and the
3627 * element in the external subset.
3628 */
3629 nbId = 0;
3630 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3631 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3632 xmlValidateAttributeIdCallback, &nbId);
3633 }
3634 if (nbId > 1) {
3635 VERROR(ctxt->userData,
3636 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3637 attr->elem, nbId, attr->name);
3638 } else if (doc->extSubset != NULL) {
3639 int extId = 0;
3640 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3641 if (elem != NULL) {
3642 extId = xmlScanIDAttributeDecl(NULL, elem);
3643 }
3644 if (extId > 1) {
3645 VERROR(ctxt->userData,
3646 "Element %s has %d ID attribute defined in the external subset : %s\n",
3647 attr->elem, extId, attr->name);
3648 } else if (extId + nbId > 1) {
3649 VERROR(ctxt->userData,
3650"Element %s has ID attributes defined in the internal and external subset : %s\n",
3651 attr->elem, attr->name);
3652 }
3653 }
3654 }
3655
3656 /* Validity Constraint: Enumeration */
3657 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3658 xmlEnumerationPtr tree = attr->tree;
3659 while (tree != NULL) {
3660 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3661 tree = tree->next;
3662 }
3663 if (tree == NULL) {
3664 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003665"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003666 attr->defaultValue, attr->name, attr->elem);
3667 ret = 0;
3668 }
3669 }
3670
3671 return(ret);
3672}
3673
3674/**
3675 * xmlValidateElementDecl:
3676 * @ctxt: the validation context
3677 * @doc: a document instance
3678 * @elem: an element definition
3679 *
3680 * Try to validate a single element definition
3681 * basically it does the following checks as described by the
3682 * XML-1.0 recommendation:
3683 * - [ VC: One ID per Element Type ]
3684 * - [ VC: No Duplicate Types ]
3685 * - [ VC: Unique Element Type Declaration ]
3686 *
3687 * returns 1 if valid or 0 otherwise
3688 */
3689
3690int
3691xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3692 xmlElementPtr elem) {
3693 int ret = 1;
3694 xmlElementPtr tst;
3695
3696 CHECK_DTD;
3697
3698 if (elem == NULL) return(1);
3699
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003700#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003701#ifdef LIBXML_REGEXP_ENABLED
3702 /* Build the regexp associated to the content model */
3703 ret = xmlValidBuildContentModel(ctxt, elem);
3704#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003705#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003706
Owen Taylor3473f882001-02-23 17:55:21 +00003707 /* No Duplicate Types */
3708 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3709 xmlElementContentPtr cur, next;
3710 const xmlChar *name;
3711
3712 cur = elem->content;
3713 while (cur != NULL) {
3714 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3715 if (cur->c1 == NULL) break;
3716 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3717 name = cur->c1->name;
3718 next = cur->c2;
3719 while (next != NULL) {
3720 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3721 if (xmlStrEqual(next->name, name)) {
3722 VERROR(ctxt->userData,
3723 "Definition of %s has duplicate references of %s\n",
3724 elem->name, name);
3725 ret = 0;
3726 }
3727 break;
3728 }
3729 if (next->c1 == NULL) break;
3730 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3731 if (xmlStrEqual(next->c1->name, name)) {
3732 VERROR(ctxt->userData,
3733 "Definition of %s has duplicate references of %s\n",
3734 elem->name, name);
3735 ret = 0;
3736 }
3737 next = next->c2;
3738 }
3739 }
3740 cur = cur->c2;
3741 }
3742 }
3743
3744 /* VC: Unique Element Type Declaration */
3745 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003746 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003747 ((tst->prefix == elem->prefix) ||
3748 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003749 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003750 VERROR(ctxt->userData, "Redefinition of element %s\n",
3751 elem->name);
3752 ret = 0;
3753 }
3754 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003755 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003756 ((tst->prefix == elem->prefix) ||
3757 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003758 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003759 VERROR(ctxt->userData, "Redefinition of element %s\n",
3760 elem->name);
3761 ret = 0;
3762 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003763 /* One ID per Element Type
3764 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003765 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3766 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003767 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003768 return(ret);
3769}
3770
3771/**
3772 * xmlValidateOneAttribute:
3773 * @ctxt: the validation context
3774 * @doc: a document instance
3775 * @elem: an element instance
3776 * @attr: an attribute instance
3777 * @value: the attribute value (without entities processing)
3778 *
3779 * Try to validate a single attribute for an element
3780 * basically it does the following checks as described by the
3781 * XML-1.0 recommendation:
3782 * - [ VC: Attribute Value Type ]
3783 * - [ VC: Fixed Attribute Default ]
3784 * - [ VC: Entity Name ]
3785 * - [ VC: Name Token ]
3786 * - [ VC: ID ]
3787 * - [ VC: IDREF ]
3788 * - [ VC: Entity Name ]
3789 * - [ VC: Notation Attributes ]
3790 *
3791 * The ID/IDREF uniqueness and matching are done separately
3792 *
3793 * returns 1 if valid or 0 otherwise
3794 */
3795
3796int
3797xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3798 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3799 /* xmlElementPtr elemDecl; */
3800 xmlAttributePtr attrDecl = NULL;
3801 int val;
3802 int ret = 1;
3803
3804 CHECK_DTD;
3805 if ((elem == NULL) || (elem->name == NULL)) return(0);
3806 if ((attr == NULL) || (attr->name == NULL)) return(0);
3807
3808 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003809 xmlChar fn[50];
3810 xmlChar *fullname;
3811
3812 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3813 if (fullname == NULL)
3814 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003815 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003816 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003817 attr->name, attr->ns->prefix);
3818 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003819 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003820 attr->name, attr->ns->prefix);
3821 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003822 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003823 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3824 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00003825 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003826 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003827 if ((fullname != fn) && (fullname != elem->name))
3828 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003829 }
3830 if (attrDecl == NULL) {
3831 if (attr->ns != NULL) {
3832 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3833 attr->name, attr->ns->prefix);
3834 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3835 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3836 attr->name, attr->ns->prefix);
3837 } else {
3838 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3839 elem->name, attr->name);
3840 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3841 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3842 elem->name, attr->name);
3843 }
3844 }
3845
3846
3847 /* Validity Constraint: Attribute Value Type */
3848 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003849 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003850 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003851 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003852 attr->name, elem->name);
3853 return(0);
3854 }
3855 attr->atype = attrDecl->atype;
3856
3857 val = xmlValidateAttributeValue(attrDecl->atype, value);
3858 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003859 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003860 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003861 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003862 attr->name, elem->name);
3863 ret = 0;
3864 }
3865
3866 /* Validity constraint: Fixed Attribute Default */
3867 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3868 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003869 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003870 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003871 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003872 attr->name, elem->name, attrDecl->defaultValue);
3873 ret = 0;
3874 }
3875 }
3876
3877 /* Validity Constraint: ID uniqueness */
3878 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3879 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3880 ret = 0;
3881 }
3882
3883 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3884 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3885 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3886 ret = 0;
3887 }
3888
3889 /* Validity Constraint: Notation Attributes */
3890 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3891 xmlEnumerationPtr tree = attrDecl->tree;
3892 xmlNotationPtr nota;
3893
3894 /* First check that the given NOTATION was declared */
3895 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3896 if (nota == NULL)
3897 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3898
3899 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003900 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003901 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003902 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003903 value, attr->name, elem->name);
3904 ret = 0;
3905 }
3906
3907 /* Second, verify that it's among the list */
3908 while (tree != NULL) {
3909 if (xmlStrEqual(tree->name, value)) break;
3910 tree = tree->next;
3911 }
3912 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003913 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003914 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003915"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003916 value, attr->name, elem->name);
3917 ret = 0;
3918 }
3919 }
3920
3921 /* Validity Constraint: Enumeration */
3922 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3923 xmlEnumerationPtr tree = attrDecl->tree;
3924 while (tree != NULL) {
3925 if (xmlStrEqual(tree->name, value)) break;
3926 tree = tree->next;
3927 }
3928 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003929 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003930 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003931 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003932 value, attr->name, elem->name);
3933 ret = 0;
3934 }
3935 }
3936
3937 /* Fixed Attribute Default */
3938 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3939 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003940 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003941 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003942 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003943 attr->name, elem->name, attrDecl->defaultValue);
3944 ret = 0;
3945 }
3946
3947 /* Extra check for the attribute value */
3948 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3949 attrDecl->atype, value);
3950
3951 return(ret);
3952}
3953
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003954/**
3955 * xmlValidateOneNamespace:
3956 * @ctxt: the validation context
3957 * @doc: a document instance
3958 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00003959 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003960 * @ns: an namespace declaration instance
3961 * @value: the attribute value (without entities processing)
3962 *
3963 * Try to validate a single namespace declaration for an element
3964 * basically it does the following checks as described by the
3965 * XML-1.0 recommendation:
3966 * - [ VC: Attribute Value Type ]
3967 * - [ VC: Fixed Attribute Default ]
3968 * - [ VC: Entity Name ]
3969 * - [ VC: Name Token ]
3970 * - [ VC: ID ]
3971 * - [ VC: IDREF ]
3972 * - [ VC: Entity Name ]
3973 * - [ VC: Notation Attributes ]
3974 *
3975 * The ID/IDREF uniqueness and matching are done separately
3976 *
3977 * returns 1 if valid or 0 otherwise
3978 */
3979
3980int
3981xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3982xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
3983 /* xmlElementPtr elemDecl; */
3984 xmlAttributePtr attrDecl = NULL;
3985 int val;
3986 int ret = 1;
3987
3988 CHECK_DTD;
3989 if ((elem == NULL) || (elem->name == NULL)) return(0);
3990 if ((ns == NULL) || (ns->href == NULL)) return(0);
3991
3992 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003993 xmlChar fn[50];
3994 xmlChar *fullname;
3995
3996 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
3997 if (fullname == NULL) {
3998 VERROR(ctxt->userData, "Out of memory\n");
3999 return(0);
4000 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004001 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004002 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004003 ns->prefix, BAD_CAST "xmlns");
4004 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004005 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004006 ns->prefix, BAD_CAST "xmlns");
4007 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004008 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004009 BAD_CAST "xmlns");
4010 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004011 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004012 BAD_CAST "xmlns");
4013 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004014 if ((fullname != fn) && (fullname != elem->name))
4015 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004016 }
4017 if (attrDecl == NULL) {
4018 if (ns->prefix != NULL) {
4019 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4020 ns->prefix, BAD_CAST "xmlns");
4021 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4022 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4023 ns->prefix, BAD_CAST "xmlns");
4024 } else {
4025 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4026 elem->name, BAD_CAST "xmlns");
4027 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4028 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4029 elem->name, BAD_CAST "xmlns");
4030 }
4031 }
4032
4033
4034 /* Validity Constraint: Attribute Value Type */
4035 if (attrDecl == NULL) {
4036 VECTXT(ctxt, elem);
4037 if (ns->prefix != NULL) {
4038 VERROR(ctxt->userData,
4039 "No declaration for attribute xmlns:%s of element %s\n",
4040 ns->prefix, elem->name);
4041 } else {
4042 VERROR(ctxt->userData,
4043 "No declaration for attribute xmlns of element %s\n",
4044 elem->name);
4045 }
4046 return(0);
4047 }
4048
4049 val = xmlValidateAttributeValue(attrDecl->atype, value);
4050 if (val == 0) {
4051 VECTXT(ctxt, elem);
4052 if (ns->prefix != NULL) {
4053 VERROR(ctxt->userData,
4054 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
4055 ns->prefix, elem->name);
4056 } else {
4057 VERROR(ctxt->userData,
4058 "Syntax of value for attribute xmlns of %s is not valid\n",
4059 elem->name);
4060 }
4061 ret = 0;
4062 }
4063
4064 /* Validity constraint: Fixed Attribute Default */
4065 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4066 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4067 VECTXT(ctxt, elem);
4068 if (ns->prefix != NULL) {
4069 VERROR(ctxt->userData,
4070 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4071 ns->prefix, elem->name, attrDecl->defaultValue);
4072 } else {
4073 VERROR(ctxt->userData,
4074 "Value for attribute xmlns of %s is different from default \"%s\"\n",
4075 elem->name, attrDecl->defaultValue);
4076 }
4077 ret = 0;
4078 }
4079 }
4080
4081 /* Validity Constraint: ID uniqueness */
4082 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4083 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4084 ret = 0;
4085 }
4086
4087 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4088 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4089 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4090 ret = 0;
4091 }
4092
4093 /* Validity Constraint: Notation Attributes */
4094 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4095 xmlEnumerationPtr tree = attrDecl->tree;
4096 xmlNotationPtr nota;
4097
4098 /* First check that the given NOTATION was declared */
4099 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4100 if (nota == NULL)
4101 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4102
4103 if (nota == NULL) {
4104 VECTXT(ctxt, elem);
4105 if (ns->prefix != NULL) {
4106 VERROR(ctxt->userData,
4107 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4108 value, ns->prefix, elem->name);
4109 } else {
4110 VERROR(ctxt->userData,
4111 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
4112 value, elem->name);
4113 }
4114 ret = 0;
4115 }
4116
4117 /* Second, verify that it's among the list */
4118 while (tree != NULL) {
4119 if (xmlStrEqual(tree->name, value)) break;
4120 tree = tree->next;
4121 }
4122 if (tree == NULL) {
4123 VECTXT(ctxt, elem);
4124 if (ns->prefix != NULL) {
4125 VERROR(ctxt->userData,
4126"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4127 value, ns->prefix, elem->name);
4128 } else {
4129 VERROR(ctxt->userData,
4130"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
4131 value, elem->name);
4132 }
4133 ret = 0;
4134 }
4135 }
4136
4137 /* Validity Constraint: Enumeration */
4138 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4139 xmlEnumerationPtr tree = attrDecl->tree;
4140 while (tree != NULL) {
4141 if (xmlStrEqual(tree->name, value)) break;
4142 tree = tree->next;
4143 }
4144 if (tree == NULL) {
4145 VECTXT(ctxt, elem);
4146 if (ns->prefix != NULL) {
4147 VERROR(ctxt->userData,
4148"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4149 value, ns->prefix, elem->name);
4150 } else {
4151 VERROR(ctxt->userData,
4152"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4153 value, elem->name);
4154 }
4155 ret = 0;
4156 }
4157 }
4158
4159 /* Fixed Attribute Default */
4160 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4161 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4162 VECTXT(ctxt, elem);
4163 if (ns->prefix != NULL) {
4164 VERROR(ctxt->userData,
4165 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4166 ns->prefix, elem->name, attrDecl->defaultValue);
4167 } else {
4168 VERROR(ctxt->userData,
4169 "Value for attribute xmlns of %s must be \"%s\"\n",
4170 elem->name, attrDecl->defaultValue);
4171 }
4172 ret = 0;
4173 }
4174
4175 /* Extra check for the attribute value */
4176 if (ns->prefix != NULL) {
4177 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4178 attrDecl->atype, value);
4179 } else {
4180 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4181 attrDecl->atype, value);
4182 }
4183
4184 return(ret);
4185}
4186
Daniel Veillard118aed72002-09-24 14:13:13 +00004187#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004188/**
4189 * xmlValidateSkipIgnorable:
4190 * @ctxt: the validation context
4191 * @child: the child list
4192 *
4193 * Skip ignorable elements w.r.t. the validation process
4194 *
4195 * returns the first element to consider for validation of the content model
4196 */
4197
4198static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004199xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004200 while (child != NULL) {
4201 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004202 /* These things are ignored (skipped) during validation. */
4203 case XML_PI_NODE:
4204 case XML_COMMENT_NODE:
4205 case XML_XINCLUDE_START:
4206 case XML_XINCLUDE_END:
4207 child = child->next;
4208 break;
4209 case XML_TEXT_NODE:
4210 if (xmlIsBlankNode(child))
4211 child = child->next;
4212 else
4213 return(child);
4214 break;
4215 /* keep current node */
4216 default:
4217 return(child);
4218 }
4219 }
4220 return(child);
4221}
4222
4223/**
4224 * xmlValidateElementType:
4225 * @ctxt: the validation context
4226 *
4227 * Try to validate the content model of an element internal function
4228 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004229 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4230 * reference is found and -3 if the validation succeeded but
4231 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004232 */
4233
4234static int
4235xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004236 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004237 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004238
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004239 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004240 if ((NODE == NULL) && (CONT == NULL))
4241 return(1);
4242 if ((NODE == NULL) &&
4243 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4244 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4245 return(1);
4246 }
4247 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004248 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004249 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004250
4251 /*
4252 * We arrive here when more states need to be examined
4253 */
4254cont:
4255
4256 /*
4257 * We just recovered from a rollback generated by a possible
4258 * epsilon transition, go directly to the analysis phase
4259 */
4260 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004261 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004262 DEBUG_VALID_STATE(NODE, CONT)
4263 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004264 goto analyze;
4265 }
4266
4267 DEBUG_VALID_STATE(NODE, CONT)
4268 /*
4269 * we may have to save a backup state here. This is the equivalent
4270 * of handling epsilon transition in NFAs.
4271 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004272 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004273 ((CONT->parent == NULL) ||
4274 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004275 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004276 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004277 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004278 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004279 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4280 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004281 }
4282
4283
4284 /*
4285 * Check first if the content matches
4286 */
4287 switch (CONT->type) {
4288 case XML_ELEMENT_CONTENT_PCDATA:
4289 if (NODE == NULL) {
4290 DEBUG_VALID_MSG("pcdata failed no node");
4291 ret = 0;
4292 break;
4293 }
4294 if (NODE->type == XML_TEXT_NODE) {
4295 DEBUG_VALID_MSG("pcdata found, skip to next");
4296 /*
4297 * go to next element in the content model
4298 * skipping ignorable elems
4299 */
4300 do {
4301 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004302 NODE = xmlValidateSkipIgnorable(NODE);
4303 if ((NODE != NULL) &&
4304 (NODE->type == XML_ENTITY_REF_NODE))
4305 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004306 } while ((NODE != NULL) &&
4307 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004308 (NODE->type != XML_TEXT_NODE) &&
4309 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004310 ret = 1;
4311 break;
4312 } else {
4313 DEBUG_VALID_MSG("pcdata failed");
4314 ret = 0;
4315 break;
4316 }
4317 break;
4318 case XML_ELEMENT_CONTENT_ELEMENT:
4319 if (NODE == NULL) {
4320 DEBUG_VALID_MSG("element failed no node");
4321 ret = 0;
4322 break;
4323 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004324 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4325 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004326 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004327 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4328 ret = (CONT->prefix == NULL);
4329 } else if (CONT->prefix == NULL) {
4330 ret = 0;
4331 } else {
4332 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4333 }
4334 }
4335 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004336 DEBUG_VALID_MSG("element found, skip to next");
4337 /*
4338 * go to next element in the content model
4339 * skipping ignorable elems
4340 */
4341 do {
4342 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004343 NODE = xmlValidateSkipIgnorable(NODE);
4344 if ((NODE != NULL) &&
4345 (NODE->type == XML_ENTITY_REF_NODE))
4346 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004347 } while ((NODE != NULL) &&
4348 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004349 (NODE->type != XML_TEXT_NODE) &&
4350 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004351 } else {
4352 DEBUG_VALID_MSG("element failed");
4353 ret = 0;
4354 break;
4355 }
4356 break;
4357 case XML_ELEMENT_CONTENT_OR:
4358 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004359 * Small optimization.
4360 */
4361 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4362 if ((NODE == NULL) ||
4363 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4364 DEPTH++;
4365 CONT = CONT->c2;
4366 goto cont;
4367 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004368 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4369 ret = (CONT->c1->prefix == NULL);
4370 } else if (CONT->c1->prefix == NULL) {
4371 ret = 0;
4372 } else {
4373 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4374 }
4375 if (ret == 0) {
4376 DEPTH++;
4377 CONT = CONT->c2;
4378 goto cont;
4379 }
Daniel Veillard85349052001-04-20 13:48:21 +00004380 }
4381
4382 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004383 * save the second branch 'or' branch
4384 */
4385 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004386 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4387 OCCURS, ROLLBACK_OR) < 0)
4388 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004389 DEPTH++;
4390 CONT = CONT->c1;
4391 goto cont;
4392 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004393 /*
4394 * Small optimization.
4395 */
4396 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4397 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4398 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4399 if ((NODE == NULL) ||
4400 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4401 DEPTH++;
4402 CONT = CONT->c2;
4403 goto cont;
4404 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004405 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4406 ret = (CONT->c1->prefix == NULL);
4407 } else if (CONT->c1->prefix == NULL) {
4408 ret = 0;
4409 } else {
4410 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4411 }
4412 if (ret == 0) {
4413 DEPTH++;
4414 CONT = CONT->c2;
4415 goto cont;
4416 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004417 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004418 DEPTH++;
4419 CONT = CONT->c1;
4420 goto cont;
4421 }
4422
4423 /*
4424 * At this point handle going up in the tree
4425 */
4426 if (ret == -1) {
4427 DEBUG_VALID_MSG("error found returning");
4428 return(ret);
4429 }
4430analyze:
4431 while (CONT != NULL) {
4432 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004433 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004434 * this level.
4435 */
4436 if (ret == 0) {
4437 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004438 xmlNodePtr cur;
4439
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004440 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004441 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004442 DEBUG_VALID_MSG("Once branch failed, rollback");
4443 if (vstateVPop(ctxt) < 0 ) {
4444 DEBUG_VALID_MSG("exhaustion, failed");
4445 return(0);
4446 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004447 if (cur != ctxt->vstate->node)
4448 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004449 goto cont;
4450 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004451 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004452 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004453 DEBUG_VALID_MSG("Plus branch failed, rollback");
4454 if (vstateVPop(ctxt) < 0 ) {
4455 DEBUG_VALID_MSG("exhaustion, failed");
4456 return(0);
4457 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004458 if (cur != ctxt->vstate->node)
4459 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004460 goto cont;
4461 }
4462 DEBUG_VALID_MSG("Plus branch found");
4463 ret = 1;
4464 break;
4465 case XML_ELEMENT_CONTENT_MULT:
4466#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004467 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004468 DEBUG_VALID_MSG("Mult branch failed");
4469 } else {
4470 DEBUG_VALID_MSG("Mult branch found");
4471 }
4472#endif
4473 ret = 1;
4474 break;
4475 case XML_ELEMENT_CONTENT_OPT:
4476 DEBUG_VALID_MSG("Option branch failed");
4477 ret = 1;
4478 break;
4479 }
4480 } else {
4481 switch (CONT->ocur) {
4482 case XML_ELEMENT_CONTENT_OPT:
4483 DEBUG_VALID_MSG("Option branch succeeded");
4484 ret = 1;
4485 break;
4486 case XML_ELEMENT_CONTENT_ONCE:
4487 DEBUG_VALID_MSG("Once branch succeeded");
4488 ret = 1;
4489 break;
4490 case XML_ELEMENT_CONTENT_PLUS:
4491 if (STATE == ROLLBACK_PARENT) {
4492 DEBUG_VALID_MSG("Plus branch rollback");
4493 ret = 1;
4494 break;
4495 }
4496 if (NODE == NULL) {
4497 DEBUG_VALID_MSG("Plus branch exhausted");
4498 ret = 1;
4499 break;
4500 }
4501 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004502 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004503 goto cont;
4504 case XML_ELEMENT_CONTENT_MULT:
4505 if (STATE == ROLLBACK_PARENT) {
4506 DEBUG_VALID_MSG("Mult branch rollback");
4507 ret = 1;
4508 break;
4509 }
4510 if (NODE == NULL) {
4511 DEBUG_VALID_MSG("Mult branch exhausted");
4512 ret = 1;
4513 break;
4514 }
4515 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004516 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004517 goto cont;
4518 }
4519 }
4520 STATE = 0;
4521
4522 /*
4523 * Then act accordingly at the parent level
4524 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004525 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004526 if (CONT->parent == NULL)
4527 break;
4528
4529 switch (CONT->parent->type) {
4530 case XML_ELEMENT_CONTENT_PCDATA:
4531 DEBUG_VALID_MSG("Error: parent pcdata");
4532 return(-1);
4533 case XML_ELEMENT_CONTENT_ELEMENT:
4534 DEBUG_VALID_MSG("Error: parent element");
4535 return(-1);
4536 case XML_ELEMENT_CONTENT_OR:
4537 if (ret == 1) {
4538 DEBUG_VALID_MSG("Or succeeded");
4539 CONT = CONT->parent;
4540 DEPTH--;
4541 } else {
4542 DEBUG_VALID_MSG("Or failed");
4543 CONT = CONT->parent;
4544 DEPTH--;
4545 }
4546 break;
4547 case XML_ELEMENT_CONTENT_SEQ:
4548 if (ret == 0) {
4549 DEBUG_VALID_MSG("Sequence failed");
4550 CONT = CONT->parent;
4551 DEPTH--;
4552 } else if (CONT == CONT->parent->c1) {
4553 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4554 CONT = CONT->parent->c2;
4555 goto cont;
4556 } else {
4557 DEBUG_VALID_MSG("Sequence succeeded");
4558 CONT = CONT->parent;
4559 DEPTH--;
4560 }
4561 }
4562 }
4563 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004564 xmlNodePtr cur;
4565
4566 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004567 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4568 if (vstateVPop(ctxt) < 0 ) {
4569 DEBUG_VALID_MSG("exhaustion, failed");
4570 return(0);
4571 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004572 if (cur != ctxt->vstate->node)
4573 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004574 goto cont;
4575 }
4576 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004577 xmlNodePtr cur;
4578
4579 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004580 DEBUG_VALID_MSG("Failure, rollback");
4581 if (vstateVPop(ctxt) < 0 ) {
4582 DEBUG_VALID_MSG("exhaustion, failed");
4583 return(0);
4584 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004585 if (cur != ctxt->vstate->node)
4586 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004587 goto cont;
4588 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004589 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004590}
Daniel Veillard23e73572002-09-19 19:56:43 +00004591#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004592
4593/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004594 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004595 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004596 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004597 * @content: An element
4598 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4599 *
4600 * This will dump the list of elements to the buffer
4601 * Intended just for the debug routine
4602 */
4603static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004604xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004605 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004606 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004607
4608 if (node == NULL) return;
4609 if (glob) strcat(buf, "(");
4610 cur = node;
4611 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004612 len = strlen(buf);
4613 if (size - len < 50) {
4614 if ((size - len > 4) && (buf[len - 1] != '.'))
4615 strcat(buf, " ...");
4616 return;
4617 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004618 switch (cur->type) {
4619 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004620 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004621 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004622 if ((size - len > 4) && (buf[len - 1] != '.'))
4623 strcat(buf, " ...");
4624 return;
4625 }
4626 strcat(buf, (char *) cur->ns->prefix);
4627 strcat(buf, ":");
4628 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004629 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004630 if ((size - len > 4) && (buf[len - 1] != '.'))
4631 strcat(buf, " ...");
4632 return;
4633 }
4634 strcat(buf, (char *) cur->name);
4635 if (cur->next != NULL)
4636 strcat(buf, " ");
4637 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004638 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004639 if (xmlIsBlankNode(cur))
4640 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004641 case XML_CDATA_SECTION_NODE:
4642 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004643 strcat(buf, "CDATA");
4644 if (cur->next != NULL)
4645 strcat(buf, " ");
4646 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004647 case XML_ATTRIBUTE_NODE:
4648 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004649#ifdef LIBXML_DOCB_ENABLED
4650 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004651#endif
4652 case XML_HTML_DOCUMENT_NODE:
4653 case XML_DOCUMENT_TYPE_NODE:
4654 case XML_DOCUMENT_FRAG_NODE:
4655 case XML_NOTATION_NODE:
4656 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004657 strcat(buf, "???");
4658 if (cur->next != NULL)
4659 strcat(buf, " ");
4660 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004661 case XML_ENTITY_NODE:
4662 case XML_PI_NODE:
4663 case XML_DTD_NODE:
4664 case XML_COMMENT_NODE:
4665 case XML_ELEMENT_DECL:
4666 case XML_ATTRIBUTE_DECL:
4667 case XML_ENTITY_DECL:
4668 case XML_XINCLUDE_START:
4669 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004670 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004671 }
4672 cur = cur->next;
4673 }
4674 if (glob) strcat(buf, ")");
4675}
4676
4677/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004678 * xmlValidateElementContent:
4679 * @ctxt: the validation context
4680 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004681 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004682 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004683 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004684 *
4685 * Try to validate the content model of an element
4686 *
4687 * returns 1 if valid or 0 if not and -1 in case of error
4688 */
4689
4690static int
4691xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004692 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004693 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004694#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004695 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004696#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004697 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004698 xmlElementContentPtr cont;
4699 const xmlChar *name;
4700
4701 if (elemDecl == NULL)
4702 return(-1);
4703 cont = elemDecl->content;
4704 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004705
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004706#ifdef LIBXML_REGEXP_ENABLED
4707 /* Build the regexp associated to the content model */
4708 if (elemDecl->contModel == NULL)
4709 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4710 if (elemDecl->contModel == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004711 return(-1);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004712 } else {
4713 xmlRegExecCtxtPtr exec;
4714
Daniel Veillardec498e12003-02-05 11:01:50 +00004715 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
4716 return(-1);
4717 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004718 ctxt->nodeMax = 0;
4719 ctxt->nodeNr = 0;
4720 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004721 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4722 if (exec != NULL) {
4723 cur = child;
4724 while (cur != NULL) {
4725 switch (cur->type) {
4726 case XML_ENTITY_REF_NODE:
4727 /*
4728 * Push the current node to be able to roll back
4729 * and process within the entity
4730 */
4731 if ((cur->children != NULL) &&
4732 (cur->children->children != NULL)) {
4733 nodeVPush(ctxt, cur);
4734 cur = cur->children->children;
4735 continue;
4736 }
4737 break;
4738 case XML_TEXT_NODE:
4739 if (xmlIsBlankNode(cur))
4740 break;
4741 ret = 0;
4742 goto fail;
4743 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004744 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004745 ret = 0;
4746 goto fail;
4747 case XML_ELEMENT_NODE:
4748 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004749 xmlChar fn[50];
4750 xmlChar *fullname;
4751
4752 fullname = xmlBuildQName(cur->name,
4753 cur->ns->prefix, fn, 50);
4754 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004755 ret = -1;
4756 goto fail;
4757 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004758 ret = xmlRegExecPushString(exec, fullname, NULL);
4759 if ((fullname != fn) && (fullname != cur->name))
4760 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004761 } else {
4762 ret = xmlRegExecPushString(exec, cur->name, NULL);
4763 }
4764 break;
4765 default:
4766 break;
4767 }
4768 /*
4769 * Switch to next element
4770 */
4771 cur = cur->next;
4772 while (cur == NULL) {
4773 cur = nodeVPop(ctxt);
4774 if (cur == NULL)
4775 break;
4776 cur = cur->next;
4777 }
4778 }
4779 ret = xmlRegExecPushString(exec, NULL, NULL);
4780fail:
4781 xmlRegFreeExecCtxt(exec);
4782 }
4783 }
4784#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004785 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004786 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004787 */
4788 ctxt->vstateMax = 8;
4789 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4790 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4791 if (ctxt->vstateTab == NULL) {
4792 xmlGenericError(xmlGenericErrorContext,
4793 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004794 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004795 }
4796 /*
4797 * The first entry in the stack is reserved to the current state
4798 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004799 ctxt->nodeMax = 0;
4800 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004801 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004802 ctxt->vstate = &ctxt->vstateTab[0];
4803 ctxt->vstateNr = 1;
4804 CONT = cont;
4805 NODE = child;
4806 DEPTH = 0;
4807 OCCURS = 0;
4808 STATE = 0;
4809 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004810 if ((ret == -3) && (warn)) {
4811 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004812 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004813 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004814 /*
4815 * An entities reference appeared at this level.
4816 * Buid a minimal representation of this node content
4817 * sufficient to run the validation process on it
4818 */
4819 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004820 cur = child;
4821 while (cur != NULL) {
4822 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004823 case XML_ENTITY_REF_NODE:
4824 /*
4825 * Push the current node to be able to roll back
4826 * and process within the entity
4827 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004828 if ((cur->children != NULL) &&
4829 (cur->children->children != NULL)) {
4830 nodeVPush(ctxt, cur);
4831 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004832 continue;
4833 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004834 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004835 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004836 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004837 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004838 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004839 case XML_CDATA_SECTION_NODE:
4840 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004841 case XML_ELEMENT_NODE:
4842 /*
4843 * Allocate a new node and minimally fills in
4844 * what's required
4845 */
4846 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4847 if (tmp == NULL) {
4848 xmlGenericError(xmlGenericErrorContext,
4849 "xmlValidateElementContent : malloc failed\n");
4850 xmlFreeNodeList(repl);
4851 ret = -1;
4852 goto done;
4853 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004854 tmp->type = cur->type;
4855 tmp->name = cur->name;
4856 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004857 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004858 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004859 if (repl == NULL)
4860 repl = last = tmp;
4861 else {
4862 last->next = tmp;
4863 last = tmp;
4864 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004865 if (cur->type == XML_CDATA_SECTION_NODE) {
4866 /*
4867 * E59 spaces in CDATA does not match the
4868 * nonterminal S
4869 */
4870 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4871 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004872 break;
4873 default:
4874 break;
4875 }
4876 /*
4877 * Switch to next element
4878 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004879 cur = cur->next;
4880 while (cur == NULL) {
4881 cur = nodeVPop(ctxt);
4882 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004883 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004884 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004885 }
4886 }
4887
4888 /*
4889 * Relaunch the validation
4890 */
4891 ctxt->vstate = &ctxt->vstateTab[0];
4892 ctxt->vstateNr = 1;
4893 CONT = cont;
4894 NODE = repl;
4895 DEPTH = 0;
4896 OCCURS = 0;
4897 STATE = 0;
4898 ret = xmlValidateElementType(ctxt);
4899 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004900#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004901 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004902 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4903 char expr[5000];
4904 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004905
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004906 expr[0] = 0;
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004907 xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004908 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004909#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004910 if (repl != NULL)
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004911 xmlSnprintfElements(&list[0], 5000, repl, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004912 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004913#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004914 xmlSnprintfElements(&list[0], 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004915
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004916 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004917 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004918 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004919 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004920 name, expr, list);
4921 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004922 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004923 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004924 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004925 expr, list);
4926 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004927 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004928 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004929 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004930 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004931 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004932 name);
4933 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004934 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004935 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004936 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004937 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004938 }
4939 ret = 0;
4940 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004941 if (ret == -3)
4942 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004943
Daniel Veillard23e73572002-09-19 19:56:43 +00004944#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004945done:
4946 /*
4947 * Deallocate the copy if done, and free up the validation stack
4948 */
4949 while (repl != NULL) {
4950 tmp = repl->next;
4951 xmlFree(repl);
4952 repl = tmp;
4953 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004954 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004955 if (ctxt->vstateTab != NULL) {
4956 xmlFree(ctxt->vstateTab);
4957 ctxt->vstateTab = NULL;
4958 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004959#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004960 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004961 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004962 if (ctxt->nodeTab != NULL) {
4963 xmlFree(ctxt->nodeTab);
4964 ctxt->nodeTab = NULL;
4965 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004966 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004967
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004968}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004969
Owen Taylor3473f882001-02-23 17:55:21 +00004970/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004971 * xmlValidateCdataElement:
4972 * @ctxt: the validation context
4973 * @doc: a document instance
4974 * @elem: an element instance
4975 *
4976 * Check that an element follows #CDATA
4977 *
4978 * returns 1 if valid or 0 otherwise
4979 */
4980static int
4981xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4982 xmlNodePtr elem) {
4983 int ret = 1;
4984 xmlNodePtr cur, child;
4985
Daniel Veillardceb09b92002-10-04 11:46:37 +00004986 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004987 return(0);
4988
4989 child = elem->children;
4990
4991 cur = child;
4992 while (cur != NULL) {
4993 switch (cur->type) {
4994 case XML_ENTITY_REF_NODE:
4995 /*
4996 * Push the current node to be able to roll back
4997 * and process within the entity
4998 */
4999 if ((cur->children != NULL) &&
5000 (cur->children->children != NULL)) {
5001 nodeVPush(ctxt, cur);
5002 cur = cur->children->children;
5003 continue;
5004 }
5005 break;
5006 case XML_COMMENT_NODE:
5007 case XML_PI_NODE:
5008 case XML_TEXT_NODE:
5009 case XML_CDATA_SECTION_NODE:
5010 break;
5011 default:
5012 ret = 0;
5013 goto done;
5014 }
5015 /*
5016 * Switch to next element
5017 */
5018 cur = cur->next;
5019 while (cur == NULL) {
5020 cur = nodeVPop(ctxt);
5021 if (cur == NULL)
5022 break;
5023 cur = cur->next;
5024 }
5025 }
5026done:
5027 ctxt->nodeMax = 0;
5028 ctxt->nodeNr = 0;
5029 if (ctxt->nodeTab != NULL) {
5030 xmlFree(ctxt->nodeTab);
5031 ctxt->nodeTab = NULL;
5032 }
5033 return(ret);
5034}
5035
5036/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005037 * xmlValidateCheckMixed:
5038 * @ctxt: the validation context
5039 * @cont: the mixed content model
5040 * @qname: the qualified name as appearing in the serialization
5041 *
5042 * Check if the given node is part of the content model.
5043 *
5044 * Returns 1 if yes, 0 if no, -1 in case of error
5045 */
5046static int
5047xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5048 xmlElementContentPtr cont, const xmlChar *qname) {
5049 while (cont != NULL) {
5050 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5051 if (xmlStrEqual(cont->name, qname))
5052 return(1);
5053 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5054 (cont->c1 != NULL) &&
5055 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5056 if (xmlStrEqual(cont->c1->name, qname))
5057 return(1);
5058 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5059 (cont->c1 == NULL) ||
5060 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5061 /* Internal error !!! */
5062 xmlGenericError(xmlGenericErrorContext,
5063 "Internal: MIXED struct bad\n");
5064 break;
5065 }
5066 cont = cont->c2;
5067 }
5068 return(0);
5069}
5070
5071/**
5072 * xmlValidGetElemDecl:
5073 * @ctxt: the validation context
5074 * @doc: a document instance
5075 * @elem: an element instance
5076 * @extsubset: pointer, (out) indicate if the declaration was found
5077 * in the external subset.
5078 *
5079 * Finds a declaration associated to an element in the document.
5080 *
5081 * returns the pointer to the declaration or NULL if not found.
5082 */
5083static xmlElementPtr
5084xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5085 xmlNodePtr elem, int *extsubset) {
5086 xmlElementPtr elemDecl = NULL;
5087 const xmlChar *prefix = NULL;
5088
5089 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5090 if (extsubset != NULL)
5091 *extsubset = 0;
5092
5093 /*
5094 * Fetch the declaration for the qualified name
5095 */
5096 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5097 prefix = elem->ns->prefix;
5098
5099 if (prefix != NULL) {
5100 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5101 elem->name, prefix);
5102 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5103 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5104 elem->name, prefix);
5105 if ((elemDecl != NULL) && (extsubset != NULL))
5106 *extsubset = 1;
5107 }
5108 }
5109
5110 /*
5111 * Fetch the declaration for the non qualified name
5112 * This is "non-strict" validation should be done on the
5113 * full QName but in that case being flexible makes sense.
5114 */
5115 if (elemDecl == NULL) {
5116 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5117 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5118 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5119 if ((elemDecl != NULL) && (extsubset != NULL))
5120 *extsubset = 1;
5121 }
5122 }
5123 if (elemDecl == NULL) {
5124 VECTXT(ctxt, elem);
5125 VERROR(ctxt->userData, "No declaration for element %s\n",
5126 elem->name);
5127 }
5128 return(elemDecl);
5129}
5130
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005131#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005132/**
5133 * xmlValidatePushElement:
5134 * @ctxt: the validation context
5135 * @doc: a document instance
5136 * @elem: an element instance
5137 * @qname: the qualified name as appearing in the serialization
5138 *
5139 * Push a new element start on the validation stack.
5140 *
5141 * returns 1 if no validation problem was found or 0 otherwise
5142 */
5143int
5144xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5145 xmlNodePtr elem, const xmlChar *qname) {
5146 int ret = 1;
5147 xmlElementPtr eDecl;
5148 int extsubset = 0;
5149
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005150/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005151 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5152 xmlValidStatePtr state = ctxt->vstate;
5153 xmlElementPtr elemDecl;
5154
5155 /*
5156 * Check the new element agaisnt the content model of the new elem.
5157 */
5158 if (state->elemDecl != NULL) {
5159 elemDecl = state->elemDecl;
5160
5161 switch(elemDecl->etype) {
5162 case XML_ELEMENT_TYPE_UNDEFINED:
5163 ret = 0;
5164 break;
5165 case XML_ELEMENT_TYPE_EMPTY:
5166 VECTXT(ctxt, state->node);
5167 VERROR(ctxt->userData,
5168 "Element %s was declared EMPTY this one has content\n",
5169 state->node->name);
5170 ret = 0;
5171 break;
5172 case XML_ELEMENT_TYPE_ANY:
5173 /* I don't think anything is required then */
5174 break;
5175 case XML_ELEMENT_TYPE_MIXED:
5176 /* simple case of declared as #PCDATA */
5177 if ((elemDecl->content != NULL) &&
5178 (elemDecl->content->type ==
5179 XML_ELEMENT_CONTENT_PCDATA)) {
5180 VECTXT(ctxt, state->node);
5181 VERROR(ctxt->userData,
5182 "Element %s was declared #PCDATA but contains non text nodes\n",
5183 state->node->name);
5184 ret = 0;
5185 } else {
5186 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5187 qname);
5188 if (ret != 1) {
5189 VECTXT(ctxt, state->node);
5190 VERROR(ctxt->userData,
5191 "Element %s is not declared in %s list of possible children\n",
5192 qname, state->node->name);
5193 }
5194 }
5195 break;
5196 case XML_ELEMENT_TYPE_ELEMENT:
5197 /*
5198 * TODO:
5199 * VC: Standalone Document Declaration
5200 * - element types with element content, if white space
5201 * occurs directly within any instance of those types.
5202 */
5203 if (state->exec != NULL) {
5204 ret = xmlRegExecPushString(state->exec, qname, NULL);
5205 if (ret < 0) {
5206 VECTXT(ctxt, state->node);
5207 VERROR(ctxt->userData,
5208 "Element %s content does not follow the DTD\nMisplaced %s\n",
5209 state->node->name, qname);
5210 ret = 0;
5211 } else {
5212 ret = 1;
5213 }
5214 }
5215 break;
5216 }
5217 }
5218 }
5219 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5220 vstateVPush(ctxt, eDecl, elem);
5221 return(ret);
5222}
5223
5224/**
5225 * xmlValidatePushCData:
5226 * @ctxt: the validation context
5227 * @data: some character data read
5228 * @len: the lenght of the data
5229 *
5230 * check the CData parsed for validation in the current stack
5231 *
5232 * returns 1 if no validation problem was found or 0 otherwise
5233 */
5234int
5235xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5236 int ret = 1;
5237
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005238/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005239 if (len <= 0)
5240 return(ret);
5241 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5242 xmlValidStatePtr state = ctxt->vstate;
5243 xmlElementPtr elemDecl;
5244
5245 /*
5246 * Check the new element agaisnt the content model of the new elem.
5247 */
5248 if (state->elemDecl != NULL) {
5249 elemDecl = state->elemDecl;
5250
5251 switch(elemDecl->etype) {
5252 case XML_ELEMENT_TYPE_UNDEFINED:
5253 ret = 0;
5254 break;
5255 case XML_ELEMENT_TYPE_EMPTY:
5256 VECTXT(ctxt, state->node);
5257 VERROR(ctxt->userData,
5258 "Element %s was declared EMPTY this one has content\n",
5259 state->node->name);
5260 ret = 0;
5261 break;
5262 case XML_ELEMENT_TYPE_ANY:
5263 break;
5264 case XML_ELEMENT_TYPE_MIXED:
5265 break;
5266 case XML_ELEMENT_TYPE_ELEMENT:
5267 if (len > 0) {
5268 int i;
5269
5270 for (i = 0;i < len;i++) {
5271 if (!IS_BLANK(data[i])) {
5272 VECTXT(ctxt, state->node);
5273 VERROR(ctxt->userData,
5274 "Element %s content does not follow the DTD\nText not allowed\n",
5275 state->node->name);
5276 ret = 0;
5277 goto done;
5278 }
5279 }
5280 /*
5281 * TODO:
5282 * VC: Standalone Document Declaration
5283 * element types with element content, if white space
5284 * occurs directly within any instance of those types.
5285 */
5286 }
5287 break;
5288 }
5289 }
5290 }
5291done:
5292 return(ret);
5293}
5294
5295/**
5296 * xmlValidatePopElement:
5297 * @ctxt: the validation context
5298 * @doc: a document instance
5299 * @elem: an element instance
5300 * @qname: the qualified name as appearing in the serialization
5301 *
5302 * Pop the element end from the validation stack.
5303 *
5304 * returns 1 if no validation problem was found or 0 otherwise
5305 */
5306int
5307xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005308 xmlNodePtr elem ATTRIBUTE_UNUSED,
5309 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005310 int ret = 1;
5311
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005312/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005313 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5314 xmlValidStatePtr state = ctxt->vstate;
5315 xmlElementPtr elemDecl;
5316
5317 /*
5318 * Check the new element agaisnt the content model of the new elem.
5319 */
5320 if (state->elemDecl != NULL) {
5321 elemDecl = state->elemDecl;
5322
5323 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5324 if (state->exec != NULL) {
5325 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5326 if (ret == 0) {
5327 VECTXT(ctxt, state->node);
5328 VERROR(ctxt->userData,
5329 "Element %s content does not follow the DTD\nExpecting more child\n",
5330 state->node->name);
5331 } else {
5332 /*
5333 * previous validation errors should not generate
5334 * a new one here
5335 */
5336 ret = 1;
5337 }
5338 }
5339 }
5340 }
5341 vstateVPop(ctxt);
5342 }
5343 return(ret);
5344}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005345#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005346
5347/**
Owen Taylor3473f882001-02-23 17:55:21 +00005348 * xmlValidateOneElement:
5349 * @ctxt: the validation context
5350 * @doc: a document instance
5351 * @elem: an element instance
5352 *
5353 * Try to validate a single element and it's attributes,
5354 * basically it does the following checks as described by the
5355 * XML-1.0 recommendation:
5356 * - [ VC: Element Valid ]
5357 * - [ VC: Required Attribute ]
5358 * Then call xmlValidateOneAttribute() for each attribute present.
5359 *
5360 * The ID/IDREF checkings are done separately
5361 *
5362 * returns 1 if valid or 0 otherwise
5363 */
5364
5365int
5366xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5367 xmlNodePtr elem) {
5368 xmlElementPtr elemDecl = NULL;
5369 xmlElementContentPtr cont;
5370 xmlAttributePtr attr;
5371 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005372 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005373 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005374 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005375
5376 CHECK_DTD;
5377
5378 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005379 switch (elem->type) {
5380 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005381 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005382 VERROR(ctxt->userData,
5383 "Attribute element not expected here\n");
5384 return(0);
5385 case XML_TEXT_NODE:
5386 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005387 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005388 VERROR(ctxt->userData, "Text element has childs !\n");
5389 return(0);
5390 }
5391 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005392 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005393 VERROR(ctxt->userData, "Text element has attributes !\n");
5394 return(0);
5395 }
5396 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005397 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005398 VERROR(ctxt->userData, "Text element has namespace !\n");
5399 return(0);
5400 }
5401 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005402 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005403 VERROR(ctxt->userData,
5404 "Text element carries namespace definitions !\n");
5405 return(0);
5406 }
5407 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005408 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005409 VERROR(ctxt->userData,
5410 "Text element has no content !\n");
5411 return(0);
5412 }
5413 return(1);
5414 case XML_XINCLUDE_START:
5415 case XML_XINCLUDE_END:
5416 return(1);
5417 case XML_CDATA_SECTION_NODE:
5418 case XML_ENTITY_REF_NODE:
5419 case XML_PI_NODE:
5420 case XML_COMMENT_NODE:
5421 return(1);
5422 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005423 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005424 VERROR(ctxt->userData,
5425 "Entity element not expected here\n");
5426 return(0);
5427 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005428 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005429 VERROR(ctxt->userData,
5430 "Notation element not expected here\n");
5431 return(0);
5432 case XML_DOCUMENT_NODE:
5433 case XML_DOCUMENT_TYPE_NODE:
5434 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005435 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005436 VERROR(ctxt->userData,
5437 "Document element not expected here\n");
5438 return(0);
5439 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005440 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005441 VERROR(ctxt->userData,
5442 "\n");
5443 return(0);
5444 case XML_ELEMENT_NODE:
5445 break;
5446 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005447 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005448 VERROR(ctxt->userData,
5449 "unknown element type %d\n", elem->type);
5450 return(0);
5451 }
Owen Taylor3473f882001-02-23 17:55:21 +00005452
5453 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005454 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005455 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005456 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5457 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005458 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005459
Daniel Veillardea7751d2002-12-20 00:16:24 +00005460 /*
5461 * If vstateNr is not zero that means continuous validation is
5462 * activated, do not try to check the content model at that level.
5463 */
5464 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005465 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005466 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005467 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005468 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005469 VERROR(ctxt->userData, "No declaration for element %s\n",
5470 elem->name);
5471 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005472 case XML_ELEMENT_TYPE_EMPTY:
5473 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005474 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005475 VERROR(ctxt->userData,
5476 "Element %s was declared EMPTY this one has content\n",
5477 elem->name);
5478 ret = 0;
5479 }
5480 break;
5481 case XML_ELEMENT_TYPE_ANY:
5482 /* I don't think anything is required then */
5483 break;
5484 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005485
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005486 /* simple case of declared as #PCDATA */
5487 if ((elemDecl->content != NULL) &&
5488 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5489 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5490 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005491 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005492 VERROR(ctxt->userData,
5493 "Element %s was declared #PCDATA but contains non text nodes\n",
5494 elem->name);
5495 }
5496 break;
5497 }
Owen Taylor3473f882001-02-23 17:55:21 +00005498 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005499 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005500 while (child != NULL) {
5501 if (child->type == XML_ELEMENT_NODE) {
5502 name = child->name;
5503 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005504 xmlChar fn[50];
5505 xmlChar *fullname;
5506
5507 fullname = xmlBuildQName(child->name, child->ns->prefix,
5508 fn, 50);
5509 if (fullname == NULL)
5510 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005511 cont = elemDecl->content;
5512 while (cont != NULL) {
5513 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005514 if (xmlStrEqual(cont->name, fullname))
5515 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005516 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5517 (cont->c1 != NULL) &&
5518 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005519 if (xmlStrEqual(cont->c1->name, fullname))
5520 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005521 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5522 (cont->c1 == NULL) ||
5523 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5524 /* Internal error !!! */
5525 xmlGenericError(xmlGenericErrorContext,
5526 "Internal: MIXED struct bad\n");
5527 break;
5528 }
5529 cont = cont->c2;
5530 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005531 if ((fullname != fn) && (fullname != child->name))
5532 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005533 if (cont != NULL)
5534 goto child_ok;
5535 }
5536 cont = elemDecl->content;
5537 while (cont != NULL) {
5538 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5539 if (xmlStrEqual(cont->name, name)) break;
5540 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5541 (cont->c1 != NULL) &&
5542 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5543 if (xmlStrEqual(cont->c1->name, name)) break;
5544 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5545 (cont->c1 == NULL) ||
5546 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5547 /* Internal error !!! */
5548 xmlGenericError(xmlGenericErrorContext,
5549 "Internal: MIXED struct bad\n");
5550 break;
5551 }
5552 cont = cont->c2;
5553 }
5554 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005555 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005556 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005557 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005558 name, elem->name);
5559 ret = 0;
5560 }
5561 }
5562child_ok:
5563 child = child->next;
5564 }
5565 break;
5566 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005567 if ((doc->standalone == 1) && (extsubset == 1)) {
5568 /*
5569 * VC: Standalone Document Declaration
5570 * - element types with element content, if white space
5571 * occurs directly within any instance of those types.
5572 */
5573 child = elem->children;
5574 while (child != NULL) {
5575 if (child->type == XML_TEXT_NODE) {
5576 const xmlChar *content = child->content;
5577
5578 while (IS_BLANK(*content))
5579 content++;
5580 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005581 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005582 VERROR(ctxt->userData,
5583"standalone: %s declared in the external subset contains white spaces nodes\n",
5584 elem->name);
5585 ret = 0;
5586 break;
5587 }
5588 }
5589 child =child->next;
5590 }
5591 }
Owen Taylor3473f882001-02-23 17:55:21 +00005592 child = elem->children;
5593 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005594 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005595 if (tmp <= 0)
5596 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005597 break;
5598 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005599 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005600
5601 /* [ VC: Required Attribute ] */
5602 attr = elemDecl->attributes;
5603 while (attr != NULL) {
5604 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005605 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005606
Daniel Veillarde4301c82002-02-13 13:32:35 +00005607 if ((attr->prefix == NULL) &&
5608 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5609 xmlNsPtr ns;
5610
5611 ns = elem->nsDef;
5612 while (ns != NULL) {
5613 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005614 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005615 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005616 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005617 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5618 xmlNsPtr ns;
5619
5620 ns = elem->nsDef;
5621 while (ns != NULL) {
5622 if (xmlStrEqual(attr->name, ns->prefix))
5623 goto found;
5624 ns = ns->next;
5625 }
5626 } else {
5627 xmlAttrPtr attrib;
5628
5629 attrib = elem->properties;
5630 while (attrib != NULL) {
5631 if (xmlStrEqual(attrib->name, attr->name)) {
5632 if (attr->prefix != NULL) {
5633 xmlNsPtr nameSpace = attrib->ns;
5634
5635 if (nameSpace == NULL)
5636 nameSpace = elem->ns;
5637 /*
5638 * qualified names handling is problematic, having a
5639 * different prefix should be possible but DTDs don't
5640 * allow to define the URI instead of the prefix :-(
5641 */
5642 if (nameSpace == NULL) {
5643 if (qualified < 0)
5644 qualified = 0;
5645 } else if (!xmlStrEqual(nameSpace->prefix,
5646 attr->prefix)) {
5647 if (qualified < 1)
5648 qualified = 1;
5649 } else
5650 goto found;
5651 } else {
5652 /*
5653 * We should allow applications to define namespaces
5654 * for their application even if the DTD doesn't
5655 * carry one, otherwise, basically we would always
5656 * break.
5657 */
5658 goto found;
5659 }
5660 }
5661 attrib = attrib->next;
5662 }
Owen Taylor3473f882001-02-23 17:55:21 +00005663 }
5664 if (qualified == -1) {
5665 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005666 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005667 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005668 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005669 elem->name, attr->name);
5670 ret = 0;
5671 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005672 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005673 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005674 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005675 elem->name, attr->prefix,attr->name);
5676 ret = 0;
5677 }
5678 } else if (qualified == 0) {
5679 VWARNING(ctxt->userData,
5680 "Element %s required attribute %s:%s has no prefix\n",
5681 elem->name, attr->prefix,attr->name);
5682 } else if (qualified == 1) {
5683 VWARNING(ctxt->userData,
5684 "Element %s required attribute %s:%s has different prefix\n",
5685 elem->name, attr->prefix,attr->name);
5686 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005687 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5688 /*
5689 * Special tests checking #FIXED namespace declarations
5690 * have the right value since this is not done as an
5691 * attribute checking
5692 */
5693 if ((attr->prefix == NULL) &&
5694 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5695 xmlNsPtr ns;
5696
5697 ns = elem->nsDef;
5698 while (ns != NULL) {
5699 if (ns->prefix == NULL) {
5700 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005701 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005702 VERROR(ctxt->userData,
5703 "Element %s namespace name for default namespace does not match the DTD\n",
5704 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005705 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005706 }
5707 goto found;
5708 }
5709 ns = ns->next;
5710 }
5711 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5712 xmlNsPtr ns;
5713
5714 ns = elem->nsDef;
5715 while (ns != NULL) {
5716 if (xmlStrEqual(attr->name, ns->prefix)) {
5717 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005718 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005719 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005720 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005721 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005722 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005723 }
5724 goto found;
5725 }
5726 ns = ns->next;
5727 }
5728 }
Owen Taylor3473f882001-02-23 17:55:21 +00005729 }
5730found:
5731 attr = attr->nexth;
5732 }
5733 return(ret);
5734}
5735
5736/**
5737 * xmlValidateRoot:
5738 * @ctxt: the validation context
5739 * @doc: a document instance
5740 *
5741 * Try to validate a the root element
5742 * basically it does the following check as described by the
5743 * XML-1.0 recommendation:
5744 * - [ VC: Root Element Type ]
5745 * it doesn't try to recurse or apply other check to the element
5746 *
5747 * returns 1 if valid or 0 otherwise
5748 */
5749
5750int
5751xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5752 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00005753 int ret;
5754
Owen Taylor3473f882001-02-23 17:55:21 +00005755 if (doc == NULL) return(0);
5756
5757 root = xmlDocGetRootElement(doc);
5758 if ((root == NULL) || (root->name == NULL)) {
5759 VERROR(ctxt->userData, "Not valid: no root element\n");
5760 return(0);
5761 }
5762
5763 /*
5764 * When doing post validation against a separate DTD, those may
5765 * no internal subset has been generated
5766 */
5767 if ((doc->intSubset != NULL) &&
5768 (doc->intSubset->name != NULL)) {
5769 /*
5770 * Check first the document root against the NQName
5771 */
5772 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5773 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005774 xmlChar fn[50];
5775 xmlChar *fullname;
5776
5777 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
5778 if (fullname == NULL) {
5779 VERROR(ctxt->userData, "Out of memory\n");
5780 return(0);
5781 }
5782 ret = xmlStrEqual(doc->intSubset->name, fullname);
5783 if ((fullname != fn) && (fullname != root->name))
5784 xmlFree(fullname);
5785 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00005786 goto name_ok;
5787 }
5788 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5789 (xmlStrEqual(root->name, BAD_CAST "html")))
5790 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005791 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005792 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005793 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005794 root->name, doc->intSubset->name);
5795 return(0);
5796
5797 }
5798 }
5799name_ok:
5800 return(1);
5801}
5802
5803
5804/**
5805 * xmlValidateElement:
5806 * @ctxt: the validation context
5807 * @doc: a document instance
5808 * @elem: an element instance
5809 *
5810 * Try to validate the subtree under an element
5811 *
5812 * returns 1 if valid or 0 otherwise
5813 */
5814
5815int
5816xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5817 xmlNodePtr child;
5818 xmlAttrPtr attr;
5819 xmlChar *value;
5820 int ret = 1;
5821
5822 if (elem == NULL) return(0);
5823
5824 /*
5825 * XInclude elements were added after parsing in the infoset,
5826 * they don't really mean anything validation wise.
5827 */
5828 if ((elem->type == XML_XINCLUDE_START) ||
5829 (elem->type == XML_XINCLUDE_END))
5830 return(1);
5831
5832 CHECK_DTD;
5833
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005834 /*
5835 * Entities references have to be handled separately
5836 */
5837 if (elem->type == XML_ENTITY_REF_NODE) {
5838 return(1);
5839 }
5840
Owen Taylor3473f882001-02-23 17:55:21 +00005841 ret &= xmlValidateOneElement(ctxt, doc, elem);
5842 attr = elem->properties;
5843 while(attr != NULL) {
5844 value = xmlNodeListGetString(doc, attr->children, 0);
5845 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5846 if (value != NULL)
5847 xmlFree(value);
5848 attr= attr->next;
5849 }
5850 child = elem->children;
5851 while (child != NULL) {
5852 ret &= xmlValidateElement(ctxt, doc, child);
5853 child = child->next;
5854 }
5855
5856 return(ret);
5857}
5858
Daniel Veillard8730c562001-02-26 10:49:57 +00005859/**
5860 * xmlValidateRef:
5861 * @ref: A reference to be validated
5862 * @ctxt: Validation context
5863 * @name: Name of ID we are searching for
5864 *
5865 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005866static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005867xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005868 const xmlChar *name) {
5869 xmlAttrPtr id;
5870 xmlAttrPtr attr;
5871
5872 if (ref == NULL)
5873 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005874 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00005875 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005876 attr = ref->attr;
5877 if (attr == NULL) {
5878 xmlChar *dup, *str = NULL, *cur, save;
5879
5880 dup = xmlStrdup(name);
5881 if (dup == NULL) {
5882 ctxt->valid = 0;
5883 return;
5884 }
5885 cur = dup;
5886 while (*cur != 0) {
5887 str = cur;
5888 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5889 save = *cur;
5890 *cur = 0;
5891 id = xmlGetID(ctxt->doc, str);
5892 if (id == NULL) {
5893 VERROR(ctxt->userData,
5894 "attribute %s line %d references an unknown ID \"%s\"\n",
5895 ref->name, ref->lineno, str);
5896 ctxt->valid = 0;
5897 }
5898 if (save == 0)
5899 break;
5900 *cur = save;
5901 while (IS_BLANK(*cur)) cur++;
5902 }
5903 xmlFree(dup);
5904 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00005905 id = xmlGetID(ctxt->doc, name);
5906 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005907 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005908 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005909 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005910 attr->name, name);
5911 ctxt->valid = 0;
5912 }
5913 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5914 xmlChar *dup, *str = NULL, *cur, save;
5915
5916 dup = xmlStrdup(name);
5917 if (dup == NULL) {
5918 ctxt->valid = 0;
5919 return;
5920 }
5921 cur = dup;
5922 while (*cur != 0) {
5923 str = cur;
5924 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5925 save = *cur;
5926 *cur = 0;
5927 id = xmlGetID(ctxt->doc, str);
5928 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005929 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005930 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005931 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005932 attr->name, str);
5933 ctxt->valid = 0;
5934 }
5935 if (save == 0)
5936 break;
5937 *cur = save;
5938 while (IS_BLANK(*cur)) cur++;
5939 }
5940 xmlFree(dup);
5941 }
5942}
5943
5944/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005945 * xmlWalkValidateList:
5946 * @data: Contents of current link
5947 * @user: Value supplied by the user
5948 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005949 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005950 */
5951static int
5952xmlWalkValidateList(const void *data, const void *user)
5953{
5954 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5955 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5956 return 1;
5957}
5958
5959/**
5960 * xmlValidateCheckRefCallback:
5961 * @ref_list: List of references
5962 * @ctxt: Validation context
5963 * @name: Name of ID we are searching for
5964 *
5965 */
5966static void
5967xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5968 const xmlChar *name) {
5969 xmlValidateMemo memo;
5970
5971 if (ref_list == NULL)
5972 return;
5973 memo.ctxt = ctxt;
5974 memo.name = name;
5975
5976 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5977
5978}
5979
5980/**
Owen Taylor3473f882001-02-23 17:55:21 +00005981 * xmlValidateDocumentFinal:
5982 * @ctxt: the validation context
5983 * @doc: a document instance
5984 *
5985 * Does the final step for the document validation once all the
5986 * incremental validation steps have been completed
5987 *
5988 * basically it does the following checks described by the XML Rec
5989 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00005990 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00005991 *
5992 * returns 1 if valid or 0 otherwise
5993 */
5994
5995int
5996xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5997 xmlRefTablePtr table;
5998
5999 if (doc == NULL) {
6000 xmlGenericError(xmlGenericErrorContext,
6001 "xmlValidateDocumentFinal: doc == NULL\n");
6002 return(0);
6003 }
6004
6005 /*
6006 * Check all the NOTATION/NOTATIONS attributes
6007 */
6008 /*
6009 * Check all the ENTITY/ENTITIES attributes definition for validity
6010 */
6011 /*
6012 * Check all the IDREF/IDREFS attributes definition for validity
6013 */
6014 table = (xmlRefTablePtr) doc->refs;
6015 ctxt->doc = doc;
6016 ctxt->valid = 1;
6017 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6018 return(ctxt->valid);
6019}
6020
6021/**
6022 * xmlValidateDtd:
6023 * @ctxt: the validation context
6024 * @doc: a document instance
6025 * @dtd: a dtd instance
6026 *
6027 * Try to validate the document against the dtd instance
6028 *
6029 * basically it does check all the definitions in the DtD.
6030 *
6031 * returns 1 if valid or 0 otherwise
6032 */
6033
6034int
6035xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6036 int ret;
6037 xmlDtdPtr oldExt;
6038 xmlNodePtr root;
6039
6040 if (dtd == NULL) return(0);
6041 if (doc == NULL) return(0);
6042 oldExt = doc->extSubset;
6043 doc->extSubset = dtd;
6044 ret = xmlValidateRoot(ctxt, doc);
6045 if (ret == 0) {
6046 doc->extSubset = oldExt;
6047 return(ret);
6048 }
6049 if (doc->ids != NULL) {
6050 xmlFreeIDTable(doc->ids);
6051 doc->ids = NULL;
6052 }
6053 if (doc->refs != NULL) {
6054 xmlFreeRefTable(doc->refs);
6055 doc->refs = NULL;
6056 }
6057 root = xmlDocGetRootElement(doc);
6058 ret = xmlValidateElement(ctxt, doc, root);
6059 ret &= xmlValidateDocumentFinal(ctxt, doc);
6060 doc->extSubset = oldExt;
6061 return(ret);
6062}
6063
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006064static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006065xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6066 const xmlChar *name ATTRIBUTE_UNUSED) {
6067 if (cur == NULL)
6068 return;
6069 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6070 xmlChar *notation = cur->content;
6071
Daniel Veillard878eab02002-02-19 13:46:09 +00006072 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006073 int ret;
6074
6075 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6076 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006077 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006078 }
6079 }
6080 }
6081}
6082
6083static void
Owen Taylor3473f882001-02-23 17:55:21 +00006084xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006085 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006086 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006087 xmlDocPtr doc;
6088 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006089
Owen Taylor3473f882001-02-23 17:55:21 +00006090 if (cur == NULL)
6091 return;
6092 switch (cur->atype) {
6093 case XML_ATTRIBUTE_CDATA:
6094 case XML_ATTRIBUTE_ID:
6095 case XML_ATTRIBUTE_IDREF :
6096 case XML_ATTRIBUTE_IDREFS:
6097 case XML_ATTRIBUTE_NMTOKEN:
6098 case XML_ATTRIBUTE_NMTOKENS:
6099 case XML_ATTRIBUTE_ENUMERATION:
6100 break;
6101 case XML_ATTRIBUTE_ENTITY:
6102 case XML_ATTRIBUTE_ENTITIES:
6103 case XML_ATTRIBUTE_NOTATION:
6104 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006105
6106 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6107 cur->atype, cur->defaultValue);
6108 if ((ret == 0) && (ctxt->valid == 1))
6109 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006110 }
6111 if (cur->tree != NULL) {
6112 xmlEnumerationPtr tree = cur->tree;
6113 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006114 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006115 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006116 if ((ret == 0) && (ctxt->valid == 1))
6117 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006118 tree = tree->next;
6119 }
6120 }
6121 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006122 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6123 doc = cur->doc;
6124 if ((doc == NULL) || (cur->elem == NULL)) {
6125 VERROR(ctxt->userData,
6126 "xmlValidateAttributeCallback(%s): internal error\n",
6127 cur->name);
6128 return;
6129 }
6130 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6131 if (elem == NULL)
6132 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6133 if (elem == NULL) {
6134 VERROR(ctxt->userData,
6135 "attribute %s: could not find decl for element %s\n",
6136 cur->name, cur->elem);
6137 return;
6138 }
6139 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6140 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006141 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00006142 cur->name, cur->elem);
6143 ctxt->valid = 0;
6144 }
6145 }
Owen Taylor3473f882001-02-23 17:55:21 +00006146}
6147
6148/**
6149 * xmlValidateDtdFinal:
6150 * @ctxt: the validation context
6151 * @doc: a document instance
6152 *
6153 * Does the final step for the dtds validation once all the
6154 * subsets have been parsed
6155 *
6156 * basically it does the following checks described by the XML Rec
6157 * - check that ENTITY and ENTITIES type attributes default or
6158 * possible values matches one of the defined entities.
6159 * - check that NOTATION type attributes default or
6160 * possible values matches one of the defined notations.
6161 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006162 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006163 */
6164
6165int
6166xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006167 xmlDtdPtr dtd;
6168 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006169 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006170
6171 if (doc == NULL) return(0);
6172 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6173 return(0);
6174 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006175 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006176 dtd = doc->intSubset;
6177 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6178 table = (xmlAttributeTablePtr) dtd->attributes;
6179 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006180 }
6181 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006182 entities = (xmlEntitiesTablePtr) dtd->entities;
6183 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6184 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006185 }
6186 dtd = doc->extSubset;
6187 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6188 table = (xmlAttributeTablePtr) dtd->attributes;
6189 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006190 }
6191 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006192 entities = (xmlEntitiesTablePtr) dtd->entities;
6193 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6194 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006195 }
6196 return(ctxt->valid);
6197}
6198
6199/**
6200 * xmlValidateDocument:
6201 * @ctxt: the validation context
6202 * @doc: a document instance
6203 *
6204 * Try to validate the document instance
6205 *
6206 * basically it does the all the checks described by the XML Rec
6207 * i.e. validates the internal and external subset (if present)
6208 * and validate the document tree.
6209 *
6210 * returns 1 if valid or 0 otherwise
6211 */
6212
6213int
6214xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6215 int ret;
6216 xmlNodePtr root;
6217
Daniel Veillard2fd85422002-10-16 14:32:41 +00006218 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6219 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00006220 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006221 }
Owen Taylor3473f882001-02-23 17:55:21 +00006222 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6223 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6224 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6225 doc->intSubset->SystemID);
6226 if (doc->extSubset == NULL) {
6227 if (doc->intSubset->SystemID != NULL) {
6228 VERROR(ctxt->userData,
6229 "Could not load the external subset \"%s\"\n",
6230 doc->intSubset->SystemID);
6231 } else {
6232 VERROR(ctxt->userData,
6233 "Could not load the external subset \"%s\"\n",
6234 doc->intSubset->ExternalID);
6235 }
6236 return(0);
6237 }
6238 }
6239
6240 if (doc->ids != NULL) {
6241 xmlFreeIDTable(doc->ids);
6242 doc->ids = NULL;
6243 }
6244 if (doc->refs != NULL) {
6245 xmlFreeRefTable(doc->refs);
6246 doc->refs = NULL;
6247 }
6248 ret = xmlValidateDtdFinal(ctxt, doc);
6249 if (!xmlValidateRoot(ctxt, doc)) return(0);
6250
6251 root = xmlDocGetRootElement(doc);
6252 ret &= xmlValidateElement(ctxt, doc, root);
6253 ret &= xmlValidateDocumentFinal(ctxt, doc);
6254 return(ret);
6255}
6256
6257
6258/************************************************************************
6259 * *
6260 * Routines for dynamic validation editing *
6261 * *
6262 ************************************************************************/
6263
6264/**
6265 * xmlValidGetPotentialChildren:
6266 * @ctree: an element content tree
6267 * @list: an array to store the list of child names
6268 * @len: a pointer to the number of element in the list
6269 * @max: the size of the array
6270 *
6271 * Build/extend a list of potential children allowed by the content tree
6272 *
6273 * returns the number of element in the list, or -1 in case of error.
6274 */
6275
6276int
6277xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6278 int *len, int max) {
6279 int i;
6280
6281 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6282 return(-1);
6283 if (*len >= max) return(*len);
6284
6285 switch (ctree->type) {
6286 case XML_ELEMENT_CONTENT_PCDATA:
6287 for (i = 0; i < *len;i++)
6288 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6289 list[(*len)++] = BAD_CAST "#PCDATA";
6290 break;
6291 case XML_ELEMENT_CONTENT_ELEMENT:
6292 for (i = 0; i < *len;i++)
6293 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6294 list[(*len)++] = ctree->name;
6295 break;
6296 case XML_ELEMENT_CONTENT_SEQ:
6297 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6298 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6299 break;
6300 case XML_ELEMENT_CONTENT_OR:
6301 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6302 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6303 break;
6304 }
6305
6306 return(*len);
6307}
6308
6309/**
6310 * xmlValidGetValidElements:
6311 * @prev: an element to insert after
6312 * @next: an element to insert next
6313 * @list: an array to store the list of child names
6314 * @max: the size of the array
6315 *
6316 * This function returns the list of authorized children to insert
6317 * within an existing tree while respecting the validity constraints
6318 * forced by the Dtd. The insertion point is defined using @prev and
6319 * @next in the following ways:
6320 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6321 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6322 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6323 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6324 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6325 *
6326 * pointers to the element names are inserted at the beginning of the array
6327 * and do not need to be freed.
6328 *
6329 * returns the number of element in the list, or -1 in case of error. If
6330 * the function returns the value @max the caller is invited to grow the
6331 * receiving array and retry.
6332 */
6333
6334int
6335xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6336 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006337 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006338 int nb_valid_elements = 0;
6339 const xmlChar *elements[256];
6340 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006341 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006342
6343 xmlNode *ref_node;
6344 xmlNode *parent;
6345 xmlNode *test_node;
6346
6347 xmlNode *prev_next;
6348 xmlNode *next_prev;
6349 xmlNode *parent_childs;
6350 xmlNode *parent_last;
6351
6352 xmlElement *element_desc;
6353
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006354 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006355
Owen Taylor3473f882001-02-23 17:55:21 +00006356 if (prev == NULL && next == NULL)
6357 return(-1);
6358
6359 if (list == NULL) return(-1);
6360 if (max <= 0) return(-1);
6361
6362 nb_valid_elements = 0;
6363 ref_node = prev ? prev : next;
6364 parent = ref_node->parent;
6365
6366 /*
6367 * Retrieves the parent element declaration
6368 */
6369 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6370 parent->name);
6371 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6372 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6373 parent->name);
6374 if (element_desc == NULL) return(-1);
6375
6376 /*
6377 * Do a backup of the current tree structure
6378 */
6379 prev_next = prev ? prev->next : NULL;
6380 next_prev = next ? next->prev : NULL;
6381 parent_childs = parent->children;
6382 parent_last = parent->last;
6383
6384 /*
6385 * Creates a dummy node and insert it into the tree
6386 */
6387 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6388 test_node->doc = ref_node->doc;
6389 test_node->parent = parent;
6390 test_node->prev = prev;
6391 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006392 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006393
6394 if (prev) prev->next = test_node;
6395 else parent->children = test_node;
6396
6397 if (next) next->prev = test_node;
6398 else parent->last = test_node;
6399
6400 /*
6401 * Insert each potential child node and check if the parent is
6402 * still valid
6403 */
6404 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6405 elements, &nb_elements, 256);
6406
6407 for (i = 0;i < nb_elements;i++) {
6408 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006409 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006410 int j;
6411
6412 for (j = 0; j < nb_valid_elements;j++)
6413 if (xmlStrEqual(elements[i], list[j])) break;
6414 list[nb_valid_elements++] = elements[i];
6415 if (nb_valid_elements >= max) break;
6416 }
6417 }
6418
6419 /*
6420 * Restore the tree structure
6421 */
6422 if (prev) prev->next = prev_next;
6423 if (next) next->prev = next_prev;
6424 parent->children = parent_childs;
6425 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006426
6427 /*
6428 * Free up the dummy node
6429 */
6430 test_node->name = name;
6431 xmlFreeNode(test_node);
6432
Owen Taylor3473f882001-02-23 17:55:21 +00006433 return(nb_valid_elements);
6434}