blob: 7949fe9a0e413252c8d0813256bef30c72667297 [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 Veillard4432df22003-09-28 18:58:27 +000028static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
29 int create);
30#ifdef LIBXML_VALID_ENABLED
31
Daniel Veillarde62d36c2001-05-15 08:53:16 +000032/* #define DEBUG_VALID_ALGO */
Daniel Veillard5acfd6b2002-09-18 16:29:02 +000033/* #define DEBUG_REGEXP_ALGO */
Daniel Veillarde62d36c2001-05-15 08:53:16 +000034
Daniel Veillarda646cfd2002-09-17 21:50:03 +000035#define TODO \
36 xmlGenericError(xmlGenericErrorContext, \
37 "Unimplemented block at %s:%d\n", \
38 __FILE__, __LINE__);
Owen Taylor3473f882001-02-23 17:55:21 +000039
Daniel Veillardea7751d2002-12-20 00:16:24 +000040#define VERROR \
41 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
Owen Taylor3473f882001-02-23 17:55:21 +000042
Daniel Veillardea7751d2002-12-20 00:16:24 +000043#define VWARNING \
44 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
45
46
47#ifdef LIBXML_REGEXP_ENABLED
48/*
49 * If regexp are enabled we can do continuous validation without the
50 * need of a tree to validate the content model. this is done in each
51 * callbacks.
52 * Each xmlValidState represent the validation state associated to the
53 * set of nodes currently open from the document root to the current element.
54 */
55
56
57typedef struct _xmlValidState {
58 xmlElementPtr elemDecl; /* pointer to the content model */
59 xmlNodePtr node; /* pointer to the current node */
60 xmlRegExecCtxtPtr exec; /* regexp runtime */
61} _xmlValidState;
62
63
64static int
65vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +000066 if ((ctxt->vstateMax == 0) || (ctxt->vstateTab == NULL)) {
Daniel Veillardea7751d2002-12-20 00:16:24 +000067 ctxt->vstateMax = 10;
68 ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax *
69 sizeof(ctxt->vstateTab[0]));
70 if (ctxt->vstateTab == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +000071 VERROR(ctxt->userData, "malloc failed !n");
Daniel Veillardea7751d2002-12-20 00:16:24 +000072 return(-1);
73 }
74 }
75
76 if (ctxt->vstateNr >= ctxt->vstateMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +000077 xmlValidState *tmp;
78
79 tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
80 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
81 if (tmp == NULL) {
Daniel Veillardea7751d2002-12-20 00:16:24 +000082 VERROR(ctxt->userData, "realloc failed !n");
83 return(-1);
84 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +000085 ctxt->vstateMax *= 2;
86 ctxt->vstateTab = tmp;
Daniel Veillardea7751d2002-12-20 00:16:24 +000087 }
88 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr];
89 ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl;
90 ctxt->vstateTab[ctxt->vstateNr].node = node;
91 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
92 if (elemDecl->contModel == NULL)
93 xmlValidBuildContentModel(ctxt, elemDecl);
94 if (elemDecl->contModel != NULL) {
95 ctxt->vstateTab[ctxt->vstateNr].exec =
96 xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
97 } else {
98 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
99 VERROR(ctxt->userData,
100 "Failed to build content model regexp for %s", node->name);
101 }
102 }
103 return(ctxt->vstateNr++);
104}
105
106static int
107vstateVPop(xmlValidCtxtPtr ctxt) {
108 xmlElementPtr elemDecl;
109
Daniel Veillard336fc7d2002-12-27 19:37:04 +0000110 if (ctxt->vstateNr < 1) return(-1);
Daniel Veillardea7751d2002-12-20 00:16:24 +0000111 ctxt->vstateNr--;
112 elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
113 ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
114 ctxt->vstateTab[ctxt->vstateNr].node = NULL;
115 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
116 xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
117 }
118 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
119 if (ctxt->vstateNr >= 1)
120 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
121 else
122 ctxt->vstate = NULL;
123 return(ctxt->vstateNr);
124}
125
126#else /* not LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000127/*
Daniel Veillard1c732d22002-11-30 11:22:59 +0000128 * If regexp are not enabled, it uses a home made algorithm less
129 * complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000130 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000131 * only restriction is on the deepness of the tree limited by the
132 * size of the occurs bitfield
133 *
134 * this is the content of a saved state for rollbacks
135 */
136
137#define ROLLBACK_OR 0
138#define ROLLBACK_PARENT 1
139
Daniel Veillardb44025c2001-10-11 22:55:55 +0000140typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000141 xmlElementContentPtr cont; /* pointer to the content model subtree */
142 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000143 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000144 unsigned char depth; /* current depth in the overall tree */
145 unsigned char state; /* ROLLBACK_XXX */
146} _xmlValidState;
147
Daniel Veillardfc57b412002-04-29 15:50:14 +0000148#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000149#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
150#define CONT ctxt->vstate->cont
151#define NODE ctxt->vstate->node
152#define DEPTH ctxt->vstate->depth
153#define OCCURS ctxt->vstate->occurs
154#define STATE ctxt->vstate->state
155
Daniel Veillard5344c602001-12-31 16:37:34 +0000156#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
157#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000158
Daniel Veillard5344c602001-12-31 16:37:34 +0000159#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
160#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000161
162static int
163vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
164 xmlNodePtr node, unsigned char depth, long occurs,
165 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000166 int i = ctxt->vstateNr - 1;
167
Daniel Veillard940492d2002-04-15 10:15:25 +0000168 if (ctxt->vstateNr > MAX_RECURSE) {
169 return(-1);
170 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000171 if (ctxt->vstateTab == NULL) {
172 ctxt->vstateMax = 8;
173 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
174 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
175 if (ctxt->vstateTab == NULL) {
176 xmlGenericError(xmlGenericErrorContext,
177 "malloc failed !n");
178 return(-1);
179 }
180 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000181 if (ctxt->vstateNr >= ctxt->vstateMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000182 xmlValidState *tmp;
183
184 tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
185 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
186 if (tmp == NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000187 xmlGenericError(xmlGenericErrorContext,
188 "realloc failed !n");
Daniel Veillard940492d2002-04-15 10:15:25 +0000189 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000190 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000191 ctxt->vstateMax *= 2;
192 ctxt->vstateTab = tmp;
Daniel Veillard06803992001-04-22 10:35:56 +0000193 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000194 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000195 /*
196 * Don't push on the stack a state already here
197 */
198 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
199 (ctxt->vstateTab[i].node == node) &&
200 (ctxt->vstateTab[i].depth == depth) &&
201 (ctxt->vstateTab[i].occurs == occurs) &&
202 (ctxt->vstateTab[i].state == state))
203 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000204 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
205 ctxt->vstateTab[ctxt->vstateNr].node = node;
206 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
207 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
208 ctxt->vstateTab[ctxt->vstateNr].state = state;
209 return(ctxt->vstateNr++);
210}
211
212static int
213vstateVPop(xmlValidCtxtPtr ctxt) {
214 if (ctxt->vstateNr <= 1) return(-1);
215 ctxt->vstateNr--;
216 ctxt->vstate = &ctxt->vstateTab[0];
217 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
218 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
219 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
220 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
221 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
222 return(ctxt->vstateNr);
223}
224
Daniel Veillard118aed72002-09-24 14:13:13 +0000225#endif /* LIBXML_REGEXP_ENABLED */
226
Daniel Veillard1c732d22002-11-30 11:22:59 +0000227static int
228nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value)
229{
230 if (ctxt->nodeMax <= 0) {
231 ctxt->nodeMax = 4;
232 ctxt->nodeTab =
233 (xmlNodePtr *) xmlMalloc(ctxt->nodeMax *
234 sizeof(ctxt->nodeTab[0]));
235 if (ctxt->nodeTab == NULL) {
236 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
237 ctxt->nodeMax = 0;
238 return (0);
239 }
240 }
241 if (ctxt->nodeNr >= ctxt->nodeMax) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000242 xmlNodePtr *tmp;
243 tmp = (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
244 ctxt->nodeMax * 2 * sizeof(ctxt->nodeTab[0]));
245 if (tmp == NULL) {
Daniel Veillard1c732d22002-11-30 11:22:59 +0000246 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
247 return (0);
248 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000249 ctxt->nodeMax *= 2;
250 ctxt->nodeTab = tmp;
Daniel Veillard1c732d22002-11-30 11:22:59 +0000251 }
252 ctxt->nodeTab[ctxt->nodeNr] = value;
253 ctxt->node = value;
254 return (ctxt->nodeNr++);
255}
256static xmlNodePtr
257nodeVPop(xmlValidCtxtPtr ctxt)
258{
259 xmlNodePtr ret;
260
261 if (ctxt->nodeNr <= 0)
262 return (0);
263 ctxt->nodeNr--;
264 if (ctxt->nodeNr > 0)
265 ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1];
266 else
267 ctxt->node = NULL;
268 ret = ctxt->nodeTab[ctxt->nodeNr];
269 ctxt->nodeTab[ctxt->nodeNr] = 0;
270 return (ret);
271}
Owen Taylor3473f882001-02-23 17:55:21 +0000272
Owen Taylor3473f882001-02-23 17:55:21 +0000273#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000274static void
275xmlValidPrintNode(xmlNodePtr cur) {
276 if (cur == NULL) {
277 xmlGenericError(xmlGenericErrorContext, "null");
278 return;
279 }
280 switch (cur->type) {
281 case XML_ELEMENT_NODE:
282 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
283 break;
284 case XML_TEXT_NODE:
285 xmlGenericError(xmlGenericErrorContext, "text ");
286 break;
287 case XML_CDATA_SECTION_NODE:
288 xmlGenericError(xmlGenericErrorContext, "cdata ");
289 break;
290 case XML_ENTITY_REF_NODE:
291 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
292 break;
293 case XML_PI_NODE:
294 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
295 break;
296 case XML_COMMENT_NODE:
297 xmlGenericError(xmlGenericErrorContext, "comment ");
298 break;
299 case XML_ATTRIBUTE_NODE:
300 xmlGenericError(xmlGenericErrorContext, "?attr? ");
301 break;
302 case XML_ENTITY_NODE:
303 xmlGenericError(xmlGenericErrorContext, "?ent? ");
304 break;
305 case XML_DOCUMENT_NODE:
306 xmlGenericError(xmlGenericErrorContext, "?doc? ");
307 break;
308 case XML_DOCUMENT_TYPE_NODE:
309 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
310 break;
311 case XML_DOCUMENT_FRAG_NODE:
312 xmlGenericError(xmlGenericErrorContext, "?frag? ");
313 break;
314 case XML_NOTATION_NODE:
315 xmlGenericError(xmlGenericErrorContext, "?nota? ");
316 break;
317 case XML_HTML_DOCUMENT_NODE:
318 xmlGenericError(xmlGenericErrorContext, "?html? ");
319 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000320#ifdef LIBXML_DOCB_ENABLED
321 case XML_DOCB_DOCUMENT_NODE:
322 xmlGenericError(xmlGenericErrorContext, "?docb? ");
323 break;
324#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000325 case XML_DTD_NODE:
326 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
327 break;
328 case XML_ELEMENT_DECL:
329 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
330 break;
331 case XML_ATTRIBUTE_DECL:
332 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
333 break;
334 case XML_ENTITY_DECL:
335 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
336 break;
337 case XML_NAMESPACE_DECL:
338 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
339 break;
340 case XML_XINCLUDE_START:
341 xmlGenericError(xmlGenericErrorContext, "incstart ");
342 break;
343 case XML_XINCLUDE_END:
344 xmlGenericError(xmlGenericErrorContext, "incend ");
345 break;
346 }
347}
348
349static void
350xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000351 if (cur == NULL)
352 xmlGenericError(xmlGenericErrorContext, "null ");
353 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000354 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000355 cur = cur->next;
356 }
357}
358
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000359static void
360xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Daniel Veillard83391282003-03-06 21:37:30 +0000361 char expr[5000];
Owen Taylor3473f882001-02-23 17:55:21 +0000362
363 expr[0] = 0;
364 xmlGenericError(xmlGenericErrorContext, "valid: ");
365 xmlValidPrintNodeList(cur);
366 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000367 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000368 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
369}
370
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000371static void
372xmlValidDebugState(xmlValidStatePtr state) {
373 xmlGenericError(xmlGenericErrorContext, "(");
374 if (state->cont == NULL)
375 xmlGenericError(xmlGenericErrorContext, "null,");
376 else
377 switch (state->cont->type) {
378 case XML_ELEMENT_CONTENT_PCDATA:
379 xmlGenericError(xmlGenericErrorContext, "pcdata,");
380 break;
381 case XML_ELEMENT_CONTENT_ELEMENT:
382 xmlGenericError(xmlGenericErrorContext, "%s,",
383 state->cont->name);
384 break;
385 case XML_ELEMENT_CONTENT_SEQ:
386 xmlGenericError(xmlGenericErrorContext, "seq,");
387 break;
388 case XML_ELEMENT_CONTENT_OR:
389 xmlGenericError(xmlGenericErrorContext, "or,");
390 break;
391 }
392 xmlValidPrintNode(state->node);
393 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
394 state->depth, state->occurs, state->state);
395}
396
397static void
398xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
399 int i, j;
400
401 xmlGenericError(xmlGenericErrorContext, "state: ");
402 xmlValidDebugState(ctxt->vstate);
403 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
404 ctxt->vstateNr - 1);
405 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
406 xmlValidDebugState(&ctxt->vstateTab[j]);
407 xmlGenericError(xmlGenericErrorContext, "\n");
408}
409
410/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000411#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000412 *****/
413
414#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000415#define DEBUG_VALID_MSG(m) \
416 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
417
Owen Taylor3473f882001-02-23 17:55:21 +0000418#else
419#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000420#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000421#endif
422
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000423/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000424
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000425#define VECTXT(ctxt, node) \
426 if ((ctxt != NULL) && (ctxt->error != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000427 (node != NULL)) { \
428 xmlChar *base = xmlNodeGetBase(NULL,node); \
429 if (base != NULL) { \
430 ctxt->error(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000431 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000432 xmlFree(base); \
433 } else \
434 ctxt->error(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000435 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000436 }
437
438#define VWCTXT(ctxt, node) \
439 if ((ctxt != NULL) && (ctxt->warning != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000440 (node != NULL)) { \
441 xmlChar *base = xmlNodeGetBase(NULL,node); \
442 if (base != NULL) { \
443 ctxt->warning(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000444 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000445 xmlFree(base); \
446 } else \
447 ctxt->warning(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000448 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000449 }
450
Owen Taylor3473f882001-02-23 17:55:21 +0000451#define CHECK_DTD \
452 if (doc == NULL) return(0); \
453 else if ((doc->intSubset == NULL) && \
454 (doc->extSubset == NULL)) return(0)
455
Owen Taylor3473f882001-02-23 17:55:21 +0000456xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
457
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000458#ifdef LIBXML_REGEXP_ENABLED
459
460/************************************************************************
461 * *
462 * Content model validation based on the regexps *
463 * *
464 ************************************************************************/
465
466/**
467 * xmlValidBuildAContentModel:
468 * @content: the content model
469 * @ctxt: the schema parser context
470 * @name: the element name whose content is being built
471 *
472 * Generate the automata sequence needed for that type
473 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000474 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000475 */
476static int
477xmlValidBuildAContentModel(xmlElementContentPtr content,
478 xmlValidCtxtPtr ctxt,
479 const xmlChar *name) {
480 if (content == NULL) {
481 VERROR(ctxt->userData,
482 "Found unexpected type = NULL in %s content model\n", name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000483 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000484 }
485 switch (content->type) {
486 case XML_ELEMENT_CONTENT_PCDATA:
487 VERROR(ctxt->userData, "ContentModel found PCDATA for element %s\n",
488 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000489 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000490 break;
491 case XML_ELEMENT_CONTENT_ELEMENT: {
492 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillardc00cda82003-04-07 10:22:39 +0000493 xmlChar fn[50];
494 xmlChar *fullname;
495
496 fullname = xmlBuildQName(content->name, content->prefix, fn, 50);
497 if (fullname == NULL) {
498 VERROR(ctxt->userData, "Out of memory\n");
499 return(0);
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000500 }
501
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000502 switch (content->ocur) {
503 case XML_ELEMENT_CONTENT_ONCE:
504 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000505 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000506 break;
507 case XML_ELEMENT_CONTENT_OPT:
508 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000509 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000510 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
511 break;
512 case XML_ELEMENT_CONTENT_PLUS:
513 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000514 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000515 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000516 ctxt->state, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000517 break;
518 case XML_ELEMENT_CONTENT_MULT:
519 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000520 ctxt->state, fullname, NULL);
Daniel Veillard57e79b32003-02-04 15:33:12 +0000521 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, ctxt->state,
522 NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000523 break;
524 }
Daniel Veillardc00cda82003-04-07 10:22:39 +0000525 if ((fullname != fn) && (fullname != content->name))
526 xmlFree(fullname);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000527 break;
528 }
529 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000530 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000531 xmlElementContentOccur ocur;
532
533 /*
534 * Simply iterate over the content
535 */
536 oldstate = ctxt->state;
537 ocur = content->ocur;
Daniel Veillardf4be0182003-02-24 19:54:33 +0000538 if (ocur != XML_ELEMENT_CONTENT_ONCE) {
539 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
540 oldstate = ctxt->state;
541 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000542 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000543 xmlValidBuildAContentModel(content->c1, ctxt, name);
544 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000545 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
546 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
547 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000548 oldend = ctxt->state;
549 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000550 switch (ocur) {
551 case XML_ELEMENT_CONTENT_ONCE:
552 break;
553 case XML_ELEMENT_CONTENT_OPT:
554 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
555 break;
556 case XML_ELEMENT_CONTENT_MULT:
557 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000558 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000559 break;
560 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000561 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000562 break;
563 }
564 break;
565 }
566 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000567 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000568 xmlElementContentOccur ocur;
569
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000570 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000571 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
572 (ocur == XML_ELEMENT_CONTENT_MULT)) {
573 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
574 ctxt->state, NULL);
575 }
576 oldstate = ctxt->state;
577 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000578
579 /*
580 * iterate over the subtypes and remerge the end with an
581 * epsilon transition
582 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000583 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000584 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000585 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000586 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000587 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000588 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
589 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000590 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000591 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000592 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
593 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000594 switch (ocur) {
595 case XML_ELEMENT_CONTENT_ONCE:
596 break;
597 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000598 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000599 break;
600 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000601 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
602 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000603 break;
604 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000605 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000606 break;
607 }
608 break;
609 }
610 default:
611 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
612 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000613 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000614 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000615 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000616}
617/**
618 * xmlValidBuildContentModel:
619 * @ctxt: a validation context
620 * @elem: an element declaration node
621 *
622 * (Re)Build the automata associated to the content model of this
623 * element
624 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000625 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000626 */
627int
628xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000629
630 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000631 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000632 if (elem->type != XML_ELEMENT_DECL)
633 return(0);
634 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
635 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000636 /* TODO: should we rebuild in this case ? */
Daniel Veillardec498e12003-02-05 11:01:50 +0000637 if (elem->contModel != NULL) {
638 if (!xmlRegexpIsDeterminist(elem->contModel)) {
639 ctxt->valid = 0;
640 return(0);
641 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000642 return(1);
Daniel Veillardec498e12003-02-05 11:01:50 +0000643 }
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000644
645 ctxt->am = xmlNewAutomata();
646 if (ctxt->am == NULL) {
647 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
648 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000649 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000650 }
William M. Brack78637da2003-07-31 14:47:38 +0000651 ctxt->state = xmlAutomataGetInitState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000652 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
653 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000654 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000655 if (xmlRegexpIsDeterminist(elem->contModel) != 1) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000656 char expr[5000];
657 expr[0] = 0;
658 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
659 VERROR(ctxt->userData, "Content model of %s is not determinist: %s\n",
660 elem->name, expr);
661#ifdef DEBUG_REGEXP_ALGO
662 xmlRegexpPrint(stderr, elem->contModel);
663#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000664 ctxt->valid = 0;
Daniel Veillardec498e12003-02-05 11:01:50 +0000665 ctxt->state = NULL;
666 xmlFreeAutomata(ctxt->am);
667 ctxt->am = NULL;
668 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000669 }
670 ctxt->state = NULL;
671 xmlFreeAutomata(ctxt->am);
672 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000673 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000674}
675
676#endif /* LIBXML_REGEXP_ENABLED */
677
Owen Taylor3473f882001-02-23 17:55:21 +0000678/****************************************************************
679 * *
680 * Util functions for data allocation/deallocation *
681 * *
682 ****************************************************************/
683
684/**
Daniel Veillarda37aab82003-06-09 09:10:36 +0000685 * xmlNewValidCtxt:
686 *
687 * Allocate a validation context structure.
688 *
689 * Returns NULL if not, otherwise the new validation context structure
690 */
691xmlValidCtxtPtr
692xmlNewValidCtxt(void) {
693 xmlValidCtxtPtr ret;
694
695 if ((ret = xmlMalloc(sizeof (xmlValidCtxt))) == NULL)
696 return (NULL);
697
698 (void) memset(ret, 0, sizeof (xmlValidCtxt));
699
700 return (ret);
701}
702
703/**
704 * xmlFreeValidCtxt:
705 * @cur: the validation context to free
706 *
707 * Free a validation context structure.
708 */
709void
710xmlFreeValidCtxt(xmlValidCtxtPtr cur) {
711 xmlFree(cur);
712}
713
Daniel Veillard4432df22003-09-28 18:58:27 +0000714#endif /* LIBXML_VALID_ENABLED */
715
Daniel Veillarda37aab82003-06-09 09:10:36 +0000716/**
Owen Taylor3473f882001-02-23 17:55:21 +0000717 * xmlNewElementContent:
718 * @name: the subelement name or NULL
719 * @type: the type of element content decl
720 *
721 * Allocate an element content structure.
722 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000723 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000724 */
725xmlElementContentPtr
Daniel Veillard2fdbd322003-08-18 12:15:38 +0000726xmlNewElementContent(const xmlChar *name, xmlElementContentType type) {
Owen Taylor3473f882001-02-23 17:55:21 +0000727 xmlElementContentPtr ret;
728
729 switch(type) {
730 case XML_ELEMENT_CONTENT_ELEMENT:
731 if (name == NULL) {
732 xmlGenericError(xmlGenericErrorContext,
733 "xmlNewElementContent : name == NULL !\n");
734 }
735 break;
736 case XML_ELEMENT_CONTENT_PCDATA:
737 case XML_ELEMENT_CONTENT_SEQ:
738 case XML_ELEMENT_CONTENT_OR:
739 if (name != NULL) {
740 xmlGenericError(xmlGenericErrorContext,
741 "xmlNewElementContent : name != NULL !\n");
742 }
743 break;
744 default:
745 xmlGenericError(xmlGenericErrorContext,
746 "xmlNewElementContent: unknown type %d\n", type);
747 return(NULL);
748 }
749 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
750 if (ret == NULL) {
751 xmlGenericError(xmlGenericErrorContext,
752 "xmlNewElementContent : out of memory!\n");
753 return(NULL);
754 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000755 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000756 ret->type = type;
757 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000758 if (name != NULL) {
759 xmlChar *prefix = NULL;
760 ret->name = xmlSplitQName2(name, &prefix);
761 if (ret->name == NULL)
762 ret->name = xmlStrdup(name);
763 ret->prefix = prefix;
764 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000765 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000766 ret->prefix = NULL;
767 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000768 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000769 return(ret);
770}
771
772/**
773 * xmlCopyElementContent:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000774 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +0000775 *
776 * Build a copy of an element content description.
777 *
778 * Returns the new xmlElementContentPtr or NULL in case of error.
779 */
780xmlElementContentPtr
781xmlCopyElementContent(xmlElementContentPtr cur) {
782 xmlElementContentPtr ret;
783
784 if (cur == NULL) return(NULL);
785 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
786 if (ret == NULL) {
787 xmlGenericError(xmlGenericErrorContext,
788 "xmlCopyElementContent : out of memory\n");
789 return(NULL);
790 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000791 if (cur->prefix != NULL)
792 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000793 ret->ocur = cur->ocur;
794 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000795 if (ret->c1 != NULL)
796 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000797 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000798 if (ret->c2 != NULL)
799 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000800 return(ret);
801}
802
803/**
804 * xmlFreeElementContent:
805 * @cur: the element content tree to free
806 *
807 * Free an element content structure. This is a recursive call !
808 */
809void
810xmlFreeElementContent(xmlElementContentPtr cur) {
811 if (cur == NULL) return;
812 switch (cur->type) {
813 case XML_ELEMENT_CONTENT_PCDATA:
814 case XML_ELEMENT_CONTENT_ELEMENT:
815 case XML_ELEMENT_CONTENT_SEQ:
816 case XML_ELEMENT_CONTENT_OR:
817 break;
818 default:
819 xmlGenericError(xmlGenericErrorContext,
820 "xmlFreeElementContent : type %d\n", cur->type);
821 return;
822 }
823 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
824 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
825 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000826 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000827 xmlFree(cur);
828}
829
Daniel Veillarda9cce9c2003-09-29 13:20:24 +0000830#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +0000831/**
832 * xmlDumpElementContent:
833 * @buf: An XML buffer
834 * @content: An element table
835 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
836 *
837 * This will dump the content of the element table as an XML DTD definition
838 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000839static void
Owen Taylor3473f882001-02-23 17:55:21 +0000840xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
841 if (content == NULL) return;
842
843 if (glob) xmlBufferWriteChar(buf, "(");
844 switch (content->type) {
845 case XML_ELEMENT_CONTENT_PCDATA:
846 xmlBufferWriteChar(buf, "#PCDATA");
847 break;
848 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000849 if (content->prefix != NULL) {
850 xmlBufferWriteCHAR(buf, content->prefix);
851 xmlBufferWriteChar(buf, ":");
852 }
Owen Taylor3473f882001-02-23 17:55:21 +0000853 xmlBufferWriteCHAR(buf, content->name);
854 break;
855 case XML_ELEMENT_CONTENT_SEQ:
856 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
857 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
858 xmlDumpElementContent(buf, content->c1, 1);
859 else
860 xmlDumpElementContent(buf, content->c1, 0);
861 xmlBufferWriteChar(buf, " , ");
862 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
863 xmlDumpElementContent(buf, content->c2, 1);
864 else
865 xmlDumpElementContent(buf, content->c2, 0);
866 break;
867 case XML_ELEMENT_CONTENT_OR:
868 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
869 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
870 xmlDumpElementContent(buf, content->c1, 1);
871 else
872 xmlDumpElementContent(buf, content->c1, 0);
873 xmlBufferWriteChar(buf, " | ");
874 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
875 xmlDumpElementContent(buf, content->c2, 1);
876 else
877 xmlDumpElementContent(buf, content->c2, 0);
878 break;
879 default:
880 xmlGenericError(xmlGenericErrorContext,
881 "xmlDumpElementContent: unknown type %d\n",
882 content->type);
883 }
884 if (glob)
885 xmlBufferWriteChar(buf, ")");
886 switch (content->ocur) {
887 case XML_ELEMENT_CONTENT_ONCE:
888 break;
889 case XML_ELEMENT_CONTENT_OPT:
890 xmlBufferWriteChar(buf, "?");
891 break;
892 case XML_ELEMENT_CONTENT_MULT:
893 xmlBufferWriteChar(buf, "*");
894 break;
895 case XML_ELEMENT_CONTENT_PLUS:
896 xmlBufferWriteChar(buf, "+");
897 break;
898 }
899}
900
901/**
902 * xmlSprintfElementContent:
903 * @buf: an output buffer
904 * @content: An element table
905 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
906 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000907 * Deprecated, unsafe, use xmlSnprintfElementContent
908 */
909void
910xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
911 xmlElementContentPtr content ATTRIBUTE_UNUSED,
912 int glob ATTRIBUTE_UNUSED) {
913}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +0000914#endif /* LIBXML_OUTPUT_ENABLED */
Daniel Veillardd3d06722001-08-15 12:06:36 +0000915
916/**
917 * xmlSnprintfElementContent:
918 * @buf: an output buffer
919 * @size: the buffer size
920 * @content: An element table
921 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
922 *
Owen Taylor3473f882001-02-23 17:55:21 +0000923 * This will dump the content of the element content definition
924 * Intended just for the debug routine
925 */
926void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000927xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
928 int len;
929
Owen Taylor3473f882001-02-23 17:55:21 +0000930 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000931 len = strlen(buf);
932 if (size - len < 50) {
933 if ((size - len > 4) && (buf[len - 1] != '.'))
934 strcat(buf, " ...");
935 return;
936 }
Owen Taylor3473f882001-02-23 17:55:21 +0000937 if (glob) strcat(buf, "(");
938 switch (content->type) {
939 case XML_ELEMENT_CONTENT_PCDATA:
940 strcat(buf, "#PCDATA");
941 break;
942 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000943 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000944 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000945 strcat(buf, " ...");
946 return;
947 }
948 strcat(buf, (char *) content->prefix);
949 strcat(buf, ":");
950 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000951 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000952 strcat(buf, " ...");
953 return;
954 }
Daniel Veillarda76fe5c2003-04-24 16:06:47 +0000955 if (content->name != NULL)
956 strcat(buf, (char *) content->name);
Owen Taylor3473f882001-02-23 17:55:21 +0000957 break;
958 case XML_ELEMENT_CONTENT_SEQ:
959 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
960 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000961 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000962 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000963 xmlSnprintfElementContent(buf, size, content->c1, 0);
964 len = strlen(buf);
965 if (size - len < 50) {
966 if ((size - len > 4) && (buf[len - 1] != '.'))
967 strcat(buf, " ...");
968 return;
969 }
Owen Taylor3473f882001-02-23 17:55:21 +0000970 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000971 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
972 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
973 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000974 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000975 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000976 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000977 break;
978 case XML_ELEMENT_CONTENT_OR:
979 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
980 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000981 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000982 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000983 xmlSnprintfElementContent(buf, size, content->c1, 0);
984 len = strlen(buf);
985 if (size - len < 50) {
986 if ((size - len > 4) && (buf[len - 1] != '.'))
987 strcat(buf, " ...");
988 return;
989 }
Owen Taylor3473f882001-02-23 17:55:21 +0000990 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000991 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
992 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
993 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000994 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000995 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000996 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000997 break;
998 }
999 if (glob)
1000 strcat(buf, ")");
1001 switch (content->ocur) {
1002 case XML_ELEMENT_CONTENT_ONCE:
1003 break;
1004 case XML_ELEMENT_CONTENT_OPT:
1005 strcat(buf, "?");
1006 break;
1007 case XML_ELEMENT_CONTENT_MULT:
1008 strcat(buf, "*");
1009 break;
1010 case XML_ELEMENT_CONTENT_PLUS:
1011 strcat(buf, "+");
1012 break;
1013 }
1014}
1015
1016/****************************************************************
1017 * *
1018 * Registration of DTD declarations *
1019 * *
1020 ****************************************************************/
1021
1022/**
1023 * xmlCreateElementTable:
1024 *
1025 * create and initialize an empty element hash table.
1026 *
1027 * Returns the xmlElementTablePtr just created or NULL in case of error.
1028 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001029static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001030xmlCreateElementTable(void) {
1031 return(xmlHashCreate(0));
1032}
1033
1034/**
1035 * xmlFreeElement:
1036 * @elem: An element
1037 *
1038 * Deallocate the memory used by an element definition
1039 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001040static void
Owen Taylor3473f882001-02-23 17:55:21 +00001041xmlFreeElement(xmlElementPtr elem) {
1042 if (elem == NULL) return;
1043 xmlUnlinkNode((xmlNodePtr) elem);
1044 xmlFreeElementContent(elem->content);
1045 if (elem->name != NULL)
1046 xmlFree((xmlChar *) elem->name);
1047 if (elem->prefix != NULL)
1048 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001049#ifdef LIBXML_REGEXP_ENABLED
1050 if (elem->contModel != NULL)
1051 xmlRegFreeRegexp(elem->contModel);
1052#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001053 xmlFree(elem);
1054}
1055
1056
1057/**
1058 * xmlAddElementDecl:
1059 * @ctxt: the validation context
1060 * @dtd: pointer to the DTD
1061 * @name: the entity name
1062 * @type: the element type
1063 * @content: the element content tree or NULL
1064 *
1065 * Register a new element declaration
1066 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001067 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001068 */
1069xmlElementPtr
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001070xmlAddElementDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1071 xmlDtdPtr dtd, const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001072 xmlElementTypeVal type,
1073 xmlElementContentPtr content) {
1074 xmlElementPtr ret;
1075 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001076 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001077 xmlChar *ns, *uqname;
1078
1079 if (dtd == NULL) {
1080 xmlGenericError(xmlGenericErrorContext,
1081 "xmlAddElementDecl: dtd == NULL\n");
1082 return(NULL);
1083 }
1084 if (name == NULL) {
1085 xmlGenericError(xmlGenericErrorContext,
1086 "xmlAddElementDecl: name == NULL\n");
1087 return(NULL);
1088 }
1089 switch (type) {
1090 case XML_ELEMENT_TYPE_EMPTY:
1091 if (content != NULL) {
1092 xmlGenericError(xmlGenericErrorContext,
1093 "xmlAddElementDecl: content != NULL for EMPTY\n");
1094 return(NULL);
1095 }
1096 break;
1097 case XML_ELEMENT_TYPE_ANY:
1098 if (content != NULL) {
1099 xmlGenericError(xmlGenericErrorContext,
1100 "xmlAddElementDecl: content != NULL for ANY\n");
1101 return(NULL);
1102 }
1103 break;
1104 case XML_ELEMENT_TYPE_MIXED:
1105 if (content == NULL) {
1106 xmlGenericError(xmlGenericErrorContext,
1107 "xmlAddElementDecl: content == NULL for MIXED\n");
1108 return(NULL);
1109 }
1110 break;
1111 case XML_ELEMENT_TYPE_ELEMENT:
1112 if (content == NULL) {
1113 xmlGenericError(xmlGenericErrorContext,
1114 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1115 return(NULL);
1116 }
1117 break;
1118 default:
1119 xmlGenericError(xmlGenericErrorContext,
1120 "xmlAddElementDecl: unknown type %d\n", type);
1121 return(NULL);
1122 }
1123
1124 /*
1125 * check if name is a QName
1126 */
1127 uqname = xmlSplitQName2(name, &ns);
1128 if (uqname != NULL)
1129 name = uqname;
1130
1131 /*
1132 * Create the Element table if needed.
1133 */
1134 table = (xmlElementTablePtr) dtd->elements;
1135 if (table == NULL) {
1136 table = xmlCreateElementTable();
1137 dtd->elements = (void *) table;
1138 }
1139 if (table == NULL) {
1140 xmlGenericError(xmlGenericErrorContext,
1141 "xmlAddElementDecl: Table creation failed!\n");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001142 if (uqname != NULL)
1143 xmlFree(uqname);
1144 if (ns != NULL)
1145 xmlFree(ns);
Owen Taylor3473f882001-02-23 17:55:21 +00001146 return(NULL);
1147 }
1148
Daniel Veillarda10efa82001-04-18 13:09:01 +00001149 /*
1150 * lookup old attributes inserted on an undefined element in the
1151 * internal subset.
1152 */
1153 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1154 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1155 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1156 oldAttributes = ret->attributes;
1157 ret->attributes = NULL;
1158 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1159 xmlFreeElement(ret);
1160 }
Owen Taylor3473f882001-02-23 17:55:21 +00001161 }
Owen Taylor3473f882001-02-23 17:55:21 +00001162
1163 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001164 * The element may already be present if one of its attribute
1165 * was registered first
1166 */
1167 ret = xmlHashLookup2(table, name, ns);
1168 if (ret != NULL) {
1169 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001170#ifdef LIBXML_VALID_ENABLED
Daniel Veillarda10efa82001-04-18 13:09:01 +00001171 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001172 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001173 */
1174 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
Daniel Veillard4432df22003-09-28 18:58:27 +00001175#endif /* LIBXML_VALID_ENABLED */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001176 if (uqname != NULL)
1177 xmlFree(uqname);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001178 if (ns != NULL)
1179 xmlFree(ns);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001180 return(NULL);
1181 }
1182 } else {
1183 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1184 if (ret == NULL) {
1185 xmlGenericError(xmlGenericErrorContext,
1186 "xmlAddElementDecl: out of memory\n");
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001187 if (uqname != NULL)
1188 xmlFree(uqname);
1189 if (ns != NULL)
1190 xmlFree(ns);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001191 return(NULL);
1192 }
1193 memset(ret, 0, sizeof(xmlElement));
1194 ret->type = XML_ELEMENT_DECL;
1195
1196 /*
1197 * fill the structure.
1198 */
1199 ret->name = xmlStrdup(name);
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00001200 if (ret->name == NULL) {
1201 xmlGenericError(xmlGenericErrorContext,
1202 "xmlAddElementDecl: out of memory\n");
1203 if (uqname != NULL)
1204 xmlFree(uqname);
1205 if (ns != NULL)
1206 xmlFree(ns);
1207 xmlFree(ret);
1208 return(NULL);
1209 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00001210 ret->prefix = ns;
1211
1212 /*
1213 * Validity Check:
1214 * Insertion must not fail
1215 */
1216 if (xmlHashAddEntry2(table, name, ns, ret)) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001217#ifdef LIBXML_VALID_ENABLED
Daniel Veillarda10efa82001-04-18 13:09:01 +00001218 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001219 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001220 */
1221 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
Daniel Veillard4432df22003-09-28 18:58:27 +00001222#endif /* LIBXML_VALID_ENABLED */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001223 xmlFreeElement(ret);
1224 if (uqname != NULL)
1225 xmlFree(uqname);
1226 return(NULL);
1227 }
William M. Brack4e52f2f2003-09-14 18:07:39 +00001228 /*
1229 * For new element, may have attributes from earlier
1230 * definition in internal subset
1231 */
1232 ret->attributes = oldAttributes;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001233 }
1234
1235 /*
1236 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001237 */
1238 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001239 ret->content = xmlCopyElementContent(content);
Owen Taylor3473f882001-02-23 17:55:21 +00001240
1241 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001242 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001243 */
1244 ret->parent = dtd;
1245 ret->doc = dtd->doc;
1246 if (dtd->last == NULL) {
1247 dtd->children = dtd->last = (xmlNodePtr) ret;
1248 } else {
1249 dtd->last->next = (xmlNodePtr) ret;
1250 ret->prev = dtd->last;
1251 dtd->last = (xmlNodePtr) ret;
1252 }
1253 if (uqname != NULL)
1254 xmlFree(uqname);
1255 return(ret);
1256}
1257
1258/**
1259 * xmlFreeElementTable:
1260 * @table: An element table
1261 *
1262 * Deallocate the memory used by an element hash table.
1263 */
1264void
1265xmlFreeElementTable(xmlElementTablePtr table) {
1266 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1267}
1268
Daniel Veillard652327a2003-09-29 18:02:38 +00001269#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001270/**
1271 * xmlCopyElement:
1272 * @elem: An element
1273 *
1274 * Build a copy of an element.
1275 *
1276 * Returns the new xmlElementPtr or NULL in case of error.
1277 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001278static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001279xmlCopyElement(xmlElementPtr elem) {
1280 xmlElementPtr cur;
1281
1282 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1283 if (cur == NULL) {
1284 xmlGenericError(xmlGenericErrorContext,
1285 "xmlCopyElement: out of memory !\n");
1286 return(NULL);
1287 }
1288 memset(cur, 0, sizeof(xmlElement));
1289 cur->type = XML_ELEMENT_DECL;
1290 cur->etype = elem->etype;
1291 if (elem->name != NULL)
1292 cur->name = xmlStrdup(elem->name);
1293 else
1294 cur->name = NULL;
1295 if (elem->prefix != NULL)
1296 cur->prefix = xmlStrdup(elem->prefix);
1297 else
1298 cur->prefix = NULL;
1299 cur->content = xmlCopyElementContent(elem->content);
1300 /* TODO : rebuild the attribute list on the copy */
1301 cur->attributes = NULL;
1302 return(cur);
1303}
1304
1305/**
1306 * xmlCopyElementTable:
1307 * @table: An element table
1308 *
1309 * Build a copy of an element table.
1310 *
1311 * Returns the new xmlElementTablePtr or NULL in case of error.
1312 */
1313xmlElementTablePtr
1314xmlCopyElementTable(xmlElementTablePtr table) {
1315 return((xmlElementTablePtr) xmlHashCopy(table,
1316 (xmlHashCopier) xmlCopyElement));
1317}
Daniel Veillard652327a2003-09-29 18:02:38 +00001318#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001319
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001320#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001321/**
1322 * xmlDumpElementDecl:
1323 * @buf: the XML buffer output
1324 * @elem: An element table
1325 *
1326 * This will dump the content of the element declaration as an XML
1327 * DTD definition
1328 */
1329void
1330xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1331 switch (elem->etype) {
1332 case XML_ELEMENT_TYPE_EMPTY:
1333 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001334 if (elem->prefix != NULL) {
1335 xmlBufferWriteCHAR(buf, elem->prefix);
1336 xmlBufferWriteChar(buf, ":");
1337 }
Owen Taylor3473f882001-02-23 17:55:21 +00001338 xmlBufferWriteCHAR(buf, elem->name);
1339 xmlBufferWriteChar(buf, " EMPTY>\n");
1340 break;
1341 case XML_ELEMENT_TYPE_ANY:
1342 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001343 if (elem->prefix != NULL) {
1344 xmlBufferWriteCHAR(buf, elem->prefix);
1345 xmlBufferWriteChar(buf, ":");
1346 }
Owen Taylor3473f882001-02-23 17:55:21 +00001347 xmlBufferWriteCHAR(buf, elem->name);
1348 xmlBufferWriteChar(buf, " ANY>\n");
1349 break;
1350 case XML_ELEMENT_TYPE_MIXED:
1351 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001352 if (elem->prefix != NULL) {
1353 xmlBufferWriteCHAR(buf, elem->prefix);
1354 xmlBufferWriteChar(buf, ":");
1355 }
Owen Taylor3473f882001-02-23 17:55:21 +00001356 xmlBufferWriteCHAR(buf, elem->name);
1357 xmlBufferWriteChar(buf, " ");
1358 xmlDumpElementContent(buf, elem->content, 1);
1359 xmlBufferWriteChar(buf, ">\n");
1360 break;
1361 case XML_ELEMENT_TYPE_ELEMENT:
1362 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001363 if (elem->prefix != NULL) {
1364 xmlBufferWriteCHAR(buf, elem->prefix);
1365 xmlBufferWriteChar(buf, ":");
1366 }
Owen Taylor3473f882001-02-23 17:55:21 +00001367 xmlBufferWriteCHAR(buf, elem->name);
1368 xmlBufferWriteChar(buf, " ");
1369 xmlDumpElementContent(buf, elem->content, 1);
1370 xmlBufferWriteChar(buf, ">\n");
1371 break;
1372 default:
1373 xmlGenericError(xmlGenericErrorContext,
1374 "xmlDumpElementDecl: internal: unknown type %d\n",
1375 elem->etype);
1376 }
1377}
1378
1379/**
1380 * xmlDumpElementTable:
1381 * @buf: the XML buffer output
1382 * @table: An element table
1383 *
1384 * This will dump the content of the element table as an XML DTD definition
1385 */
1386void
1387xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1388 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1389}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001390#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001391
1392/**
1393 * xmlCreateEnumeration:
1394 * @name: the enumeration name or NULL
1395 *
1396 * create and initialize an enumeration attribute node.
1397 *
1398 * Returns the xmlEnumerationPtr just created or NULL in case
1399 * of error.
1400 */
1401xmlEnumerationPtr
Daniel Veillard2fdbd322003-08-18 12:15:38 +00001402xmlCreateEnumeration(const xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00001403 xmlEnumerationPtr ret;
1404
1405 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1406 if (ret == NULL) {
1407 xmlGenericError(xmlGenericErrorContext,
1408 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1409 (long)sizeof(xmlEnumeration));
1410 return(NULL);
1411 }
1412 memset(ret, 0, sizeof(xmlEnumeration));
1413
1414 if (name != NULL)
1415 ret->name = xmlStrdup(name);
1416 return(ret);
1417}
1418
1419/**
1420 * xmlFreeEnumeration:
1421 * @cur: the tree to free.
1422 *
1423 * free an enumeration attribute node (recursive).
1424 */
1425void
1426xmlFreeEnumeration(xmlEnumerationPtr cur) {
1427 if (cur == NULL) return;
1428
1429 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1430
1431 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001432 xmlFree(cur);
1433}
1434
Daniel Veillard652327a2003-09-29 18:02:38 +00001435#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001436/**
1437 * xmlCopyEnumeration:
1438 * @cur: the tree to copy.
1439 *
1440 * Copy an enumeration attribute node (recursive).
1441 *
1442 * Returns the xmlEnumerationPtr just created or NULL in case
1443 * of error.
1444 */
1445xmlEnumerationPtr
1446xmlCopyEnumeration(xmlEnumerationPtr cur) {
1447 xmlEnumerationPtr ret;
1448
1449 if (cur == NULL) return(NULL);
1450 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1451
1452 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1453 else ret->next = NULL;
1454
1455 return(ret);
1456}
Daniel Veillard652327a2003-09-29 18:02:38 +00001457#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001458
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001459#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001460/**
1461 * xmlDumpEnumeration:
1462 * @buf: the XML buffer output
1463 * @enum: An enumeration
1464 *
1465 * This will dump the content of the enumeration
1466 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001467static void
Owen Taylor3473f882001-02-23 17:55:21 +00001468xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1469 if (cur == NULL) return;
1470
1471 xmlBufferWriteCHAR(buf, cur->name);
1472 if (cur->next == NULL)
1473 xmlBufferWriteChar(buf, ")");
1474 else {
1475 xmlBufferWriteChar(buf, " | ");
1476 xmlDumpEnumeration(buf, cur->next);
1477 }
1478}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001479#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001480
1481/**
1482 * xmlCreateAttributeTable:
1483 *
1484 * create and initialize an empty attribute hash table.
1485 *
1486 * Returns the xmlAttributeTablePtr just created or NULL in case
1487 * of error.
1488 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001489static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001490xmlCreateAttributeTable(void) {
1491 return(xmlHashCreate(0));
1492}
1493
Daniel Veillard4432df22003-09-28 18:58:27 +00001494#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001495/**
1496 * xmlScanAttributeDeclCallback:
1497 * @attr: the attribute decl
1498 * @list: the list to update
1499 *
1500 * Callback called by xmlScanAttributeDecl when a new attribute
1501 * has to be entered in the list.
1502 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001503static void
Owen Taylor3473f882001-02-23 17:55:21 +00001504xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001505 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001506 attr->nexth = *list;
1507 *list = attr;
1508}
1509
1510/**
1511 * xmlScanAttributeDecl:
1512 * @dtd: pointer to the DTD
1513 * @elem: the element name
1514 *
1515 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001516 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001517 *
1518 * Returns the pointer to the first attribute decl in the chain,
1519 * possibly NULL.
1520 */
1521xmlAttributePtr
1522xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1523 xmlAttributePtr ret = NULL;
1524 xmlAttributeTablePtr table;
1525
1526 if (dtd == NULL) {
1527 xmlGenericError(xmlGenericErrorContext,
1528 "xmlScanAttributeDecl: dtd == NULL\n");
1529 return(NULL);
1530 }
1531 if (elem == NULL) {
1532 xmlGenericError(xmlGenericErrorContext,
1533 "xmlScanAttributeDecl: elem == NULL\n");
1534 return(NULL);
1535 }
1536 table = (xmlAttributeTablePtr) dtd->attributes;
1537 if (table == NULL)
1538 return(NULL);
1539
1540 /* WRONG !!! */
1541 xmlHashScan3(table, NULL, NULL, elem,
1542 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1543 return(ret);
1544}
1545
1546/**
1547 * xmlScanIDAttributeDecl:
1548 * @ctxt: the validation context
1549 * @elem: the element name
1550 *
1551 * Verify that the element don't have too many ID attributes
1552 * declared.
1553 *
1554 * Returns the number of ID attributes found.
1555 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001556static int
Owen Taylor3473f882001-02-23 17:55:21 +00001557xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1558 xmlAttributePtr cur;
1559 int ret = 0;
1560
1561 if (elem == NULL) return(0);
1562 cur = elem->attributes;
1563 while (cur != NULL) {
1564 if (cur->atype == XML_ATTRIBUTE_ID) {
1565 ret ++;
1566 if (ret > 1)
1567 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001568 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001569 elem->name, cur->name);
1570 }
1571 cur = cur->nexth;
1572 }
1573 return(ret);
1574}
Daniel Veillard4432df22003-09-28 18:58:27 +00001575#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001576
1577/**
1578 * xmlFreeAttribute:
1579 * @elem: An attribute
1580 *
1581 * Deallocate the memory used by an attribute definition
1582 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001583static void
Owen Taylor3473f882001-02-23 17:55:21 +00001584xmlFreeAttribute(xmlAttributePtr attr) {
1585 if (attr == NULL) return;
1586 xmlUnlinkNode((xmlNodePtr) attr);
1587 if (attr->tree != NULL)
1588 xmlFreeEnumeration(attr->tree);
1589 if (attr->elem != NULL)
1590 xmlFree((xmlChar *) attr->elem);
1591 if (attr->name != NULL)
1592 xmlFree((xmlChar *) attr->name);
1593 if (attr->defaultValue != NULL)
1594 xmlFree((xmlChar *) attr->defaultValue);
1595 if (attr->prefix != NULL)
1596 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001597 xmlFree(attr);
1598}
1599
1600
1601/**
1602 * xmlAddAttributeDecl:
1603 * @ctxt: the validation context
1604 * @dtd: pointer to the DTD
1605 * @elem: the element name
1606 * @name: the attribute name
1607 * @ns: the attribute namespace prefix
1608 * @type: the attribute type
1609 * @def: the attribute default type
1610 * @defaultValue: the attribute default value
1611 * @tree: if it's an enumeration, the associated list
1612 *
1613 * Register a new attribute declaration
1614 * Note that @tree becomes the ownership of the DTD
1615 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001616 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001617 */
1618xmlAttributePtr
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001619xmlAddAttributeDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1620 xmlDtdPtr dtd, const xmlChar *elem,
Owen Taylor3473f882001-02-23 17:55:21 +00001621 const xmlChar *name, const xmlChar *ns,
1622 xmlAttributeType type, xmlAttributeDefault def,
1623 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1624 xmlAttributePtr ret;
1625 xmlAttributeTablePtr table;
1626 xmlElementPtr elemDef;
1627
1628 if (dtd == NULL) {
1629 xmlGenericError(xmlGenericErrorContext,
1630 "xmlAddAttributeDecl: dtd == NULL\n");
1631 xmlFreeEnumeration(tree);
1632 return(NULL);
1633 }
1634 if (name == NULL) {
1635 xmlGenericError(xmlGenericErrorContext,
1636 "xmlAddAttributeDecl: name == NULL\n");
1637 xmlFreeEnumeration(tree);
1638 return(NULL);
1639 }
1640 if (elem == NULL) {
1641 xmlGenericError(xmlGenericErrorContext,
1642 "xmlAddAttributeDecl: elem == NULL\n");
1643 xmlFreeEnumeration(tree);
1644 return(NULL);
1645 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001646
Daniel Veillard4432df22003-09-28 18:58:27 +00001647#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001648 /*
1649 * Check the type and possibly the default value.
1650 */
1651 switch (type) {
1652 case XML_ATTRIBUTE_CDATA:
1653 break;
1654 case XML_ATTRIBUTE_ID:
1655 break;
1656 case XML_ATTRIBUTE_IDREF:
1657 break;
1658 case XML_ATTRIBUTE_IDREFS:
1659 break;
1660 case XML_ATTRIBUTE_ENTITY:
1661 break;
1662 case XML_ATTRIBUTE_ENTITIES:
1663 break;
1664 case XML_ATTRIBUTE_NMTOKEN:
1665 break;
1666 case XML_ATTRIBUTE_NMTOKENS:
1667 break;
1668 case XML_ATTRIBUTE_ENUMERATION:
1669 break;
1670 case XML_ATTRIBUTE_NOTATION:
1671 break;
1672 default:
1673 xmlGenericError(xmlGenericErrorContext,
1674 "xmlAddAttributeDecl: unknown type %d\n", type);
1675 xmlFreeEnumeration(tree);
1676 return(NULL);
1677 }
1678 if ((defaultValue != NULL) &&
1679 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001680 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001681 elem, name, defaultValue);
1682 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001683 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001684 }
Daniel Veillard4432df22003-09-28 18:58:27 +00001685#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001686
1687 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001688 * Check first that an attribute defined in the external subset wasn't
1689 * already defined in the internal subset
1690 */
1691 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1692 (dtd->doc->intSubset != NULL) &&
1693 (dtd->doc->intSubset->attributes != NULL)) {
1694 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1695 if (ret != NULL)
1696 return(NULL);
1697 }
1698
1699 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001700 * Create the Attribute table if needed.
1701 */
1702 table = (xmlAttributeTablePtr) dtd->attributes;
1703 if (table == NULL) {
1704 table = xmlCreateAttributeTable();
1705 dtd->attributes = (void *) table;
1706 }
1707 if (table == NULL) {
1708 xmlGenericError(xmlGenericErrorContext,
1709 "xmlAddAttributeDecl: Table creation failed!\n");
1710 return(NULL);
1711 }
1712
1713
1714 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1715 if (ret == NULL) {
1716 xmlGenericError(xmlGenericErrorContext,
1717 "xmlAddAttributeDecl: out of memory\n");
1718 return(NULL);
1719 }
1720 memset(ret, 0, sizeof(xmlAttribute));
1721 ret->type = XML_ATTRIBUTE_DECL;
1722
1723 /*
1724 * fill the structure.
1725 */
1726 ret->atype = type;
1727 ret->name = xmlStrdup(name);
1728 ret->prefix = xmlStrdup(ns);
1729 ret->elem = xmlStrdup(elem);
1730 ret->def = def;
1731 ret->tree = tree;
1732 if (defaultValue != NULL)
1733 ret->defaultValue = xmlStrdup(defaultValue);
1734
1735 /*
1736 * Validity Check:
1737 * Search the DTD for previous declarations of the ATTLIST
1738 */
1739 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001740#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001741 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001742 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001743 */
1744 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001745 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001746 name, elem);
Daniel Veillard4432df22003-09-28 18:58:27 +00001747#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001748 xmlFreeAttribute(ret);
1749 return(NULL);
1750 }
1751
1752 /*
1753 * Validity Check:
1754 * Multiple ID per element
1755 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001756 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001757 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001758
Daniel Veillard4432df22003-09-28 18:58:27 +00001759#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001760 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001761 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001762 VERROR(ctxt->userData,
1763 "Element %s has too may ID attributes defined : %s\n",
1764 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001765 ctxt->valid = 0;
1766 }
Daniel Veillard4432df22003-09-28 18:58:27 +00001767#endif /* LIBXML_VALID_ENABLED */
Daniel Veillardc7612992002-02-17 22:47:37 +00001768
Daniel Veillard48da9102001-08-07 01:10:10 +00001769 /*
1770 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001771 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001772 */
1773 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1774 ((ret->prefix != NULL &&
1775 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1776 ret->nexth = elemDef->attributes;
1777 elemDef->attributes = ret;
1778 } else {
1779 xmlAttributePtr tmp = elemDef->attributes;
1780
1781 while ((tmp != NULL) &&
1782 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1783 ((ret->prefix != NULL &&
1784 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1785 if (tmp->nexth == NULL)
1786 break;
1787 tmp = tmp->nexth;
1788 }
1789 if (tmp != NULL) {
1790 ret->nexth = tmp->nexth;
1791 tmp->nexth = ret;
1792 } else {
1793 ret->nexth = elemDef->attributes;
1794 elemDef->attributes = ret;
1795 }
1796 }
Owen Taylor3473f882001-02-23 17:55:21 +00001797 }
1798
1799 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001800 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001801 */
1802 ret->parent = dtd;
1803 ret->doc = dtd->doc;
1804 if (dtd->last == NULL) {
1805 dtd->children = dtd->last = (xmlNodePtr) ret;
1806 } else {
1807 dtd->last->next = (xmlNodePtr) ret;
1808 ret->prev = dtd->last;
1809 dtd->last = (xmlNodePtr) ret;
1810 }
1811 return(ret);
1812}
1813
1814/**
1815 * xmlFreeAttributeTable:
1816 * @table: An attribute table
1817 *
1818 * Deallocate the memory used by an entities hash table.
1819 */
1820void
1821xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1822 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1823}
1824
Daniel Veillard652327a2003-09-29 18:02:38 +00001825#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001826/**
1827 * xmlCopyAttribute:
1828 * @attr: An attribute
1829 *
1830 * Build a copy of an attribute.
1831 *
1832 * Returns the new xmlAttributePtr or NULL in case of error.
1833 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001834static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001835xmlCopyAttribute(xmlAttributePtr attr) {
1836 xmlAttributePtr cur;
1837
1838 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1839 if (cur == NULL) {
1840 xmlGenericError(xmlGenericErrorContext,
1841 "xmlCopyAttribute: out of memory !\n");
1842 return(NULL);
1843 }
1844 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001845 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001846 cur->atype = attr->atype;
1847 cur->def = attr->def;
1848 cur->tree = xmlCopyEnumeration(attr->tree);
1849 if (attr->elem != NULL)
1850 cur->elem = xmlStrdup(attr->elem);
1851 if (attr->name != NULL)
1852 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001853 if (attr->prefix != NULL)
1854 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001855 if (attr->defaultValue != NULL)
1856 cur->defaultValue = xmlStrdup(attr->defaultValue);
1857 return(cur);
1858}
1859
1860/**
1861 * xmlCopyAttributeTable:
1862 * @table: An attribute table
1863 *
1864 * Build a copy of an attribute table.
1865 *
1866 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1867 */
1868xmlAttributeTablePtr
1869xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1870 return((xmlAttributeTablePtr) xmlHashCopy(table,
1871 (xmlHashCopier) xmlCopyAttribute));
1872}
Daniel Veillard652327a2003-09-29 18:02:38 +00001873#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001874
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001875#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001876/**
1877 * xmlDumpAttributeDecl:
1878 * @buf: the XML buffer output
1879 * @attr: An attribute declaration
1880 *
1881 * This will dump the content of the attribute declaration as an XML
1882 * DTD definition
1883 */
1884void
1885xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1886 xmlBufferWriteChar(buf, "<!ATTLIST ");
1887 xmlBufferWriteCHAR(buf, attr->elem);
1888 xmlBufferWriteChar(buf, " ");
1889 if (attr->prefix != NULL) {
1890 xmlBufferWriteCHAR(buf, attr->prefix);
1891 xmlBufferWriteChar(buf, ":");
1892 }
1893 xmlBufferWriteCHAR(buf, attr->name);
1894 switch (attr->atype) {
1895 case XML_ATTRIBUTE_CDATA:
1896 xmlBufferWriteChar(buf, " CDATA");
1897 break;
1898 case XML_ATTRIBUTE_ID:
1899 xmlBufferWriteChar(buf, " ID");
1900 break;
1901 case XML_ATTRIBUTE_IDREF:
1902 xmlBufferWriteChar(buf, " IDREF");
1903 break;
1904 case XML_ATTRIBUTE_IDREFS:
1905 xmlBufferWriteChar(buf, " IDREFS");
1906 break;
1907 case XML_ATTRIBUTE_ENTITY:
1908 xmlBufferWriteChar(buf, " ENTITY");
1909 break;
1910 case XML_ATTRIBUTE_ENTITIES:
1911 xmlBufferWriteChar(buf, " ENTITIES");
1912 break;
1913 case XML_ATTRIBUTE_NMTOKEN:
1914 xmlBufferWriteChar(buf, " NMTOKEN");
1915 break;
1916 case XML_ATTRIBUTE_NMTOKENS:
1917 xmlBufferWriteChar(buf, " NMTOKENS");
1918 break;
1919 case XML_ATTRIBUTE_ENUMERATION:
1920 xmlBufferWriteChar(buf, " (");
1921 xmlDumpEnumeration(buf, attr->tree);
1922 break;
1923 case XML_ATTRIBUTE_NOTATION:
1924 xmlBufferWriteChar(buf, " NOTATION (");
1925 xmlDumpEnumeration(buf, attr->tree);
1926 break;
1927 default:
1928 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001929 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001930 attr->atype);
1931 }
1932 switch (attr->def) {
1933 case XML_ATTRIBUTE_NONE:
1934 break;
1935 case XML_ATTRIBUTE_REQUIRED:
1936 xmlBufferWriteChar(buf, " #REQUIRED");
1937 break;
1938 case XML_ATTRIBUTE_IMPLIED:
1939 xmlBufferWriteChar(buf, " #IMPLIED");
1940 break;
1941 case XML_ATTRIBUTE_FIXED:
1942 xmlBufferWriteChar(buf, " #FIXED");
1943 break;
1944 default:
1945 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001946 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001947 attr->def);
1948 }
1949 if (attr->defaultValue != NULL) {
1950 xmlBufferWriteChar(buf, " ");
1951 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1952 }
1953 xmlBufferWriteChar(buf, ">\n");
1954}
1955
1956/**
1957 * xmlDumpAttributeTable:
1958 * @buf: the XML buffer output
1959 * @table: An attribute table
1960 *
1961 * This will dump the content of the attribute table as an XML DTD definition
1962 */
1963void
1964xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1965 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1966}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001967#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001968
1969/************************************************************************
1970 * *
1971 * NOTATIONs *
1972 * *
1973 ************************************************************************/
1974/**
1975 * xmlCreateNotationTable:
1976 *
1977 * create and initialize an empty notation hash table.
1978 *
1979 * Returns the xmlNotationTablePtr just created or NULL in case
1980 * of error.
1981 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001982static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001983xmlCreateNotationTable(void) {
1984 return(xmlHashCreate(0));
1985}
1986
1987/**
1988 * xmlFreeNotation:
1989 * @not: A notation
1990 *
1991 * Deallocate the memory used by an notation definition
1992 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001993static void
Owen Taylor3473f882001-02-23 17:55:21 +00001994xmlFreeNotation(xmlNotationPtr nota) {
1995 if (nota == NULL) return;
1996 if (nota->name != NULL)
1997 xmlFree((xmlChar *) nota->name);
1998 if (nota->PublicID != NULL)
1999 xmlFree((xmlChar *) nota->PublicID);
2000 if (nota->SystemID != NULL)
2001 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002002 xmlFree(nota);
2003}
2004
2005
2006/**
2007 * xmlAddNotationDecl:
2008 * @dtd: pointer to the DTD
2009 * @ctxt: the validation context
2010 * @name: the entity name
2011 * @PublicID: the public identifier or NULL
2012 * @SystemID: the system identifier or NULL
2013 *
2014 * Register a new notation declaration
2015 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002016 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00002017 */
2018xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002019xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002020 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00002021 const xmlChar *PublicID, const xmlChar *SystemID) {
2022 xmlNotationPtr ret;
2023 xmlNotationTablePtr table;
2024
2025 if (dtd == NULL) {
2026 xmlGenericError(xmlGenericErrorContext,
2027 "xmlAddNotationDecl: dtd == NULL\n");
2028 return(NULL);
2029 }
2030 if (name == NULL) {
2031 xmlGenericError(xmlGenericErrorContext,
2032 "xmlAddNotationDecl: name == NULL\n");
2033 return(NULL);
2034 }
2035 if ((PublicID == NULL) && (SystemID == NULL)) {
2036 xmlGenericError(xmlGenericErrorContext,
2037 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002038 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002039 }
2040
2041 /*
2042 * Create the Notation table if needed.
2043 */
2044 table = (xmlNotationTablePtr) dtd->notations;
2045 if (table == NULL)
2046 dtd->notations = table = xmlCreateNotationTable();
2047 if (table == NULL) {
2048 xmlGenericError(xmlGenericErrorContext,
2049 "xmlAddNotationDecl: Table creation failed!\n");
2050 return(NULL);
2051 }
2052
2053 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2054 if (ret == NULL) {
2055 xmlGenericError(xmlGenericErrorContext,
2056 "xmlAddNotationDecl: out of memory\n");
2057 return(NULL);
2058 }
2059 memset(ret, 0, sizeof(xmlNotation));
2060
2061 /*
2062 * fill the structure.
2063 */
2064 ret->name = xmlStrdup(name);
2065 if (SystemID != NULL)
2066 ret->SystemID = xmlStrdup(SystemID);
2067 if (PublicID != NULL)
2068 ret->PublicID = xmlStrdup(PublicID);
2069
2070 /*
2071 * Validity Check:
2072 * Check the DTD for previous declarations of the ATTLIST
2073 */
2074 if (xmlHashAddEntry(table, name, ret)) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002075#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002076 xmlGenericError(xmlGenericErrorContext,
2077 "xmlAddNotationDecl: %s already defined\n", name);
Daniel Veillard4432df22003-09-28 18:58:27 +00002078#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002079 xmlFreeNotation(ret);
2080 return(NULL);
2081 }
2082 return(ret);
2083}
2084
2085/**
2086 * xmlFreeNotationTable:
2087 * @table: An notation table
2088 *
2089 * Deallocate the memory used by an entities hash table.
2090 */
2091void
2092xmlFreeNotationTable(xmlNotationTablePtr table) {
2093 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2094}
2095
Daniel Veillard652327a2003-09-29 18:02:38 +00002096#ifdef LIBXML_TREE_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002097/**
2098 * xmlCopyNotation:
2099 * @nota: A notation
2100 *
2101 * Build a copy of a notation.
2102 *
2103 * Returns the new xmlNotationPtr or NULL in case of error.
2104 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002105static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002106xmlCopyNotation(xmlNotationPtr nota) {
2107 xmlNotationPtr cur;
2108
2109 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2110 if (cur == NULL) {
2111 xmlGenericError(xmlGenericErrorContext,
2112 "xmlCopyNotation: out of memory !\n");
2113 return(NULL);
2114 }
2115 if (nota->name != NULL)
2116 cur->name = xmlStrdup(nota->name);
2117 else
2118 cur->name = NULL;
2119 if (nota->PublicID != NULL)
2120 cur->PublicID = xmlStrdup(nota->PublicID);
2121 else
2122 cur->PublicID = NULL;
2123 if (nota->SystemID != NULL)
2124 cur->SystemID = xmlStrdup(nota->SystemID);
2125 else
2126 cur->SystemID = NULL;
2127 return(cur);
2128}
2129
2130/**
2131 * xmlCopyNotationTable:
2132 * @table: A notation table
2133 *
2134 * Build a copy of a notation table.
2135 *
2136 * Returns the new xmlNotationTablePtr or NULL in case of error.
2137 */
2138xmlNotationTablePtr
2139xmlCopyNotationTable(xmlNotationTablePtr table) {
2140 return((xmlNotationTablePtr) xmlHashCopy(table,
2141 (xmlHashCopier) xmlCopyNotation));
2142}
Daniel Veillard652327a2003-09-29 18:02:38 +00002143#endif /* LIBXML_TREE_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002144
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002145#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002146/**
2147 * xmlDumpNotationDecl:
2148 * @buf: the XML buffer output
2149 * @nota: A notation declaration
2150 *
2151 * This will dump the content the notation declaration as an XML DTD definition
2152 */
2153void
2154xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2155 xmlBufferWriteChar(buf, "<!NOTATION ");
2156 xmlBufferWriteCHAR(buf, nota->name);
2157 if (nota->PublicID != NULL) {
2158 xmlBufferWriteChar(buf, " PUBLIC ");
2159 xmlBufferWriteQuotedString(buf, nota->PublicID);
2160 if (nota->SystemID != NULL) {
2161 xmlBufferWriteChar(buf, " ");
2162 xmlBufferWriteCHAR(buf, nota->SystemID);
2163 }
2164 } else {
2165 xmlBufferWriteChar(buf, " SYSTEM ");
2166 xmlBufferWriteCHAR(buf, nota->SystemID);
2167 }
2168 xmlBufferWriteChar(buf, " >\n");
2169}
2170
2171/**
2172 * xmlDumpNotationTable:
2173 * @buf: the XML buffer output
2174 * @table: A notation table
2175 *
2176 * This will dump the content of the notation table as an XML DTD definition
2177 */
2178void
2179xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2180 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2181}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002182#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002183
2184/************************************************************************
2185 * *
2186 * IDs *
2187 * *
2188 ************************************************************************/
2189/**
2190 * xmlCreateIDTable:
2191 *
2192 * create and initialize an empty id hash table.
2193 *
2194 * Returns the xmlIDTablePtr just created or NULL in case
2195 * of error.
2196 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002197static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002198xmlCreateIDTable(void) {
2199 return(xmlHashCreate(0));
2200}
2201
2202/**
2203 * xmlFreeID:
2204 * @not: A id
2205 *
2206 * Deallocate the memory used by an id definition
2207 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002208static void
Owen Taylor3473f882001-02-23 17:55:21 +00002209xmlFreeID(xmlIDPtr id) {
2210 if (id == NULL) return;
2211 if (id->value != NULL)
2212 xmlFree((xmlChar *) id->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002213 if (id->name != NULL)
2214 xmlFree((xmlChar *) id->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002215 xmlFree(id);
2216}
2217
2218/**
2219 * xmlAddID:
2220 * @ctxt: the validation context
2221 * @doc: pointer to the document
2222 * @value: the value name
2223 * @attr: the attribute holding the ID
2224 *
2225 * Register a new id declaration
2226 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002227 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002228 */
2229xmlIDPtr
2230xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2231 xmlAttrPtr attr) {
2232 xmlIDPtr ret;
2233 xmlIDTablePtr table;
2234
2235 if (doc == NULL) {
2236 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002237 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002238 return(NULL);
2239 }
2240 if (value == NULL) {
2241 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002242 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002243 return(NULL);
2244 }
2245 if (attr == NULL) {
2246 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002247 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002248 return(NULL);
2249 }
2250
2251 /*
2252 * Create the ID table if needed.
2253 */
2254 table = (xmlIDTablePtr) doc->ids;
2255 if (table == NULL)
2256 doc->ids = table = xmlCreateIDTable();
2257 if (table == NULL) {
2258 xmlGenericError(xmlGenericErrorContext,
2259 "xmlAddID: Table creation failed!\n");
2260 return(NULL);
2261 }
2262
2263 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2264 if (ret == NULL) {
2265 xmlGenericError(xmlGenericErrorContext,
2266 "xmlAddID: out of memory\n");
2267 return(NULL);
2268 }
2269
2270 /*
2271 * fill the structure.
2272 */
2273 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002274 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2275 /*
2276 * Operating in streaming mode, attr is gonna disapear
2277 */
2278 ret->name = xmlStrdup(attr->name);
2279 ret->attr = NULL;
2280 } else {
2281 ret->attr = attr;
2282 ret->name = NULL;
2283 }
2284 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002285
2286 if (xmlHashAddEntry(table, value, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002287#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002288 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002289 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002290 */
Daniel Veillard76575762002-09-05 14:21:15 +00002291 if (ctxt != NULL) {
2292 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002293 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002294 }
Daniel Veillard4432df22003-09-28 18:58:27 +00002295#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002296 xmlFreeID(ret);
2297 return(NULL);
2298 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002299 if (attr != NULL)
2300 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002301 return(ret);
2302}
2303
2304/**
2305 * xmlFreeIDTable:
2306 * @table: An id table
2307 *
2308 * Deallocate the memory used by an ID hash table.
2309 */
2310void
2311xmlFreeIDTable(xmlIDTablePtr table) {
2312 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2313}
2314
2315/**
2316 * xmlIsID:
2317 * @doc: the document
2318 * @elem: the element carrying the attribute
2319 * @attr: the attribute
2320 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002321 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002322 * then this is done if DTD loading has been requested. In the case
2323 * of HTML documents parsed with the HTML parser, then ID detection is
2324 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002325 *
2326 * Returns 0 or 1 depending on the lookup result
2327 */
2328int
2329xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2330 if (doc == NULL) return(0);
2331 if (attr == NULL) return(0);
2332 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2333 return(0);
2334 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2335 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2336 (xmlStrEqual(BAD_CAST "name", attr->name)))
2337 return(1);
2338 return(0);
2339 } else {
2340 xmlAttributePtr attrDecl;
2341
2342 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002343 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00002344 xmlChar fn[50];
Daniel Veillard37f961d2002-07-06 17:53:56 +00002345 xmlChar *fullname;
Daniel Veillardc00cda82003-04-07 10:22:39 +00002346
2347 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002348 if (fullname == NULL)
2349 return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002350 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2351 attr->name);
2352 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2353 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2354 attr->name);
Daniel Veillardc00cda82003-04-07 10:22:39 +00002355 if ((fullname != fn) && (fullname != elem->name))
2356 xmlFree(fullname);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002357 } else {
2358 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2359 attr->name);
2360 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2361 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2362 attr->name);
2363 }
Owen Taylor3473f882001-02-23 17:55:21 +00002364
2365 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2366 return(1);
2367 }
2368 return(0);
2369}
2370
2371/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002372 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002373 * @doc: the document
2374 * @attr: the attribute
2375 *
2376 * Remove the given attribute from the ID table maintained internally.
2377 *
2378 * Returns -1 if the lookup failed and 0 otherwise
2379 */
2380int
2381xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2382 xmlAttrPtr cur;
2383 xmlIDTablePtr table;
2384 xmlChar *ID;
2385
2386 if (doc == NULL) return(-1);
2387 if (attr == NULL) return(-1);
2388 table = (xmlIDTablePtr) doc->ids;
2389 if (table == NULL)
2390 return(-1);
2391
2392 if (attr == NULL)
2393 return(-1);
2394 ID = xmlNodeListGetString(doc, attr->children, 1);
2395 if (ID == NULL)
2396 return(-1);
2397 cur = xmlHashLookup(table, ID);
2398 if (cur != attr) {
2399 xmlFree(ID);
2400 return(-1);
2401 }
2402 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2403 xmlFree(ID);
2404 return(0);
2405}
2406
2407/**
2408 * xmlGetID:
2409 * @doc: pointer to the document
2410 * @ID: the ID value
2411 *
2412 * Search the attribute declaring the given ID
2413 *
2414 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2415 */
2416xmlAttrPtr
2417xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2418 xmlIDTablePtr table;
2419 xmlIDPtr id;
2420
2421 if (doc == NULL) {
2422 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2423 return(NULL);
2424 }
2425
2426 if (ID == NULL) {
2427 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2428 return(NULL);
2429 }
2430
2431 table = (xmlIDTablePtr) doc->ids;
2432 if (table == NULL)
2433 return(NULL);
2434
2435 id = xmlHashLookup(table, ID);
2436 if (id == NULL)
2437 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002438 if (id->attr == NULL) {
2439 /*
2440 * We are operating on a stream, return a well known reference
2441 * since the attribute node doesn't exist anymore
2442 */
2443 return((xmlAttrPtr) doc);
2444 }
Owen Taylor3473f882001-02-23 17:55:21 +00002445 return(id->attr);
2446}
2447
2448/************************************************************************
2449 * *
2450 * Refs *
2451 * *
2452 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002453typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002454{
2455 xmlListPtr l;
2456 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002457} xmlRemoveMemo;
2458
2459typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2460
2461typedef struct xmlValidateMemo_t
2462{
2463 xmlValidCtxtPtr ctxt;
2464 const xmlChar *name;
2465} xmlValidateMemo;
2466
2467typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002468
2469/**
2470 * xmlCreateRefTable:
2471 *
2472 * create and initialize an empty ref hash table.
2473 *
2474 * Returns the xmlRefTablePtr just created or NULL in case
2475 * of error.
2476 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002477static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002478xmlCreateRefTable(void) {
2479 return(xmlHashCreate(0));
2480}
2481
2482/**
2483 * xmlFreeRef:
2484 * @lk: A list link
2485 *
2486 * Deallocate the memory used by a ref definition
2487 */
2488static void
2489xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002490 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2491 if (ref == NULL) return;
2492 if (ref->value != NULL)
2493 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002494 if (ref->name != NULL)
2495 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002496 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002497}
2498
2499/**
2500 * xmlFreeRefList:
2501 * @list_ref: A list of references.
2502 *
2503 * Deallocate the memory used by a list of references
2504 */
2505static void
2506xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002507 if (list_ref == NULL) return;
2508 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002509}
2510
2511/**
2512 * xmlWalkRemoveRef:
2513 * @data: Contents of current link
2514 * @user: Value supplied by the user
2515 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002516 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002517 */
2518static int
2519xmlWalkRemoveRef(const void *data, const void *user)
2520{
Daniel Veillard37721922001-05-04 15:21:12 +00002521 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2522 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2523 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002524
Daniel Veillard37721922001-05-04 15:21:12 +00002525 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2526 xmlListRemoveFirst(ref_list, (void *)data);
2527 return 0;
2528 }
2529 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002530}
2531
2532/**
2533 * xmlAddRef:
2534 * @ctxt: the validation context
2535 * @doc: pointer to the document
2536 * @value: the value name
2537 * @attr: the attribute holding the Ref
2538 *
2539 * Register a new ref declaration
2540 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002541 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002542 */
2543xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002544xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002545 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002546 xmlRefPtr ret;
2547 xmlRefTablePtr table;
2548 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002549
Daniel Veillard37721922001-05-04 15:21:12 +00002550 if (doc == NULL) {
2551 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002552 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002553 return(NULL);
2554 }
2555 if (value == NULL) {
2556 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002557 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002558 return(NULL);
2559 }
2560 if (attr == NULL) {
2561 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002562 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002563 return(NULL);
2564 }
Owen Taylor3473f882001-02-23 17:55:21 +00002565
Daniel Veillard37721922001-05-04 15:21:12 +00002566 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002567 * Create the Ref table if needed.
2568 */
Daniel Veillard37721922001-05-04 15:21:12 +00002569 table = (xmlRefTablePtr) doc->refs;
2570 if (table == NULL)
2571 doc->refs = table = xmlCreateRefTable();
2572 if (table == NULL) {
2573 xmlGenericError(xmlGenericErrorContext,
2574 "xmlAddRef: Table creation failed!\n");
2575 return(NULL);
2576 }
Owen Taylor3473f882001-02-23 17:55:21 +00002577
Daniel Veillard37721922001-05-04 15:21:12 +00002578 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2579 if (ret == NULL) {
2580 xmlGenericError(xmlGenericErrorContext,
2581 "xmlAddRef: out of memory\n");
2582 return(NULL);
2583 }
Owen Taylor3473f882001-02-23 17:55:21 +00002584
Daniel Veillard37721922001-05-04 15:21:12 +00002585 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002586 * fill the structure.
2587 */
Daniel Veillard37721922001-05-04 15:21:12 +00002588 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002589 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2590 /*
2591 * Operating in streaming mode, attr is gonna disapear
2592 */
2593 ret->name = xmlStrdup(attr->name);
2594 ret->attr = NULL;
2595 } else {
2596 ret->name = NULL;
2597 ret->attr = attr;
2598 }
2599 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002600
Daniel Veillard37721922001-05-04 15:21:12 +00002601 /* To add a reference :-
2602 * References are maintained as a list of references,
2603 * Lookup the entry, if no entry create new nodelist
2604 * Add the owning node to the NodeList
2605 * Return the ref
2606 */
Owen Taylor3473f882001-02-23 17:55:21 +00002607
Daniel Veillard37721922001-05-04 15:21:12 +00002608 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2609 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2610 xmlGenericError(xmlGenericErrorContext,
2611 "xmlAddRef: Reference list creation failed!\n");
2612 return(NULL);
2613 }
2614 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2615 xmlListDelete(ref_list);
2616 xmlGenericError(xmlGenericErrorContext,
2617 "xmlAddRef: Reference list insertion failed!\n");
2618 return(NULL);
2619 }
2620 }
2621 xmlListInsert(ref_list, ret);
2622 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002623}
2624
2625/**
2626 * xmlFreeRefTable:
2627 * @table: An ref table
2628 *
2629 * Deallocate the memory used by an Ref hash table.
2630 */
2631void
2632xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002633 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002634}
2635
2636/**
2637 * xmlIsRef:
2638 * @doc: the document
2639 * @elem: the element carrying the attribute
2640 * @attr: the attribute
2641 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002642 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002643 * then this is simple, otherwise we use an heuristic: name Ref (upper
2644 * or lowercase).
2645 *
2646 * Returns 0 or 1 depending on the lookup result
2647 */
2648int
2649xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002650 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2651 return(0);
2652 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2653 /* TODO @@@ */
2654 return(0);
2655 } else {
2656 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002657
Daniel Veillard37721922001-05-04 15:21:12 +00002658 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2659 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2660 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2661 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002662
Daniel Veillard37721922001-05-04 15:21:12 +00002663 if ((attrDecl != NULL) &&
2664 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2665 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2666 return(1);
2667 }
2668 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002669}
2670
2671/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002672 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002673 * @doc: the document
2674 * @attr: the attribute
2675 *
2676 * Remove the given attribute from the Ref table maintained internally.
2677 *
2678 * Returns -1 if the lookup failed and 0 otherwise
2679 */
2680int
2681xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002682 xmlListPtr ref_list;
2683 xmlRefTablePtr table;
2684 xmlChar *ID;
2685 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002686
Daniel Veillard37721922001-05-04 15:21:12 +00002687 if (doc == NULL) return(-1);
2688 if (attr == NULL) return(-1);
2689 table = (xmlRefTablePtr) doc->refs;
2690 if (table == NULL)
2691 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002692
Daniel Veillard37721922001-05-04 15:21:12 +00002693 if (attr == NULL)
2694 return(-1);
2695 ID = xmlNodeListGetString(doc, attr->children, 1);
2696 if (ID == NULL)
2697 return(-1);
2698 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002699
Daniel Veillard37721922001-05-04 15:21:12 +00002700 if(ref_list == NULL) {
2701 xmlFree(ID);
2702 return (-1);
2703 }
2704 /* At this point, ref_list refers to a list of references which
2705 * have the same key as the supplied attr. Our list of references
2706 * is ordered by reference address and we don't have that information
2707 * here to use when removing. We'll have to walk the list and
2708 * check for a matching attribute, when we find one stop the walk
2709 * and remove the entry.
2710 * The list is ordered by reference, so that means we don't have the
2711 * key. Passing the list and the reference to the walker means we
2712 * will have enough data to be able to remove the entry.
2713 */
2714 target.l = ref_list;
2715 target.ap = attr;
2716
2717 /* Remove the supplied attr from our list */
2718 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002719
Daniel Veillard37721922001-05-04 15:21:12 +00002720 /*If the list is empty then remove the list entry in the hash */
2721 if (xmlListEmpty(ref_list))
2722 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2723 xmlFreeRefList);
2724 xmlFree(ID);
2725 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002726}
2727
2728/**
2729 * xmlGetRefs:
2730 * @doc: pointer to the document
2731 * @ID: the ID value
2732 *
2733 * Find the set of references for the supplied ID.
2734 *
2735 * Returns NULL if not found, otherwise node set for the ID.
2736 */
2737xmlListPtr
2738xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002739 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002740
Daniel Veillard37721922001-05-04 15:21:12 +00002741 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002742 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002743 return(NULL);
2744 }
Owen Taylor3473f882001-02-23 17:55:21 +00002745
Daniel Veillard37721922001-05-04 15:21:12 +00002746 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002747 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002748 return(NULL);
2749 }
Owen Taylor3473f882001-02-23 17:55:21 +00002750
Daniel Veillard37721922001-05-04 15:21:12 +00002751 table = (xmlRefTablePtr) doc->refs;
2752 if (table == NULL)
2753 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002754
Daniel Veillard37721922001-05-04 15:21:12 +00002755 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002756}
2757
2758/************************************************************************
2759 * *
2760 * Routines for validity checking *
2761 * *
2762 ************************************************************************/
2763
2764/**
2765 * xmlGetDtdElementDesc:
2766 * @dtd: a pointer to the DtD to search
2767 * @name: the element name
2768 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002769 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002770 *
2771 * returns the xmlElementPtr if found or NULL
2772 */
2773
2774xmlElementPtr
2775xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2776 xmlElementTablePtr table;
2777 xmlElementPtr cur;
2778 xmlChar *uqname = NULL, *prefix = NULL;
2779
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00002780 if ((dtd == NULL) || (name == NULL)) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002781 if (dtd->elements == NULL)
2782 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002783 table = (xmlElementTablePtr) dtd->elements;
2784
2785 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002786 if (uqname != NULL)
2787 name = uqname;
2788 cur = xmlHashLookup2(table, name, prefix);
2789 if (prefix != NULL) xmlFree(prefix);
2790 if (uqname != NULL) xmlFree(uqname);
2791 return(cur);
2792}
2793/**
2794 * xmlGetDtdElementDesc2:
2795 * @dtd: a pointer to the DtD to search
2796 * @name: the element name
2797 * @create: create an empty description if not found
2798 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002799 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002800 *
2801 * returns the xmlElementPtr if found or NULL
2802 */
2803
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002804static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002805xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2806 xmlElementTablePtr table;
2807 xmlElementPtr cur;
2808 xmlChar *uqname = NULL, *prefix = NULL;
2809
2810 if (dtd == NULL) return(NULL);
2811 if (dtd->elements == NULL) {
2812 if (!create)
2813 return(NULL);
2814 /*
2815 * Create the Element table if needed.
2816 */
2817 table = (xmlElementTablePtr) dtd->elements;
2818 if (table == NULL) {
2819 table = xmlCreateElementTable();
2820 dtd->elements = (void *) table;
2821 }
2822 if (table == NULL) {
2823 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002824 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002825 return(NULL);
2826 }
2827 }
2828 table = (xmlElementTablePtr) dtd->elements;
2829
2830 uqname = xmlSplitQName2(name, &prefix);
2831 if (uqname != NULL)
2832 name = uqname;
2833 cur = xmlHashLookup2(table, name, prefix);
2834 if ((cur == NULL) && (create)) {
2835 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2836 if (cur == NULL) {
2837 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002838 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002839 return(NULL);
2840 }
2841 memset(cur, 0, sizeof(xmlElement));
2842 cur->type = XML_ELEMENT_DECL;
2843
2844 /*
2845 * fill the structure.
2846 */
2847 cur->name = xmlStrdup(name);
2848 cur->prefix = xmlStrdup(prefix);
2849 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2850
2851 xmlHashAddEntry2(table, name, prefix, cur);
2852 }
2853 if (prefix != NULL) xmlFree(prefix);
2854 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002855 return(cur);
2856}
2857
2858/**
2859 * xmlGetDtdQElementDesc:
2860 * @dtd: a pointer to the DtD to search
2861 * @name: the element name
2862 * @prefix: the element namespace prefix
2863 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002864 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002865 *
2866 * returns the xmlElementPtr if found or NULL
2867 */
2868
Daniel Veillard48da9102001-08-07 01:10:10 +00002869xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002870xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2871 const xmlChar *prefix) {
2872 xmlElementTablePtr table;
2873
2874 if (dtd == NULL) return(NULL);
2875 if (dtd->elements == NULL) return(NULL);
2876 table = (xmlElementTablePtr) dtd->elements;
2877
2878 return(xmlHashLookup2(table, name, prefix));
2879}
2880
2881/**
2882 * xmlGetDtdAttrDesc:
2883 * @dtd: a pointer to the DtD to search
2884 * @elem: the element name
2885 * @name: the attribute name
2886 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002887 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002888 * this element.
2889 *
2890 * returns the xmlAttributePtr if found or NULL
2891 */
2892
2893xmlAttributePtr
2894xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2895 xmlAttributeTablePtr table;
2896 xmlAttributePtr cur;
2897 xmlChar *uqname = NULL, *prefix = NULL;
2898
2899 if (dtd == NULL) return(NULL);
2900 if (dtd->attributes == NULL) return(NULL);
2901
2902 table = (xmlAttributeTablePtr) dtd->attributes;
2903 if (table == NULL)
2904 return(NULL);
2905
2906 uqname = xmlSplitQName2(name, &prefix);
2907
2908 if (uqname != NULL) {
2909 cur = xmlHashLookup3(table, uqname, prefix, elem);
2910 if (prefix != NULL) xmlFree(prefix);
2911 if (uqname != NULL) xmlFree(uqname);
2912 } else
2913 cur = xmlHashLookup3(table, name, NULL, elem);
2914 return(cur);
2915}
2916
2917/**
2918 * xmlGetDtdQAttrDesc:
2919 * @dtd: a pointer to the DtD to search
2920 * @elem: the element name
2921 * @name: the attribute name
2922 * @prefix: the attribute namespace prefix
2923 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002924 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002925 * this element.
2926 *
2927 * returns the xmlAttributePtr if found or NULL
2928 */
2929
Daniel Veillard48da9102001-08-07 01:10:10 +00002930xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002931xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2932 const xmlChar *prefix) {
2933 xmlAttributeTablePtr table;
2934
2935 if (dtd == NULL) return(NULL);
2936 if (dtd->attributes == NULL) return(NULL);
2937 table = (xmlAttributeTablePtr) dtd->attributes;
2938
2939 return(xmlHashLookup3(table, name, prefix, elem));
2940}
2941
2942/**
2943 * xmlGetDtdNotationDesc:
2944 * @dtd: a pointer to the DtD to search
2945 * @name: the notation name
2946 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002947 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002948 *
2949 * returns the xmlNotationPtr if found or NULL
2950 */
2951
2952xmlNotationPtr
2953xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2954 xmlNotationTablePtr table;
2955
2956 if (dtd == NULL) return(NULL);
2957 if (dtd->notations == NULL) return(NULL);
2958 table = (xmlNotationTablePtr) dtd->notations;
2959
2960 return(xmlHashLookup(table, name));
2961}
2962
Daniel Veillard4432df22003-09-28 18:58:27 +00002963#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002964/**
2965 * xmlValidateNotationUse:
2966 * @ctxt: the validation context
2967 * @doc: the document
2968 * @notationName: the notation name to check
2969 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002970 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002971 * - [ VC: Notation Declared ]
2972 *
2973 * returns 1 if valid or 0 otherwise
2974 */
2975
2976int
2977xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2978 const xmlChar *notationName) {
2979 xmlNotationPtr notaDecl;
2980 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2981
2982 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2983 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2984 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2985
Daniel Veillarde637c4a2003-03-30 21:10:09 +00002986 if ((notaDecl == NULL) && (ctxt != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00002987 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2988 notationName);
2989 return(0);
2990 }
2991 return(1);
2992}
Daniel Veillard4432df22003-09-28 18:58:27 +00002993#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002994
2995/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002996 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00002997 * @doc: the document
2998 * @name: the element name
2999 *
3000 * Search in the DtDs whether an element accept Mixed content (or ANY)
3001 * basically if it is supposed to accept text childs
3002 *
3003 * returns 0 if no, 1 if yes, and -1 if no element description is available
3004 */
3005
3006int
3007xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
3008 xmlElementPtr elemDecl;
3009
3010 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3011
3012 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
3013 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3014 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
3015 if (elemDecl == NULL) return(-1);
3016 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003017 case XML_ELEMENT_TYPE_UNDEFINED:
3018 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003019 case XML_ELEMENT_TYPE_ELEMENT:
3020 return(0);
3021 case XML_ELEMENT_TYPE_EMPTY:
3022 /*
3023 * return 1 for EMPTY since we want VC error to pop up
3024 * on <empty> </empty> for example
3025 */
3026 case XML_ELEMENT_TYPE_ANY:
3027 case XML_ELEMENT_TYPE_MIXED:
3028 return(1);
3029 }
3030 return(1);
3031}
3032
Daniel Veillard4432df22003-09-28 18:58:27 +00003033#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00003034/**
3035 * xmlValidateNameValue:
3036 * @value: an Name value
3037 *
3038 * Validate that the given value match Name production
3039 *
3040 * returns 1 if valid or 0 otherwise
3041 */
3042
Daniel Veillard9b731d72002-04-14 12:56:08 +00003043int
Owen Taylor3473f882001-02-23 17:55:21 +00003044xmlValidateNameValue(const xmlChar *value) {
3045 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003046 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003047
3048 if (value == NULL) return(0);
3049 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003050 val = xmlStringCurrentChar(NULL, cur, &len);
3051 cur += len;
3052 if (!IS_LETTER(val) && (val != '_') &&
3053 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003054 return(0);
3055 }
3056
Daniel Veillardd8224e02002-01-13 15:43:22 +00003057 val = xmlStringCurrentChar(NULL, cur, &len);
3058 cur += len;
3059 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3060 (val == '.') || (val == '-') ||
3061 (val == '_') || (val == ':') ||
3062 (IS_COMBINING(val)) ||
3063 (IS_EXTENDER(val))) {
3064 val = xmlStringCurrentChar(NULL, cur, &len);
3065 cur += len;
3066 }
Owen Taylor3473f882001-02-23 17:55:21 +00003067
Daniel Veillardd8224e02002-01-13 15:43:22 +00003068 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003069
3070 return(1);
3071}
3072
3073/**
3074 * xmlValidateNamesValue:
3075 * @value: an Names value
3076 *
3077 * Validate that the given value match Names production
3078 *
3079 * returns 1 if valid or 0 otherwise
3080 */
3081
Daniel Veillard9b731d72002-04-14 12:56:08 +00003082int
Owen Taylor3473f882001-02-23 17:55:21 +00003083xmlValidateNamesValue(const xmlChar *value) {
3084 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003085 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003086
3087 if (value == NULL) return(0);
3088 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003089 val = xmlStringCurrentChar(NULL, cur, &len);
3090 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003091
Daniel Veillardd8224e02002-01-13 15:43:22 +00003092 if (!IS_LETTER(val) && (val != '_') &&
3093 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003094 return(0);
3095 }
3096
Daniel Veillardd8224e02002-01-13 15:43:22 +00003097 val = xmlStringCurrentChar(NULL, cur, &len);
3098 cur += len;
3099 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3100 (val == '.') || (val == '-') ||
3101 (val == '_') || (val == ':') ||
3102 (IS_COMBINING(val)) ||
3103 (IS_EXTENDER(val))) {
3104 val = xmlStringCurrentChar(NULL, cur, &len);
3105 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003106 }
3107
Daniel Veillardd8224e02002-01-13 15:43:22 +00003108 while (IS_BLANK(val)) {
3109 while (IS_BLANK(val)) {
3110 val = xmlStringCurrentChar(NULL, cur, &len);
3111 cur += len;
3112 }
3113
3114 if (!IS_LETTER(val) && (val != '_') &&
3115 (val != ':')) {
3116 return(0);
3117 }
3118 val = xmlStringCurrentChar(NULL, cur, &len);
3119 cur += len;
3120
3121 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3122 (val == '.') || (val == '-') ||
3123 (val == '_') || (val == ':') ||
3124 (IS_COMBINING(val)) ||
3125 (IS_EXTENDER(val))) {
3126 val = xmlStringCurrentChar(NULL, cur, &len);
3127 cur += len;
3128 }
3129 }
3130
3131 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003132
3133 return(1);
3134}
3135
3136/**
3137 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003138 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003139 *
3140 * Validate that the given value match Nmtoken production
3141 *
3142 * [ VC: Name Token ]
3143 *
3144 * returns 1 if valid or 0 otherwise
3145 */
3146
Daniel Veillard9b731d72002-04-14 12:56:08 +00003147int
Owen Taylor3473f882001-02-23 17:55:21 +00003148xmlValidateNmtokenValue(const xmlChar *value) {
3149 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003150 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003151
3152 if (value == NULL) return(0);
3153 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003154 val = xmlStringCurrentChar(NULL, cur, &len);
3155 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003156
Daniel Veillardd8224e02002-01-13 15:43:22 +00003157 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3158 (val != '.') && (val != '-') &&
3159 (val != '_') && (val != ':') &&
3160 (!IS_COMBINING(val)) &&
3161 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003162 return(0);
3163
Daniel Veillardd8224e02002-01-13 15:43:22 +00003164 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3165 (val == '.') || (val == '-') ||
3166 (val == '_') || (val == ':') ||
3167 (IS_COMBINING(val)) ||
3168 (IS_EXTENDER(val))) {
3169 val = xmlStringCurrentChar(NULL, cur, &len);
3170 cur += len;
3171 }
Owen Taylor3473f882001-02-23 17:55:21 +00003172
Daniel Veillardd8224e02002-01-13 15:43:22 +00003173 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003174
3175 return(1);
3176}
3177
3178/**
3179 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003180 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003181 *
3182 * Validate that the given value match Nmtokens production
3183 *
3184 * [ VC: Name Token ]
3185 *
3186 * returns 1 if valid or 0 otherwise
3187 */
3188
Daniel Veillard9b731d72002-04-14 12:56:08 +00003189int
Owen Taylor3473f882001-02-23 17:55:21 +00003190xmlValidateNmtokensValue(const xmlChar *value) {
3191 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003192 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003193
3194 if (value == NULL) return(0);
3195 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003196 val = xmlStringCurrentChar(NULL, cur, &len);
3197 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003198
Daniel Veillardd8224e02002-01-13 15:43:22 +00003199 while (IS_BLANK(val)) {
3200 val = xmlStringCurrentChar(NULL, cur, &len);
3201 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003202 }
3203
Daniel Veillardd8224e02002-01-13 15:43:22 +00003204 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3205 (val != '.') && (val != '-') &&
3206 (val != '_') && (val != ':') &&
3207 (!IS_COMBINING(val)) &&
3208 (!IS_EXTENDER(val)))
3209 return(0);
3210
3211 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3212 (val == '.') || (val == '-') ||
3213 (val == '_') || (val == ':') ||
3214 (IS_COMBINING(val)) ||
3215 (IS_EXTENDER(val))) {
3216 val = xmlStringCurrentChar(NULL, cur, &len);
3217 cur += len;
3218 }
3219
3220 while (IS_BLANK(val)) {
3221 while (IS_BLANK(val)) {
3222 val = xmlStringCurrentChar(NULL, cur, &len);
3223 cur += len;
3224 }
3225 if (val == 0) return(1);
3226
3227 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3228 (val != '.') && (val != '-') &&
3229 (val != '_') && (val != ':') &&
3230 (!IS_COMBINING(val)) &&
3231 (!IS_EXTENDER(val)))
3232 return(0);
3233
3234 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3235 (val == '.') || (val == '-') ||
3236 (val == '_') || (val == ':') ||
3237 (IS_COMBINING(val)) ||
3238 (IS_EXTENDER(val))) {
3239 val = xmlStringCurrentChar(NULL, cur, &len);
3240 cur += len;
3241 }
3242 }
3243
3244 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003245
3246 return(1);
3247}
3248
3249/**
3250 * xmlValidateNotationDecl:
3251 * @ctxt: the validation context
3252 * @doc: a document instance
3253 * @nota: a notation definition
3254 *
3255 * Try to validate a single notation definition
3256 * basically it does the following checks as described by the
3257 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003258 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003259 * But this function get called anyway ...
3260 *
3261 * returns 1 if valid or 0 otherwise
3262 */
3263
3264int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003265xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3266 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003267 int ret = 1;
3268
3269 return(ret);
3270}
3271
3272/**
3273 * xmlValidateAttributeValue:
3274 * @type: an attribute type
3275 * @value: an attribute value
3276 *
3277 * Validate that the given attribute value match the proper production
3278 *
3279 * [ VC: ID ]
3280 * Values of type ID must match the Name production....
3281 *
3282 * [ VC: IDREF ]
3283 * Values of type IDREF must match the Name production, and values
3284 * of type IDREFS must match Names ...
3285 *
3286 * [ VC: Entity Name ]
3287 * Values of type ENTITY must match the Name production, values
3288 * of type ENTITIES must match Names ...
3289 *
3290 * [ VC: Name Token ]
3291 * Values of type NMTOKEN must match the Nmtoken production; values
3292 * of type NMTOKENS must match Nmtokens.
3293 *
3294 * returns 1 if valid or 0 otherwise
3295 */
3296
3297int
3298xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3299 switch (type) {
3300 case XML_ATTRIBUTE_ENTITIES:
3301 case XML_ATTRIBUTE_IDREFS:
3302 return(xmlValidateNamesValue(value));
3303 case XML_ATTRIBUTE_ENTITY:
3304 case XML_ATTRIBUTE_IDREF:
3305 case XML_ATTRIBUTE_ID:
3306 case XML_ATTRIBUTE_NOTATION:
3307 return(xmlValidateNameValue(value));
3308 case XML_ATTRIBUTE_NMTOKENS:
3309 case XML_ATTRIBUTE_ENUMERATION:
3310 return(xmlValidateNmtokensValue(value));
3311 case XML_ATTRIBUTE_NMTOKEN:
3312 return(xmlValidateNmtokenValue(value));
3313 case XML_ATTRIBUTE_CDATA:
3314 break;
3315 }
3316 return(1);
3317}
3318
3319/**
3320 * xmlValidateAttributeValue2:
3321 * @ctxt: the validation context
3322 * @doc: the document
3323 * @name: the attribute name (used for error reporting only)
3324 * @type: the attribute type
3325 * @value: the attribute value
3326 *
3327 * Validate that the given attribute value match a given type.
3328 * This typically cannot be done before having finished parsing
3329 * the subsets.
3330 *
3331 * [ VC: IDREF ]
3332 * Values of type IDREF must match one of the declared IDs
3333 * Values of type IDREFS must match a sequence of the declared IDs
3334 * each Name must match the value of an ID attribute on some element
3335 * in the XML document; i.e. IDREF values must match the value of
3336 * some ID attribute
3337 *
3338 * [ VC: Entity Name ]
3339 * Values of type ENTITY must match one declared entity
3340 * Values of type ENTITIES must match a sequence of declared entities
3341 *
3342 * [ VC: Notation Attributes ]
3343 * all notation names in the declaration must be declared.
3344 *
3345 * returns 1 if valid or 0 otherwise
3346 */
3347
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003348static int
Owen Taylor3473f882001-02-23 17:55:21 +00003349xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3350 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3351 int ret = 1;
3352 switch (type) {
3353 case XML_ATTRIBUTE_IDREFS:
3354 case XML_ATTRIBUTE_IDREF:
3355 case XML_ATTRIBUTE_ID:
3356 case XML_ATTRIBUTE_NMTOKENS:
3357 case XML_ATTRIBUTE_ENUMERATION:
3358 case XML_ATTRIBUTE_NMTOKEN:
3359 case XML_ATTRIBUTE_CDATA:
3360 break;
3361 case XML_ATTRIBUTE_ENTITY: {
3362 xmlEntityPtr ent;
3363
3364 ent = xmlGetDocEntity(doc, value);
Daniel Veillard62998c02003-09-15 12:56:36 +00003365 /* yeah it's a bit messy... */
Daniel Veillard878eab02002-02-19 13:46:09 +00003366 if ((ent == NULL) && (doc->standalone == 1)) {
3367 doc->standalone = 0;
3368 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003369 }
Owen Taylor3473f882001-02-23 17:55:21 +00003370 if (ent == NULL) {
3371 VERROR(ctxt->userData,
3372 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3373 name, value);
3374 ret = 0;
3375 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3376 VERROR(ctxt->userData,
3377 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3378 name, value);
3379 ret = 0;
3380 }
3381 break;
3382 }
3383 case XML_ATTRIBUTE_ENTITIES: {
3384 xmlChar *dup, *nam = NULL, *cur, save;
3385 xmlEntityPtr ent;
3386
3387 dup = xmlStrdup(value);
3388 if (dup == NULL)
3389 return(0);
3390 cur = dup;
3391 while (*cur != 0) {
3392 nam = cur;
3393 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3394 save = *cur;
3395 *cur = 0;
3396 ent = xmlGetDocEntity(doc, nam);
3397 if (ent == NULL) {
3398 VERROR(ctxt->userData,
3399 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3400 name, nam);
3401 ret = 0;
3402 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3403 VERROR(ctxt->userData,
3404 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3405 name, nam);
3406 ret = 0;
3407 }
3408 if (save == 0)
3409 break;
3410 *cur = save;
3411 while (IS_BLANK(*cur)) cur++;
3412 }
3413 xmlFree(dup);
3414 break;
3415 }
3416 case XML_ATTRIBUTE_NOTATION: {
3417 xmlNotationPtr nota;
3418
3419 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3420 if ((nota == NULL) && (doc->extSubset != NULL))
3421 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3422
3423 if (nota == NULL) {
3424 VERROR(ctxt->userData,
3425 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3426 name, value);
3427 ret = 0;
3428 }
3429 break;
3430 }
3431 }
3432 return(ret);
3433}
3434
3435/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003436 * xmlValidCtxtNormalizeAttributeValue:
3437 * @ctxt: the validation context
3438 * @doc: the document
3439 * @elem: the parent
3440 * @name: the attribute name
3441 * @value: the attribute value
3442 * @ctxt: the validation context or NULL
3443 *
3444 * Does the validation related extra step of the normalization of attribute
3445 * values:
3446 *
3447 * If the declared value is not CDATA, then the XML processor must further
3448 * process the normalized attribute value by discarding any leading and
3449 * trailing space (#x20) characters, and by replacing sequences of space
3450 * (#x20) characters by single space (#x20) character.
3451 *
3452 * Also check VC: Standalone Document Declaration in P32, and update
3453 * ctxt->valid accordingly
3454 *
3455 * returns a new normalized string if normalization is needed, NULL otherwise
3456 * the caller must free the returned value.
3457 */
3458
3459xmlChar *
3460xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3461 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3462 xmlChar *ret, *dst;
3463 const xmlChar *src;
3464 xmlAttributePtr attrDecl = NULL;
3465 int extsubset = 0;
3466
3467 if (doc == NULL) return(NULL);
3468 if (elem == NULL) return(NULL);
3469 if (name == NULL) return(NULL);
3470 if (value == NULL) return(NULL);
3471
3472 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003473 xmlChar fn[50];
3474 xmlChar *fullname;
3475
3476 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3477 if (fullname == NULL)
3478 return(0);
3479 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003480 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003481 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003482 if (attrDecl != NULL)
3483 extsubset = 1;
3484 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003485 if ((fullname != fn) && (fullname != elem->name))
3486 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003487 }
3488 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3489 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3490 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3491 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3492 if (attrDecl != NULL)
3493 extsubset = 1;
3494 }
3495
3496 if (attrDecl == NULL)
3497 return(NULL);
3498 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3499 return(NULL);
3500
3501 ret = xmlStrdup(value);
3502 if (ret == NULL)
3503 return(NULL);
3504 src = value;
3505 dst = ret;
3506 while (*src == 0x20) src++;
3507 while (*src != 0) {
3508 if (*src == 0x20) {
3509 while (*src == 0x20) src++;
3510 if (*src != 0)
3511 *dst++ = 0x20;
3512 } else {
3513 *dst++ = *src++;
3514 }
3515 }
3516 *dst = 0;
3517 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3518 VERROR(ctxt->userData,
3519"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3520 name, elem->name);
3521 ctxt->valid = 0;
3522 }
3523 return(ret);
3524}
3525
3526/**
Owen Taylor3473f882001-02-23 17:55:21 +00003527 * xmlValidNormalizeAttributeValue:
3528 * @doc: the document
3529 * @elem: the parent
3530 * @name: the attribute name
3531 * @value: the attribute value
3532 *
3533 * Does the validation related extra step of the normalization of attribute
3534 * values:
3535 *
3536 * If the declared value is not CDATA, then the XML processor must further
3537 * process the normalized attribute value by discarding any leading and
3538 * trailing space (#x20) characters, and by replacing sequences of space
3539 * (#x20) characters by single space (#x20) character.
3540 *
Daniel Veillard652327a2003-09-29 18:02:38 +00003541 * Returns a new normalized string if normalization is needed, NULL otherwise
Owen Taylor3473f882001-02-23 17:55:21 +00003542 * the caller must free the returned value.
3543 */
3544
3545xmlChar *
3546xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3547 const xmlChar *name, const xmlChar *value) {
3548 xmlChar *ret, *dst;
3549 const xmlChar *src;
3550 xmlAttributePtr attrDecl = NULL;
3551
3552 if (doc == NULL) return(NULL);
3553 if (elem == NULL) return(NULL);
3554 if (name == NULL) return(NULL);
3555 if (value == NULL) return(NULL);
3556
3557 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003558 xmlChar fn[50];
3559 xmlChar *fullname;
3560
3561 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3562 if (fullname == NULL)
3563 return(0);
3564 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003565 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003566 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3567 if ((fullname != fn) && (fullname != elem->name))
3568 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003569 }
3570 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3571 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3572 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3573
3574 if (attrDecl == NULL)
3575 return(NULL);
3576 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3577 return(NULL);
3578
3579 ret = xmlStrdup(value);
3580 if (ret == NULL)
3581 return(NULL);
3582 src = value;
3583 dst = ret;
3584 while (*src == 0x20) src++;
3585 while (*src != 0) {
3586 if (*src == 0x20) {
3587 while (*src == 0x20) src++;
3588 if (*src != 0)
3589 *dst++ = 0x20;
3590 } else {
3591 *dst++ = *src++;
3592 }
3593 }
3594 *dst = 0;
3595 return(ret);
3596}
3597
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003598static void
Owen Taylor3473f882001-02-23 17:55:21 +00003599xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003600 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003601 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3602}
3603
3604/**
3605 * xmlValidateAttributeDecl:
3606 * @ctxt: the validation context
3607 * @doc: a document instance
3608 * @attr: an attribute definition
3609 *
3610 * Try to validate a single attribute definition
3611 * basically it does the following checks as described by the
3612 * XML-1.0 recommendation:
3613 * - [ VC: Attribute Default Legal ]
3614 * - [ VC: Enumeration ]
3615 * - [ VC: ID Attribute Default ]
3616 *
3617 * The ID/IDREF uniqueness and matching are done separately
3618 *
3619 * returns 1 if valid or 0 otherwise
3620 */
3621
3622int
3623xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3624 xmlAttributePtr attr) {
3625 int ret = 1;
3626 int val;
3627 CHECK_DTD;
3628 if(attr == NULL) return(1);
3629
3630 /* Attribute Default Legal */
3631 /* Enumeration */
3632 if (attr->defaultValue != NULL) {
3633 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3634 if (val == 0) {
3635 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003636 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003637 attr->name, attr->elem);
3638 }
3639 ret &= val;
3640 }
3641
3642 /* ID Attribute Default */
3643 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3644 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3645 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3646 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003647 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003648 attr->name, attr->elem);
3649 ret = 0;
3650 }
3651
3652 /* One ID per Element Type */
3653 if (attr->atype == XML_ATTRIBUTE_ID) {
3654 int nbId;
3655
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003656 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003657 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3658 attr->elem);
3659 if (elem != NULL) {
3660 nbId = xmlScanIDAttributeDecl(NULL, elem);
3661 } else {
3662 xmlAttributeTablePtr table;
3663
3664 /*
3665 * The attribute may be declared in the internal subset and the
3666 * element in the external subset.
3667 */
3668 nbId = 0;
3669 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3670 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3671 xmlValidateAttributeIdCallback, &nbId);
3672 }
3673 if (nbId > 1) {
3674 VERROR(ctxt->userData,
3675 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3676 attr->elem, nbId, attr->name);
3677 } else if (doc->extSubset != NULL) {
3678 int extId = 0;
3679 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3680 if (elem != NULL) {
3681 extId = xmlScanIDAttributeDecl(NULL, elem);
3682 }
3683 if (extId > 1) {
3684 VERROR(ctxt->userData,
3685 "Element %s has %d ID attribute defined in the external subset : %s\n",
3686 attr->elem, extId, attr->name);
3687 } else if (extId + nbId > 1) {
3688 VERROR(ctxt->userData,
3689"Element %s has ID attributes defined in the internal and external subset : %s\n",
3690 attr->elem, attr->name);
3691 }
3692 }
3693 }
3694
3695 /* Validity Constraint: Enumeration */
3696 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3697 xmlEnumerationPtr tree = attr->tree;
3698 while (tree != NULL) {
3699 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3700 tree = tree->next;
3701 }
3702 if (tree == NULL) {
3703 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003704"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003705 attr->defaultValue, attr->name, attr->elem);
3706 ret = 0;
3707 }
3708 }
3709
3710 return(ret);
3711}
3712
3713/**
3714 * xmlValidateElementDecl:
3715 * @ctxt: the validation context
3716 * @doc: a document instance
3717 * @elem: an element definition
3718 *
3719 * Try to validate a single element definition
3720 * basically it does the following checks as described by the
3721 * XML-1.0 recommendation:
3722 * - [ VC: One ID per Element Type ]
3723 * - [ VC: No Duplicate Types ]
3724 * - [ VC: Unique Element Type Declaration ]
3725 *
3726 * returns 1 if valid or 0 otherwise
3727 */
3728
3729int
3730xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3731 xmlElementPtr elem) {
3732 int ret = 1;
3733 xmlElementPtr tst;
3734
3735 CHECK_DTD;
3736
3737 if (elem == NULL) return(1);
3738
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003739#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003740#ifdef LIBXML_REGEXP_ENABLED
3741 /* Build the regexp associated to the content model */
3742 ret = xmlValidBuildContentModel(ctxt, elem);
3743#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003744#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003745
Owen Taylor3473f882001-02-23 17:55:21 +00003746 /* No Duplicate Types */
3747 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3748 xmlElementContentPtr cur, next;
3749 const xmlChar *name;
3750
3751 cur = elem->content;
3752 while (cur != NULL) {
3753 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3754 if (cur->c1 == NULL) break;
3755 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3756 name = cur->c1->name;
3757 next = cur->c2;
3758 while (next != NULL) {
3759 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard7b68df92003-08-03 22:58:54 +00003760 if ((xmlStrEqual(next->name, name)) &&
3761 (xmlStrEqual(next->prefix, cur->prefix))) {
3762 if (cur->prefix == NULL) {
3763 VERROR(ctxt->userData,
Owen Taylor3473f882001-02-23 17:55:21 +00003764 "Definition of %s has duplicate references of %s\n",
Daniel Veillard7b68df92003-08-03 22:58:54 +00003765 elem->name, name);
3766 } else {
3767 VERROR(ctxt->userData,
3768 "Definition of %s has duplicate references of %s:%s\n",
3769 elem->name, cur->prefix, name);
3770 }
Owen Taylor3473f882001-02-23 17:55:21 +00003771 ret = 0;
3772 }
3773 break;
3774 }
3775 if (next->c1 == NULL) break;
3776 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
Daniel Veillard7b68df92003-08-03 22:58:54 +00003777 if ((xmlStrEqual(next->c1->name, name)) &&
3778 (xmlStrEqual(next->c1->prefix, cur->prefix))) {
3779 if (cur->prefix == NULL) {
3780 VERROR(ctxt->userData,
3781 "Definition of %s has duplicate references to %s\n",
3782 elem->name, name);
3783 } else {
3784 VERROR(ctxt->userData,
3785 "Definition of %s has duplicate references to %s:%s\n",
3786 elem->name, cur->prefix, name);
3787 }
Owen Taylor3473f882001-02-23 17:55:21 +00003788 ret = 0;
3789 }
3790 next = next->c2;
3791 }
3792 }
3793 cur = cur->c2;
3794 }
3795 }
3796
3797 /* VC: Unique Element Type Declaration */
3798 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003799 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003800 ((tst->prefix == elem->prefix) ||
3801 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003802 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003803 VERROR(ctxt->userData, "Redefinition of element %s\n",
3804 elem->name);
3805 ret = 0;
3806 }
3807 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003808 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003809 ((tst->prefix == elem->prefix) ||
3810 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003811 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003812 VERROR(ctxt->userData, "Redefinition of element %s\n",
3813 elem->name);
3814 ret = 0;
3815 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003816 /* One ID per Element Type
3817 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003818 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3819 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003820 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003821 return(ret);
3822}
3823
3824/**
3825 * xmlValidateOneAttribute:
3826 * @ctxt: the validation context
3827 * @doc: a document instance
3828 * @elem: an element instance
3829 * @attr: an attribute instance
3830 * @value: the attribute value (without entities processing)
3831 *
3832 * Try to validate a single attribute for an element
3833 * basically it does the following checks as described by the
3834 * XML-1.0 recommendation:
3835 * - [ VC: Attribute Value Type ]
3836 * - [ VC: Fixed Attribute Default ]
3837 * - [ VC: Entity Name ]
3838 * - [ VC: Name Token ]
3839 * - [ VC: ID ]
3840 * - [ VC: IDREF ]
3841 * - [ VC: Entity Name ]
3842 * - [ VC: Notation Attributes ]
3843 *
3844 * The ID/IDREF uniqueness and matching are done separately
3845 *
3846 * returns 1 if valid or 0 otherwise
3847 */
3848
3849int
3850xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
Daniel Veillard07cb8222003-09-10 10:51:05 +00003851 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value)
3852{
Owen Taylor3473f882001-02-23 17:55:21 +00003853 xmlAttributePtr attrDecl = NULL;
3854 int val;
3855 int ret = 1;
3856
3857 CHECK_DTD;
3858 if ((elem == NULL) || (elem->name == NULL)) return(0);
3859 if ((attr == NULL) || (attr->name == NULL)) return(0);
3860
3861 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003862 xmlChar fn[50];
3863 xmlChar *fullname;
3864
3865 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3866 if (fullname == NULL)
3867 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003868 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003869 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003870 attr->name, attr->ns->prefix);
3871 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003872 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003873 attr->name, attr->ns->prefix);
3874 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003875 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003876 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3877 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00003878 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003879 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003880 if ((fullname != fn) && (fullname != elem->name))
3881 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003882 }
3883 if (attrDecl == NULL) {
3884 if (attr->ns != NULL) {
3885 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3886 attr->name, attr->ns->prefix);
3887 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3888 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3889 attr->name, attr->ns->prefix);
3890 } else {
3891 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3892 elem->name, attr->name);
3893 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3894 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3895 elem->name, attr->name);
3896 }
3897 }
3898
3899
3900 /* Validity Constraint: Attribute Value Type */
3901 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003902 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003903 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003904 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003905 attr->name, elem->name);
3906 return(0);
3907 }
3908 attr->atype = attrDecl->atype;
3909
3910 val = xmlValidateAttributeValue(attrDecl->atype, value);
3911 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003912 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003913 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003914 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003915 attr->name, elem->name);
3916 ret = 0;
3917 }
3918
3919 /* Validity constraint: Fixed Attribute Default */
3920 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3921 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003922 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003923 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003924 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003925 attr->name, elem->name, attrDecl->defaultValue);
3926 ret = 0;
3927 }
3928 }
3929
3930 /* Validity Constraint: ID uniqueness */
3931 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3932 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3933 ret = 0;
3934 }
3935
3936 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3937 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3938 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3939 ret = 0;
3940 }
3941
3942 /* Validity Constraint: Notation Attributes */
3943 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3944 xmlEnumerationPtr tree = attrDecl->tree;
3945 xmlNotationPtr nota;
3946
3947 /* First check that the given NOTATION was declared */
3948 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3949 if (nota == NULL)
3950 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3951
3952 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003953 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003954 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003955 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003956 value, attr->name, elem->name);
3957 ret = 0;
3958 }
3959
3960 /* Second, verify that it's among the list */
3961 while (tree != NULL) {
3962 if (xmlStrEqual(tree->name, value)) break;
3963 tree = tree->next;
3964 }
3965 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003966 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003967 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003968"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003969 value, attr->name, elem->name);
3970 ret = 0;
3971 }
3972 }
3973
3974 /* Validity Constraint: Enumeration */
3975 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3976 xmlEnumerationPtr tree = attrDecl->tree;
3977 while (tree != NULL) {
3978 if (xmlStrEqual(tree->name, value)) break;
3979 tree = tree->next;
3980 }
3981 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003982 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003983 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003984 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003985 value, attr->name, elem->name);
3986 ret = 0;
3987 }
3988 }
3989
3990 /* Fixed Attribute Default */
3991 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3992 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003993 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003994 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003995 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003996 attr->name, elem->name, attrDecl->defaultValue);
3997 ret = 0;
3998 }
3999
4000 /* Extra check for the attribute value */
4001 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
4002 attrDecl->atype, value);
4003
4004 return(ret);
4005}
4006
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004007/**
4008 * xmlValidateOneNamespace:
4009 * @ctxt: the validation context
4010 * @doc: a document instance
4011 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00004012 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004013 * @ns: an namespace declaration instance
4014 * @value: the attribute value (without entities processing)
4015 *
4016 * Try to validate a single namespace declaration for an element
4017 * basically it does the following checks as described by the
4018 * XML-1.0 recommendation:
4019 * - [ VC: Attribute Value Type ]
4020 * - [ VC: Fixed Attribute Default ]
4021 * - [ VC: Entity Name ]
4022 * - [ VC: Name Token ]
4023 * - [ VC: ID ]
4024 * - [ VC: IDREF ]
4025 * - [ VC: Entity Name ]
4026 * - [ VC: Notation Attributes ]
4027 *
4028 * The ID/IDREF uniqueness and matching are done separately
4029 *
4030 * returns 1 if valid or 0 otherwise
4031 */
4032
4033int
4034xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4035xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
4036 /* xmlElementPtr elemDecl; */
4037 xmlAttributePtr attrDecl = NULL;
4038 int val;
4039 int ret = 1;
4040
4041 CHECK_DTD;
4042 if ((elem == NULL) || (elem->name == NULL)) return(0);
4043 if ((ns == NULL) || (ns->href == NULL)) return(0);
4044
4045 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004046 xmlChar fn[50];
4047 xmlChar *fullname;
4048
4049 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
4050 if (fullname == NULL) {
4051 VERROR(ctxt->userData, "Out of memory\n");
4052 return(0);
4053 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004054 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004055 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004056 ns->prefix, BAD_CAST "xmlns");
4057 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004058 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004059 ns->prefix, BAD_CAST "xmlns");
4060 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004061 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004062 BAD_CAST "xmlns");
4063 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004064 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004065 BAD_CAST "xmlns");
4066 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004067 if ((fullname != fn) && (fullname != elem->name))
4068 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004069 }
4070 if (attrDecl == NULL) {
4071 if (ns->prefix != NULL) {
4072 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4073 ns->prefix, BAD_CAST "xmlns");
4074 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4075 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4076 ns->prefix, BAD_CAST "xmlns");
4077 } else {
4078 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4079 elem->name, BAD_CAST "xmlns");
4080 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4081 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4082 elem->name, BAD_CAST "xmlns");
4083 }
4084 }
4085
4086
4087 /* Validity Constraint: Attribute Value Type */
4088 if (attrDecl == NULL) {
4089 VECTXT(ctxt, elem);
4090 if (ns->prefix != NULL) {
4091 VERROR(ctxt->userData,
4092 "No declaration for attribute xmlns:%s of element %s\n",
4093 ns->prefix, elem->name);
4094 } else {
4095 VERROR(ctxt->userData,
4096 "No declaration for attribute xmlns of element %s\n",
4097 elem->name);
4098 }
4099 return(0);
4100 }
4101
4102 val = xmlValidateAttributeValue(attrDecl->atype, value);
4103 if (val == 0) {
4104 VECTXT(ctxt, elem);
4105 if (ns->prefix != NULL) {
4106 VERROR(ctxt->userData,
4107 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
4108 ns->prefix, elem->name);
4109 } else {
4110 VERROR(ctxt->userData,
4111 "Syntax of value for attribute xmlns of %s is not valid\n",
4112 elem->name);
4113 }
4114 ret = 0;
4115 }
4116
4117 /* Validity constraint: Fixed Attribute Default */
4118 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4119 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4120 VECTXT(ctxt, elem);
4121 if (ns->prefix != NULL) {
4122 VERROR(ctxt->userData,
4123 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4124 ns->prefix, elem->name, attrDecl->defaultValue);
4125 } else {
4126 VERROR(ctxt->userData,
4127 "Value for attribute xmlns of %s is different from default \"%s\"\n",
4128 elem->name, attrDecl->defaultValue);
4129 }
4130 ret = 0;
4131 }
4132 }
4133
4134 /* Validity Constraint: ID uniqueness */
4135 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4136 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4137 ret = 0;
4138 }
4139
4140 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4141 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4142 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4143 ret = 0;
4144 }
4145
4146 /* Validity Constraint: Notation Attributes */
4147 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4148 xmlEnumerationPtr tree = attrDecl->tree;
4149 xmlNotationPtr nota;
4150
4151 /* First check that the given NOTATION was declared */
4152 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4153 if (nota == NULL)
4154 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4155
4156 if (nota == NULL) {
4157 VECTXT(ctxt, elem);
4158 if (ns->prefix != NULL) {
4159 VERROR(ctxt->userData,
4160 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4161 value, ns->prefix, elem->name);
4162 } else {
4163 VERROR(ctxt->userData,
4164 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
4165 value, elem->name);
4166 }
4167 ret = 0;
4168 }
4169
4170 /* Second, verify that it's among the list */
4171 while (tree != NULL) {
4172 if (xmlStrEqual(tree->name, value)) break;
4173 tree = tree->next;
4174 }
4175 if (tree == NULL) {
4176 VECTXT(ctxt, elem);
4177 if (ns->prefix != NULL) {
4178 VERROR(ctxt->userData,
4179"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4180 value, ns->prefix, elem->name);
4181 } else {
4182 VERROR(ctxt->userData,
4183"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
4184 value, elem->name);
4185 }
4186 ret = 0;
4187 }
4188 }
4189
4190 /* Validity Constraint: Enumeration */
4191 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4192 xmlEnumerationPtr tree = attrDecl->tree;
4193 while (tree != NULL) {
4194 if (xmlStrEqual(tree->name, value)) break;
4195 tree = tree->next;
4196 }
4197 if (tree == NULL) {
4198 VECTXT(ctxt, elem);
4199 if (ns->prefix != NULL) {
4200 VERROR(ctxt->userData,
4201"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4202 value, ns->prefix, elem->name);
4203 } else {
4204 VERROR(ctxt->userData,
4205"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4206 value, elem->name);
4207 }
4208 ret = 0;
4209 }
4210 }
4211
4212 /* Fixed Attribute Default */
4213 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4214 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4215 VECTXT(ctxt, elem);
4216 if (ns->prefix != NULL) {
4217 VERROR(ctxt->userData,
4218 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4219 ns->prefix, elem->name, attrDecl->defaultValue);
4220 } else {
4221 VERROR(ctxt->userData,
4222 "Value for attribute xmlns of %s must be \"%s\"\n",
4223 elem->name, attrDecl->defaultValue);
4224 }
4225 ret = 0;
4226 }
4227
4228 /* Extra check for the attribute value */
4229 if (ns->prefix != NULL) {
4230 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4231 attrDecl->atype, value);
4232 } else {
4233 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4234 attrDecl->atype, value);
4235 }
4236
4237 return(ret);
4238}
4239
Daniel Veillard118aed72002-09-24 14:13:13 +00004240#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004241/**
4242 * xmlValidateSkipIgnorable:
4243 * @ctxt: the validation context
4244 * @child: the child list
4245 *
4246 * Skip ignorable elements w.r.t. the validation process
4247 *
4248 * returns the first element to consider for validation of the content model
4249 */
4250
4251static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004252xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004253 while (child != NULL) {
4254 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004255 /* These things are ignored (skipped) during validation. */
4256 case XML_PI_NODE:
4257 case XML_COMMENT_NODE:
4258 case XML_XINCLUDE_START:
4259 case XML_XINCLUDE_END:
4260 child = child->next;
4261 break;
4262 case XML_TEXT_NODE:
4263 if (xmlIsBlankNode(child))
4264 child = child->next;
4265 else
4266 return(child);
4267 break;
4268 /* keep current node */
4269 default:
4270 return(child);
4271 }
4272 }
4273 return(child);
4274}
4275
4276/**
4277 * xmlValidateElementType:
4278 * @ctxt: the validation context
4279 *
4280 * Try to validate the content model of an element internal function
4281 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004282 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4283 * reference is found and -3 if the validation succeeded but
4284 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004285 */
4286
4287static int
4288xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004289 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004290 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004291
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004292 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004293 if ((NODE == NULL) && (CONT == NULL))
4294 return(1);
4295 if ((NODE == NULL) &&
4296 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4297 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4298 return(1);
4299 }
4300 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004301 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004302 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004303
4304 /*
4305 * We arrive here when more states need to be examined
4306 */
4307cont:
4308
4309 /*
4310 * We just recovered from a rollback generated by a possible
4311 * epsilon transition, go directly to the analysis phase
4312 */
4313 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004314 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004315 DEBUG_VALID_STATE(NODE, CONT)
4316 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004317 goto analyze;
4318 }
4319
4320 DEBUG_VALID_STATE(NODE, CONT)
4321 /*
4322 * we may have to save a backup state here. This is the equivalent
4323 * of handling epsilon transition in NFAs.
4324 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004325 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004326 ((CONT->parent == NULL) ||
4327 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004328 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004329 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004330 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004331 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004332 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4333 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004334 }
4335
4336
4337 /*
4338 * Check first if the content matches
4339 */
4340 switch (CONT->type) {
4341 case XML_ELEMENT_CONTENT_PCDATA:
4342 if (NODE == NULL) {
4343 DEBUG_VALID_MSG("pcdata failed no node");
4344 ret = 0;
4345 break;
4346 }
4347 if (NODE->type == XML_TEXT_NODE) {
4348 DEBUG_VALID_MSG("pcdata found, skip to next");
4349 /*
4350 * go to next element in the content model
4351 * skipping ignorable elems
4352 */
4353 do {
4354 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004355 NODE = xmlValidateSkipIgnorable(NODE);
4356 if ((NODE != NULL) &&
4357 (NODE->type == XML_ENTITY_REF_NODE))
4358 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004359 } while ((NODE != NULL) &&
4360 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004361 (NODE->type != XML_TEXT_NODE) &&
4362 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004363 ret = 1;
4364 break;
4365 } else {
4366 DEBUG_VALID_MSG("pcdata failed");
4367 ret = 0;
4368 break;
4369 }
4370 break;
4371 case XML_ELEMENT_CONTENT_ELEMENT:
4372 if (NODE == NULL) {
4373 DEBUG_VALID_MSG("element failed no node");
4374 ret = 0;
4375 break;
4376 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004377 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4378 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004379 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004380 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4381 ret = (CONT->prefix == NULL);
4382 } else if (CONT->prefix == NULL) {
4383 ret = 0;
4384 } else {
4385 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4386 }
4387 }
4388 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004389 DEBUG_VALID_MSG("element found, skip to next");
4390 /*
4391 * go to next element in the content model
4392 * skipping ignorable elems
4393 */
4394 do {
4395 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004396 NODE = xmlValidateSkipIgnorable(NODE);
4397 if ((NODE != NULL) &&
4398 (NODE->type == XML_ENTITY_REF_NODE))
4399 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004400 } while ((NODE != NULL) &&
4401 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004402 (NODE->type != XML_TEXT_NODE) &&
4403 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004404 } else {
4405 DEBUG_VALID_MSG("element failed");
4406 ret = 0;
4407 break;
4408 }
4409 break;
4410 case XML_ELEMENT_CONTENT_OR:
4411 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004412 * Small optimization.
4413 */
4414 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4415 if ((NODE == NULL) ||
4416 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4417 DEPTH++;
4418 CONT = CONT->c2;
4419 goto cont;
4420 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004421 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4422 ret = (CONT->c1->prefix == NULL);
4423 } else if (CONT->c1->prefix == NULL) {
4424 ret = 0;
4425 } else {
4426 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4427 }
4428 if (ret == 0) {
4429 DEPTH++;
4430 CONT = CONT->c2;
4431 goto cont;
4432 }
Daniel Veillard85349052001-04-20 13:48:21 +00004433 }
4434
4435 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004436 * save the second branch 'or' branch
4437 */
4438 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004439 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4440 OCCURS, ROLLBACK_OR) < 0)
4441 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004442 DEPTH++;
4443 CONT = CONT->c1;
4444 goto cont;
4445 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004446 /*
4447 * Small optimization.
4448 */
4449 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4450 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4451 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4452 if ((NODE == NULL) ||
4453 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4454 DEPTH++;
4455 CONT = CONT->c2;
4456 goto cont;
4457 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004458 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4459 ret = (CONT->c1->prefix == NULL);
4460 } else if (CONT->c1->prefix == NULL) {
4461 ret = 0;
4462 } else {
4463 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4464 }
4465 if (ret == 0) {
4466 DEPTH++;
4467 CONT = CONT->c2;
4468 goto cont;
4469 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004470 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004471 DEPTH++;
4472 CONT = CONT->c1;
4473 goto cont;
4474 }
4475
4476 /*
4477 * At this point handle going up in the tree
4478 */
4479 if (ret == -1) {
4480 DEBUG_VALID_MSG("error found returning");
4481 return(ret);
4482 }
4483analyze:
4484 while (CONT != NULL) {
4485 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004486 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004487 * this level.
4488 */
4489 if (ret == 0) {
4490 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004491 xmlNodePtr cur;
4492
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004493 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004494 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004495 DEBUG_VALID_MSG("Once branch failed, rollback");
4496 if (vstateVPop(ctxt) < 0 ) {
4497 DEBUG_VALID_MSG("exhaustion, failed");
4498 return(0);
4499 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004500 if (cur != ctxt->vstate->node)
4501 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004502 goto cont;
4503 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004504 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004505 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004506 DEBUG_VALID_MSG("Plus branch failed, rollback");
4507 if (vstateVPop(ctxt) < 0 ) {
4508 DEBUG_VALID_MSG("exhaustion, failed");
4509 return(0);
4510 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004511 if (cur != ctxt->vstate->node)
4512 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004513 goto cont;
4514 }
4515 DEBUG_VALID_MSG("Plus branch found");
4516 ret = 1;
4517 break;
4518 case XML_ELEMENT_CONTENT_MULT:
4519#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004520 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004521 DEBUG_VALID_MSG("Mult branch failed");
4522 } else {
4523 DEBUG_VALID_MSG("Mult branch found");
4524 }
4525#endif
4526 ret = 1;
4527 break;
4528 case XML_ELEMENT_CONTENT_OPT:
4529 DEBUG_VALID_MSG("Option branch failed");
4530 ret = 1;
4531 break;
4532 }
4533 } else {
4534 switch (CONT->ocur) {
4535 case XML_ELEMENT_CONTENT_OPT:
4536 DEBUG_VALID_MSG("Option branch succeeded");
4537 ret = 1;
4538 break;
4539 case XML_ELEMENT_CONTENT_ONCE:
4540 DEBUG_VALID_MSG("Once branch succeeded");
4541 ret = 1;
4542 break;
4543 case XML_ELEMENT_CONTENT_PLUS:
4544 if (STATE == ROLLBACK_PARENT) {
4545 DEBUG_VALID_MSG("Plus branch rollback");
4546 ret = 1;
4547 break;
4548 }
4549 if (NODE == NULL) {
4550 DEBUG_VALID_MSG("Plus branch exhausted");
4551 ret = 1;
4552 break;
4553 }
4554 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004555 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004556 goto cont;
4557 case XML_ELEMENT_CONTENT_MULT:
4558 if (STATE == ROLLBACK_PARENT) {
4559 DEBUG_VALID_MSG("Mult branch rollback");
4560 ret = 1;
4561 break;
4562 }
4563 if (NODE == NULL) {
4564 DEBUG_VALID_MSG("Mult branch exhausted");
4565 ret = 1;
4566 break;
4567 }
4568 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004569 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004570 goto cont;
4571 }
4572 }
4573 STATE = 0;
4574
4575 /*
4576 * Then act accordingly at the parent level
4577 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004578 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004579 if (CONT->parent == NULL)
4580 break;
4581
4582 switch (CONT->parent->type) {
4583 case XML_ELEMENT_CONTENT_PCDATA:
4584 DEBUG_VALID_MSG("Error: parent pcdata");
4585 return(-1);
4586 case XML_ELEMENT_CONTENT_ELEMENT:
4587 DEBUG_VALID_MSG("Error: parent element");
4588 return(-1);
4589 case XML_ELEMENT_CONTENT_OR:
4590 if (ret == 1) {
4591 DEBUG_VALID_MSG("Or succeeded");
4592 CONT = CONT->parent;
4593 DEPTH--;
4594 } else {
4595 DEBUG_VALID_MSG("Or failed");
4596 CONT = CONT->parent;
4597 DEPTH--;
4598 }
4599 break;
4600 case XML_ELEMENT_CONTENT_SEQ:
4601 if (ret == 0) {
4602 DEBUG_VALID_MSG("Sequence failed");
4603 CONT = CONT->parent;
4604 DEPTH--;
4605 } else if (CONT == CONT->parent->c1) {
4606 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4607 CONT = CONT->parent->c2;
4608 goto cont;
4609 } else {
4610 DEBUG_VALID_MSG("Sequence succeeded");
4611 CONT = CONT->parent;
4612 DEPTH--;
4613 }
4614 }
4615 }
4616 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004617 xmlNodePtr cur;
4618
4619 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004620 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4621 if (vstateVPop(ctxt) < 0 ) {
4622 DEBUG_VALID_MSG("exhaustion, failed");
4623 return(0);
4624 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004625 if (cur != ctxt->vstate->node)
4626 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004627 goto cont;
4628 }
4629 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004630 xmlNodePtr cur;
4631
4632 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004633 DEBUG_VALID_MSG("Failure, rollback");
4634 if (vstateVPop(ctxt) < 0 ) {
4635 DEBUG_VALID_MSG("exhaustion, failed");
4636 return(0);
4637 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004638 if (cur != ctxt->vstate->node)
4639 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004640 goto cont;
4641 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004642 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004643}
Daniel Veillard23e73572002-09-19 19:56:43 +00004644#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004645
4646/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004647 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004648 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004649 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004650 * @content: An element
4651 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4652 *
4653 * This will dump the list of elements to the buffer
4654 * Intended just for the debug routine
4655 */
4656static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004657xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004658 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004659 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004660
4661 if (node == NULL) return;
4662 if (glob) strcat(buf, "(");
4663 cur = node;
4664 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004665 len = strlen(buf);
4666 if (size - len < 50) {
4667 if ((size - len > 4) && (buf[len - 1] != '.'))
4668 strcat(buf, " ...");
4669 return;
4670 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004671 switch (cur->type) {
4672 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004673 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004674 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004675 if ((size - len > 4) && (buf[len - 1] != '.'))
4676 strcat(buf, " ...");
4677 return;
4678 }
4679 strcat(buf, (char *) cur->ns->prefix);
4680 strcat(buf, ":");
4681 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004682 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004683 if ((size - len > 4) && (buf[len - 1] != '.'))
4684 strcat(buf, " ...");
4685 return;
4686 }
4687 strcat(buf, (char *) cur->name);
4688 if (cur->next != NULL)
4689 strcat(buf, " ");
4690 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004691 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004692 if (xmlIsBlankNode(cur))
4693 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004694 case XML_CDATA_SECTION_NODE:
4695 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004696 strcat(buf, "CDATA");
4697 if (cur->next != NULL)
4698 strcat(buf, " ");
4699 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004700 case XML_ATTRIBUTE_NODE:
4701 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004702#ifdef LIBXML_DOCB_ENABLED
4703 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004704#endif
4705 case XML_HTML_DOCUMENT_NODE:
4706 case XML_DOCUMENT_TYPE_NODE:
4707 case XML_DOCUMENT_FRAG_NODE:
4708 case XML_NOTATION_NODE:
4709 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004710 strcat(buf, "???");
4711 if (cur->next != NULL)
4712 strcat(buf, " ");
4713 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004714 case XML_ENTITY_NODE:
4715 case XML_PI_NODE:
4716 case XML_DTD_NODE:
4717 case XML_COMMENT_NODE:
4718 case XML_ELEMENT_DECL:
4719 case XML_ATTRIBUTE_DECL:
4720 case XML_ENTITY_DECL:
4721 case XML_XINCLUDE_START:
4722 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004723 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004724 }
4725 cur = cur->next;
4726 }
4727 if (glob) strcat(buf, ")");
4728}
4729
4730/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004731 * xmlValidateElementContent:
4732 * @ctxt: the validation context
4733 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004734 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004735 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004736 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004737 *
4738 * Try to validate the content model of an element
4739 *
4740 * returns 1 if valid or 0 if not and -1 in case of error
4741 */
4742
4743static int
4744xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004745 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004746 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004747#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004748 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004749#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004750 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004751 xmlElementContentPtr cont;
4752 const xmlChar *name;
4753
4754 if (elemDecl == NULL)
4755 return(-1);
4756 cont = elemDecl->content;
4757 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004758
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004759#ifdef LIBXML_REGEXP_ENABLED
4760 /* Build the regexp associated to the content model */
4761 if (elemDecl->contModel == NULL)
4762 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4763 if (elemDecl->contModel == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004764 return(-1);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004765 } else {
4766 xmlRegExecCtxtPtr exec;
4767
Daniel Veillardec498e12003-02-05 11:01:50 +00004768 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
4769 return(-1);
4770 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004771 ctxt->nodeMax = 0;
4772 ctxt->nodeNr = 0;
4773 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004774 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4775 if (exec != NULL) {
4776 cur = child;
4777 while (cur != NULL) {
4778 switch (cur->type) {
4779 case XML_ENTITY_REF_NODE:
4780 /*
4781 * Push the current node to be able to roll back
4782 * and process within the entity
4783 */
4784 if ((cur->children != NULL) &&
4785 (cur->children->children != NULL)) {
4786 nodeVPush(ctxt, cur);
4787 cur = cur->children->children;
4788 continue;
4789 }
4790 break;
4791 case XML_TEXT_NODE:
4792 if (xmlIsBlankNode(cur))
4793 break;
4794 ret = 0;
4795 goto fail;
4796 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004797 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004798 ret = 0;
4799 goto fail;
4800 case XML_ELEMENT_NODE:
4801 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004802 xmlChar fn[50];
4803 xmlChar *fullname;
4804
4805 fullname = xmlBuildQName(cur->name,
4806 cur->ns->prefix, fn, 50);
4807 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004808 ret = -1;
4809 goto fail;
4810 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004811 ret = xmlRegExecPushString(exec, fullname, NULL);
4812 if ((fullname != fn) && (fullname != cur->name))
4813 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004814 } else {
4815 ret = xmlRegExecPushString(exec, cur->name, NULL);
4816 }
4817 break;
4818 default:
4819 break;
4820 }
4821 /*
4822 * Switch to next element
4823 */
4824 cur = cur->next;
4825 while (cur == NULL) {
4826 cur = nodeVPop(ctxt);
4827 if (cur == NULL)
4828 break;
4829 cur = cur->next;
4830 }
4831 }
4832 ret = xmlRegExecPushString(exec, NULL, NULL);
4833fail:
4834 xmlRegFreeExecCtxt(exec);
4835 }
4836 }
4837#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004838 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004839 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004840 */
4841 ctxt->vstateMax = 8;
4842 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4843 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4844 if (ctxt->vstateTab == NULL) {
4845 xmlGenericError(xmlGenericErrorContext,
4846 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004847 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004848 }
4849 /*
4850 * The first entry in the stack is reserved to the current state
4851 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004852 ctxt->nodeMax = 0;
4853 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004854 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004855 ctxt->vstate = &ctxt->vstateTab[0];
4856 ctxt->vstateNr = 1;
4857 CONT = cont;
4858 NODE = child;
4859 DEPTH = 0;
4860 OCCURS = 0;
4861 STATE = 0;
4862 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004863 if ((ret == -3) && (warn)) {
4864 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004865 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004866 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004867 /*
4868 * An entities reference appeared at this level.
4869 * Buid a minimal representation of this node content
4870 * sufficient to run the validation process on it
4871 */
4872 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004873 cur = child;
4874 while (cur != NULL) {
4875 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004876 case XML_ENTITY_REF_NODE:
4877 /*
4878 * Push the current node to be able to roll back
4879 * and process within the entity
4880 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004881 if ((cur->children != NULL) &&
4882 (cur->children->children != NULL)) {
4883 nodeVPush(ctxt, cur);
4884 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004885 continue;
4886 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004887 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004888 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004889 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004890 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004891 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004892 case XML_CDATA_SECTION_NODE:
4893 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004894 case XML_ELEMENT_NODE:
4895 /*
4896 * Allocate a new node and minimally fills in
4897 * what's required
4898 */
4899 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4900 if (tmp == NULL) {
4901 xmlGenericError(xmlGenericErrorContext,
4902 "xmlValidateElementContent : malloc failed\n");
4903 xmlFreeNodeList(repl);
4904 ret = -1;
4905 goto done;
4906 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004907 tmp->type = cur->type;
4908 tmp->name = cur->name;
4909 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004910 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004911 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004912 if (repl == NULL)
4913 repl = last = tmp;
4914 else {
4915 last->next = tmp;
4916 last = tmp;
4917 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004918 if (cur->type == XML_CDATA_SECTION_NODE) {
4919 /*
4920 * E59 spaces in CDATA does not match the
4921 * nonterminal S
4922 */
4923 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4924 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004925 break;
4926 default:
4927 break;
4928 }
4929 /*
4930 * Switch to next element
4931 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004932 cur = cur->next;
4933 while (cur == NULL) {
4934 cur = nodeVPop(ctxt);
4935 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004936 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004937 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004938 }
4939 }
4940
4941 /*
4942 * Relaunch the validation
4943 */
4944 ctxt->vstate = &ctxt->vstateTab[0];
4945 ctxt->vstateNr = 1;
4946 CONT = cont;
4947 NODE = repl;
4948 DEPTH = 0;
4949 OCCURS = 0;
4950 STATE = 0;
4951 ret = xmlValidateElementType(ctxt);
4952 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004953#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004954 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004955 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4956 char expr[5000];
4957 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004958
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004959 expr[0] = 0;
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004960 xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004961 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004962#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004963 if (repl != NULL)
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004964 xmlSnprintfElements(&list[0], 5000, repl, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004965 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004966#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004967 xmlSnprintfElements(&list[0], 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004968
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004969 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004970 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004971 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004972 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004973 name, expr, list);
4974 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004975 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004976 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004977 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004978 expr, list);
4979 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004980 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004981 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004982 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004983 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004984 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004985 name);
4986 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004987 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004988 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004989 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004990 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004991 }
4992 ret = 0;
4993 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004994 if (ret == -3)
4995 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004996
Daniel Veillard23e73572002-09-19 19:56:43 +00004997#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004998done:
4999 /*
5000 * Deallocate the copy if done, and free up the validation stack
5001 */
5002 while (repl != NULL) {
5003 tmp = repl->next;
5004 xmlFree(repl);
5005 repl = tmp;
5006 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005007 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005008 if (ctxt->vstateTab != NULL) {
5009 xmlFree(ctxt->vstateTab);
5010 ctxt->vstateTab = NULL;
5011 }
Daniel Veillard01992e02002-10-09 10:20:30 +00005012#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005013 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00005014 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005015 if (ctxt->nodeTab != NULL) {
5016 xmlFree(ctxt->nodeTab);
5017 ctxt->nodeTab = NULL;
5018 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005019 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005020
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005021}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005022
Owen Taylor3473f882001-02-23 17:55:21 +00005023/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005024 * xmlValidateCdataElement:
5025 * @ctxt: the validation context
5026 * @doc: a document instance
5027 * @elem: an element instance
5028 *
5029 * Check that an element follows #CDATA
5030 *
5031 * returns 1 if valid or 0 otherwise
5032 */
5033static int
5034xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5035 xmlNodePtr elem) {
5036 int ret = 1;
5037 xmlNodePtr cur, child;
5038
Daniel Veillardceb09b92002-10-04 11:46:37 +00005039 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005040 return(0);
5041
5042 child = elem->children;
5043
5044 cur = child;
5045 while (cur != NULL) {
5046 switch (cur->type) {
5047 case XML_ENTITY_REF_NODE:
5048 /*
5049 * Push the current node to be able to roll back
5050 * and process within the entity
5051 */
5052 if ((cur->children != NULL) &&
5053 (cur->children->children != NULL)) {
5054 nodeVPush(ctxt, cur);
5055 cur = cur->children->children;
5056 continue;
5057 }
5058 break;
5059 case XML_COMMENT_NODE:
5060 case XML_PI_NODE:
5061 case XML_TEXT_NODE:
5062 case XML_CDATA_SECTION_NODE:
5063 break;
5064 default:
5065 ret = 0;
5066 goto done;
5067 }
5068 /*
5069 * Switch to next element
5070 */
5071 cur = cur->next;
5072 while (cur == NULL) {
5073 cur = nodeVPop(ctxt);
5074 if (cur == NULL)
5075 break;
5076 cur = cur->next;
5077 }
5078 }
5079done:
5080 ctxt->nodeMax = 0;
5081 ctxt->nodeNr = 0;
5082 if (ctxt->nodeTab != NULL) {
5083 xmlFree(ctxt->nodeTab);
5084 ctxt->nodeTab = NULL;
5085 }
5086 return(ret);
5087}
5088
5089/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005090 * xmlValidateCheckMixed:
5091 * @ctxt: the validation context
5092 * @cont: the mixed content model
5093 * @qname: the qualified name as appearing in the serialization
5094 *
5095 * Check if the given node is part of the content model.
5096 *
5097 * Returns 1 if yes, 0 if no, -1 in case of error
5098 */
5099static int
5100xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5101 xmlElementContentPtr cont, const xmlChar *qname) {
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005102 const xmlChar *name;
5103 int plen;
5104 name = xmlSplitQName3(qname, &plen);
5105
5106 if (name == NULL) {
5107 while (cont != NULL) {
5108 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5109 if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname)))
5110 return(1);
5111 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5112 (cont->c1 != NULL) &&
5113 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5114 if ((cont->c1->prefix == NULL) &&
5115 (xmlStrEqual(cont->c1->name, qname)))
5116 return(1);
5117 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5118 (cont->c1 == NULL) ||
5119 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5120 /* Internal error !!! */
5121 xmlGenericError(xmlGenericErrorContext,
5122 "Internal: MIXED struct bad\n");
5123 break;
5124 }
5125 cont = cont->c2;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005126 }
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005127 } else {
5128 while (cont != NULL) {
5129 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5130 if ((cont->prefix != NULL) &&
5131 (xmlStrncmp(cont->prefix, qname, plen) == 0) &&
5132 (xmlStrEqual(cont->name, name)))
5133 return(1);
5134 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5135 (cont->c1 != NULL) &&
5136 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5137 if ((cont->c1->prefix != NULL) &&
5138 (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) &&
5139 (xmlStrEqual(cont->c1->name, name)))
5140 return(1);
5141 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5142 (cont->c1 == NULL) ||
5143 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5144 /* Internal error !!! */
5145 xmlGenericError(xmlGenericErrorContext,
5146 "Internal: MIXED struct bad\n");
5147 break;
5148 }
5149 cont = cont->c2;
5150 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005151 }
5152 return(0);
5153}
5154
5155/**
5156 * xmlValidGetElemDecl:
5157 * @ctxt: the validation context
5158 * @doc: a document instance
5159 * @elem: an element instance
5160 * @extsubset: pointer, (out) indicate if the declaration was found
5161 * in the external subset.
5162 *
5163 * Finds a declaration associated to an element in the document.
5164 *
5165 * returns the pointer to the declaration or NULL if not found.
5166 */
5167static xmlElementPtr
5168xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5169 xmlNodePtr elem, int *extsubset) {
5170 xmlElementPtr elemDecl = NULL;
5171 const xmlChar *prefix = NULL;
5172
5173 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5174 if (extsubset != NULL)
5175 *extsubset = 0;
5176
5177 /*
5178 * Fetch the declaration for the qualified name
5179 */
5180 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5181 prefix = elem->ns->prefix;
5182
5183 if (prefix != NULL) {
5184 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5185 elem->name, prefix);
5186 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5187 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5188 elem->name, prefix);
5189 if ((elemDecl != NULL) && (extsubset != NULL))
5190 *extsubset = 1;
5191 }
5192 }
5193
5194 /*
5195 * Fetch the declaration for the non qualified name
5196 * This is "non-strict" validation should be done on the
5197 * full QName but in that case being flexible makes sense.
5198 */
5199 if (elemDecl == NULL) {
5200 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5201 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5202 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5203 if ((elemDecl != NULL) && (extsubset != NULL))
5204 *extsubset = 1;
5205 }
5206 }
5207 if (elemDecl == NULL) {
5208 VECTXT(ctxt, elem);
5209 VERROR(ctxt->userData, "No declaration for element %s\n",
5210 elem->name);
5211 }
5212 return(elemDecl);
5213}
5214
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005215#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005216/**
5217 * xmlValidatePushElement:
5218 * @ctxt: the validation context
5219 * @doc: a document instance
5220 * @elem: an element instance
5221 * @qname: the qualified name as appearing in the serialization
5222 *
5223 * Push a new element start on the validation stack.
5224 *
5225 * returns 1 if no validation problem was found or 0 otherwise
5226 */
5227int
5228xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5229 xmlNodePtr elem, const xmlChar *qname) {
5230 int ret = 1;
5231 xmlElementPtr eDecl;
5232 int extsubset = 0;
5233
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005234/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005235 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5236 xmlValidStatePtr state = ctxt->vstate;
5237 xmlElementPtr elemDecl;
5238
5239 /*
5240 * Check the new element agaisnt the content model of the new elem.
5241 */
5242 if (state->elemDecl != NULL) {
5243 elemDecl = state->elemDecl;
5244
5245 switch(elemDecl->etype) {
5246 case XML_ELEMENT_TYPE_UNDEFINED:
5247 ret = 0;
5248 break;
5249 case XML_ELEMENT_TYPE_EMPTY:
5250 VECTXT(ctxt, state->node);
5251 VERROR(ctxt->userData,
5252 "Element %s was declared EMPTY this one has content\n",
5253 state->node->name);
5254 ret = 0;
5255 break;
5256 case XML_ELEMENT_TYPE_ANY:
5257 /* I don't think anything is required then */
5258 break;
5259 case XML_ELEMENT_TYPE_MIXED:
5260 /* simple case of declared as #PCDATA */
5261 if ((elemDecl->content != NULL) &&
5262 (elemDecl->content->type ==
5263 XML_ELEMENT_CONTENT_PCDATA)) {
5264 VECTXT(ctxt, state->node);
5265 VERROR(ctxt->userData,
5266 "Element %s was declared #PCDATA but contains non text nodes\n",
5267 state->node->name);
5268 ret = 0;
5269 } else {
5270 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5271 qname);
5272 if (ret != 1) {
5273 VECTXT(ctxt, state->node);
5274 VERROR(ctxt->userData,
5275 "Element %s is not declared in %s list of possible children\n",
5276 qname, state->node->name);
5277 }
5278 }
5279 break;
5280 case XML_ELEMENT_TYPE_ELEMENT:
5281 /*
5282 * TODO:
5283 * VC: Standalone Document Declaration
5284 * - element types with element content, if white space
5285 * occurs directly within any instance of those types.
5286 */
5287 if (state->exec != NULL) {
5288 ret = xmlRegExecPushString(state->exec, qname, NULL);
5289 if (ret < 0) {
5290 VECTXT(ctxt, state->node);
5291 VERROR(ctxt->userData,
5292 "Element %s content does not follow the DTD\nMisplaced %s\n",
5293 state->node->name, qname);
5294 ret = 0;
5295 } else {
5296 ret = 1;
5297 }
5298 }
5299 break;
5300 }
5301 }
5302 }
5303 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5304 vstateVPush(ctxt, eDecl, elem);
5305 return(ret);
5306}
5307
5308/**
5309 * xmlValidatePushCData:
5310 * @ctxt: the validation context
5311 * @data: some character data read
5312 * @len: the lenght of the data
5313 *
5314 * check the CData parsed for validation in the current stack
5315 *
5316 * returns 1 if no validation problem was found or 0 otherwise
5317 */
5318int
5319xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5320 int ret = 1;
5321
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005322/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005323 if (len <= 0)
5324 return(ret);
5325 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5326 xmlValidStatePtr state = ctxt->vstate;
5327 xmlElementPtr elemDecl;
5328
5329 /*
5330 * Check the new element agaisnt the content model of the new elem.
5331 */
5332 if (state->elemDecl != NULL) {
5333 elemDecl = state->elemDecl;
5334
5335 switch(elemDecl->etype) {
5336 case XML_ELEMENT_TYPE_UNDEFINED:
5337 ret = 0;
5338 break;
5339 case XML_ELEMENT_TYPE_EMPTY:
5340 VECTXT(ctxt, state->node);
5341 VERROR(ctxt->userData,
5342 "Element %s was declared EMPTY this one has content\n",
5343 state->node->name);
5344 ret = 0;
5345 break;
5346 case XML_ELEMENT_TYPE_ANY:
5347 break;
5348 case XML_ELEMENT_TYPE_MIXED:
5349 break;
5350 case XML_ELEMENT_TYPE_ELEMENT:
5351 if (len > 0) {
5352 int i;
5353
5354 for (i = 0;i < len;i++) {
5355 if (!IS_BLANK(data[i])) {
5356 VECTXT(ctxt, state->node);
5357 VERROR(ctxt->userData,
5358 "Element %s content does not follow the DTD\nText not allowed\n",
5359 state->node->name);
5360 ret = 0;
5361 goto done;
5362 }
5363 }
5364 /*
5365 * TODO:
5366 * VC: Standalone Document Declaration
5367 * element types with element content, if white space
5368 * occurs directly within any instance of those types.
5369 */
5370 }
5371 break;
5372 }
5373 }
5374 }
5375done:
5376 return(ret);
5377}
5378
5379/**
5380 * xmlValidatePopElement:
5381 * @ctxt: the validation context
5382 * @doc: a document instance
5383 * @elem: an element instance
5384 * @qname: the qualified name as appearing in the serialization
5385 *
5386 * Pop the element end from the validation stack.
5387 *
5388 * returns 1 if no validation problem was found or 0 otherwise
5389 */
5390int
5391xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005392 xmlNodePtr elem ATTRIBUTE_UNUSED,
5393 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005394 int ret = 1;
5395
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005396/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005397 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5398 xmlValidStatePtr state = ctxt->vstate;
5399 xmlElementPtr elemDecl;
5400
5401 /*
5402 * Check the new element agaisnt the content model of the new elem.
5403 */
5404 if (state->elemDecl != NULL) {
5405 elemDecl = state->elemDecl;
5406
5407 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5408 if (state->exec != NULL) {
5409 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5410 if (ret == 0) {
5411 VECTXT(ctxt, state->node);
5412 VERROR(ctxt->userData,
5413 "Element %s content does not follow the DTD\nExpecting more child\n",
5414 state->node->name);
5415 } else {
5416 /*
5417 * previous validation errors should not generate
5418 * a new one here
5419 */
5420 ret = 1;
5421 }
5422 }
5423 }
5424 }
5425 vstateVPop(ctxt);
5426 }
5427 return(ret);
5428}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005429#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005430
5431/**
Owen Taylor3473f882001-02-23 17:55:21 +00005432 * xmlValidateOneElement:
5433 * @ctxt: the validation context
5434 * @doc: a document instance
5435 * @elem: an element instance
5436 *
5437 * Try to validate a single element and it's attributes,
5438 * basically it does the following checks as described by the
5439 * XML-1.0 recommendation:
5440 * - [ VC: Element Valid ]
5441 * - [ VC: Required Attribute ]
5442 * Then call xmlValidateOneAttribute() for each attribute present.
5443 *
5444 * The ID/IDREF checkings are done separately
5445 *
5446 * returns 1 if valid or 0 otherwise
5447 */
5448
5449int
5450xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5451 xmlNodePtr elem) {
5452 xmlElementPtr elemDecl = NULL;
5453 xmlElementContentPtr cont;
5454 xmlAttributePtr attr;
5455 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005456 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005457 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005458 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005459
5460 CHECK_DTD;
5461
5462 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005463 switch (elem->type) {
5464 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005465 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005466 VERROR(ctxt->userData,
5467 "Attribute element not expected here\n");
5468 return(0);
5469 case XML_TEXT_NODE:
5470 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005471 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005472 VERROR(ctxt->userData, "Text element has childs !\n");
5473 return(0);
5474 }
5475 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005476 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005477 VERROR(ctxt->userData, "Text element has attributes !\n");
5478 return(0);
5479 }
5480 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005481 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005482 VERROR(ctxt->userData, "Text element has namespace !\n");
5483 return(0);
5484 }
5485 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005486 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005487 VERROR(ctxt->userData,
5488 "Text element carries namespace definitions !\n");
5489 return(0);
5490 }
5491 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005492 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005493 VERROR(ctxt->userData,
5494 "Text element has no content !\n");
5495 return(0);
5496 }
5497 return(1);
5498 case XML_XINCLUDE_START:
5499 case XML_XINCLUDE_END:
5500 return(1);
5501 case XML_CDATA_SECTION_NODE:
5502 case XML_ENTITY_REF_NODE:
5503 case XML_PI_NODE:
5504 case XML_COMMENT_NODE:
5505 return(1);
5506 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005507 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005508 VERROR(ctxt->userData,
5509 "Entity element not expected here\n");
5510 return(0);
5511 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005512 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005513 VERROR(ctxt->userData,
5514 "Notation element not expected here\n");
5515 return(0);
5516 case XML_DOCUMENT_NODE:
5517 case XML_DOCUMENT_TYPE_NODE:
5518 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005519 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005520 VERROR(ctxt->userData,
5521 "Document element not expected here\n");
5522 return(0);
5523 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005524 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005525 VERROR(ctxt->userData,
5526 "\n");
5527 return(0);
5528 case XML_ELEMENT_NODE:
5529 break;
5530 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005531 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005532 VERROR(ctxt->userData,
5533 "unknown element type %d\n", elem->type);
5534 return(0);
5535 }
Owen Taylor3473f882001-02-23 17:55:21 +00005536
5537 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005538 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005539 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005540 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5541 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005542 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005543
Daniel Veillardea7751d2002-12-20 00:16:24 +00005544 /*
5545 * If vstateNr is not zero that means continuous validation is
5546 * activated, do not try to check the content model at that level.
5547 */
5548 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005549 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005550 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005551 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005552 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005553 VERROR(ctxt->userData, "No declaration for element %s\n",
5554 elem->name);
5555 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005556 case XML_ELEMENT_TYPE_EMPTY:
5557 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005558 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005559 VERROR(ctxt->userData,
5560 "Element %s was declared EMPTY this one has content\n",
5561 elem->name);
5562 ret = 0;
5563 }
5564 break;
5565 case XML_ELEMENT_TYPE_ANY:
5566 /* I don't think anything is required then */
5567 break;
5568 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005569
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005570 /* simple case of declared as #PCDATA */
5571 if ((elemDecl->content != NULL) &&
5572 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5573 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5574 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005575 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005576 VERROR(ctxt->userData,
5577 "Element %s was declared #PCDATA but contains non text nodes\n",
5578 elem->name);
5579 }
5580 break;
5581 }
Owen Taylor3473f882001-02-23 17:55:21 +00005582 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005583 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005584 while (child != NULL) {
5585 if (child->type == XML_ELEMENT_NODE) {
5586 name = child->name;
5587 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005588 xmlChar fn[50];
5589 xmlChar *fullname;
5590
5591 fullname = xmlBuildQName(child->name, child->ns->prefix,
5592 fn, 50);
5593 if (fullname == NULL)
5594 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005595 cont = elemDecl->content;
5596 while (cont != NULL) {
5597 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005598 if (xmlStrEqual(cont->name, fullname))
5599 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005600 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5601 (cont->c1 != NULL) &&
5602 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005603 if (xmlStrEqual(cont->c1->name, fullname))
5604 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005605 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5606 (cont->c1 == NULL) ||
5607 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5608 /* Internal error !!! */
5609 xmlGenericError(xmlGenericErrorContext,
5610 "Internal: MIXED struct bad\n");
5611 break;
5612 }
5613 cont = cont->c2;
5614 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005615 if ((fullname != fn) && (fullname != child->name))
5616 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005617 if (cont != NULL)
5618 goto child_ok;
5619 }
5620 cont = elemDecl->content;
5621 while (cont != NULL) {
5622 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5623 if (xmlStrEqual(cont->name, name)) break;
5624 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5625 (cont->c1 != NULL) &&
5626 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5627 if (xmlStrEqual(cont->c1->name, name)) break;
5628 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5629 (cont->c1 == NULL) ||
5630 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5631 /* Internal error !!! */
5632 xmlGenericError(xmlGenericErrorContext,
5633 "Internal: MIXED struct bad\n");
5634 break;
5635 }
5636 cont = cont->c2;
5637 }
5638 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005639 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005640 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005641 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005642 name, elem->name);
5643 ret = 0;
5644 }
5645 }
5646child_ok:
5647 child = child->next;
5648 }
5649 break;
5650 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005651 if ((doc->standalone == 1) && (extsubset == 1)) {
5652 /*
5653 * VC: Standalone Document Declaration
5654 * - element types with element content, if white space
5655 * occurs directly within any instance of those types.
5656 */
5657 child = elem->children;
5658 while (child != NULL) {
5659 if (child->type == XML_TEXT_NODE) {
5660 const xmlChar *content = child->content;
5661
5662 while (IS_BLANK(*content))
5663 content++;
5664 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005665 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005666 VERROR(ctxt->userData,
5667"standalone: %s declared in the external subset contains white spaces nodes\n",
5668 elem->name);
5669 ret = 0;
5670 break;
5671 }
5672 }
5673 child =child->next;
5674 }
5675 }
Owen Taylor3473f882001-02-23 17:55:21 +00005676 child = elem->children;
5677 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005678 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005679 if (tmp <= 0)
5680 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005681 break;
5682 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005683 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005684
5685 /* [ VC: Required Attribute ] */
5686 attr = elemDecl->attributes;
5687 while (attr != NULL) {
5688 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005689 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005690
Daniel Veillarde4301c82002-02-13 13:32:35 +00005691 if ((attr->prefix == NULL) &&
5692 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5693 xmlNsPtr ns;
5694
5695 ns = elem->nsDef;
5696 while (ns != NULL) {
5697 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005698 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005699 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005700 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005701 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5702 xmlNsPtr ns;
5703
5704 ns = elem->nsDef;
5705 while (ns != NULL) {
5706 if (xmlStrEqual(attr->name, ns->prefix))
5707 goto found;
5708 ns = ns->next;
5709 }
5710 } else {
5711 xmlAttrPtr attrib;
5712
5713 attrib = elem->properties;
5714 while (attrib != NULL) {
5715 if (xmlStrEqual(attrib->name, attr->name)) {
5716 if (attr->prefix != NULL) {
5717 xmlNsPtr nameSpace = attrib->ns;
5718
5719 if (nameSpace == NULL)
5720 nameSpace = elem->ns;
5721 /*
5722 * qualified names handling is problematic, having a
5723 * different prefix should be possible but DTDs don't
5724 * allow to define the URI instead of the prefix :-(
5725 */
5726 if (nameSpace == NULL) {
5727 if (qualified < 0)
5728 qualified = 0;
5729 } else if (!xmlStrEqual(nameSpace->prefix,
5730 attr->prefix)) {
5731 if (qualified < 1)
5732 qualified = 1;
5733 } else
5734 goto found;
5735 } else {
5736 /*
5737 * We should allow applications to define namespaces
5738 * for their application even if the DTD doesn't
5739 * carry one, otherwise, basically we would always
5740 * break.
5741 */
5742 goto found;
5743 }
5744 }
5745 attrib = attrib->next;
5746 }
Owen Taylor3473f882001-02-23 17:55:21 +00005747 }
5748 if (qualified == -1) {
5749 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005750 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005751 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005752 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005753 elem->name, attr->name);
5754 ret = 0;
5755 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005756 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005757 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005758 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005759 elem->name, attr->prefix,attr->name);
5760 ret = 0;
5761 }
5762 } else if (qualified == 0) {
5763 VWARNING(ctxt->userData,
5764 "Element %s required attribute %s:%s has no prefix\n",
5765 elem->name, attr->prefix,attr->name);
5766 } else if (qualified == 1) {
5767 VWARNING(ctxt->userData,
5768 "Element %s required attribute %s:%s has different prefix\n",
5769 elem->name, attr->prefix,attr->name);
5770 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005771 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5772 /*
5773 * Special tests checking #FIXED namespace declarations
5774 * have the right value since this is not done as an
5775 * attribute checking
5776 */
5777 if ((attr->prefix == NULL) &&
5778 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5779 xmlNsPtr ns;
5780
5781 ns = elem->nsDef;
5782 while (ns != NULL) {
5783 if (ns->prefix == NULL) {
5784 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005785 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005786 VERROR(ctxt->userData,
5787 "Element %s namespace name for default namespace does not match the DTD\n",
5788 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005789 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005790 }
5791 goto found;
5792 }
5793 ns = ns->next;
5794 }
5795 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5796 xmlNsPtr ns;
5797
5798 ns = elem->nsDef;
5799 while (ns != NULL) {
5800 if (xmlStrEqual(attr->name, ns->prefix)) {
5801 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005802 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005803 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005804 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005805 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005806 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005807 }
5808 goto found;
5809 }
5810 ns = ns->next;
5811 }
5812 }
Owen Taylor3473f882001-02-23 17:55:21 +00005813 }
5814found:
5815 attr = attr->nexth;
5816 }
5817 return(ret);
5818}
5819
5820/**
5821 * xmlValidateRoot:
5822 * @ctxt: the validation context
5823 * @doc: a document instance
5824 *
5825 * Try to validate a the root element
5826 * basically it does the following check as described by the
5827 * XML-1.0 recommendation:
5828 * - [ VC: Root Element Type ]
5829 * it doesn't try to recurse or apply other check to the element
5830 *
5831 * returns 1 if valid or 0 otherwise
5832 */
5833
5834int
5835xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5836 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00005837 int ret;
5838
Owen Taylor3473f882001-02-23 17:55:21 +00005839 if (doc == NULL) return(0);
5840
5841 root = xmlDocGetRootElement(doc);
5842 if ((root == NULL) || (root->name == NULL)) {
5843 VERROR(ctxt->userData, "Not valid: no root element\n");
5844 return(0);
5845 }
5846
5847 /*
5848 * When doing post validation against a separate DTD, those may
5849 * no internal subset has been generated
5850 */
5851 if ((doc->intSubset != NULL) &&
5852 (doc->intSubset->name != NULL)) {
5853 /*
5854 * Check first the document root against the NQName
5855 */
5856 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5857 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005858 xmlChar fn[50];
5859 xmlChar *fullname;
5860
5861 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
5862 if (fullname == NULL) {
5863 VERROR(ctxt->userData, "Out of memory\n");
5864 return(0);
5865 }
5866 ret = xmlStrEqual(doc->intSubset->name, fullname);
5867 if ((fullname != fn) && (fullname != root->name))
5868 xmlFree(fullname);
5869 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00005870 goto name_ok;
5871 }
5872 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5873 (xmlStrEqual(root->name, BAD_CAST "html")))
5874 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005875 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005876 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005877 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005878 root->name, doc->intSubset->name);
5879 return(0);
5880
5881 }
5882 }
5883name_ok:
5884 return(1);
5885}
5886
5887
5888/**
5889 * xmlValidateElement:
5890 * @ctxt: the validation context
5891 * @doc: a document instance
5892 * @elem: an element instance
5893 *
5894 * Try to validate the subtree under an element
5895 *
5896 * returns 1 if valid or 0 otherwise
5897 */
5898
5899int
5900xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5901 xmlNodePtr child;
5902 xmlAttrPtr attr;
5903 xmlChar *value;
5904 int ret = 1;
5905
5906 if (elem == NULL) return(0);
5907
5908 /*
5909 * XInclude elements were added after parsing in the infoset,
5910 * they don't really mean anything validation wise.
5911 */
5912 if ((elem->type == XML_XINCLUDE_START) ||
5913 (elem->type == XML_XINCLUDE_END))
5914 return(1);
5915
5916 CHECK_DTD;
5917
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005918 /*
5919 * Entities references have to be handled separately
5920 */
5921 if (elem->type == XML_ENTITY_REF_NODE) {
5922 return(1);
5923 }
5924
Owen Taylor3473f882001-02-23 17:55:21 +00005925 ret &= xmlValidateOneElement(ctxt, doc, elem);
5926 attr = elem->properties;
5927 while(attr != NULL) {
5928 value = xmlNodeListGetString(doc, attr->children, 0);
5929 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5930 if (value != NULL)
5931 xmlFree(value);
5932 attr= attr->next;
5933 }
5934 child = elem->children;
5935 while (child != NULL) {
5936 ret &= xmlValidateElement(ctxt, doc, child);
5937 child = child->next;
5938 }
5939
5940 return(ret);
5941}
5942
Daniel Veillard8730c562001-02-26 10:49:57 +00005943/**
5944 * xmlValidateRef:
5945 * @ref: A reference to be validated
5946 * @ctxt: Validation context
5947 * @name: Name of ID we are searching for
5948 *
5949 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005950static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005951xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005952 const xmlChar *name) {
5953 xmlAttrPtr id;
5954 xmlAttrPtr attr;
5955
5956 if (ref == NULL)
5957 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005958 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00005959 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005960 attr = ref->attr;
5961 if (attr == NULL) {
5962 xmlChar *dup, *str = NULL, *cur, save;
5963
5964 dup = xmlStrdup(name);
5965 if (dup == NULL) {
5966 ctxt->valid = 0;
5967 return;
5968 }
5969 cur = dup;
5970 while (*cur != 0) {
5971 str = cur;
5972 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5973 save = *cur;
5974 *cur = 0;
5975 id = xmlGetID(ctxt->doc, str);
5976 if (id == NULL) {
5977 VERROR(ctxt->userData,
5978 "attribute %s line %d references an unknown ID \"%s\"\n",
5979 ref->name, ref->lineno, str);
5980 ctxt->valid = 0;
5981 }
5982 if (save == 0)
5983 break;
5984 *cur = save;
5985 while (IS_BLANK(*cur)) cur++;
5986 }
5987 xmlFree(dup);
5988 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00005989 id = xmlGetID(ctxt->doc, name);
5990 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005991 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005992 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005993 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005994 attr->name, name);
5995 ctxt->valid = 0;
5996 }
5997 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5998 xmlChar *dup, *str = NULL, *cur, save;
5999
6000 dup = xmlStrdup(name);
6001 if (dup == NULL) {
6002 ctxt->valid = 0;
6003 return;
6004 }
6005 cur = dup;
6006 while (*cur != 0) {
6007 str = cur;
6008 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
6009 save = *cur;
6010 *cur = 0;
6011 id = xmlGetID(ctxt->doc, str);
6012 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00006013 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00006014 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006015 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00006016 attr->name, str);
6017 ctxt->valid = 0;
6018 }
6019 if (save == 0)
6020 break;
6021 *cur = save;
6022 while (IS_BLANK(*cur)) cur++;
6023 }
6024 xmlFree(dup);
6025 }
6026}
6027
6028/**
Daniel Veillard8730c562001-02-26 10:49:57 +00006029 * xmlWalkValidateList:
6030 * @data: Contents of current link
6031 * @user: Value supplied by the user
6032 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00006033 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00006034 */
6035static int
6036xmlWalkValidateList(const void *data, const void *user)
6037{
6038 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
6039 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
6040 return 1;
6041}
6042
6043/**
6044 * xmlValidateCheckRefCallback:
6045 * @ref_list: List of references
6046 * @ctxt: Validation context
6047 * @name: Name of ID we are searching for
6048 *
6049 */
6050static void
6051xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
6052 const xmlChar *name) {
6053 xmlValidateMemo memo;
6054
6055 if (ref_list == NULL)
6056 return;
6057 memo.ctxt = ctxt;
6058 memo.name = name;
6059
6060 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
6061
6062}
6063
6064/**
Owen Taylor3473f882001-02-23 17:55:21 +00006065 * xmlValidateDocumentFinal:
6066 * @ctxt: the validation context
6067 * @doc: a document instance
6068 *
6069 * Does the final step for the document validation once all the
6070 * incremental validation steps have been completed
6071 *
6072 * basically it does the following checks described by the XML Rec
6073 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006074 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00006075 *
6076 * returns 1 if valid or 0 otherwise
6077 */
6078
6079int
6080xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6081 xmlRefTablePtr table;
6082
6083 if (doc == NULL) {
6084 xmlGenericError(xmlGenericErrorContext,
6085 "xmlValidateDocumentFinal: doc == NULL\n");
6086 return(0);
6087 }
6088
6089 /*
6090 * Check all the NOTATION/NOTATIONS attributes
6091 */
6092 /*
6093 * Check all the ENTITY/ENTITIES attributes definition for validity
6094 */
6095 /*
6096 * Check all the IDREF/IDREFS attributes definition for validity
6097 */
6098 table = (xmlRefTablePtr) doc->refs;
6099 ctxt->doc = doc;
6100 ctxt->valid = 1;
6101 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6102 return(ctxt->valid);
6103}
6104
6105/**
6106 * xmlValidateDtd:
6107 * @ctxt: the validation context
6108 * @doc: a document instance
6109 * @dtd: a dtd instance
6110 *
6111 * Try to validate the document against the dtd instance
6112 *
6113 * basically it does check all the definitions in the DtD.
6114 *
6115 * returns 1 if valid or 0 otherwise
6116 */
6117
6118int
6119xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6120 int ret;
6121 xmlDtdPtr oldExt;
6122 xmlNodePtr root;
6123
6124 if (dtd == NULL) return(0);
6125 if (doc == NULL) return(0);
6126 oldExt = doc->extSubset;
6127 doc->extSubset = dtd;
6128 ret = xmlValidateRoot(ctxt, doc);
6129 if (ret == 0) {
6130 doc->extSubset = oldExt;
6131 return(ret);
6132 }
6133 if (doc->ids != NULL) {
6134 xmlFreeIDTable(doc->ids);
6135 doc->ids = NULL;
6136 }
6137 if (doc->refs != NULL) {
6138 xmlFreeRefTable(doc->refs);
6139 doc->refs = NULL;
6140 }
6141 root = xmlDocGetRootElement(doc);
6142 ret = xmlValidateElement(ctxt, doc, root);
6143 ret &= xmlValidateDocumentFinal(ctxt, doc);
6144 doc->extSubset = oldExt;
6145 return(ret);
6146}
6147
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006148static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006149xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6150 const xmlChar *name ATTRIBUTE_UNUSED) {
6151 if (cur == NULL)
6152 return;
6153 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6154 xmlChar *notation = cur->content;
6155
Daniel Veillard878eab02002-02-19 13:46:09 +00006156 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006157 int ret;
6158
6159 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6160 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006161 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006162 }
6163 }
6164 }
6165}
6166
6167static void
Owen Taylor3473f882001-02-23 17:55:21 +00006168xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006169 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006170 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006171 xmlDocPtr doc;
6172 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006173
Owen Taylor3473f882001-02-23 17:55:21 +00006174 if (cur == NULL)
6175 return;
6176 switch (cur->atype) {
6177 case XML_ATTRIBUTE_CDATA:
6178 case XML_ATTRIBUTE_ID:
6179 case XML_ATTRIBUTE_IDREF :
6180 case XML_ATTRIBUTE_IDREFS:
6181 case XML_ATTRIBUTE_NMTOKEN:
6182 case XML_ATTRIBUTE_NMTOKENS:
6183 case XML_ATTRIBUTE_ENUMERATION:
6184 break;
6185 case XML_ATTRIBUTE_ENTITY:
6186 case XML_ATTRIBUTE_ENTITIES:
6187 case XML_ATTRIBUTE_NOTATION:
6188 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006189
6190 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6191 cur->atype, cur->defaultValue);
6192 if ((ret == 0) && (ctxt->valid == 1))
6193 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006194 }
6195 if (cur->tree != NULL) {
6196 xmlEnumerationPtr tree = cur->tree;
6197 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006198 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006199 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006200 if ((ret == 0) && (ctxt->valid == 1))
6201 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006202 tree = tree->next;
6203 }
6204 }
6205 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006206 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6207 doc = cur->doc;
6208 if ((doc == NULL) || (cur->elem == NULL)) {
6209 VERROR(ctxt->userData,
6210 "xmlValidateAttributeCallback(%s): internal error\n",
6211 cur->name);
6212 return;
6213 }
6214 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6215 if (elem == NULL)
6216 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6217 if (elem == NULL) {
6218 VERROR(ctxt->userData,
6219 "attribute %s: could not find decl for element %s\n",
6220 cur->name, cur->elem);
6221 return;
6222 }
6223 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6224 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006225 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00006226 cur->name, cur->elem);
6227 ctxt->valid = 0;
6228 }
6229 }
Owen Taylor3473f882001-02-23 17:55:21 +00006230}
6231
6232/**
6233 * xmlValidateDtdFinal:
6234 * @ctxt: the validation context
6235 * @doc: a document instance
6236 *
6237 * Does the final step for the dtds validation once all the
6238 * subsets have been parsed
6239 *
6240 * basically it does the following checks described by the XML Rec
6241 * - check that ENTITY and ENTITIES type attributes default or
6242 * possible values matches one of the defined entities.
6243 * - check that NOTATION type attributes default or
6244 * possible values matches one of the defined notations.
6245 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006246 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006247 */
6248
6249int
6250xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006251 xmlDtdPtr dtd;
6252 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006253 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006254
6255 if (doc == NULL) return(0);
6256 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6257 return(0);
6258 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006259 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006260 dtd = doc->intSubset;
6261 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6262 table = (xmlAttributeTablePtr) dtd->attributes;
6263 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006264 }
6265 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006266 entities = (xmlEntitiesTablePtr) dtd->entities;
6267 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6268 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006269 }
6270 dtd = doc->extSubset;
6271 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6272 table = (xmlAttributeTablePtr) dtd->attributes;
6273 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006274 }
6275 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006276 entities = (xmlEntitiesTablePtr) dtd->entities;
6277 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6278 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006279 }
6280 return(ctxt->valid);
6281}
6282
6283/**
6284 * xmlValidateDocument:
6285 * @ctxt: the validation context
6286 * @doc: a document instance
6287 *
6288 * Try to validate the document instance
6289 *
6290 * basically it does the all the checks described by the XML Rec
6291 * i.e. validates the internal and external subset (if present)
6292 * and validate the document tree.
6293 *
6294 * returns 1 if valid or 0 otherwise
6295 */
6296
6297int
6298xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6299 int ret;
6300 xmlNodePtr root;
6301
Daniel Veillard2fd85422002-10-16 14:32:41 +00006302 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6303 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00006304 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006305 }
Owen Taylor3473f882001-02-23 17:55:21 +00006306 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6307 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6308 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6309 doc->intSubset->SystemID);
6310 if (doc->extSubset == NULL) {
6311 if (doc->intSubset->SystemID != NULL) {
6312 VERROR(ctxt->userData,
6313 "Could not load the external subset \"%s\"\n",
6314 doc->intSubset->SystemID);
6315 } else {
6316 VERROR(ctxt->userData,
6317 "Could not load the external subset \"%s\"\n",
6318 doc->intSubset->ExternalID);
6319 }
6320 return(0);
6321 }
6322 }
6323
6324 if (doc->ids != NULL) {
6325 xmlFreeIDTable(doc->ids);
6326 doc->ids = NULL;
6327 }
6328 if (doc->refs != NULL) {
6329 xmlFreeRefTable(doc->refs);
6330 doc->refs = NULL;
6331 }
6332 ret = xmlValidateDtdFinal(ctxt, doc);
6333 if (!xmlValidateRoot(ctxt, doc)) return(0);
6334
6335 root = xmlDocGetRootElement(doc);
6336 ret &= xmlValidateElement(ctxt, doc, root);
6337 ret &= xmlValidateDocumentFinal(ctxt, doc);
6338 return(ret);
6339}
6340
Owen Taylor3473f882001-02-23 17:55:21 +00006341/************************************************************************
6342 * *
6343 * Routines for dynamic validation editing *
6344 * *
6345 ************************************************************************/
6346
6347/**
6348 * xmlValidGetPotentialChildren:
6349 * @ctree: an element content tree
6350 * @list: an array to store the list of child names
6351 * @len: a pointer to the number of element in the list
6352 * @max: the size of the array
6353 *
6354 * Build/extend a list of potential children allowed by the content tree
6355 *
6356 * returns the number of element in the list, or -1 in case of error.
6357 */
6358
6359int
6360xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6361 int *len, int max) {
6362 int i;
6363
6364 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6365 return(-1);
6366 if (*len >= max) return(*len);
6367
6368 switch (ctree->type) {
6369 case XML_ELEMENT_CONTENT_PCDATA:
6370 for (i = 0; i < *len;i++)
6371 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6372 list[(*len)++] = BAD_CAST "#PCDATA";
6373 break;
6374 case XML_ELEMENT_CONTENT_ELEMENT:
6375 for (i = 0; i < *len;i++)
6376 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6377 list[(*len)++] = ctree->name;
6378 break;
6379 case XML_ELEMENT_CONTENT_SEQ:
6380 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6381 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6382 break;
6383 case XML_ELEMENT_CONTENT_OR:
6384 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6385 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6386 break;
6387 }
6388
6389 return(*len);
6390}
6391
6392/**
6393 * xmlValidGetValidElements:
6394 * @prev: an element to insert after
6395 * @next: an element to insert next
6396 * @list: an array to store the list of child names
6397 * @max: the size of the array
6398 *
6399 * This function returns the list of authorized children to insert
6400 * within an existing tree while respecting the validity constraints
6401 * forced by the Dtd. The insertion point is defined using @prev and
6402 * @next in the following ways:
6403 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6404 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6405 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6406 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6407 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6408 *
6409 * pointers to the element names are inserted at the beginning of the array
6410 * and do not need to be freed.
6411 *
6412 * returns the number of element in the list, or -1 in case of error. If
6413 * the function returns the value @max the caller is invited to grow the
6414 * receiving array and retry.
6415 */
6416
6417int
6418xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6419 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006420 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006421 int nb_valid_elements = 0;
6422 const xmlChar *elements[256];
6423 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006424 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006425
6426 xmlNode *ref_node;
6427 xmlNode *parent;
6428 xmlNode *test_node;
6429
6430 xmlNode *prev_next;
6431 xmlNode *next_prev;
6432 xmlNode *parent_childs;
6433 xmlNode *parent_last;
6434
6435 xmlElement *element_desc;
6436
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006437 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006438
Owen Taylor3473f882001-02-23 17:55:21 +00006439 if (prev == NULL && next == NULL)
6440 return(-1);
6441
6442 if (list == NULL) return(-1);
6443 if (max <= 0) return(-1);
6444
6445 nb_valid_elements = 0;
6446 ref_node = prev ? prev : next;
6447 parent = ref_node->parent;
6448
6449 /*
6450 * Retrieves the parent element declaration
6451 */
6452 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6453 parent->name);
6454 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6455 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6456 parent->name);
6457 if (element_desc == NULL) return(-1);
6458
6459 /*
6460 * Do a backup of the current tree structure
6461 */
6462 prev_next = prev ? prev->next : NULL;
6463 next_prev = next ? next->prev : NULL;
6464 parent_childs = parent->children;
6465 parent_last = parent->last;
6466
6467 /*
6468 * Creates a dummy node and insert it into the tree
6469 */
6470 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6471 test_node->doc = ref_node->doc;
6472 test_node->parent = parent;
6473 test_node->prev = prev;
6474 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006475 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006476
6477 if (prev) prev->next = test_node;
6478 else parent->children = test_node;
6479
6480 if (next) next->prev = test_node;
6481 else parent->last = test_node;
6482
6483 /*
6484 * Insert each potential child node and check if the parent is
6485 * still valid
6486 */
6487 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6488 elements, &nb_elements, 256);
6489
6490 for (i = 0;i < nb_elements;i++) {
6491 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006492 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006493 int j;
6494
6495 for (j = 0; j < nb_valid_elements;j++)
6496 if (xmlStrEqual(elements[i], list[j])) break;
6497 list[nb_valid_elements++] = elements[i];
6498 if (nb_valid_elements >= max) break;
6499 }
6500 }
6501
6502 /*
6503 * Restore the tree structure
6504 */
6505 if (prev) prev->next = prev_next;
6506 if (next) next->prev = next_prev;
6507 parent->children = parent_childs;
6508 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006509
6510 /*
6511 * Free up the dummy node
6512 */
6513 test_node->name = name;
6514 xmlFreeNode(test_node);
6515
Owen Taylor3473f882001-02-23 17:55:21 +00006516 return(nb_valid_elements);
6517}
Daniel Veillard4432df22003-09-28 18:58:27 +00006518#endif /* LIBXML_VALID_ENABLED */
6519