blob: 7ebd686b8a14888dd58b6b2068895f801a158717 [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
1269/**
1270 * xmlCopyElement:
1271 * @elem: An element
1272 *
1273 * Build a copy of an element.
1274 *
1275 * Returns the new xmlElementPtr or NULL in case of error.
1276 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001277static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001278xmlCopyElement(xmlElementPtr elem) {
1279 xmlElementPtr cur;
1280
1281 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1282 if (cur == NULL) {
1283 xmlGenericError(xmlGenericErrorContext,
1284 "xmlCopyElement: out of memory !\n");
1285 return(NULL);
1286 }
1287 memset(cur, 0, sizeof(xmlElement));
1288 cur->type = XML_ELEMENT_DECL;
1289 cur->etype = elem->etype;
1290 if (elem->name != NULL)
1291 cur->name = xmlStrdup(elem->name);
1292 else
1293 cur->name = NULL;
1294 if (elem->prefix != NULL)
1295 cur->prefix = xmlStrdup(elem->prefix);
1296 else
1297 cur->prefix = NULL;
1298 cur->content = xmlCopyElementContent(elem->content);
1299 /* TODO : rebuild the attribute list on the copy */
1300 cur->attributes = NULL;
1301 return(cur);
1302}
1303
1304/**
1305 * xmlCopyElementTable:
1306 * @table: An element table
1307 *
1308 * Build a copy of an element table.
1309 *
1310 * Returns the new xmlElementTablePtr or NULL in case of error.
1311 */
1312xmlElementTablePtr
1313xmlCopyElementTable(xmlElementTablePtr table) {
1314 return((xmlElementTablePtr) xmlHashCopy(table,
1315 (xmlHashCopier) xmlCopyElement));
1316}
1317
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001318#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001319/**
1320 * xmlDumpElementDecl:
1321 * @buf: the XML buffer output
1322 * @elem: An element table
1323 *
1324 * This will dump the content of the element declaration as an XML
1325 * DTD definition
1326 */
1327void
1328xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1329 switch (elem->etype) {
1330 case XML_ELEMENT_TYPE_EMPTY:
1331 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001332 if (elem->prefix != NULL) {
1333 xmlBufferWriteCHAR(buf, elem->prefix);
1334 xmlBufferWriteChar(buf, ":");
1335 }
Owen Taylor3473f882001-02-23 17:55:21 +00001336 xmlBufferWriteCHAR(buf, elem->name);
1337 xmlBufferWriteChar(buf, " EMPTY>\n");
1338 break;
1339 case XML_ELEMENT_TYPE_ANY:
1340 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001341 if (elem->prefix != NULL) {
1342 xmlBufferWriteCHAR(buf, elem->prefix);
1343 xmlBufferWriteChar(buf, ":");
1344 }
Owen Taylor3473f882001-02-23 17:55:21 +00001345 xmlBufferWriteCHAR(buf, elem->name);
1346 xmlBufferWriteChar(buf, " ANY>\n");
1347 break;
1348 case XML_ELEMENT_TYPE_MIXED:
1349 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001350 if (elem->prefix != NULL) {
1351 xmlBufferWriteCHAR(buf, elem->prefix);
1352 xmlBufferWriteChar(buf, ":");
1353 }
Owen Taylor3473f882001-02-23 17:55:21 +00001354 xmlBufferWriteCHAR(buf, elem->name);
1355 xmlBufferWriteChar(buf, " ");
1356 xmlDumpElementContent(buf, elem->content, 1);
1357 xmlBufferWriteChar(buf, ">\n");
1358 break;
1359 case XML_ELEMENT_TYPE_ELEMENT:
1360 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001361 if (elem->prefix != NULL) {
1362 xmlBufferWriteCHAR(buf, elem->prefix);
1363 xmlBufferWriteChar(buf, ":");
1364 }
Owen Taylor3473f882001-02-23 17:55:21 +00001365 xmlBufferWriteCHAR(buf, elem->name);
1366 xmlBufferWriteChar(buf, " ");
1367 xmlDumpElementContent(buf, elem->content, 1);
1368 xmlBufferWriteChar(buf, ">\n");
1369 break;
1370 default:
1371 xmlGenericError(xmlGenericErrorContext,
1372 "xmlDumpElementDecl: internal: unknown type %d\n",
1373 elem->etype);
1374 }
1375}
1376
1377/**
1378 * xmlDumpElementTable:
1379 * @buf: the XML buffer output
1380 * @table: An element table
1381 *
1382 * This will dump the content of the element table as an XML DTD definition
1383 */
1384void
1385xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1386 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1387}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001388#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001389
1390/**
1391 * xmlCreateEnumeration:
1392 * @name: the enumeration name or NULL
1393 *
1394 * create and initialize an enumeration attribute node.
1395 *
1396 * Returns the xmlEnumerationPtr just created or NULL in case
1397 * of error.
1398 */
1399xmlEnumerationPtr
Daniel Veillard2fdbd322003-08-18 12:15:38 +00001400xmlCreateEnumeration(const xmlChar *name) {
Owen Taylor3473f882001-02-23 17:55:21 +00001401 xmlEnumerationPtr ret;
1402
1403 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1404 if (ret == NULL) {
1405 xmlGenericError(xmlGenericErrorContext,
1406 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1407 (long)sizeof(xmlEnumeration));
1408 return(NULL);
1409 }
1410 memset(ret, 0, sizeof(xmlEnumeration));
1411
1412 if (name != NULL)
1413 ret->name = xmlStrdup(name);
1414 return(ret);
1415}
1416
1417/**
1418 * xmlFreeEnumeration:
1419 * @cur: the tree to free.
1420 *
1421 * free an enumeration attribute node (recursive).
1422 */
1423void
1424xmlFreeEnumeration(xmlEnumerationPtr cur) {
1425 if (cur == NULL) return;
1426
1427 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1428
1429 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001430 xmlFree(cur);
1431}
1432
1433/**
1434 * xmlCopyEnumeration:
1435 * @cur: the tree to copy.
1436 *
1437 * Copy an enumeration attribute node (recursive).
1438 *
1439 * Returns the xmlEnumerationPtr just created or NULL in case
1440 * of error.
1441 */
1442xmlEnumerationPtr
1443xmlCopyEnumeration(xmlEnumerationPtr cur) {
1444 xmlEnumerationPtr ret;
1445
1446 if (cur == NULL) return(NULL);
1447 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1448
1449 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1450 else ret->next = NULL;
1451
1452 return(ret);
1453}
1454
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001455#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001456/**
1457 * xmlDumpEnumeration:
1458 * @buf: the XML buffer output
1459 * @enum: An enumeration
1460 *
1461 * This will dump the content of the enumeration
1462 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001463static void
Owen Taylor3473f882001-02-23 17:55:21 +00001464xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1465 if (cur == NULL) return;
1466
1467 xmlBufferWriteCHAR(buf, cur->name);
1468 if (cur->next == NULL)
1469 xmlBufferWriteChar(buf, ")");
1470 else {
1471 xmlBufferWriteChar(buf, " | ");
1472 xmlDumpEnumeration(buf, cur->next);
1473 }
1474}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001475#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001476
1477/**
1478 * xmlCreateAttributeTable:
1479 *
1480 * create and initialize an empty attribute hash table.
1481 *
1482 * Returns the xmlAttributeTablePtr just created or NULL in case
1483 * of error.
1484 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001485static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001486xmlCreateAttributeTable(void) {
1487 return(xmlHashCreate(0));
1488}
1489
Daniel Veillard4432df22003-09-28 18:58:27 +00001490#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001491/**
1492 * xmlScanAttributeDeclCallback:
1493 * @attr: the attribute decl
1494 * @list: the list to update
1495 *
1496 * Callback called by xmlScanAttributeDecl when a new attribute
1497 * has to be entered in the list.
1498 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001499static void
Owen Taylor3473f882001-02-23 17:55:21 +00001500xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001501 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001502 attr->nexth = *list;
1503 *list = attr;
1504}
1505
1506/**
1507 * xmlScanAttributeDecl:
1508 * @dtd: pointer to the DTD
1509 * @elem: the element name
1510 *
1511 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001512 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001513 *
1514 * Returns the pointer to the first attribute decl in the chain,
1515 * possibly NULL.
1516 */
1517xmlAttributePtr
1518xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1519 xmlAttributePtr ret = NULL;
1520 xmlAttributeTablePtr table;
1521
1522 if (dtd == NULL) {
1523 xmlGenericError(xmlGenericErrorContext,
1524 "xmlScanAttributeDecl: dtd == NULL\n");
1525 return(NULL);
1526 }
1527 if (elem == NULL) {
1528 xmlGenericError(xmlGenericErrorContext,
1529 "xmlScanAttributeDecl: elem == NULL\n");
1530 return(NULL);
1531 }
1532 table = (xmlAttributeTablePtr) dtd->attributes;
1533 if (table == NULL)
1534 return(NULL);
1535
1536 /* WRONG !!! */
1537 xmlHashScan3(table, NULL, NULL, elem,
1538 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1539 return(ret);
1540}
1541
1542/**
1543 * xmlScanIDAttributeDecl:
1544 * @ctxt: the validation context
1545 * @elem: the element name
1546 *
1547 * Verify that the element don't have too many ID attributes
1548 * declared.
1549 *
1550 * Returns the number of ID attributes found.
1551 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001552static int
Owen Taylor3473f882001-02-23 17:55:21 +00001553xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1554 xmlAttributePtr cur;
1555 int ret = 0;
1556
1557 if (elem == NULL) return(0);
1558 cur = elem->attributes;
1559 while (cur != NULL) {
1560 if (cur->atype == XML_ATTRIBUTE_ID) {
1561 ret ++;
1562 if (ret > 1)
1563 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001564 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001565 elem->name, cur->name);
1566 }
1567 cur = cur->nexth;
1568 }
1569 return(ret);
1570}
Daniel Veillard4432df22003-09-28 18:58:27 +00001571#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001572
1573/**
1574 * xmlFreeAttribute:
1575 * @elem: An attribute
1576 *
1577 * Deallocate the memory used by an attribute definition
1578 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001579static void
Owen Taylor3473f882001-02-23 17:55:21 +00001580xmlFreeAttribute(xmlAttributePtr attr) {
1581 if (attr == NULL) return;
1582 xmlUnlinkNode((xmlNodePtr) attr);
1583 if (attr->tree != NULL)
1584 xmlFreeEnumeration(attr->tree);
1585 if (attr->elem != NULL)
1586 xmlFree((xmlChar *) attr->elem);
1587 if (attr->name != NULL)
1588 xmlFree((xmlChar *) attr->name);
1589 if (attr->defaultValue != NULL)
1590 xmlFree((xmlChar *) attr->defaultValue);
1591 if (attr->prefix != NULL)
1592 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001593 xmlFree(attr);
1594}
1595
1596
1597/**
1598 * xmlAddAttributeDecl:
1599 * @ctxt: the validation context
1600 * @dtd: pointer to the DTD
1601 * @elem: the element name
1602 * @name: the attribute name
1603 * @ns: the attribute namespace prefix
1604 * @type: the attribute type
1605 * @def: the attribute default type
1606 * @defaultValue: the attribute default value
1607 * @tree: if it's an enumeration, the associated list
1608 *
1609 * Register a new attribute declaration
1610 * Note that @tree becomes the ownership of the DTD
1611 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001612 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001613 */
1614xmlAttributePtr
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001615xmlAddAttributeDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
1616 xmlDtdPtr dtd, const xmlChar *elem,
Owen Taylor3473f882001-02-23 17:55:21 +00001617 const xmlChar *name, const xmlChar *ns,
1618 xmlAttributeType type, xmlAttributeDefault def,
1619 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1620 xmlAttributePtr ret;
1621 xmlAttributeTablePtr table;
1622 xmlElementPtr elemDef;
1623
1624 if (dtd == NULL) {
1625 xmlGenericError(xmlGenericErrorContext,
1626 "xmlAddAttributeDecl: dtd == NULL\n");
1627 xmlFreeEnumeration(tree);
1628 return(NULL);
1629 }
1630 if (name == NULL) {
1631 xmlGenericError(xmlGenericErrorContext,
1632 "xmlAddAttributeDecl: name == NULL\n");
1633 xmlFreeEnumeration(tree);
1634 return(NULL);
1635 }
1636 if (elem == NULL) {
1637 xmlGenericError(xmlGenericErrorContext,
1638 "xmlAddAttributeDecl: elem == NULL\n");
1639 xmlFreeEnumeration(tree);
1640 return(NULL);
1641 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001642
Daniel Veillard4432df22003-09-28 18:58:27 +00001643#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001644 /*
1645 * Check the type and possibly the default value.
1646 */
1647 switch (type) {
1648 case XML_ATTRIBUTE_CDATA:
1649 break;
1650 case XML_ATTRIBUTE_ID:
1651 break;
1652 case XML_ATTRIBUTE_IDREF:
1653 break;
1654 case XML_ATTRIBUTE_IDREFS:
1655 break;
1656 case XML_ATTRIBUTE_ENTITY:
1657 break;
1658 case XML_ATTRIBUTE_ENTITIES:
1659 break;
1660 case XML_ATTRIBUTE_NMTOKEN:
1661 break;
1662 case XML_ATTRIBUTE_NMTOKENS:
1663 break;
1664 case XML_ATTRIBUTE_ENUMERATION:
1665 break;
1666 case XML_ATTRIBUTE_NOTATION:
1667 break;
1668 default:
1669 xmlGenericError(xmlGenericErrorContext,
1670 "xmlAddAttributeDecl: unknown type %d\n", type);
1671 xmlFreeEnumeration(tree);
1672 return(NULL);
1673 }
1674 if ((defaultValue != NULL) &&
1675 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001676 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001677 elem, name, defaultValue);
1678 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001679 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001680 }
Daniel Veillard4432df22003-09-28 18:58:27 +00001681#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001682
1683 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001684 * Check first that an attribute defined in the external subset wasn't
1685 * already defined in the internal subset
1686 */
1687 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1688 (dtd->doc->intSubset != NULL) &&
1689 (dtd->doc->intSubset->attributes != NULL)) {
1690 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1691 if (ret != NULL)
1692 return(NULL);
1693 }
1694
1695 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001696 * Create the Attribute table if needed.
1697 */
1698 table = (xmlAttributeTablePtr) dtd->attributes;
1699 if (table == NULL) {
1700 table = xmlCreateAttributeTable();
1701 dtd->attributes = (void *) table;
1702 }
1703 if (table == NULL) {
1704 xmlGenericError(xmlGenericErrorContext,
1705 "xmlAddAttributeDecl: Table creation failed!\n");
1706 return(NULL);
1707 }
1708
1709
1710 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1711 if (ret == NULL) {
1712 xmlGenericError(xmlGenericErrorContext,
1713 "xmlAddAttributeDecl: out of memory\n");
1714 return(NULL);
1715 }
1716 memset(ret, 0, sizeof(xmlAttribute));
1717 ret->type = XML_ATTRIBUTE_DECL;
1718
1719 /*
1720 * fill the structure.
1721 */
1722 ret->atype = type;
1723 ret->name = xmlStrdup(name);
1724 ret->prefix = xmlStrdup(ns);
1725 ret->elem = xmlStrdup(elem);
1726 ret->def = def;
1727 ret->tree = tree;
1728 if (defaultValue != NULL)
1729 ret->defaultValue = xmlStrdup(defaultValue);
1730
1731 /*
1732 * Validity Check:
1733 * Search the DTD for previous declarations of the ATTLIST
1734 */
1735 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00001736#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001737 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001738 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001739 */
1740 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001741 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001742 name, elem);
Daniel Veillard4432df22003-09-28 18:58:27 +00001743#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001744 xmlFreeAttribute(ret);
1745 return(NULL);
1746 }
1747
1748 /*
1749 * Validity Check:
1750 * Multiple ID per element
1751 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001752 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001753 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001754
Daniel Veillard4432df22003-09-28 18:58:27 +00001755#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001756 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001757 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001758 VERROR(ctxt->userData,
1759 "Element %s has too may ID attributes defined : %s\n",
1760 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001761 ctxt->valid = 0;
1762 }
Daniel Veillard4432df22003-09-28 18:58:27 +00001763#endif /* LIBXML_VALID_ENABLED */
Daniel Veillardc7612992002-02-17 22:47:37 +00001764
Daniel Veillard48da9102001-08-07 01:10:10 +00001765 /*
1766 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001767 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001768 */
1769 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1770 ((ret->prefix != NULL &&
1771 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1772 ret->nexth = elemDef->attributes;
1773 elemDef->attributes = ret;
1774 } else {
1775 xmlAttributePtr tmp = elemDef->attributes;
1776
1777 while ((tmp != NULL) &&
1778 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1779 ((ret->prefix != NULL &&
1780 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1781 if (tmp->nexth == NULL)
1782 break;
1783 tmp = tmp->nexth;
1784 }
1785 if (tmp != NULL) {
1786 ret->nexth = tmp->nexth;
1787 tmp->nexth = ret;
1788 } else {
1789 ret->nexth = elemDef->attributes;
1790 elemDef->attributes = ret;
1791 }
1792 }
Owen Taylor3473f882001-02-23 17:55:21 +00001793 }
1794
1795 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001796 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001797 */
1798 ret->parent = dtd;
1799 ret->doc = dtd->doc;
1800 if (dtd->last == NULL) {
1801 dtd->children = dtd->last = (xmlNodePtr) ret;
1802 } else {
1803 dtd->last->next = (xmlNodePtr) ret;
1804 ret->prev = dtd->last;
1805 dtd->last = (xmlNodePtr) ret;
1806 }
1807 return(ret);
1808}
1809
1810/**
1811 * xmlFreeAttributeTable:
1812 * @table: An attribute table
1813 *
1814 * Deallocate the memory used by an entities hash table.
1815 */
1816void
1817xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1818 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1819}
1820
1821/**
1822 * xmlCopyAttribute:
1823 * @attr: An attribute
1824 *
1825 * Build a copy of an attribute.
1826 *
1827 * Returns the new xmlAttributePtr or NULL in case of error.
1828 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001829static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001830xmlCopyAttribute(xmlAttributePtr attr) {
1831 xmlAttributePtr cur;
1832
1833 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1834 if (cur == NULL) {
1835 xmlGenericError(xmlGenericErrorContext,
1836 "xmlCopyAttribute: out of memory !\n");
1837 return(NULL);
1838 }
1839 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001840 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001841 cur->atype = attr->atype;
1842 cur->def = attr->def;
1843 cur->tree = xmlCopyEnumeration(attr->tree);
1844 if (attr->elem != NULL)
1845 cur->elem = xmlStrdup(attr->elem);
1846 if (attr->name != NULL)
1847 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001848 if (attr->prefix != NULL)
1849 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001850 if (attr->defaultValue != NULL)
1851 cur->defaultValue = xmlStrdup(attr->defaultValue);
1852 return(cur);
1853}
1854
1855/**
1856 * xmlCopyAttributeTable:
1857 * @table: An attribute table
1858 *
1859 * Build a copy of an attribute table.
1860 *
1861 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1862 */
1863xmlAttributeTablePtr
1864xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1865 return((xmlAttributeTablePtr) xmlHashCopy(table,
1866 (xmlHashCopier) xmlCopyAttribute));
1867}
1868
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001869#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00001870/**
1871 * xmlDumpAttributeDecl:
1872 * @buf: the XML buffer output
1873 * @attr: An attribute declaration
1874 *
1875 * This will dump the content of the attribute declaration as an XML
1876 * DTD definition
1877 */
1878void
1879xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1880 xmlBufferWriteChar(buf, "<!ATTLIST ");
1881 xmlBufferWriteCHAR(buf, attr->elem);
1882 xmlBufferWriteChar(buf, " ");
1883 if (attr->prefix != NULL) {
1884 xmlBufferWriteCHAR(buf, attr->prefix);
1885 xmlBufferWriteChar(buf, ":");
1886 }
1887 xmlBufferWriteCHAR(buf, attr->name);
1888 switch (attr->atype) {
1889 case XML_ATTRIBUTE_CDATA:
1890 xmlBufferWriteChar(buf, " CDATA");
1891 break;
1892 case XML_ATTRIBUTE_ID:
1893 xmlBufferWriteChar(buf, " ID");
1894 break;
1895 case XML_ATTRIBUTE_IDREF:
1896 xmlBufferWriteChar(buf, " IDREF");
1897 break;
1898 case XML_ATTRIBUTE_IDREFS:
1899 xmlBufferWriteChar(buf, " IDREFS");
1900 break;
1901 case XML_ATTRIBUTE_ENTITY:
1902 xmlBufferWriteChar(buf, " ENTITY");
1903 break;
1904 case XML_ATTRIBUTE_ENTITIES:
1905 xmlBufferWriteChar(buf, " ENTITIES");
1906 break;
1907 case XML_ATTRIBUTE_NMTOKEN:
1908 xmlBufferWriteChar(buf, " NMTOKEN");
1909 break;
1910 case XML_ATTRIBUTE_NMTOKENS:
1911 xmlBufferWriteChar(buf, " NMTOKENS");
1912 break;
1913 case XML_ATTRIBUTE_ENUMERATION:
1914 xmlBufferWriteChar(buf, " (");
1915 xmlDumpEnumeration(buf, attr->tree);
1916 break;
1917 case XML_ATTRIBUTE_NOTATION:
1918 xmlBufferWriteChar(buf, " NOTATION (");
1919 xmlDumpEnumeration(buf, attr->tree);
1920 break;
1921 default:
1922 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001923 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001924 attr->atype);
1925 }
1926 switch (attr->def) {
1927 case XML_ATTRIBUTE_NONE:
1928 break;
1929 case XML_ATTRIBUTE_REQUIRED:
1930 xmlBufferWriteChar(buf, " #REQUIRED");
1931 break;
1932 case XML_ATTRIBUTE_IMPLIED:
1933 xmlBufferWriteChar(buf, " #IMPLIED");
1934 break;
1935 case XML_ATTRIBUTE_FIXED:
1936 xmlBufferWriteChar(buf, " #FIXED");
1937 break;
1938 default:
1939 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001940 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001941 attr->def);
1942 }
1943 if (attr->defaultValue != NULL) {
1944 xmlBufferWriteChar(buf, " ");
1945 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1946 }
1947 xmlBufferWriteChar(buf, ">\n");
1948}
1949
1950/**
1951 * xmlDumpAttributeTable:
1952 * @buf: the XML buffer output
1953 * @table: An attribute table
1954 *
1955 * This will dump the content of the attribute table as an XML DTD definition
1956 */
1957void
1958xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1959 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1960}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00001961#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00001962
1963/************************************************************************
1964 * *
1965 * NOTATIONs *
1966 * *
1967 ************************************************************************/
1968/**
1969 * xmlCreateNotationTable:
1970 *
1971 * create and initialize an empty notation hash table.
1972 *
1973 * Returns the xmlNotationTablePtr just created or NULL in case
1974 * of error.
1975 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001976static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001977xmlCreateNotationTable(void) {
1978 return(xmlHashCreate(0));
1979}
1980
1981/**
1982 * xmlFreeNotation:
1983 * @not: A notation
1984 *
1985 * Deallocate the memory used by an notation definition
1986 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001987static void
Owen Taylor3473f882001-02-23 17:55:21 +00001988xmlFreeNotation(xmlNotationPtr nota) {
1989 if (nota == NULL) return;
1990 if (nota->name != NULL)
1991 xmlFree((xmlChar *) nota->name);
1992 if (nota->PublicID != NULL)
1993 xmlFree((xmlChar *) nota->PublicID);
1994 if (nota->SystemID != NULL)
1995 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001996 xmlFree(nota);
1997}
1998
1999
2000/**
2001 * xmlAddNotationDecl:
2002 * @dtd: pointer to the DTD
2003 * @ctxt: the validation context
2004 * @name: the entity name
2005 * @PublicID: the public identifier or NULL
2006 * @SystemID: the system identifier or NULL
2007 *
2008 * Register a new notation declaration
2009 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002010 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00002011 */
2012xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002013xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002014 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00002015 const xmlChar *PublicID, const xmlChar *SystemID) {
2016 xmlNotationPtr ret;
2017 xmlNotationTablePtr table;
2018
2019 if (dtd == NULL) {
2020 xmlGenericError(xmlGenericErrorContext,
2021 "xmlAddNotationDecl: dtd == NULL\n");
2022 return(NULL);
2023 }
2024 if (name == NULL) {
2025 xmlGenericError(xmlGenericErrorContext,
2026 "xmlAddNotationDecl: name == NULL\n");
2027 return(NULL);
2028 }
2029 if ((PublicID == NULL) && (SystemID == NULL)) {
2030 xmlGenericError(xmlGenericErrorContext,
2031 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002032 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002033 }
2034
2035 /*
2036 * Create the Notation table if needed.
2037 */
2038 table = (xmlNotationTablePtr) dtd->notations;
2039 if (table == NULL)
2040 dtd->notations = table = xmlCreateNotationTable();
2041 if (table == NULL) {
2042 xmlGenericError(xmlGenericErrorContext,
2043 "xmlAddNotationDecl: Table creation failed!\n");
2044 return(NULL);
2045 }
2046
2047 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2048 if (ret == NULL) {
2049 xmlGenericError(xmlGenericErrorContext,
2050 "xmlAddNotationDecl: out of memory\n");
2051 return(NULL);
2052 }
2053 memset(ret, 0, sizeof(xmlNotation));
2054
2055 /*
2056 * fill the structure.
2057 */
2058 ret->name = xmlStrdup(name);
2059 if (SystemID != NULL)
2060 ret->SystemID = xmlStrdup(SystemID);
2061 if (PublicID != NULL)
2062 ret->PublicID = xmlStrdup(PublicID);
2063
2064 /*
2065 * Validity Check:
2066 * Check the DTD for previous declarations of the ATTLIST
2067 */
2068 if (xmlHashAddEntry(table, name, ret)) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002069#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002070 xmlGenericError(xmlGenericErrorContext,
2071 "xmlAddNotationDecl: %s already defined\n", name);
Daniel Veillard4432df22003-09-28 18:58:27 +00002072#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002073 xmlFreeNotation(ret);
2074 return(NULL);
2075 }
2076 return(ret);
2077}
2078
2079/**
2080 * xmlFreeNotationTable:
2081 * @table: An notation table
2082 *
2083 * Deallocate the memory used by an entities hash table.
2084 */
2085void
2086xmlFreeNotationTable(xmlNotationTablePtr table) {
2087 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2088}
2089
2090/**
2091 * xmlCopyNotation:
2092 * @nota: A notation
2093 *
2094 * Build a copy of a notation.
2095 *
2096 * Returns the new xmlNotationPtr or NULL in case of error.
2097 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002098static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002099xmlCopyNotation(xmlNotationPtr nota) {
2100 xmlNotationPtr cur;
2101
2102 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2103 if (cur == NULL) {
2104 xmlGenericError(xmlGenericErrorContext,
2105 "xmlCopyNotation: out of memory !\n");
2106 return(NULL);
2107 }
2108 if (nota->name != NULL)
2109 cur->name = xmlStrdup(nota->name);
2110 else
2111 cur->name = NULL;
2112 if (nota->PublicID != NULL)
2113 cur->PublicID = xmlStrdup(nota->PublicID);
2114 else
2115 cur->PublicID = NULL;
2116 if (nota->SystemID != NULL)
2117 cur->SystemID = xmlStrdup(nota->SystemID);
2118 else
2119 cur->SystemID = NULL;
2120 return(cur);
2121}
2122
2123/**
2124 * xmlCopyNotationTable:
2125 * @table: A notation table
2126 *
2127 * Build a copy of a notation table.
2128 *
2129 * Returns the new xmlNotationTablePtr or NULL in case of error.
2130 */
2131xmlNotationTablePtr
2132xmlCopyNotationTable(xmlNotationTablePtr table) {
2133 return((xmlNotationTablePtr) xmlHashCopy(table,
2134 (xmlHashCopier) xmlCopyNotation));
2135}
2136
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002137#ifdef LIBXML_OUTPUT_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002138/**
2139 * xmlDumpNotationDecl:
2140 * @buf: the XML buffer output
2141 * @nota: A notation declaration
2142 *
2143 * This will dump the content the notation declaration as an XML DTD definition
2144 */
2145void
2146xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2147 xmlBufferWriteChar(buf, "<!NOTATION ");
2148 xmlBufferWriteCHAR(buf, nota->name);
2149 if (nota->PublicID != NULL) {
2150 xmlBufferWriteChar(buf, " PUBLIC ");
2151 xmlBufferWriteQuotedString(buf, nota->PublicID);
2152 if (nota->SystemID != NULL) {
2153 xmlBufferWriteChar(buf, " ");
2154 xmlBufferWriteCHAR(buf, nota->SystemID);
2155 }
2156 } else {
2157 xmlBufferWriteChar(buf, " SYSTEM ");
2158 xmlBufferWriteCHAR(buf, nota->SystemID);
2159 }
2160 xmlBufferWriteChar(buf, " >\n");
2161}
2162
2163/**
2164 * xmlDumpNotationTable:
2165 * @buf: the XML buffer output
2166 * @table: A notation table
2167 *
2168 * This will dump the content of the notation table as an XML DTD definition
2169 */
2170void
2171xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2172 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2173}
Daniel Veillarda9cce9c2003-09-29 13:20:24 +00002174#endif /* LIBXML_OUTPUT_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002175
2176/************************************************************************
2177 * *
2178 * IDs *
2179 * *
2180 ************************************************************************/
2181/**
2182 * xmlCreateIDTable:
2183 *
2184 * create and initialize an empty id hash table.
2185 *
2186 * Returns the xmlIDTablePtr just created or NULL in case
2187 * of error.
2188 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002189static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002190xmlCreateIDTable(void) {
2191 return(xmlHashCreate(0));
2192}
2193
2194/**
2195 * xmlFreeID:
2196 * @not: A id
2197 *
2198 * Deallocate the memory used by an id definition
2199 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002200static void
Owen Taylor3473f882001-02-23 17:55:21 +00002201xmlFreeID(xmlIDPtr id) {
2202 if (id == NULL) return;
2203 if (id->value != NULL)
2204 xmlFree((xmlChar *) id->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002205 if (id->name != NULL)
2206 xmlFree((xmlChar *) id->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002207 xmlFree(id);
2208}
2209
2210/**
2211 * xmlAddID:
2212 * @ctxt: the validation context
2213 * @doc: pointer to the document
2214 * @value: the value name
2215 * @attr: the attribute holding the ID
2216 *
2217 * Register a new id declaration
2218 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002219 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002220 */
2221xmlIDPtr
2222xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2223 xmlAttrPtr attr) {
2224 xmlIDPtr ret;
2225 xmlIDTablePtr table;
2226
2227 if (doc == NULL) {
2228 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002229 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002230 return(NULL);
2231 }
2232 if (value == NULL) {
2233 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002234 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002235 return(NULL);
2236 }
2237 if (attr == NULL) {
2238 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002239 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002240 return(NULL);
2241 }
2242
2243 /*
2244 * Create the ID table if needed.
2245 */
2246 table = (xmlIDTablePtr) doc->ids;
2247 if (table == NULL)
2248 doc->ids = table = xmlCreateIDTable();
2249 if (table == NULL) {
2250 xmlGenericError(xmlGenericErrorContext,
2251 "xmlAddID: Table creation failed!\n");
2252 return(NULL);
2253 }
2254
2255 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2256 if (ret == NULL) {
2257 xmlGenericError(xmlGenericErrorContext,
2258 "xmlAddID: out of memory\n");
2259 return(NULL);
2260 }
2261
2262 /*
2263 * fill the structure.
2264 */
2265 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002266 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2267 /*
2268 * Operating in streaming mode, attr is gonna disapear
2269 */
2270 ret->name = xmlStrdup(attr->name);
2271 ret->attr = NULL;
2272 } else {
2273 ret->attr = attr;
2274 ret->name = NULL;
2275 }
2276 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002277
2278 if (xmlHashAddEntry(table, value, ret) < 0) {
Daniel Veillard4432df22003-09-28 18:58:27 +00002279#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002280 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002281 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002282 */
Daniel Veillard76575762002-09-05 14:21:15 +00002283 if (ctxt != NULL) {
2284 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002285 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002286 }
Daniel Veillard4432df22003-09-28 18:58:27 +00002287#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002288 xmlFreeID(ret);
2289 return(NULL);
2290 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002291 if (attr != NULL)
2292 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002293 return(ret);
2294}
2295
2296/**
2297 * xmlFreeIDTable:
2298 * @table: An id table
2299 *
2300 * Deallocate the memory used by an ID hash table.
2301 */
2302void
2303xmlFreeIDTable(xmlIDTablePtr table) {
2304 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2305}
2306
2307/**
2308 * xmlIsID:
2309 * @doc: the document
2310 * @elem: the element carrying the attribute
2311 * @attr: the attribute
2312 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002313 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002314 * then this is done if DTD loading has been requested. In the case
2315 * of HTML documents parsed with the HTML parser, then ID detection is
2316 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002317 *
2318 * Returns 0 or 1 depending on the lookup result
2319 */
2320int
2321xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2322 if (doc == NULL) return(0);
2323 if (attr == NULL) return(0);
2324 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2325 return(0);
2326 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2327 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2328 (xmlStrEqual(BAD_CAST "name", attr->name)))
2329 return(1);
2330 return(0);
2331 } else {
2332 xmlAttributePtr attrDecl;
2333
2334 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002335 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00002336 xmlChar fn[50];
Daniel Veillard37f961d2002-07-06 17:53:56 +00002337 xmlChar *fullname;
Daniel Veillardc00cda82003-04-07 10:22:39 +00002338
2339 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002340 if (fullname == NULL)
2341 return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002342 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2343 attr->name);
2344 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2345 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2346 attr->name);
Daniel Veillardc00cda82003-04-07 10:22:39 +00002347 if ((fullname != fn) && (fullname != elem->name))
2348 xmlFree(fullname);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002349 } else {
2350 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2351 attr->name);
2352 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2353 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2354 attr->name);
2355 }
Owen Taylor3473f882001-02-23 17:55:21 +00002356
2357 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2358 return(1);
2359 }
2360 return(0);
2361}
2362
2363/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002364 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002365 * @doc: the document
2366 * @attr: the attribute
2367 *
2368 * Remove the given attribute from the ID table maintained internally.
2369 *
2370 * Returns -1 if the lookup failed and 0 otherwise
2371 */
2372int
2373xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2374 xmlAttrPtr cur;
2375 xmlIDTablePtr table;
2376 xmlChar *ID;
2377
2378 if (doc == NULL) return(-1);
2379 if (attr == NULL) return(-1);
2380 table = (xmlIDTablePtr) doc->ids;
2381 if (table == NULL)
2382 return(-1);
2383
2384 if (attr == NULL)
2385 return(-1);
2386 ID = xmlNodeListGetString(doc, attr->children, 1);
2387 if (ID == NULL)
2388 return(-1);
2389 cur = xmlHashLookup(table, ID);
2390 if (cur != attr) {
2391 xmlFree(ID);
2392 return(-1);
2393 }
2394 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2395 xmlFree(ID);
2396 return(0);
2397}
2398
2399/**
2400 * xmlGetID:
2401 * @doc: pointer to the document
2402 * @ID: the ID value
2403 *
2404 * Search the attribute declaring the given ID
2405 *
2406 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2407 */
2408xmlAttrPtr
2409xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2410 xmlIDTablePtr table;
2411 xmlIDPtr id;
2412
2413 if (doc == NULL) {
2414 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2415 return(NULL);
2416 }
2417
2418 if (ID == NULL) {
2419 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2420 return(NULL);
2421 }
2422
2423 table = (xmlIDTablePtr) doc->ids;
2424 if (table == NULL)
2425 return(NULL);
2426
2427 id = xmlHashLookup(table, ID);
2428 if (id == NULL)
2429 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002430 if (id->attr == NULL) {
2431 /*
2432 * We are operating on a stream, return a well known reference
2433 * since the attribute node doesn't exist anymore
2434 */
2435 return((xmlAttrPtr) doc);
2436 }
Owen Taylor3473f882001-02-23 17:55:21 +00002437 return(id->attr);
2438}
2439
2440/************************************************************************
2441 * *
2442 * Refs *
2443 * *
2444 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002445typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002446{
2447 xmlListPtr l;
2448 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002449} xmlRemoveMemo;
2450
2451typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2452
2453typedef struct xmlValidateMemo_t
2454{
2455 xmlValidCtxtPtr ctxt;
2456 const xmlChar *name;
2457} xmlValidateMemo;
2458
2459typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002460
2461/**
2462 * xmlCreateRefTable:
2463 *
2464 * create and initialize an empty ref hash table.
2465 *
2466 * Returns the xmlRefTablePtr just created or NULL in case
2467 * of error.
2468 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002469static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002470xmlCreateRefTable(void) {
2471 return(xmlHashCreate(0));
2472}
2473
2474/**
2475 * xmlFreeRef:
2476 * @lk: A list link
2477 *
2478 * Deallocate the memory used by a ref definition
2479 */
2480static void
2481xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002482 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2483 if (ref == NULL) return;
2484 if (ref->value != NULL)
2485 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002486 if (ref->name != NULL)
2487 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002488 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002489}
2490
2491/**
2492 * xmlFreeRefList:
2493 * @list_ref: A list of references.
2494 *
2495 * Deallocate the memory used by a list of references
2496 */
2497static void
2498xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002499 if (list_ref == NULL) return;
2500 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002501}
2502
2503/**
2504 * xmlWalkRemoveRef:
2505 * @data: Contents of current link
2506 * @user: Value supplied by the user
2507 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002508 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002509 */
2510static int
2511xmlWalkRemoveRef(const void *data, const void *user)
2512{
Daniel Veillard37721922001-05-04 15:21:12 +00002513 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2514 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2515 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002516
Daniel Veillard37721922001-05-04 15:21:12 +00002517 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2518 xmlListRemoveFirst(ref_list, (void *)data);
2519 return 0;
2520 }
2521 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002522}
2523
2524/**
2525 * xmlAddRef:
2526 * @ctxt: the validation context
2527 * @doc: pointer to the document
2528 * @value: the value name
2529 * @attr: the attribute holding the Ref
2530 *
2531 * Register a new ref declaration
2532 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002533 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002534 */
2535xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002536xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002537 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002538 xmlRefPtr ret;
2539 xmlRefTablePtr table;
2540 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002541
Daniel Veillard37721922001-05-04 15:21:12 +00002542 if (doc == NULL) {
2543 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002544 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002545 return(NULL);
2546 }
2547 if (value == NULL) {
2548 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002549 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002550 return(NULL);
2551 }
2552 if (attr == NULL) {
2553 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002554 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002555 return(NULL);
2556 }
Owen Taylor3473f882001-02-23 17:55:21 +00002557
Daniel Veillard37721922001-05-04 15:21:12 +00002558 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002559 * Create the Ref table if needed.
2560 */
Daniel Veillard37721922001-05-04 15:21:12 +00002561 table = (xmlRefTablePtr) doc->refs;
2562 if (table == NULL)
2563 doc->refs = table = xmlCreateRefTable();
2564 if (table == NULL) {
2565 xmlGenericError(xmlGenericErrorContext,
2566 "xmlAddRef: Table creation failed!\n");
2567 return(NULL);
2568 }
Owen Taylor3473f882001-02-23 17:55:21 +00002569
Daniel Veillard37721922001-05-04 15:21:12 +00002570 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2571 if (ret == NULL) {
2572 xmlGenericError(xmlGenericErrorContext,
2573 "xmlAddRef: out of memory\n");
2574 return(NULL);
2575 }
Owen Taylor3473f882001-02-23 17:55:21 +00002576
Daniel Veillard37721922001-05-04 15:21:12 +00002577 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002578 * fill the structure.
2579 */
Daniel Veillard37721922001-05-04 15:21:12 +00002580 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002581 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2582 /*
2583 * Operating in streaming mode, attr is gonna disapear
2584 */
2585 ret->name = xmlStrdup(attr->name);
2586 ret->attr = NULL;
2587 } else {
2588 ret->name = NULL;
2589 ret->attr = attr;
2590 }
2591 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002592
Daniel Veillard37721922001-05-04 15:21:12 +00002593 /* To add a reference :-
2594 * References are maintained as a list of references,
2595 * Lookup the entry, if no entry create new nodelist
2596 * Add the owning node to the NodeList
2597 * Return the ref
2598 */
Owen Taylor3473f882001-02-23 17:55:21 +00002599
Daniel Veillard37721922001-05-04 15:21:12 +00002600 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2601 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2602 xmlGenericError(xmlGenericErrorContext,
2603 "xmlAddRef: Reference list creation failed!\n");
2604 return(NULL);
2605 }
2606 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2607 xmlListDelete(ref_list);
2608 xmlGenericError(xmlGenericErrorContext,
2609 "xmlAddRef: Reference list insertion failed!\n");
2610 return(NULL);
2611 }
2612 }
2613 xmlListInsert(ref_list, ret);
2614 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002615}
2616
2617/**
2618 * xmlFreeRefTable:
2619 * @table: An ref table
2620 *
2621 * Deallocate the memory used by an Ref hash table.
2622 */
2623void
2624xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002625 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002626}
2627
2628/**
2629 * xmlIsRef:
2630 * @doc: the document
2631 * @elem: the element carrying the attribute
2632 * @attr: the attribute
2633 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002634 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002635 * then this is simple, otherwise we use an heuristic: name Ref (upper
2636 * or lowercase).
2637 *
2638 * Returns 0 or 1 depending on the lookup result
2639 */
2640int
2641xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002642 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2643 return(0);
2644 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2645 /* TODO @@@ */
2646 return(0);
2647 } else {
2648 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002649
Daniel Veillard37721922001-05-04 15:21:12 +00002650 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2651 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2652 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2653 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002654
Daniel Veillard37721922001-05-04 15:21:12 +00002655 if ((attrDecl != NULL) &&
2656 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2657 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2658 return(1);
2659 }
2660 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002661}
2662
2663/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002664 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002665 * @doc: the document
2666 * @attr: the attribute
2667 *
2668 * Remove the given attribute from the Ref table maintained internally.
2669 *
2670 * Returns -1 if the lookup failed and 0 otherwise
2671 */
2672int
2673xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002674 xmlListPtr ref_list;
2675 xmlRefTablePtr table;
2676 xmlChar *ID;
2677 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002678
Daniel Veillard37721922001-05-04 15:21:12 +00002679 if (doc == NULL) return(-1);
2680 if (attr == NULL) return(-1);
2681 table = (xmlRefTablePtr) doc->refs;
2682 if (table == NULL)
2683 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002684
Daniel Veillard37721922001-05-04 15:21:12 +00002685 if (attr == NULL)
2686 return(-1);
2687 ID = xmlNodeListGetString(doc, attr->children, 1);
2688 if (ID == NULL)
2689 return(-1);
2690 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002691
Daniel Veillard37721922001-05-04 15:21:12 +00002692 if(ref_list == NULL) {
2693 xmlFree(ID);
2694 return (-1);
2695 }
2696 /* At this point, ref_list refers to a list of references which
2697 * have the same key as the supplied attr. Our list of references
2698 * is ordered by reference address and we don't have that information
2699 * here to use when removing. We'll have to walk the list and
2700 * check for a matching attribute, when we find one stop the walk
2701 * and remove the entry.
2702 * The list is ordered by reference, so that means we don't have the
2703 * key. Passing the list and the reference to the walker means we
2704 * will have enough data to be able to remove the entry.
2705 */
2706 target.l = ref_list;
2707 target.ap = attr;
2708
2709 /* Remove the supplied attr from our list */
2710 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002711
Daniel Veillard37721922001-05-04 15:21:12 +00002712 /*If the list is empty then remove the list entry in the hash */
2713 if (xmlListEmpty(ref_list))
2714 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2715 xmlFreeRefList);
2716 xmlFree(ID);
2717 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002718}
2719
2720/**
2721 * xmlGetRefs:
2722 * @doc: pointer to the document
2723 * @ID: the ID value
2724 *
2725 * Find the set of references for the supplied ID.
2726 *
2727 * Returns NULL if not found, otherwise node set for the ID.
2728 */
2729xmlListPtr
2730xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002731 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002732
Daniel Veillard37721922001-05-04 15:21:12 +00002733 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002734 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002735 return(NULL);
2736 }
Owen Taylor3473f882001-02-23 17:55:21 +00002737
Daniel Veillard37721922001-05-04 15:21:12 +00002738 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002739 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002740 return(NULL);
2741 }
Owen Taylor3473f882001-02-23 17:55:21 +00002742
Daniel Veillard37721922001-05-04 15:21:12 +00002743 table = (xmlRefTablePtr) doc->refs;
2744 if (table == NULL)
2745 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002746
Daniel Veillard37721922001-05-04 15:21:12 +00002747 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002748}
2749
2750/************************************************************************
2751 * *
2752 * Routines for validity checking *
2753 * *
2754 ************************************************************************/
2755
2756/**
2757 * xmlGetDtdElementDesc:
2758 * @dtd: a pointer to the DtD to search
2759 * @name: the element name
2760 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002761 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002762 *
2763 * returns the xmlElementPtr if found or NULL
2764 */
2765
2766xmlElementPtr
2767xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2768 xmlElementTablePtr table;
2769 xmlElementPtr cur;
2770 xmlChar *uqname = NULL, *prefix = NULL;
2771
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00002772 if ((dtd == NULL) || (name == NULL)) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002773 if (dtd->elements == NULL)
2774 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002775 table = (xmlElementTablePtr) dtd->elements;
2776
2777 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002778 if (uqname != NULL)
2779 name = uqname;
2780 cur = xmlHashLookup2(table, name, prefix);
2781 if (prefix != NULL) xmlFree(prefix);
2782 if (uqname != NULL) xmlFree(uqname);
2783 return(cur);
2784}
2785/**
2786 * xmlGetDtdElementDesc2:
2787 * @dtd: a pointer to the DtD to search
2788 * @name: the element name
2789 * @create: create an empty description if not found
2790 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002791 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002792 *
2793 * returns the xmlElementPtr if found or NULL
2794 */
2795
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002796static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002797xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2798 xmlElementTablePtr table;
2799 xmlElementPtr cur;
2800 xmlChar *uqname = NULL, *prefix = NULL;
2801
2802 if (dtd == NULL) return(NULL);
2803 if (dtd->elements == NULL) {
2804 if (!create)
2805 return(NULL);
2806 /*
2807 * Create the Element table if needed.
2808 */
2809 table = (xmlElementTablePtr) dtd->elements;
2810 if (table == NULL) {
2811 table = xmlCreateElementTable();
2812 dtd->elements = (void *) table;
2813 }
2814 if (table == NULL) {
2815 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002816 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002817 return(NULL);
2818 }
2819 }
2820 table = (xmlElementTablePtr) dtd->elements;
2821
2822 uqname = xmlSplitQName2(name, &prefix);
2823 if (uqname != NULL)
2824 name = uqname;
2825 cur = xmlHashLookup2(table, name, prefix);
2826 if ((cur == NULL) && (create)) {
2827 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2828 if (cur == NULL) {
2829 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002830 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002831 return(NULL);
2832 }
2833 memset(cur, 0, sizeof(xmlElement));
2834 cur->type = XML_ELEMENT_DECL;
2835
2836 /*
2837 * fill the structure.
2838 */
2839 cur->name = xmlStrdup(name);
2840 cur->prefix = xmlStrdup(prefix);
2841 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2842
2843 xmlHashAddEntry2(table, name, prefix, cur);
2844 }
2845 if (prefix != NULL) xmlFree(prefix);
2846 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002847 return(cur);
2848}
2849
2850/**
2851 * xmlGetDtdQElementDesc:
2852 * @dtd: a pointer to the DtD to search
2853 * @name: the element name
2854 * @prefix: the element namespace prefix
2855 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002856 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002857 *
2858 * returns the xmlElementPtr if found or NULL
2859 */
2860
Daniel Veillard48da9102001-08-07 01:10:10 +00002861xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002862xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2863 const xmlChar *prefix) {
2864 xmlElementTablePtr table;
2865
2866 if (dtd == NULL) return(NULL);
2867 if (dtd->elements == NULL) return(NULL);
2868 table = (xmlElementTablePtr) dtd->elements;
2869
2870 return(xmlHashLookup2(table, name, prefix));
2871}
2872
2873/**
2874 * xmlGetDtdAttrDesc:
2875 * @dtd: a pointer to the DtD to search
2876 * @elem: the element name
2877 * @name: the attribute name
2878 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002879 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002880 * this element.
2881 *
2882 * returns the xmlAttributePtr if found or NULL
2883 */
2884
2885xmlAttributePtr
2886xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2887 xmlAttributeTablePtr table;
2888 xmlAttributePtr cur;
2889 xmlChar *uqname = NULL, *prefix = NULL;
2890
2891 if (dtd == NULL) return(NULL);
2892 if (dtd->attributes == NULL) return(NULL);
2893
2894 table = (xmlAttributeTablePtr) dtd->attributes;
2895 if (table == NULL)
2896 return(NULL);
2897
2898 uqname = xmlSplitQName2(name, &prefix);
2899
2900 if (uqname != NULL) {
2901 cur = xmlHashLookup3(table, uqname, prefix, elem);
2902 if (prefix != NULL) xmlFree(prefix);
2903 if (uqname != NULL) xmlFree(uqname);
2904 } else
2905 cur = xmlHashLookup3(table, name, NULL, elem);
2906 return(cur);
2907}
2908
2909/**
2910 * xmlGetDtdQAttrDesc:
2911 * @dtd: a pointer to the DtD to search
2912 * @elem: the element name
2913 * @name: the attribute name
2914 * @prefix: the attribute namespace prefix
2915 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002916 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002917 * this element.
2918 *
2919 * returns the xmlAttributePtr if found or NULL
2920 */
2921
Daniel Veillard48da9102001-08-07 01:10:10 +00002922xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002923xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2924 const xmlChar *prefix) {
2925 xmlAttributeTablePtr table;
2926
2927 if (dtd == NULL) return(NULL);
2928 if (dtd->attributes == NULL) return(NULL);
2929 table = (xmlAttributeTablePtr) dtd->attributes;
2930
2931 return(xmlHashLookup3(table, name, prefix, elem));
2932}
2933
2934/**
2935 * xmlGetDtdNotationDesc:
2936 * @dtd: a pointer to the DtD to search
2937 * @name: the notation name
2938 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002939 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002940 *
2941 * returns the xmlNotationPtr if found or NULL
2942 */
2943
2944xmlNotationPtr
2945xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2946 xmlNotationTablePtr table;
2947
2948 if (dtd == NULL) return(NULL);
2949 if (dtd->notations == NULL) return(NULL);
2950 table = (xmlNotationTablePtr) dtd->notations;
2951
2952 return(xmlHashLookup(table, name));
2953}
2954
Daniel Veillard4432df22003-09-28 18:58:27 +00002955#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00002956/**
2957 * xmlValidateNotationUse:
2958 * @ctxt: the validation context
2959 * @doc: the document
2960 * @notationName: the notation name to check
2961 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002962 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002963 * - [ VC: Notation Declared ]
2964 *
2965 * returns 1 if valid or 0 otherwise
2966 */
2967
2968int
2969xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2970 const xmlChar *notationName) {
2971 xmlNotationPtr notaDecl;
2972 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2973
2974 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2975 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2976 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2977
Daniel Veillarde637c4a2003-03-30 21:10:09 +00002978 if ((notaDecl == NULL) && (ctxt != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00002979 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2980 notationName);
2981 return(0);
2982 }
2983 return(1);
2984}
Daniel Veillard4432df22003-09-28 18:58:27 +00002985#endif /* LIBXML_VALID_ENABLED */
Owen Taylor3473f882001-02-23 17:55:21 +00002986
2987/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002988 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00002989 * @doc: the document
2990 * @name: the element name
2991 *
2992 * Search in the DtDs whether an element accept Mixed content (or ANY)
2993 * basically if it is supposed to accept text childs
2994 *
2995 * returns 0 if no, 1 if yes, and -1 if no element description is available
2996 */
2997
2998int
2999xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
3000 xmlElementPtr elemDecl;
3001
3002 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3003
3004 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
3005 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3006 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
3007 if (elemDecl == NULL) return(-1);
3008 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003009 case XML_ELEMENT_TYPE_UNDEFINED:
3010 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003011 case XML_ELEMENT_TYPE_ELEMENT:
3012 return(0);
3013 case XML_ELEMENT_TYPE_EMPTY:
3014 /*
3015 * return 1 for EMPTY since we want VC error to pop up
3016 * on <empty> </empty> for example
3017 */
3018 case XML_ELEMENT_TYPE_ANY:
3019 case XML_ELEMENT_TYPE_MIXED:
3020 return(1);
3021 }
3022 return(1);
3023}
3024
Daniel Veillard4432df22003-09-28 18:58:27 +00003025#ifdef LIBXML_VALID_ENABLED
Owen Taylor3473f882001-02-23 17:55:21 +00003026/**
3027 * xmlValidateNameValue:
3028 * @value: an Name value
3029 *
3030 * Validate that the given value match Name production
3031 *
3032 * returns 1 if valid or 0 otherwise
3033 */
3034
Daniel Veillard9b731d72002-04-14 12:56:08 +00003035int
Owen Taylor3473f882001-02-23 17:55:21 +00003036xmlValidateNameValue(const xmlChar *value) {
3037 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003038 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003039
3040 if (value == NULL) return(0);
3041 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003042 val = xmlStringCurrentChar(NULL, cur, &len);
3043 cur += len;
3044 if (!IS_LETTER(val) && (val != '_') &&
3045 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003046 return(0);
3047 }
3048
Daniel Veillardd8224e02002-01-13 15:43:22 +00003049 val = xmlStringCurrentChar(NULL, cur, &len);
3050 cur += len;
3051 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3052 (val == '.') || (val == '-') ||
3053 (val == '_') || (val == ':') ||
3054 (IS_COMBINING(val)) ||
3055 (IS_EXTENDER(val))) {
3056 val = xmlStringCurrentChar(NULL, cur, &len);
3057 cur += len;
3058 }
Owen Taylor3473f882001-02-23 17:55:21 +00003059
Daniel Veillardd8224e02002-01-13 15:43:22 +00003060 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003061
3062 return(1);
3063}
3064
3065/**
3066 * xmlValidateNamesValue:
3067 * @value: an Names value
3068 *
3069 * Validate that the given value match Names production
3070 *
3071 * returns 1 if valid or 0 otherwise
3072 */
3073
Daniel Veillard9b731d72002-04-14 12:56:08 +00003074int
Owen Taylor3473f882001-02-23 17:55:21 +00003075xmlValidateNamesValue(const xmlChar *value) {
3076 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003077 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003078
3079 if (value == NULL) return(0);
3080 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003081 val = xmlStringCurrentChar(NULL, cur, &len);
3082 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003083
Daniel Veillardd8224e02002-01-13 15:43:22 +00003084 if (!IS_LETTER(val) && (val != '_') &&
3085 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003086 return(0);
3087 }
3088
Daniel Veillardd8224e02002-01-13 15:43:22 +00003089 val = xmlStringCurrentChar(NULL, cur, &len);
3090 cur += len;
3091 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3092 (val == '.') || (val == '-') ||
3093 (val == '_') || (val == ':') ||
3094 (IS_COMBINING(val)) ||
3095 (IS_EXTENDER(val))) {
3096 val = xmlStringCurrentChar(NULL, cur, &len);
3097 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003098 }
3099
Daniel Veillardd8224e02002-01-13 15:43:22 +00003100 while (IS_BLANK(val)) {
3101 while (IS_BLANK(val)) {
3102 val = xmlStringCurrentChar(NULL, cur, &len);
3103 cur += len;
3104 }
3105
3106 if (!IS_LETTER(val) && (val != '_') &&
3107 (val != ':')) {
3108 return(0);
3109 }
3110 val = xmlStringCurrentChar(NULL, cur, &len);
3111 cur += len;
3112
3113 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3114 (val == '.') || (val == '-') ||
3115 (val == '_') || (val == ':') ||
3116 (IS_COMBINING(val)) ||
3117 (IS_EXTENDER(val))) {
3118 val = xmlStringCurrentChar(NULL, cur, &len);
3119 cur += len;
3120 }
3121 }
3122
3123 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003124
3125 return(1);
3126}
3127
3128/**
3129 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003130 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003131 *
3132 * Validate that the given value match Nmtoken production
3133 *
3134 * [ VC: Name Token ]
3135 *
3136 * returns 1 if valid or 0 otherwise
3137 */
3138
Daniel Veillard9b731d72002-04-14 12:56:08 +00003139int
Owen Taylor3473f882001-02-23 17:55:21 +00003140xmlValidateNmtokenValue(const xmlChar *value) {
3141 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003142 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003143
3144 if (value == NULL) return(0);
3145 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003146 val = xmlStringCurrentChar(NULL, cur, &len);
3147 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003148
Daniel Veillardd8224e02002-01-13 15:43:22 +00003149 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3150 (val != '.') && (val != '-') &&
3151 (val != '_') && (val != ':') &&
3152 (!IS_COMBINING(val)) &&
3153 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003154 return(0);
3155
Daniel Veillardd8224e02002-01-13 15:43:22 +00003156 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3157 (val == '.') || (val == '-') ||
3158 (val == '_') || (val == ':') ||
3159 (IS_COMBINING(val)) ||
3160 (IS_EXTENDER(val))) {
3161 val = xmlStringCurrentChar(NULL, cur, &len);
3162 cur += len;
3163 }
Owen Taylor3473f882001-02-23 17:55:21 +00003164
Daniel Veillardd8224e02002-01-13 15:43:22 +00003165 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003166
3167 return(1);
3168}
3169
3170/**
3171 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003172 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003173 *
3174 * Validate that the given value match Nmtokens production
3175 *
3176 * [ VC: Name Token ]
3177 *
3178 * returns 1 if valid or 0 otherwise
3179 */
3180
Daniel Veillard9b731d72002-04-14 12:56:08 +00003181int
Owen Taylor3473f882001-02-23 17:55:21 +00003182xmlValidateNmtokensValue(const xmlChar *value) {
3183 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003184 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003185
3186 if (value == NULL) return(0);
3187 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003188 val = xmlStringCurrentChar(NULL, cur, &len);
3189 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003190
Daniel Veillardd8224e02002-01-13 15:43:22 +00003191 while (IS_BLANK(val)) {
3192 val = xmlStringCurrentChar(NULL, cur, &len);
3193 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003194 }
3195
Daniel Veillardd8224e02002-01-13 15:43:22 +00003196 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3197 (val != '.') && (val != '-') &&
3198 (val != '_') && (val != ':') &&
3199 (!IS_COMBINING(val)) &&
3200 (!IS_EXTENDER(val)))
3201 return(0);
3202
3203 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3204 (val == '.') || (val == '-') ||
3205 (val == '_') || (val == ':') ||
3206 (IS_COMBINING(val)) ||
3207 (IS_EXTENDER(val))) {
3208 val = xmlStringCurrentChar(NULL, cur, &len);
3209 cur += len;
3210 }
3211
3212 while (IS_BLANK(val)) {
3213 while (IS_BLANK(val)) {
3214 val = xmlStringCurrentChar(NULL, cur, &len);
3215 cur += len;
3216 }
3217 if (val == 0) return(1);
3218
3219 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3220 (val != '.') && (val != '-') &&
3221 (val != '_') && (val != ':') &&
3222 (!IS_COMBINING(val)) &&
3223 (!IS_EXTENDER(val)))
3224 return(0);
3225
3226 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3227 (val == '.') || (val == '-') ||
3228 (val == '_') || (val == ':') ||
3229 (IS_COMBINING(val)) ||
3230 (IS_EXTENDER(val))) {
3231 val = xmlStringCurrentChar(NULL, cur, &len);
3232 cur += len;
3233 }
3234 }
3235
3236 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003237
3238 return(1);
3239}
3240
3241/**
3242 * xmlValidateNotationDecl:
3243 * @ctxt: the validation context
3244 * @doc: a document instance
3245 * @nota: a notation definition
3246 *
3247 * Try to validate a single notation definition
3248 * basically it does the following checks as described by the
3249 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003250 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003251 * But this function get called anyway ...
3252 *
3253 * returns 1 if valid or 0 otherwise
3254 */
3255
3256int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003257xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3258 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003259 int ret = 1;
3260
3261 return(ret);
3262}
3263
3264/**
3265 * xmlValidateAttributeValue:
3266 * @type: an attribute type
3267 * @value: an attribute value
3268 *
3269 * Validate that the given attribute value match the proper production
3270 *
3271 * [ VC: ID ]
3272 * Values of type ID must match the Name production....
3273 *
3274 * [ VC: IDREF ]
3275 * Values of type IDREF must match the Name production, and values
3276 * of type IDREFS must match Names ...
3277 *
3278 * [ VC: Entity Name ]
3279 * Values of type ENTITY must match the Name production, values
3280 * of type ENTITIES must match Names ...
3281 *
3282 * [ VC: Name Token ]
3283 * Values of type NMTOKEN must match the Nmtoken production; values
3284 * of type NMTOKENS must match Nmtokens.
3285 *
3286 * returns 1 if valid or 0 otherwise
3287 */
3288
3289int
3290xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3291 switch (type) {
3292 case XML_ATTRIBUTE_ENTITIES:
3293 case XML_ATTRIBUTE_IDREFS:
3294 return(xmlValidateNamesValue(value));
3295 case XML_ATTRIBUTE_ENTITY:
3296 case XML_ATTRIBUTE_IDREF:
3297 case XML_ATTRIBUTE_ID:
3298 case XML_ATTRIBUTE_NOTATION:
3299 return(xmlValidateNameValue(value));
3300 case XML_ATTRIBUTE_NMTOKENS:
3301 case XML_ATTRIBUTE_ENUMERATION:
3302 return(xmlValidateNmtokensValue(value));
3303 case XML_ATTRIBUTE_NMTOKEN:
3304 return(xmlValidateNmtokenValue(value));
3305 case XML_ATTRIBUTE_CDATA:
3306 break;
3307 }
3308 return(1);
3309}
3310
3311/**
3312 * xmlValidateAttributeValue2:
3313 * @ctxt: the validation context
3314 * @doc: the document
3315 * @name: the attribute name (used for error reporting only)
3316 * @type: the attribute type
3317 * @value: the attribute value
3318 *
3319 * Validate that the given attribute value match a given type.
3320 * This typically cannot be done before having finished parsing
3321 * the subsets.
3322 *
3323 * [ VC: IDREF ]
3324 * Values of type IDREF must match one of the declared IDs
3325 * Values of type IDREFS must match a sequence of the declared IDs
3326 * each Name must match the value of an ID attribute on some element
3327 * in the XML document; i.e. IDREF values must match the value of
3328 * some ID attribute
3329 *
3330 * [ VC: Entity Name ]
3331 * Values of type ENTITY must match one declared entity
3332 * Values of type ENTITIES must match a sequence of declared entities
3333 *
3334 * [ VC: Notation Attributes ]
3335 * all notation names in the declaration must be declared.
3336 *
3337 * returns 1 if valid or 0 otherwise
3338 */
3339
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003340static int
Owen Taylor3473f882001-02-23 17:55:21 +00003341xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3342 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3343 int ret = 1;
3344 switch (type) {
3345 case XML_ATTRIBUTE_IDREFS:
3346 case XML_ATTRIBUTE_IDREF:
3347 case XML_ATTRIBUTE_ID:
3348 case XML_ATTRIBUTE_NMTOKENS:
3349 case XML_ATTRIBUTE_ENUMERATION:
3350 case XML_ATTRIBUTE_NMTOKEN:
3351 case XML_ATTRIBUTE_CDATA:
3352 break;
3353 case XML_ATTRIBUTE_ENTITY: {
3354 xmlEntityPtr ent;
3355
3356 ent = xmlGetDocEntity(doc, value);
Daniel Veillard62998c02003-09-15 12:56:36 +00003357 /* yeah it's a bit messy... */
Daniel Veillard878eab02002-02-19 13:46:09 +00003358 if ((ent == NULL) && (doc->standalone == 1)) {
3359 doc->standalone = 0;
3360 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003361 }
Owen Taylor3473f882001-02-23 17:55:21 +00003362 if (ent == NULL) {
3363 VERROR(ctxt->userData,
3364 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3365 name, value);
3366 ret = 0;
3367 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3368 VERROR(ctxt->userData,
3369 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3370 name, value);
3371 ret = 0;
3372 }
3373 break;
3374 }
3375 case XML_ATTRIBUTE_ENTITIES: {
3376 xmlChar *dup, *nam = NULL, *cur, save;
3377 xmlEntityPtr ent;
3378
3379 dup = xmlStrdup(value);
3380 if (dup == NULL)
3381 return(0);
3382 cur = dup;
3383 while (*cur != 0) {
3384 nam = cur;
3385 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3386 save = *cur;
3387 *cur = 0;
3388 ent = xmlGetDocEntity(doc, nam);
3389 if (ent == NULL) {
3390 VERROR(ctxt->userData,
3391 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3392 name, nam);
3393 ret = 0;
3394 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3395 VERROR(ctxt->userData,
3396 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3397 name, nam);
3398 ret = 0;
3399 }
3400 if (save == 0)
3401 break;
3402 *cur = save;
3403 while (IS_BLANK(*cur)) cur++;
3404 }
3405 xmlFree(dup);
3406 break;
3407 }
3408 case XML_ATTRIBUTE_NOTATION: {
3409 xmlNotationPtr nota;
3410
3411 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3412 if ((nota == NULL) && (doc->extSubset != NULL))
3413 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3414
3415 if (nota == NULL) {
3416 VERROR(ctxt->userData,
3417 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3418 name, value);
3419 ret = 0;
3420 }
3421 break;
3422 }
3423 }
3424 return(ret);
3425}
3426
3427/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003428 * xmlValidCtxtNormalizeAttributeValue:
3429 * @ctxt: the validation context
3430 * @doc: the document
3431 * @elem: the parent
3432 * @name: the attribute name
3433 * @value: the attribute value
3434 * @ctxt: the validation context or NULL
3435 *
3436 * Does the validation related extra step of the normalization of attribute
3437 * values:
3438 *
3439 * If the declared value is not CDATA, then the XML processor must further
3440 * process the normalized attribute value by discarding any leading and
3441 * trailing space (#x20) characters, and by replacing sequences of space
3442 * (#x20) characters by single space (#x20) character.
3443 *
3444 * Also check VC: Standalone Document Declaration in P32, and update
3445 * ctxt->valid accordingly
3446 *
3447 * returns a new normalized string if normalization is needed, NULL otherwise
3448 * the caller must free the returned value.
3449 */
3450
3451xmlChar *
3452xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3453 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3454 xmlChar *ret, *dst;
3455 const xmlChar *src;
3456 xmlAttributePtr attrDecl = NULL;
3457 int extsubset = 0;
3458
3459 if (doc == NULL) return(NULL);
3460 if (elem == NULL) return(NULL);
3461 if (name == NULL) return(NULL);
3462 if (value == NULL) return(NULL);
3463
3464 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003465 xmlChar fn[50];
3466 xmlChar *fullname;
3467
3468 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3469 if (fullname == NULL)
3470 return(0);
3471 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003472 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003473 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003474 if (attrDecl != NULL)
3475 extsubset = 1;
3476 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003477 if ((fullname != fn) && (fullname != elem->name))
3478 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003479 }
3480 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3481 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3482 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3483 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3484 if (attrDecl != NULL)
3485 extsubset = 1;
3486 }
3487
3488 if (attrDecl == NULL)
3489 return(NULL);
3490 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3491 return(NULL);
3492
3493 ret = xmlStrdup(value);
3494 if (ret == NULL)
3495 return(NULL);
3496 src = value;
3497 dst = ret;
3498 while (*src == 0x20) src++;
3499 while (*src != 0) {
3500 if (*src == 0x20) {
3501 while (*src == 0x20) src++;
3502 if (*src != 0)
3503 *dst++ = 0x20;
3504 } else {
3505 *dst++ = *src++;
3506 }
3507 }
3508 *dst = 0;
3509 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3510 VERROR(ctxt->userData,
3511"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3512 name, elem->name);
3513 ctxt->valid = 0;
3514 }
3515 return(ret);
3516}
3517
3518/**
Owen Taylor3473f882001-02-23 17:55:21 +00003519 * xmlValidNormalizeAttributeValue:
3520 * @doc: the document
3521 * @elem: the parent
3522 * @name: the attribute name
3523 * @value: the attribute value
3524 *
3525 * Does the validation related extra step of the normalization of attribute
3526 * values:
3527 *
3528 * If the declared value is not CDATA, then the XML processor must further
3529 * process the normalized attribute value by discarding any leading and
3530 * trailing space (#x20) characters, and by replacing sequences of space
3531 * (#x20) characters by single space (#x20) character.
3532 *
3533 * returns a new normalized string if normalization is needed, NULL otherwise
3534 * the caller must free the returned value.
3535 */
3536
3537xmlChar *
3538xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3539 const xmlChar *name, const xmlChar *value) {
3540 xmlChar *ret, *dst;
3541 const xmlChar *src;
3542 xmlAttributePtr attrDecl = NULL;
3543
3544 if (doc == NULL) return(NULL);
3545 if (elem == NULL) return(NULL);
3546 if (name == NULL) return(NULL);
3547 if (value == NULL) return(NULL);
3548
3549 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003550 xmlChar fn[50];
3551 xmlChar *fullname;
3552
3553 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3554 if (fullname == NULL)
3555 return(0);
3556 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003557 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003558 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3559 if ((fullname != fn) && (fullname != elem->name))
3560 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003561 }
3562 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3563 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3564 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3565
3566 if (attrDecl == NULL)
3567 return(NULL);
3568 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3569 return(NULL);
3570
3571 ret = xmlStrdup(value);
3572 if (ret == NULL)
3573 return(NULL);
3574 src = value;
3575 dst = ret;
3576 while (*src == 0x20) src++;
3577 while (*src != 0) {
3578 if (*src == 0x20) {
3579 while (*src == 0x20) src++;
3580 if (*src != 0)
3581 *dst++ = 0x20;
3582 } else {
3583 *dst++ = *src++;
3584 }
3585 }
3586 *dst = 0;
3587 return(ret);
3588}
3589
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003590static void
Owen Taylor3473f882001-02-23 17:55:21 +00003591xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003592 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003593 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3594}
3595
3596/**
3597 * xmlValidateAttributeDecl:
3598 * @ctxt: the validation context
3599 * @doc: a document instance
3600 * @attr: an attribute definition
3601 *
3602 * Try to validate a single attribute definition
3603 * basically it does the following checks as described by the
3604 * XML-1.0 recommendation:
3605 * - [ VC: Attribute Default Legal ]
3606 * - [ VC: Enumeration ]
3607 * - [ VC: ID Attribute Default ]
3608 *
3609 * The ID/IDREF uniqueness and matching are done separately
3610 *
3611 * returns 1 if valid or 0 otherwise
3612 */
3613
3614int
3615xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3616 xmlAttributePtr attr) {
3617 int ret = 1;
3618 int val;
3619 CHECK_DTD;
3620 if(attr == NULL) return(1);
3621
3622 /* Attribute Default Legal */
3623 /* Enumeration */
3624 if (attr->defaultValue != NULL) {
3625 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3626 if (val == 0) {
3627 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003628 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003629 attr->name, attr->elem);
3630 }
3631 ret &= val;
3632 }
3633
3634 /* ID Attribute Default */
3635 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3636 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3637 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3638 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003639 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003640 attr->name, attr->elem);
3641 ret = 0;
3642 }
3643
3644 /* One ID per Element Type */
3645 if (attr->atype == XML_ATTRIBUTE_ID) {
3646 int nbId;
3647
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003648 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003649 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3650 attr->elem);
3651 if (elem != NULL) {
3652 nbId = xmlScanIDAttributeDecl(NULL, elem);
3653 } else {
3654 xmlAttributeTablePtr table;
3655
3656 /*
3657 * The attribute may be declared in the internal subset and the
3658 * element in the external subset.
3659 */
3660 nbId = 0;
3661 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3662 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3663 xmlValidateAttributeIdCallback, &nbId);
3664 }
3665 if (nbId > 1) {
3666 VERROR(ctxt->userData,
3667 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3668 attr->elem, nbId, attr->name);
3669 } else if (doc->extSubset != NULL) {
3670 int extId = 0;
3671 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3672 if (elem != NULL) {
3673 extId = xmlScanIDAttributeDecl(NULL, elem);
3674 }
3675 if (extId > 1) {
3676 VERROR(ctxt->userData,
3677 "Element %s has %d ID attribute defined in the external subset : %s\n",
3678 attr->elem, extId, attr->name);
3679 } else if (extId + nbId > 1) {
3680 VERROR(ctxt->userData,
3681"Element %s has ID attributes defined in the internal and external subset : %s\n",
3682 attr->elem, attr->name);
3683 }
3684 }
3685 }
3686
3687 /* Validity Constraint: Enumeration */
3688 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3689 xmlEnumerationPtr tree = attr->tree;
3690 while (tree != NULL) {
3691 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3692 tree = tree->next;
3693 }
3694 if (tree == NULL) {
3695 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003696"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003697 attr->defaultValue, attr->name, attr->elem);
3698 ret = 0;
3699 }
3700 }
3701
3702 return(ret);
3703}
3704
3705/**
3706 * xmlValidateElementDecl:
3707 * @ctxt: the validation context
3708 * @doc: a document instance
3709 * @elem: an element definition
3710 *
3711 * Try to validate a single element definition
3712 * basically it does the following checks as described by the
3713 * XML-1.0 recommendation:
3714 * - [ VC: One ID per Element Type ]
3715 * - [ VC: No Duplicate Types ]
3716 * - [ VC: Unique Element Type Declaration ]
3717 *
3718 * returns 1 if valid or 0 otherwise
3719 */
3720
3721int
3722xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3723 xmlElementPtr elem) {
3724 int ret = 1;
3725 xmlElementPtr tst;
3726
3727 CHECK_DTD;
3728
3729 if (elem == NULL) return(1);
3730
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003731#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003732#ifdef LIBXML_REGEXP_ENABLED
3733 /* Build the regexp associated to the content model */
3734 ret = xmlValidBuildContentModel(ctxt, elem);
3735#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003736#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003737
Owen Taylor3473f882001-02-23 17:55:21 +00003738 /* No Duplicate Types */
3739 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3740 xmlElementContentPtr cur, next;
3741 const xmlChar *name;
3742
3743 cur = elem->content;
3744 while (cur != NULL) {
3745 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3746 if (cur->c1 == NULL) break;
3747 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3748 name = cur->c1->name;
3749 next = cur->c2;
3750 while (next != NULL) {
3751 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard7b68df92003-08-03 22:58:54 +00003752 if ((xmlStrEqual(next->name, name)) &&
3753 (xmlStrEqual(next->prefix, cur->prefix))) {
3754 if (cur->prefix == NULL) {
3755 VERROR(ctxt->userData,
Owen Taylor3473f882001-02-23 17:55:21 +00003756 "Definition of %s has duplicate references of %s\n",
Daniel Veillard7b68df92003-08-03 22:58:54 +00003757 elem->name, name);
3758 } else {
3759 VERROR(ctxt->userData,
3760 "Definition of %s has duplicate references of %s:%s\n",
3761 elem->name, cur->prefix, name);
3762 }
Owen Taylor3473f882001-02-23 17:55:21 +00003763 ret = 0;
3764 }
3765 break;
3766 }
3767 if (next->c1 == NULL) break;
3768 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
Daniel Veillard7b68df92003-08-03 22:58:54 +00003769 if ((xmlStrEqual(next->c1->name, name)) &&
3770 (xmlStrEqual(next->c1->prefix, cur->prefix))) {
3771 if (cur->prefix == NULL) {
3772 VERROR(ctxt->userData,
3773 "Definition of %s has duplicate references to %s\n",
3774 elem->name, name);
3775 } else {
3776 VERROR(ctxt->userData,
3777 "Definition of %s has duplicate references to %s:%s\n",
3778 elem->name, cur->prefix, name);
3779 }
Owen Taylor3473f882001-02-23 17:55:21 +00003780 ret = 0;
3781 }
3782 next = next->c2;
3783 }
3784 }
3785 cur = cur->c2;
3786 }
3787 }
3788
3789 /* VC: Unique Element Type Declaration */
3790 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003791 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003792 ((tst->prefix == elem->prefix) ||
3793 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003794 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003795 VERROR(ctxt->userData, "Redefinition of element %s\n",
3796 elem->name);
3797 ret = 0;
3798 }
3799 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003800 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003801 ((tst->prefix == elem->prefix) ||
3802 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003803 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003804 VERROR(ctxt->userData, "Redefinition of element %s\n",
3805 elem->name);
3806 ret = 0;
3807 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003808 /* One ID per Element Type
3809 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003810 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3811 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003812 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003813 return(ret);
3814}
3815
3816/**
3817 * xmlValidateOneAttribute:
3818 * @ctxt: the validation context
3819 * @doc: a document instance
3820 * @elem: an element instance
3821 * @attr: an attribute instance
3822 * @value: the attribute value (without entities processing)
3823 *
3824 * Try to validate a single attribute for an element
3825 * basically it does the following checks as described by the
3826 * XML-1.0 recommendation:
3827 * - [ VC: Attribute Value Type ]
3828 * - [ VC: Fixed Attribute Default ]
3829 * - [ VC: Entity Name ]
3830 * - [ VC: Name Token ]
3831 * - [ VC: ID ]
3832 * - [ VC: IDREF ]
3833 * - [ VC: Entity Name ]
3834 * - [ VC: Notation Attributes ]
3835 *
3836 * The ID/IDREF uniqueness and matching are done separately
3837 *
3838 * returns 1 if valid or 0 otherwise
3839 */
3840
3841int
3842xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
Daniel Veillard07cb8222003-09-10 10:51:05 +00003843 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value)
3844{
Owen Taylor3473f882001-02-23 17:55:21 +00003845 xmlAttributePtr attrDecl = NULL;
3846 int val;
3847 int ret = 1;
3848
3849 CHECK_DTD;
3850 if ((elem == NULL) || (elem->name == NULL)) return(0);
3851 if ((attr == NULL) || (attr->name == NULL)) return(0);
3852
3853 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003854 xmlChar fn[50];
3855 xmlChar *fullname;
3856
3857 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3858 if (fullname == NULL)
3859 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003860 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003861 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003862 attr->name, attr->ns->prefix);
3863 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003864 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003865 attr->name, attr->ns->prefix);
3866 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003867 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003868 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3869 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00003870 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003871 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003872 if ((fullname != fn) && (fullname != elem->name))
3873 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003874 }
3875 if (attrDecl == NULL) {
3876 if (attr->ns != NULL) {
3877 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3878 attr->name, attr->ns->prefix);
3879 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3880 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3881 attr->name, attr->ns->prefix);
3882 } else {
3883 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3884 elem->name, attr->name);
3885 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3886 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3887 elem->name, attr->name);
3888 }
3889 }
3890
3891
3892 /* Validity Constraint: Attribute Value Type */
3893 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003894 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003895 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003896 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003897 attr->name, elem->name);
3898 return(0);
3899 }
3900 attr->atype = attrDecl->atype;
3901
3902 val = xmlValidateAttributeValue(attrDecl->atype, value);
3903 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003904 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003905 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003906 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003907 attr->name, elem->name);
3908 ret = 0;
3909 }
3910
3911 /* Validity constraint: Fixed Attribute Default */
3912 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3913 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003914 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003915 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003916 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003917 attr->name, elem->name, attrDecl->defaultValue);
3918 ret = 0;
3919 }
3920 }
3921
3922 /* Validity Constraint: ID uniqueness */
3923 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3924 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3925 ret = 0;
3926 }
3927
3928 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3929 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3930 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3931 ret = 0;
3932 }
3933
3934 /* Validity Constraint: Notation Attributes */
3935 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3936 xmlEnumerationPtr tree = attrDecl->tree;
3937 xmlNotationPtr nota;
3938
3939 /* First check that the given NOTATION was declared */
3940 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3941 if (nota == NULL)
3942 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3943
3944 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003945 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003946 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003947 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003948 value, attr->name, elem->name);
3949 ret = 0;
3950 }
3951
3952 /* Second, verify that it's among the list */
3953 while (tree != NULL) {
3954 if (xmlStrEqual(tree->name, value)) break;
3955 tree = tree->next;
3956 }
3957 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003958 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003959 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003960"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003961 value, attr->name, elem->name);
3962 ret = 0;
3963 }
3964 }
3965
3966 /* Validity Constraint: Enumeration */
3967 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3968 xmlEnumerationPtr tree = attrDecl->tree;
3969 while (tree != NULL) {
3970 if (xmlStrEqual(tree->name, value)) break;
3971 tree = tree->next;
3972 }
3973 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003974 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003975 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003976 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003977 value, attr->name, elem->name);
3978 ret = 0;
3979 }
3980 }
3981
3982 /* Fixed Attribute Default */
3983 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3984 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003985 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003986 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003987 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003988 attr->name, elem->name, attrDecl->defaultValue);
3989 ret = 0;
3990 }
3991
3992 /* Extra check for the attribute value */
3993 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3994 attrDecl->atype, value);
3995
3996 return(ret);
3997}
3998
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003999/**
4000 * xmlValidateOneNamespace:
4001 * @ctxt: the validation context
4002 * @doc: a document instance
4003 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00004004 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004005 * @ns: an namespace declaration instance
4006 * @value: the attribute value (without entities processing)
4007 *
4008 * Try to validate a single namespace declaration for an element
4009 * basically it does the following checks as described by the
4010 * XML-1.0 recommendation:
4011 * - [ VC: Attribute Value Type ]
4012 * - [ VC: Fixed Attribute Default ]
4013 * - [ VC: Entity Name ]
4014 * - [ VC: Name Token ]
4015 * - [ VC: ID ]
4016 * - [ VC: IDREF ]
4017 * - [ VC: Entity Name ]
4018 * - [ VC: Notation Attributes ]
4019 *
4020 * The ID/IDREF uniqueness and matching are done separately
4021 *
4022 * returns 1 if valid or 0 otherwise
4023 */
4024
4025int
4026xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4027xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
4028 /* xmlElementPtr elemDecl; */
4029 xmlAttributePtr attrDecl = NULL;
4030 int val;
4031 int ret = 1;
4032
4033 CHECK_DTD;
4034 if ((elem == NULL) || (elem->name == NULL)) return(0);
4035 if ((ns == NULL) || (ns->href == NULL)) return(0);
4036
4037 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004038 xmlChar fn[50];
4039 xmlChar *fullname;
4040
4041 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
4042 if (fullname == NULL) {
4043 VERROR(ctxt->userData, "Out of memory\n");
4044 return(0);
4045 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004046 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004047 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004048 ns->prefix, BAD_CAST "xmlns");
4049 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004050 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004051 ns->prefix, BAD_CAST "xmlns");
4052 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004053 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004054 BAD_CAST "xmlns");
4055 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00004056 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004057 BAD_CAST "xmlns");
4058 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004059 if ((fullname != fn) && (fullname != elem->name))
4060 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00004061 }
4062 if (attrDecl == NULL) {
4063 if (ns->prefix != NULL) {
4064 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4065 ns->prefix, BAD_CAST "xmlns");
4066 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4067 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4068 ns->prefix, BAD_CAST "xmlns");
4069 } else {
4070 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4071 elem->name, BAD_CAST "xmlns");
4072 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4073 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4074 elem->name, BAD_CAST "xmlns");
4075 }
4076 }
4077
4078
4079 /* Validity Constraint: Attribute Value Type */
4080 if (attrDecl == NULL) {
4081 VECTXT(ctxt, elem);
4082 if (ns->prefix != NULL) {
4083 VERROR(ctxt->userData,
4084 "No declaration for attribute xmlns:%s of element %s\n",
4085 ns->prefix, elem->name);
4086 } else {
4087 VERROR(ctxt->userData,
4088 "No declaration for attribute xmlns of element %s\n",
4089 elem->name);
4090 }
4091 return(0);
4092 }
4093
4094 val = xmlValidateAttributeValue(attrDecl->atype, value);
4095 if (val == 0) {
4096 VECTXT(ctxt, elem);
4097 if (ns->prefix != NULL) {
4098 VERROR(ctxt->userData,
4099 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
4100 ns->prefix, elem->name);
4101 } else {
4102 VERROR(ctxt->userData,
4103 "Syntax of value for attribute xmlns of %s is not valid\n",
4104 elem->name);
4105 }
4106 ret = 0;
4107 }
4108
4109 /* Validity constraint: Fixed Attribute Default */
4110 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4111 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4112 VECTXT(ctxt, elem);
4113 if (ns->prefix != NULL) {
4114 VERROR(ctxt->userData,
4115 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4116 ns->prefix, elem->name, attrDecl->defaultValue);
4117 } else {
4118 VERROR(ctxt->userData,
4119 "Value for attribute xmlns of %s is different from default \"%s\"\n",
4120 elem->name, attrDecl->defaultValue);
4121 }
4122 ret = 0;
4123 }
4124 }
4125
4126 /* Validity Constraint: ID uniqueness */
4127 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4128 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4129 ret = 0;
4130 }
4131
4132 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4133 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4134 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4135 ret = 0;
4136 }
4137
4138 /* Validity Constraint: Notation Attributes */
4139 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4140 xmlEnumerationPtr tree = attrDecl->tree;
4141 xmlNotationPtr nota;
4142
4143 /* First check that the given NOTATION was declared */
4144 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4145 if (nota == NULL)
4146 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4147
4148 if (nota == NULL) {
4149 VECTXT(ctxt, elem);
4150 if (ns->prefix != NULL) {
4151 VERROR(ctxt->userData,
4152 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4153 value, ns->prefix, elem->name);
4154 } else {
4155 VERROR(ctxt->userData,
4156 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
4157 value, elem->name);
4158 }
4159 ret = 0;
4160 }
4161
4162 /* Second, verify that it's among the list */
4163 while (tree != NULL) {
4164 if (xmlStrEqual(tree->name, value)) break;
4165 tree = tree->next;
4166 }
4167 if (tree == NULL) {
4168 VECTXT(ctxt, elem);
4169 if (ns->prefix != NULL) {
4170 VERROR(ctxt->userData,
4171"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4172 value, ns->prefix, elem->name);
4173 } else {
4174 VERROR(ctxt->userData,
4175"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
4176 value, elem->name);
4177 }
4178 ret = 0;
4179 }
4180 }
4181
4182 /* Validity Constraint: Enumeration */
4183 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4184 xmlEnumerationPtr tree = attrDecl->tree;
4185 while (tree != NULL) {
4186 if (xmlStrEqual(tree->name, value)) break;
4187 tree = tree->next;
4188 }
4189 if (tree == NULL) {
4190 VECTXT(ctxt, elem);
4191 if (ns->prefix != NULL) {
4192 VERROR(ctxt->userData,
4193"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4194 value, ns->prefix, elem->name);
4195 } else {
4196 VERROR(ctxt->userData,
4197"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4198 value, elem->name);
4199 }
4200 ret = 0;
4201 }
4202 }
4203
4204 /* Fixed Attribute Default */
4205 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4206 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4207 VECTXT(ctxt, elem);
4208 if (ns->prefix != NULL) {
4209 VERROR(ctxt->userData,
4210 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4211 ns->prefix, elem->name, attrDecl->defaultValue);
4212 } else {
4213 VERROR(ctxt->userData,
4214 "Value for attribute xmlns of %s must be \"%s\"\n",
4215 elem->name, attrDecl->defaultValue);
4216 }
4217 ret = 0;
4218 }
4219
4220 /* Extra check for the attribute value */
4221 if (ns->prefix != NULL) {
4222 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4223 attrDecl->atype, value);
4224 } else {
4225 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4226 attrDecl->atype, value);
4227 }
4228
4229 return(ret);
4230}
4231
Daniel Veillard118aed72002-09-24 14:13:13 +00004232#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004233/**
4234 * xmlValidateSkipIgnorable:
4235 * @ctxt: the validation context
4236 * @child: the child list
4237 *
4238 * Skip ignorable elements w.r.t. the validation process
4239 *
4240 * returns the first element to consider for validation of the content model
4241 */
4242
4243static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004244xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004245 while (child != NULL) {
4246 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004247 /* These things are ignored (skipped) during validation. */
4248 case XML_PI_NODE:
4249 case XML_COMMENT_NODE:
4250 case XML_XINCLUDE_START:
4251 case XML_XINCLUDE_END:
4252 child = child->next;
4253 break;
4254 case XML_TEXT_NODE:
4255 if (xmlIsBlankNode(child))
4256 child = child->next;
4257 else
4258 return(child);
4259 break;
4260 /* keep current node */
4261 default:
4262 return(child);
4263 }
4264 }
4265 return(child);
4266}
4267
4268/**
4269 * xmlValidateElementType:
4270 * @ctxt: the validation context
4271 *
4272 * Try to validate the content model of an element internal function
4273 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004274 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4275 * reference is found and -3 if the validation succeeded but
4276 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004277 */
4278
4279static int
4280xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004281 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004282 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004283
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004284 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004285 if ((NODE == NULL) && (CONT == NULL))
4286 return(1);
4287 if ((NODE == NULL) &&
4288 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4289 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4290 return(1);
4291 }
4292 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004293 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004294 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004295
4296 /*
4297 * We arrive here when more states need to be examined
4298 */
4299cont:
4300
4301 /*
4302 * We just recovered from a rollback generated by a possible
4303 * epsilon transition, go directly to the analysis phase
4304 */
4305 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004306 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004307 DEBUG_VALID_STATE(NODE, CONT)
4308 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004309 goto analyze;
4310 }
4311
4312 DEBUG_VALID_STATE(NODE, CONT)
4313 /*
4314 * we may have to save a backup state here. This is the equivalent
4315 * of handling epsilon transition in NFAs.
4316 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004317 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004318 ((CONT->parent == NULL) ||
4319 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004320 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004321 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004322 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004323 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004324 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4325 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004326 }
4327
4328
4329 /*
4330 * Check first if the content matches
4331 */
4332 switch (CONT->type) {
4333 case XML_ELEMENT_CONTENT_PCDATA:
4334 if (NODE == NULL) {
4335 DEBUG_VALID_MSG("pcdata failed no node");
4336 ret = 0;
4337 break;
4338 }
4339 if (NODE->type == XML_TEXT_NODE) {
4340 DEBUG_VALID_MSG("pcdata found, skip to next");
4341 /*
4342 * go to next element in the content model
4343 * skipping ignorable elems
4344 */
4345 do {
4346 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004347 NODE = xmlValidateSkipIgnorable(NODE);
4348 if ((NODE != NULL) &&
4349 (NODE->type == XML_ENTITY_REF_NODE))
4350 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004351 } while ((NODE != NULL) &&
4352 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004353 (NODE->type != XML_TEXT_NODE) &&
4354 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004355 ret = 1;
4356 break;
4357 } else {
4358 DEBUG_VALID_MSG("pcdata failed");
4359 ret = 0;
4360 break;
4361 }
4362 break;
4363 case XML_ELEMENT_CONTENT_ELEMENT:
4364 if (NODE == NULL) {
4365 DEBUG_VALID_MSG("element failed no node");
4366 ret = 0;
4367 break;
4368 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004369 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4370 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004371 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004372 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4373 ret = (CONT->prefix == NULL);
4374 } else if (CONT->prefix == NULL) {
4375 ret = 0;
4376 } else {
4377 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4378 }
4379 }
4380 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004381 DEBUG_VALID_MSG("element found, skip to next");
4382 /*
4383 * go to next element in the content model
4384 * skipping ignorable elems
4385 */
4386 do {
4387 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004388 NODE = xmlValidateSkipIgnorable(NODE);
4389 if ((NODE != NULL) &&
4390 (NODE->type == XML_ENTITY_REF_NODE))
4391 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004392 } while ((NODE != NULL) &&
4393 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004394 (NODE->type != XML_TEXT_NODE) &&
4395 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004396 } else {
4397 DEBUG_VALID_MSG("element failed");
4398 ret = 0;
4399 break;
4400 }
4401 break;
4402 case XML_ELEMENT_CONTENT_OR:
4403 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004404 * Small optimization.
4405 */
4406 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4407 if ((NODE == NULL) ||
4408 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4409 DEPTH++;
4410 CONT = CONT->c2;
4411 goto cont;
4412 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004413 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4414 ret = (CONT->c1->prefix == NULL);
4415 } else if (CONT->c1->prefix == NULL) {
4416 ret = 0;
4417 } else {
4418 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4419 }
4420 if (ret == 0) {
4421 DEPTH++;
4422 CONT = CONT->c2;
4423 goto cont;
4424 }
Daniel Veillard85349052001-04-20 13:48:21 +00004425 }
4426
4427 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004428 * save the second branch 'or' branch
4429 */
4430 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004431 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4432 OCCURS, ROLLBACK_OR) < 0)
4433 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004434 DEPTH++;
4435 CONT = CONT->c1;
4436 goto cont;
4437 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004438 /*
4439 * Small optimization.
4440 */
4441 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4442 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4443 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4444 if ((NODE == NULL) ||
4445 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4446 DEPTH++;
4447 CONT = CONT->c2;
4448 goto cont;
4449 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004450 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4451 ret = (CONT->c1->prefix == NULL);
4452 } else if (CONT->c1->prefix == NULL) {
4453 ret = 0;
4454 } else {
4455 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4456 }
4457 if (ret == 0) {
4458 DEPTH++;
4459 CONT = CONT->c2;
4460 goto cont;
4461 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004462 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004463 DEPTH++;
4464 CONT = CONT->c1;
4465 goto cont;
4466 }
4467
4468 /*
4469 * At this point handle going up in the tree
4470 */
4471 if (ret == -1) {
4472 DEBUG_VALID_MSG("error found returning");
4473 return(ret);
4474 }
4475analyze:
4476 while (CONT != NULL) {
4477 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004478 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004479 * this level.
4480 */
4481 if (ret == 0) {
4482 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004483 xmlNodePtr cur;
4484
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004485 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004486 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004487 DEBUG_VALID_MSG("Once branch failed, rollback");
4488 if (vstateVPop(ctxt) < 0 ) {
4489 DEBUG_VALID_MSG("exhaustion, failed");
4490 return(0);
4491 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004492 if (cur != ctxt->vstate->node)
4493 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004494 goto cont;
4495 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004496 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004497 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004498 DEBUG_VALID_MSG("Plus branch failed, rollback");
4499 if (vstateVPop(ctxt) < 0 ) {
4500 DEBUG_VALID_MSG("exhaustion, failed");
4501 return(0);
4502 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004503 if (cur != ctxt->vstate->node)
4504 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004505 goto cont;
4506 }
4507 DEBUG_VALID_MSG("Plus branch found");
4508 ret = 1;
4509 break;
4510 case XML_ELEMENT_CONTENT_MULT:
4511#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004512 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004513 DEBUG_VALID_MSG("Mult branch failed");
4514 } else {
4515 DEBUG_VALID_MSG("Mult branch found");
4516 }
4517#endif
4518 ret = 1;
4519 break;
4520 case XML_ELEMENT_CONTENT_OPT:
4521 DEBUG_VALID_MSG("Option branch failed");
4522 ret = 1;
4523 break;
4524 }
4525 } else {
4526 switch (CONT->ocur) {
4527 case XML_ELEMENT_CONTENT_OPT:
4528 DEBUG_VALID_MSG("Option branch succeeded");
4529 ret = 1;
4530 break;
4531 case XML_ELEMENT_CONTENT_ONCE:
4532 DEBUG_VALID_MSG("Once branch succeeded");
4533 ret = 1;
4534 break;
4535 case XML_ELEMENT_CONTENT_PLUS:
4536 if (STATE == ROLLBACK_PARENT) {
4537 DEBUG_VALID_MSG("Plus branch rollback");
4538 ret = 1;
4539 break;
4540 }
4541 if (NODE == NULL) {
4542 DEBUG_VALID_MSG("Plus branch exhausted");
4543 ret = 1;
4544 break;
4545 }
4546 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004547 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004548 goto cont;
4549 case XML_ELEMENT_CONTENT_MULT:
4550 if (STATE == ROLLBACK_PARENT) {
4551 DEBUG_VALID_MSG("Mult branch rollback");
4552 ret = 1;
4553 break;
4554 }
4555 if (NODE == NULL) {
4556 DEBUG_VALID_MSG("Mult branch exhausted");
4557 ret = 1;
4558 break;
4559 }
4560 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004561 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004562 goto cont;
4563 }
4564 }
4565 STATE = 0;
4566
4567 /*
4568 * Then act accordingly at the parent level
4569 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004570 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004571 if (CONT->parent == NULL)
4572 break;
4573
4574 switch (CONT->parent->type) {
4575 case XML_ELEMENT_CONTENT_PCDATA:
4576 DEBUG_VALID_MSG("Error: parent pcdata");
4577 return(-1);
4578 case XML_ELEMENT_CONTENT_ELEMENT:
4579 DEBUG_VALID_MSG("Error: parent element");
4580 return(-1);
4581 case XML_ELEMENT_CONTENT_OR:
4582 if (ret == 1) {
4583 DEBUG_VALID_MSG("Or succeeded");
4584 CONT = CONT->parent;
4585 DEPTH--;
4586 } else {
4587 DEBUG_VALID_MSG("Or failed");
4588 CONT = CONT->parent;
4589 DEPTH--;
4590 }
4591 break;
4592 case XML_ELEMENT_CONTENT_SEQ:
4593 if (ret == 0) {
4594 DEBUG_VALID_MSG("Sequence failed");
4595 CONT = CONT->parent;
4596 DEPTH--;
4597 } else if (CONT == CONT->parent->c1) {
4598 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4599 CONT = CONT->parent->c2;
4600 goto cont;
4601 } else {
4602 DEBUG_VALID_MSG("Sequence succeeded");
4603 CONT = CONT->parent;
4604 DEPTH--;
4605 }
4606 }
4607 }
4608 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004609 xmlNodePtr cur;
4610
4611 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004612 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4613 if (vstateVPop(ctxt) < 0 ) {
4614 DEBUG_VALID_MSG("exhaustion, failed");
4615 return(0);
4616 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004617 if (cur != ctxt->vstate->node)
4618 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004619 goto cont;
4620 }
4621 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004622 xmlNodePtr cur;
4623
4624 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004625 DEBUG_VALID_MSG("Failure, rollback");
4626 if (vstateVPop(ctxt) < 0 ) {
4627 DEBUG_VALID_MSG("exhaustion, failed");
4628 return(0);
4629 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004630 if (cur != ctxt->vstate->node)
4631 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004632 goto cont;
4633 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004634 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004635}
Daniel Veillard23e73572002-09-19 19:56:43 +00004636#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004637
4638/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004639 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004640 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004641 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004642 * @content: An element
4643 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4644 *
4645 * This will dump the list of elements to the buffer
4646 * Intended just for the debug routine
4647 */
4648static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004649xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004650 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004651 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004652
4653 if (node == NULL) return;
4654 if (glob) strcat(buf, "(");
4655 cur = node;
4656 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004657 len = strlen(buf);
4658 if (size - len < 50) {
4659 if ((size - len > 4) && (buf[len - 1] != '.'))
4660 strcat(buf, " ...");
4661 return;
4662 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004663 switch (cur->type) {
4664 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004665 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004666 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004667 if ((size - len > 4) && (buf[len - 1] != '.'))
4668 strcat(buf, " ...");
4669 return;
4670 }
4671 strcat(buf, (char *) cur->ns->prefix);
4672 strcat(buf, ":");
4673 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004674 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004675 if ((size - len > 4) && (buf[len - 1] != '.'))
4676 strcat(buf, " ...");
4677 return;
4678 }
4679 strcat(buf, (char *) cur->name);
4680 if (cur->next != NULL)
4681 strcat(buf, " ");
4682 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004683 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004684 if (xmlIsBlankNode(cur))
4685 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004686 case XML_CDATA_SECTION_NODE:
4687 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004688 strcat(buf, "CDATA");
4689 if (cur->next != NULL)
4690 strcat(buf, " ");
4691 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004692 case XML_ATTRIBUTE_NODE:
4693 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004694#ifdef LIBXML_DOCB_ENABLED
4695 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004696#endif
4697 case XML_HTML_DOCUMENT_NODE:
4698 case XML_DOCUMENT_TYPE_NODE:
4699 case XML_DOCUMENT_FRAG_NODE:
4700 case XML_NOTATION_NODE:
4701 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004702 strcat(buf, "???");
4703 if (cur->next != NULL)
4704 strcat(buf, " ");
4705 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004706 case XML_ENTITY_NODE:
4707 case XML_PI_NODE:
4708 case XML_DTD_NODE:
4709 case XML_COMMENT_NODE:
4710 case XML_ELEMENT_DECL:
4711 case XML_ATTRIBUTE_DECL:
4712 case XML_ENTITY_DECL:
4713 case XML_XINCLUDE_START:
4714 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004715 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004716 }
4717 cur = cur->next;
4718 }
4719 if (glob) strcat(buf, ")");
4720}
4721
4722/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004723 * xmlValidateElementContent:
4724 * @ctxt: the validation context
4725 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004726 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004727 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004728 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004729 *
4730 * Try to validate the content model of an element
4731 *
4732 * returns 1 if valid or 0 if not and -1 in case of error
4733 */
4734
4735static int
4736xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004737 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004738 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004739#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004740 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004741#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004742 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004743 xmlElementContentPtr cont;
4744 const xmlChar *name;
4745
4746 if (elemDecl == NULL)
4747 return(-1);
4748 cont = elemDecl->content;
4749 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004750
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004751#ifdef LIBXML_REGEXP_ENABLED
4752 /* Build the regexp associated to the content model */
4753 if (elemDecl->contModel == NULL)
4754 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4755 if (elemDecl->contModel == NULL) {
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004756 return(-1);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004757 } else {
4758 xmlRegExecCtxtPtr exec;
4759
Daniel Veillardec498e12003-02-05 11:01:50 +00004760 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
4761 return(-1);
4762 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004763 ctxt->nodeMax = 0;
4764 ctxt->nodeNr = 0;
4765 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004766 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4767 if (exec != NULL) {
4768 cur = child;
4769 while (cur != NULL) {
4770 switch (cur->type) {
4771 case XML_ENTITY_REF_NODE:
4772 /*
4773 * Push the current node to be able to roll back
4774 * and process within the entity
4775 */
4776 if ((cur->children != NULL) &&
4777 (cur->children->children != NULL)) {
4778 nodeVPush(ctxt, cur);
4779 cur = cur->children->children;
4780 continue;
4781 }
4782 break;
4783 case XML_TEXT_NODE:
4784 if (xmlIsBlankNode(cur))
4785 break;
4786 ret = 0;
4787 goto fail;
4788 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004789 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004790 ret = 0;
4791 goto fail;
4792 case XML_ELEMENT_NODE:
4793 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004794 xmlChar fn[50];
4795 xmlChar *fullname;
4796
4797 fullname = xmlBuildQName(cur->name,
4798 cur->ns->prefix, fn, 50);
4799 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004800 ret = -1;
4801 goto fail;
4802 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004803 ret = xmlRegExecPushString(exec, fullname, NULL);
4804 if ((fullname != fn) && (fullname != cur->name))
4805 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004806 } else {
4807 ret = xmlRegExecPushString(exec, cur->name, NULL);
4808 }
4809 break;
4810 default:
4811 break;
4812 }
4813 /*
4814 * Switch to next element
4815 */
4816 cur = cur->next;
4817 while (cur == NULL) {
4818 cur = nodeVPop(ctxt);
4819 if (cur == NULL)
4820 break;
4821 cur = cur->next;
4822 }
4823 }
4824 ret = xmlRegExecPushString(exec, NULL, NULL);
4825fail:
4826 xmlRegFreeExecCtxt(exec);
4827 }
4828 }
4829#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004830 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004831 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004832 */
4833 ctxt->vstateMax = 8;
4834 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4835 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4836 if (ctxt->vstateTab == NULL) {
4837 xmlGenericError(xmlGenericErrorContext,
4838 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004839 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004840 }
4841 /*
4842 * The first entry in the stack is reserved to the current state
4843 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004844 ctxt->nodeMax = 0;
4845 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004846 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004847 ctxt->vstate = &ctxt->vstateTab[0];
4848 ctxt->vstateNr = 1;
4849 CONT = cont;
4850 NODE = child;
4851 DEPTH = 0;
4852 OCCURS = 0;
4853 STATE = 0;
4854 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004855 if ((ret == -3) && (warn)) {
4856 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004857 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004858 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004859 /*
4860 * An entities reference appeared at this level.
4861 * Buid a minimal representation of this node content
4862 * sufficient to run the validation process on it
4863 */
4864 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004865 cur = child;
4866 while (cur != NULL) {
4867 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004868 case XML_ENTITY_REF_NODE:
4869 /*
4870 * Push the current node to be able to roll back
4871 * and process within the entity
4872 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004873 if ((cur->children != NULL) &&
4874 (cur->children->children != NULL)) {
4875 nodeVPush(ctxt, cur);
4876 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004877 continue;
4878 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004879 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004880 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004881 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004882 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004883 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004884 case XML_CDATA_SECTION_NODE:
4885 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004886 case XML_ELEMENT_NODE:
4887 /*
4888 * Allocate a new node and minimally fills in
4889 * what's required
4890 */
4891 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4892 if (tmp == NULL) {
4893 xmlGenericError(xmlGenericErrorContext,
4894 "xmlValidateElementContent : malloc failed\n");
4895 xmlFreeNodeList(repl);
4896 ret = -1;
4897 goto done;
4898 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004899 tmp->type = cur->type;
4900 tmp->name = cur->name;
4901 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004902 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004903 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004904 if (repl == NULL)
4905 repl = last = tmp;
4906 else {
4907 last->next = tmp;
4908 last = tmp;
4909 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004910 if (cur->type == XML_CDATA_SECTION_NODE) {
4911 /*
4912 * E59 spaces in CDATA does not match the
4913 * nonterminal S
4914 */
4915 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4916 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004917 break;
4918 default:
4919 break;
4920 }
4921 /*
4922 * Switch to next element
4923 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004924 cur = cur->next;
4925 while (cur == NULL) {
4926 cur = nodeVPop(ctxt);
4927 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004928 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004929 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004930 }
4931 }
4932
4933 /*
4934 * Relaunch the validation
4935 */
4936 ctxt->vstate = &ctxt->vstateTab[0];
4937 ctxt->vstateNr = 1;
4938 CONT = cont;
4939 NODE = repl;
4940 DEPTH = 0;
4941 OCCURS = 0;
4942 STATE = 0;
4943 ret = xmlValidateElementType(ctxt);
4944 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004945#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004946 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004947 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4948 char expr[5000];
4949 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004950
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004951 expr[0] = 0;
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004952 xmlSnprintfElementContent(&expr[0], 5000, cont, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004953 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004954#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004955 if (repl != NULL)
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004956 xmlSnprintfElements(&list[0], 5000, repl, 1);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004957 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004958#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillarda76fe5c2003-04-24 16:06:47 +00004959 xmlSnprintfElements(&list[0], 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004960
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004961 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004962 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004963 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004964 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004965 name, expr, list);
4966 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004967 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004968 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004969 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004970 expr, list);
4971 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004972 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004973 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004974 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004975 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004976 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004977 name);
4978 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004979 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004980 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004981 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004982 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004983 }
4984 ret = 0;
4985 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004986 if (ret == -3)
4987 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004988
Daniel Veillard23e73572002-09-19 19:56:43 +00004989#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004990done:
4991 /*
4992 * Deallocate the copy if done, and free up the validation stack
4993 */
4994 while (repl != NULL) {
4995 tmp = repl->next;
4996 xmlFree(repl);
4997 repl = tmp;
4998 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004999 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005000 if (ctxt->vstateTab != NULL) {
5001 xmlFree(ctxt->vstateTab);
5002 ctxt->vstateTab = NULL;
5003 }
Daniel Veillard01992e02002-10-09 10:20:30 +00005004#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005005 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00005006 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00005007 if (ctxt->nodeTab != NULL) {
5008 xmlFree(ctxt->nodeTab);
5009 ctxt->nodeTab = NULL;
5010 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005011 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00005012
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005013}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00005014
Owen Taylor3473f882001-02-23 17:55:21 +00005015/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005016 * xmlValidateCdataElement:
5017 * @ctxt: the validation context
5018 * @doc: a document instance
5019 * @elem: an element instance
5020 *
5021 * Check that an element follows #CDATA
5022 *
5023 * returns 1 if valid or 0 otherwise
5024 */
5025static int
5026xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5027 xmlNodePtr elem) {
5028 int ret = 1;
5029 xmlNodePtr cur, child;
5030
Daniel Veillardceb09b92002-10-04 11:46:37 +00005031 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005032 return(0);
5033
5034 child = elem->children;
5035
5036 cur = child;
5037 while (cur != NULL) {
5038 switch (cur->type) {
5039 case XML_ENTITY_REF_NODE:
5040 /*
5041 * Push the current node to be able to roll back
5042 * and process within the entity
5043 */
5044 if ((cur->children != NULL) &&
5045 (cur->children->children != NULL)) {
5046 nodeVPush(ctxt, cur);
5047 cur = cur->children->children;
5048 continue;
5049 }
5050 break;
5051 case XML_COMMENT_NODE:
5052 case XML_PI_NODE:
5053 case XML_TEXT_NODE:
5054 case XML_CDATA_SECTION_NODE:
5055 break;
5056 default:
5057 ret = 0;
5058 goto done;
5059 }
5060 /*
5061 * Switch to next element
5062 */
5063 cur = cur->next;
5064 while (cur == NULL) {
5065 cur = nodeVPop(ctxt);
5066 if (cur == NULL)
5067 break;
5068 cur = cur->next;
5069 }
5070 }
5071done:
5072 ctxt->nodeMax = 0;
5073 ctxt->nodeNr = 0;
5074 if (ctxt->nodeTab != NULL) {
5075 xmlFree(ctxt->nodeTab);
5076 ctxt->nodeTab = NULL;
5077 }
5078 return(ret);
5079}
5080
5081/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005082 * xmlValidateCheckMixed:
5083 * @ctxt: the validation context
5084 * @cont: the mixed content model
5085 * @qname: the qualified name as appearing in the serialization
5086 *
5087 * Check if the given node is part of the content model.
5088 *
5089 * Returns 1 if yes, 0 if no, -1 in case of error
5090 */
5091static int
5092xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5093 xmlElementContentPtr cont, const xmlChar *qname) {
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005094 const xmlChar *name;
5095 int plen;
5096 name = xmlSplitQName3(qname, &plen);
5097
5098 if (name == NULL) {
5099 while (cont != NULL) {
5100 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5101 if ((cont->prefix == NULL) && (xmlStrEqual(cont->name, qname)))
5102 return(1);
5103 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5104 (cont->c1 != NULL) &&
5105 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5106 if ((cont->c1->prefix == NULL) &&
5107 (xmlStrEqual(cont->c1->name, qname)))
5108 return(1);
5109 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5110 (cont->c1 == NULL) ||
5111 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5112 /* Internal error !!! */
5113 xmlGenericError(xmlGenericErrorContext,
5114 "Internal: MIXED struct bad\n");
5115 break;
5116 }
5117 cont = cont->c2;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005118 }
Daniel Veillard8d73bcb2003-08-04 01:06:15 +00005119 } else {
5120 while (cont != NULL) {
5121 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5122 if ((cont->prefix != NULL) &&
5123 (xmlStrncmp(cont->prefix, qname, plen) == 0) &&
5124 (xmlStrEqual(cont->name, name)))
5125 return(1);
5126 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5127 (cont->c1 != NULL) &&
5128 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5129 if ((cont->c1->prefix != NULL) &&
5130 (xmlStrncmp(cont->c1->prefix, qname, plen) == 0) &&
5131 (xmlStrEqual(cont->c1->name, name)))
5132 return(1);
5133 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5134 (cont->c1 == NULL) ||
5135 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5136 /* Internal error !!! */
5137 xmlGenericError(xmlGenericErrorContext,
5138 "Internal: MIXED struct bad\n");
5139 break;
5140 }
5141 cont = cont->c2;
5142 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005143 }
5144 return(0);
5145}
5146
5147/**
5148 * xmlValidGetElemDecl:
5149 * @ctxt: the validation context
5150 * @doc: a document instance
5151 * @elem: an element instance
5152 * @extsubset: pointer, (out) indicate if the declaration was found
5153 * in the external subset.
5154 *
5155 * Finds a declaration associated to an element in the document.
5156 *
5157 * returns the pointer to the declaration or NULL if not found.
5158 */
5159static xmlElementPtr
5160xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5161 xmlNodePtr elem, int *extsubset) {
5162 xmlElementPtr elemDecl = NULL;
5163 const xmlChar *prefix = NULL;
5164
5165 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5166 if (extsubset != NULL)
5167 *extsubset = 0;
5168
5169 /*
5170 * Fetch the declaration for the qualified name
5171 */
5172 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5173 prefix = elem->ns->prefix;
5174
5175 if (prefix != NULL) {
5176 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5177 elem->name, prefix);
5178 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5179 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5180 elem->name, prefix);
5181 if ((elemDecl != NULL) && (extsubset != NULL))
5182 *extsubset = 1;
5183 }
5184 }
5185
5186 /*
5187 * Fetch the declaration for the non qualified name
5188 * This is "non-strict" validation should be done on the
5189 * full QName but in that case being flexible makes sense.
5190 */
5191 if (elemDecl == NULL) {
5192 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5193 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5194 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5195 if ((elemDecl != NULL) && (extsubset != NULL))
5196 *extsubset = 1;
5197 }
5198 }
5199 if (elemDecl == NULL) {
5200 VECTXT(ctxt, elem);
5201 VERROR(ctxt->userData, "No declaration for element %s\n",
5202 elem->name);
5203 }
5204 return(elemDecl);
5205}
5206
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005207#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005208/**
5209 * xmlValidatePushElement:
5210 * @ctxt: the validation context
5211 * @doc: a document instance
5212 * @elem: an element instance
5213 * @qname: the qualified name as appearing in the serialization
5214 *
5215 * Push a new element start on the validation stack.
5216 *
5217 * returns 1 if no validation problem was found or 0 otherwise
5218 */
5219int
5220xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5221 xmlNodePtr elem, const xmlChar *qname) {
5222 int ret = 1;
5223 xmlElementPtr eDecl;
5224 int extsubset = 0;
5225
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005226/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005227 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5228 xmlValidStatePtr state = ctxt->vstate;
5229 xmlElementPtr elemDecl;
5230
5231 /*
5232 * Check the new element agaisnt the content model of the new elem.
5233 */
5234 if (state->elemDecl != NULL) {
5235 elemDecl = state->elemDecl;
5236
5237 switch(elemDecl->etype) {
5238 case XML_ELEMENT_TYPE_UNDEFINED:
5239 ret = 0;
5240 break;
5241 case XML_ELEMENT_TYPE_EMPTY:
5242 VECTXT(ctxt, state->node);
5243 VERROR(ctxt->userData,
5244 "Element %s was declared EMPTY this one has content\n",
5245 state->node->name);
5246 ret = 0;
5247 break;
5248 case XML_ELEMENT_TYPE_ANY:
5249 /* I don't think anything is required then */
5250 break;
5251 case XML_ELEMENT_TYPE_MIXED:
5252 /* simple case of declared as #PCDATA */
5253 if ((elemDecl->content != NULL) &&
5254 (elemDecl->content->type ==
5255 XML_ELEMENT_CONTENT_PCDATA)) {
5256 VECTXT(ctxt, state->node);
5257 VERROR(ctxt->userData,
5258 "Element %s was declared #PCDATA but contains non text nodes\n",
5259 state->node->name);
5260 ret = 0;
5261 } else {
5262 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5263 qname);
5264 if (ret != 1) {
5265 VECTXT(ctxt, state->node);
5266 VERROR(ctxt->userData,
5267 "Element %s is not declared in %s list of possible children\n",
5268 qname, state->node->name);
5269 }
5270 }
5271 break;
5272 case XML_ELEMENT_TYPE_ELEMENT:
5273 /*
5274 * TODO:
5275 * VC: Standalone Document Declaration
5276 * - element types with element content, if white space
5277 * occurs directly within any instance of those types.
5278 */
5279 if (state->exec != NULL) {
5280 ret = xmlRegExecPushString(state->exec, qname, NULL);
5281 if (ret < 0) {
5282 VECTXT(ctxt, state->node);
5283 VERROR(ctxt->userData,
5284 "Element %s content does not follow the DTD\nMisplaced %s\n",
5285 state->node->name, qname);
5286 ret = 0;
5287 } else {
5288 ret = 1;
5289 }
5290 }
5291 break;
5292 }
5293 }
5294 }
5295 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5296 vstateVPush(ctxt, eDecl, elem);
5297 return(ret);
5298}
5299
5300/**
5301 * xmlValidatePushCData:
5302 * @ctxt: the validation context
5303 * @data: some character data read
5304 * @len: the lenght of the data
5305 *
5306 * check the CData parsed for validation in the current stack
5307 *
5308 * returns 1 if no validation problem was found or 0 otherwise
5309 */
5310int
5311xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5312 int ret = 1;
5313
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005314/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005315 if (len <= 0)
5316 return(ret);
5317 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5318 xmlValidStatePtr state = ctxt->vstate;
5319 xmlElementPtr elemDecl;
5320
5321 /*
5322 * Check the new element agaisnt the content model of the new elem.
5323 */
5324 if (state->elemDecl != NULL) {
5325 elemDecl = state->elemDecl;
5326
5327 switch(elemDecl->etype) {
5328 case XML_ELEMENT_TYPE_UNDEFINED:
5329 ret = 0;
5330 break;
5331 case XML_ELEMENT_TYPE_EMPTY:
5332 VECTXT(ctxt, state->node);
5333 VERROR(ctxt->userData,
5334 "Element %s was declared EMPTY this one has content\n",
5335 state->node->name);
5336 ret = 0;
5337 break;
5338 case XML_ELEMENT_TYPE_ANY:
5339 break;
5340 case XML_ELEMENT_TYPE_MIXED:
5341 break;
5342 case XML_ELEMENT_TYPE_ELEMENT:
5343 if (len > 0) {
5344 int i;
5345
5346 for (i = 0;i < len;i++) {
5347 if (!IS_BLANK(data[i])) {
5348 VECTXT(ctxt, state->node);
5349 VERROR(ctxt->userData,
5350 "Element %s content does not follow the DTD\nText not allowed\n",
5351 state->node->name);
5352 ret = 0;
5353 goto done;
5354 }
5355 }
5356 /*
5357 * TODO:
5358 * VC: Standalone Document Declaration
5359 * element types with element content, if white space
5360 * occurs directly within any instance of those types.
5361 */
5362 }
5363 break;
5364 }
5365 }
5366 }
5367done:
5368 return(ret);
5369}
5370
5371/**
5372 * xmlValidatePopElement:
5373 * @ctxt: the validation context
5374 * @doc: a document instance
5375 * @elem: an element instance
5376 * @qname: the qualified name as appearing in the serialization
5377 *
5378 * Pop the element end from the validation stack.
5379 *
5380 * returns 1 if no validation problem was found or 0 otherwise
5381 */
5382int
5383xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005384 xmlNodePtr elem ATTRIBUTE_UNUSED,
5385 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005386 int ret = 1;
5387
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005388/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005389 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5390 xmlValidStatePtr state = ctxt->vstate;
5391 xmlElementPtr elemDecl;
5392
5393 /*
5394 * Check the new element agaisnt the content model of the new elem.
5395 */
5396 if (state->elemDecl != NULL) {
5397 elemDecl = state->elemDecl;
5398
5399 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5400 if (state->exec != NULL) {
5401 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5402 if (ret == 0) {
5403 VECTXT(ctxt, state->node);
5404 VERROR(ctxt->userData,
5405 "Element %s content does not follow the DTD\nExpecting more child\n",
5406 state->node->name);
5407 } else {
5408 /*
5409 * previous validation errors should not generate
5410 * a new one here
5411 */
5412 ret = 1;
5413 }
5414 }
5415 }
5416 }
5417 vstateVPop(ctxt);
5418 }
5419 return(ret);
5420}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005421#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005422
5423/**
Owen Taylor3473f882001-02-23 17:55:21 +00005424 * xmlValidateOneElement:
5425 * @ctxt: the validation context
5426 * @doc: a document instance
5427 * @elem: an element instance
5428 *
5429 * Try to validate a single element and it's attributes,
5430 * basically it does the following checks as described by the
5431 * XML-1.0 recommendation:
5432 * - [ VC: Element Valid ]
5433 * - [ VC: Required Attribute ]
5434 * Then call xmlValidateOneAttribute() for each attribute present.
5435 *
5436 * The ID/IDREF checkings are done separately
5437 *
5438 * returns 1 if valid or 0 otherwise
5439 */
5440
5441int
5442xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5443 xmlNodePtr elem) {
5444 xmlElementPtr elemDecl = NULL;
5445 xmlElementContentPtr cont;
5446 xmlAttributePtr attr;
5447 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005448 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005449 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005450 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005451
5452 CHECK_DTD;
5453
5454 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005455 switch (elem->type) {
5456 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005457 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005458 VERROR(ctxt->userData,
5459 "Attribute element not expected here\n");
5460 return(0);
5461 case XML_TEXT_NODE:
5462 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005463 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005464 VERROR(ctxt->userData, "Text element has childs !\n");
5465 return(0);
5466 }
5467 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005468 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005469 VERROR(ctxt->userData, "Text element has attributes !\n");
5470 return(0);
5471 }
5472 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005473 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005474 VERROR(ctxt->userData, "Text element has namespace !\n");
5475 return(0);
5476 }
5477 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005478 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005479 VERROR(ctxt->userData,
5480 "Text element carries namespace definitions !\n");
5481 return(0);
5482 }
5483 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005484 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005485 VERROR(ctxt->userData,
5486 "Text element has no content !\n");
5487 return(0);
5488 }
5489 return(1);
5490 case XML_XINCLUDE_START:
5491 case XML_XINCLUDE_END:
5492 return(1);
5493 case XML_CDATA_SECTION_NODE:
5494 case XML_ENTITY_REF_NODE:
5495 case XML_PI_NODE:
5496 case XML_COMMENT_NODE:
5497 return(1);
5498 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005499 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005500 VERROR(ctxt->userData,
5501 "Entity element not expected here\n");
5502 return(0);
5503 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005504 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005505 VERROR(ctxt->userData,
5506 "Notation element not expected here\n");
5507 return(0);
5508 case XML_DOCUMENT_NODE:
5509 case XML_DOCUMENT_TYPE_NODE:
5510 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005511 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005512 VERROR(ctxt->userData,
5513 "Document element not expected here\n");
5514 return(0);
5515 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005516 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005517 VERROR(ctxt->userData,
5518 "\n");
5519 return(0);
5520 case XML_ELEMENT_NODE:
5521 break;
5522 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005523 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005524 VERROR(ctxt->userData,
5525 "unknown element type %d\n", elem->type);
5526 return(0);
5527 }
Owen Taylor3473f882001-02-23 17:55:21 +00005528
5529 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005530 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005531 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005532 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5533 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005534 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005535
Daniel Veillardea7751d2002-12-20 00:16:24 +00005536 /*
5537 * If vstateNr is not zero that means continuous validation is
5538 * activated, do not try to check the content model at that level.
5539 */
5540 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005541 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005542 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005543 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005544 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005545 VERROR(ctxt->userData, "No declaration for element %s\n",
5546 elem->name);
5547 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005548 case XML_ELEMENT_TYPE_EMPTY:
5549 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005550 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005551 VERROR(ctxt->userData,
5552 "Element %s was declared EMPTY this one has content\n",
5553 elem->name);
5554 ret = 0;
5555 }
5556 break;
5557 case XML_ELEMENT_TYPE_ANY:
5558 /* I don't think anything is required then */
5559 break;
5560 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005561
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005562 /* simple case of declared as #PCDATA */
5563 if ((elemDecl->content != NULL) &&
5564 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5565 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5566 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005567 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005568 VERROR(ctxt->userData,
5569 "Element %s was declared #PCDATA but contains non text nodes\n",
5570 elem->name);
5571 }
5572 break;
5573 }
Owen Taylor3473f882001-02-23 17:55:21 +00005574 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005575 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005576 while (child != NULL) {
5577 if (child->type == XML_ELEMENT_NODE) {
5578 name = child->name;
5579 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005580 xmlChar fn[50];
5581 xmlChar *fullname;
5582
5583 fullname = xmlBuildQName(child->name, child->ns->prefix,
5584 fn, 50);
5585 if (fullname == NULL)
5586 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005587 cont = elemDecl->content;
5588 while (cont != NULL) {
5589 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005590 if (xmlStrEqual(cont->name, fullname))
5591 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005592 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5593 (cont->c1 != NULL) &&
5594 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005595 if (xmlStrEqual(cont->c1->name, fullname))
5596 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005597 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5598 (cont->c1 == NULL) ||
5599 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5600 /* Internal error !!! */
5601 xmlGenericError(xmlGenericErrorContext,
5602 "Internal: MIXED struct bad\n");
5603 break;
5604 }
5605 cont = cont->c2;
5606 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005607 if ((fullname != fn) && (fullname != child->name))
5608 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005609 if (cont != NULL)
5610 goto child_ok;
5611 }
5612 cont = elemDecl->content;
5613 while (cont != NULL) {
5614 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5615 if (xmlStrEqual(cont->name, name)) break;
5616 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5617 (cont->c1 != NULL) &&
5618 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5619 if (xmlStrEqual(cont->c1->name, name)) break;
5620 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5621 (cont->c1 == NULL) ||
5622 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5623 /* Internal error !!! */
5624 xmlGenericError(xmlGenericErrorContext,
5625 "Internal: MIXED struct bad\n");
5626 break;
5627 }
5628 cont = cont->c2;
5629 }
5630 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005631 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005632 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005633 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005634 name, elem->name);
5635 ret = 0;
5636 }
5637 }
5638child_ok:
5639 child = child->next;
5640 }
5641 break;
5642 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005643 if ((doc->standalone == 1) && (extsubset == 1)) {
5644 /*
5645 * VC: Standalone Document Declaration
5646 * - element types with element content, if white space
5647 * occurs directly within any instance of those types.
5648 */
5649 child = elem->children;
5650 while (child != NULL) {
5651 if (child->type == XML_TEXT_NODE) {
5652 const xmlChar *content = child->content;
5653
5654 while (IS_BLANK(*content))
5655 content++;
5656 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005657 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005658 VERROR(ctxt->userData,
5659"standalone: %s declared in the external subset contains white spaces nodes\n",
5660 elem->name);
5661 ret = 0;
5662 break;
5663 }
5664 }
5665 child =child->next;
5666 }
5667 }
Owen Taylor3473f882001-02-23 17:55:21 +00005668 child = elem->children;
5669 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005670 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005671 if (tmp <= 0)
5672 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005673 break;
5674 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005675 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005676
5677 /* [ VC: Required Attribute ] */
5678 attr = elemDecl->attributes;
5679 while (attr != NULL) {
5680 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005681 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005682
Daniel Veillarde4301c82002-02-13 13:32:35 +00005683 if ((attr->prefix == NULL) &&
5684 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5685 xmlNsPtr ns;
5686
5687 ns = elem->nsDef;
5688 while (ns != NULL) {
5689 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005690 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005691 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005692 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005693 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5694 xmlNsPtr ns;
5695
5696 ns = elem->nsDef;
5697 while (ns != NULL) {
5698 if (xmlStrEqual(attr->name, ns->prefix))
5699 goto found;
5700 ns = ns->next;
5701 }
5702 } else {
5703 xmlAttrPtr attrib;
5704
5705 attrib = elem->properties;
5706 while (attrib != NULL) {
5707 if (xmlStrEqual(attrib->name, attr->name)) {
5708 if (attr->prefix != NULL) {
5709 xmlNsPtr nameSpace = attrib->ns;
5710
5711 if (nameSpace == NULL)
5712 nameSpace = elem->ns;
5713 /*
5714 * qualified names handling is problematic, having a
5715 * different prefix should be possible but DTDs don't
5716 * allow to define the URI instead of the prefix :-(
5717 */
5718 if (nameSpace == NULL) {
5719 if (qualified < 0)
5720 qualified = 0;
5721 } else if (!xmlStrEqual(nameSpace->prefix,
5722 attr->prefix)) {
5723 if (qualified < 1)
5724 qualified = 1;
5725 } else
5726 goto found;
5727 } else {
5728 /*
5729 * We should allow applications to define namespaces
5730 * for their application even if the DTD doesn't
5731 * carry one, otherwise, basically we would always
5732 * break.
5733 */
5734 goto found;
5735 }
5736 }
5737 attrib = attrib->next;
5738 }
Owen Taylor3473f882001-02-23 17:55:21 +00005739 }
5740 if (qualified == -1) {
5741 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005742 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005743 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005744 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005745 elem->name, attr->name);
5746 ret = 0;
5747 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005748 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005749 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005750 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005751 elem->name, attr->prefix,attr->name);
5752 ret = 0;
5753 }
5754 } else if (qualified == 0) {
5755 VWARNING(ctxt->userData,
5756 "Element %s required attribute %s:%s has no prefix\n",
5757 elem->name, attr->prefix,attr->name);
5758 } else if (qualified == 1) {
5759 VWARNING(ctxt->userData,
5760 "Element %s required attribute %s:%s has different prefix\n",
5761 elem->name, attr->prefix,attr->name);
5762 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005763 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5764 /*
5765 * Special tests checking #FIXED namespace declarations
5766 * have the right value since this is not done as an
5767 * attribute checking
5768 */
5769 if ((attr->prefix == NULL) &&
5770 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5771 xmlNsPtr ns;
5772
5773 ns = elem->nsDef;
5774 while (ns != NULL) {
5775 if (ns->prefix == NULL) {
5776 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005777 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005778 VERROR(ctxt->userData,
5779 "Element %s namespace name for default namespace does not match the DTD\n",
5780 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005781 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005782 }
5783 goto found;
5784 }
5785 ns = ns->next;
5786 }
5787 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5788 xmlNsPtr ns;
5789
5790 ns = elem->nsDef;
5791 while (ns != NULL) {
5792 if (xmlStrEqual(attr->name, ns->prefix)) {
5793 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005794 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005795 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005796 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005797 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005798 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005799 }
5800 goto found;
5801 }
5802 ns = ns->next;
5803 }
5804 }
Owen Taylor3473f882001-02-23 17:55:21 +00005805 }
5806found:
5807 attr = attr->nexth;
5808 }
5809 return(ret);
5810}
5811
5812/**
5813 * xmlValidateRoot:
5814 * @ctxt: the validation context
5815 * @doc: a document instance
5816 *
5817 * Try to validate a the root element
5818 * basically it does the following check as described by the
5819 * XML-1.0 recommendation:
5820 * - [ VC: Root Element Type ]
5821 * it doesn't try to recurse or apply other check to the element
5822 *
5823 * returns 1 if valid or 0 otherwise
5824 */
5825
5826int
5827xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5828 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00005829 int ret;
5830
Owen Taylor3473f882001-02-23 17:55:21 +00005831 if (doc == NULL) return(0);
5832
5833 root = xmlDocGetRootElement(doc);
5834 if ((root == NULL) || (root->name == NULL)) {
5835 VERROR(ctxt->userData, "Not valid: no root element\n");
5836 return(0);
5837 }
5838
5839 /*
5840 * When doing post validation against a separate DTD, those may
5841 * no internal subset has been generated
5842 */
5843 if ((doc->intSubset != NULL) &&
5844 (doc->intSubset->name != NULL)) {
5845 /*
5846 * Check first the document root against the NQName
5847 */
5848 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5849 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005850 xmlChar fn[50];
5851 xmlChar *fullname;
5852
5853 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
5854 if (fullname == NULL) {
5855 VERROR(ctxt->userData, "Out of memory\n");
5856 return(0);
5857 }
5858 ret = xmlStrEqual(doc->intSubset->name, fullname);
5859 if ((fullname != fn) && (fullname != root->name))
5860 xmlFree(fullname);
5861 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00005862 goto name_ok;
5863 }
5864 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5865 (xmlStrEqual(root->name, BAD_CAST "html")))
5866 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005867 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005868 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005869 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005870 root->name, doc->intSubset->name);
5871 return(0);
5872
5873 }
5874 }
5875name_ok:
5876 return(1);
5877}
5878
5879
5880/**
5881 * xmlValidateElement:
5882 * @ctxt: the validation context
5883 * @doc: a document instance
5884 * @elem: an element instance
5885 *
5886 * Try to validate the subtree under an element
5887 *
5888 * returns 1 if valid or 0 otherwise
5889 */
5890
5891int
5892xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5893 xmlNodePtr child;
5894 xmlAttrPtr attr;
5895 xmlChar *value;
5896 int ret = 1;
5897
5898 if (elem == NULL) return(0);
5899
5900 /*
5901 * XInclude elements were added after parsing in the infoset,
5902 * they don't really mean anything validation wise.
5903 */
5904 if ((elem->type == XML_XINCLUDE_START) ||
5905 (elem->type == XML_XINCLUDE_END))
5906 return(1);
5907
5908 CHECK_DTD;
5909
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005910 /*
5911 * Entities references have to be handled separately
5912 */
5913 if (elem->type == XML_ENTITY_REF_NODE) {
5914 return(1);
5915 }
5916
Owen Taylor3473f882001-02-23 17:55:21 +00005917 ret &= xmlValidateOneElement(ctxt, doc, elem);
5918 attr = elem->properties;
5919 while(attr != NULL) {
5920 value = xmlNodeListGetString(doc, attr->children, 0);
5921 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5922 if (value != NULL)
5923 xmlFree(value);
5924 attr= attr->next;
5925 }
5926 child = elem->children;
5927 while (child != NULL) {
5928 ret &= xmlValidateElement(ctxt, doc, child);
5929 child = child->next;
5930 }
5931
5932 return(ret);
5933}
5934
Daniel Veillard8730c562001-02-26 10:49:57 +00005935/**
5936 * xmlValidateRef:
5937 * @ref: A reference to be validated
5938 * @ctxt: Validation context
5939 * @name: Name of ID we are searching for
5940 *
5941 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005942static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005943xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005944 const xmlChar *name) {
5945 xmlAttrPtr id;
5946 xmlAttrPtr attr;
5947
5948 if (ref == NULL)
5949 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005950 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00005951 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005952 attr = ref->attr;
5953 if (attr == NULL) {
5954 xmlChar *dup, *str = NULL, *cur, save;
5955
5956 dup = xmlStrdup(name);
5957 if (dup == NULL) {
5958 ctxt->valid = 0;
5959 return;
5960 }
5961 cur = dup;
5962 while (*cur != 0) {
5963 str = cur;
5964 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5965 save = *cur;
5966 *cur = 0;
5967 id = xmlGetID(ctxt->doc, str);
5968 if (id == NULL) {
5969 VERROR(ctxt->userData,
5970 "attribute %s line %d references an unknown ID \"%s\"\n",
5971 ref->name, ref->lineno, str);
5972 ctxt->valid = 0;
5973 }
5974 if (save == 0)
5975 break;
5976 *cur = save;
5977 while (IS_BLANK(*cur)) cur++;
5978 }
5979 xmlFree(dup);
5980 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00005981 id = xmlGetID(ctxt->doc, name);
5982 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005983 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005984 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005985 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005986 attr->name, name);
5987 ctxt->valid = 0;
5988 }
5989 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5990 xmlChar *dup, *str = NULL, *cur, save;
5991
5992 dup = xmlStrdup(name);
5993 if (dup == NULL) {
5994 ctxt->valid = 0;
5995 return;
5996 }
5997 cur = dup;
5998 while (*cur != 0) {
5999 str = cur;
6000 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
6001 save = *cur;
6002 *cur = 0;
6003 id = xmlGetID(ctxt->doc, str);
6004 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00006005 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00006006 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00006007 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00006008 attr->name, str);
6009 ctxt->valid = 0;
6010 }
6011 if (save == 0)
6012 break;
6013 *cur = save;
6014 while (IS_BLANK(*cur)) cur++;
6015 }
6016 xmlFree(dup);
6017 }
6018}
6019
6020/**
Daniel Veillard8730c562001-02-26 10:49:57 +00006021 * xmlWalkValidateList:
6022 * @data: Contents of current link
6023 * @user: Value supplied by the user
6024 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00006025 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00006026 */
6027static int
6028xmlWalkValidateList(const void *data, const void *user)
6029{
6030 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
6031 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
6032 return 1;
6033}
6034
6035/**
6036 * xmlValidateCheckRefCallback:
6037 * @ref_list: List of references
6038 * @ctxt: Validation context
6039 * @name: Name of ID we are searching for
6040 *
6041 */
6042static void
6043xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
6044 const xmlChar *name) {
6045 xmlValidateMemo memo;
6046
6047 if (ref_list == NULL)
6048 return;
6049 memo.ctxt = ctxt;
6050 memo.name = name;
6051
6052 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
6053
6054}
6055
6056/**
Owen Taylor3473f882001-02-23 17:55:21 +00006057 * xmlValidateDocumentFinal:
6058 * @ctxt: the validation context
6059 * @doc: a document instance
6060 *
6061 * Does the final step for the document validation once all the
6062 * incremental validation steps have been completed
6063 *
6064 * basically it does the following checks described by the XML Rec
6065 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00006066 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00006067 *
6068 * returns 1 if valid or 0 otherwise
6069 */
6070
6071int
6072xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6073 xmlRefTablePtr table;
6074
6075 if (doc == NULL) {
6076 xmlGenericError(xmlGenericErrorContext,
6077 "xmlValidateDocumentFinal: doc == NULL\n");
6078 return(0);
6079 }
6080
6081 /*
6082 * Check all the NOTATION/NOTATIONS attributes
6083 */
6084 /*
6085 * Check all the ENTITY/ENTITIES attributes definition for validity
6086 */
6087 /*
6088 * Check all the IDREF/IDREFS attributes definition for validity
6089 */
6090 table = (xmlRefTablePtr) doc->refs;
6091 ctxt->doc = doc;
6092 ctxt->valid = 1;
6093 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6094 return(ctxt->valid);
6095}
6096
6097/**
6098 * xmlValidateDtd:
6099 * @ctxt: the validation context
6100 * @doc: a document instance
6101 * @dtd: a dtd instance
6102 *
6103 * Try to validate the document against the dtd instance
6104 *
6105 * basically it does check all the definitions in the DtD.
6106 *
6107 * returns 1 if valid or 0 otherwise
6108 */
6109
6110int
6111xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6112 int ret;
6113 xmlDtdPtr oldExt;
6114 xmlNodePtr root;
6115
6116 if (dtd == NULL) return(0);
6117 if (doc == NULL) return(0);
6118 oldExt = doc->extSubset;
6119 doc->extSubset = dtd;
6120 ret = xmlValidateRoot(ctxt, doc);
6121 if (ret == 0) {
6122 doc->extSubset = oldExt;
6123 return(ret);
6124 }
6125 if (doc->ids != NULL) {
6126 xmlFreeIDTable(doc->ids);
6127 doc->ids = NULL;
6128 }
6129 if (doc->refs != NULL) {
6130 xmlFreeRefTable(doc->refs);
6131 doc->refs = NULL;
6132 }
6133 root = xmlDocGetRootElement(doc);
6134 ret = xmlValidateElement(ctxt, doc, root);
6135 ret &= xmlValidateDocumentFinal(ctxt, doc);
6136 doc->extSubset = oldExt;
6137 return(ret);
6138}
6139
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006140static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006141xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6142 const xmlChar *name ATTRIBUTE_UNUSED) {
6143 if (cur == NULL)
6144 return;
6145 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6146 xmlChar *notation = cur->content;
6147
Daniel Veillard878eab02002-02-19 13:46:09 +00006148 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006149 int ret;
6150
6151 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6152 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006153 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006154 }
6155 }
6156 }
6157}
6158
6159static void
Owen Taylor3473f882001-02-23 17:55:21 +00006160xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006161 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006162 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006163 xmlDocPtr doc;
6164 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006165
Owen Taylor3473f882001-02-23 17:55:21 +00006166 if (cur == NULL)
6167 return;
6168 switch (cur->atype) {
6169 case XML_ATTRIBUTE_CDATA:
6170 case XML_ATTRIBUTE_ID:
6171 case XML_ATTRIBUTE_IDREF :
6172 case XML_ATTRIBUTE_IDREFS:
6173 case XML_ATTRIBUTE_NMTOKEN:
6174 case XML_ATTRIBUTE_NMTOKENS:
6175 case XML_ATTRIBUTE_ENUMERATION:
6176 break;
6177 case XML_ATTRIBUTE_ENTITY:
6178 case XML_ATTRIBUTE_ENTITIES:
6179 case XML_ATTRIBUTE_NOTATION:
6180 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006181
6182 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6183 cur->atype, cur->defaultValue);
6184 if ((ret == 0) && (ctxt->valid == 1))
6185 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006186 }
6187 if (cur->tree != NULL) {
6188 xmlEnumerationPtr tree = cur->tree;
6189 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006190 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006191 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006192 if ((ret == 0) && (ctxt->valid == 1))
6193 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006194 tree = tree->next;
6195 }
6196 }
6197 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006198 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6199 doc = cur->doc;
6200 if ((doc == NULL) || (cur->elem == NULL)) {
6201 VERROR(ctxt->userData,
6202 "xmlValidateAttributeCallback(%s): internal error\n",
6203 cur->name);
6204 return;
6205 }
6206 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6207 if (elem == NULL)
6208 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6209 if (elem == NULL) {
6210 VERROR(ctxt->userData,
6211 "attribute %s: could not find decl for element %s\n",
6212 cur->name, cur->elem);
6213 return;
6214 }
6215 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6216 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006217 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00006218 cur->name, cur->elem);
6219 ctxt->valid = 0;
6220 }
6221 }
Owen Taylor3473f882001-02-23 17:55:21 +00006222}
6223
6224/**
6225 * xmlValidateDtdFinal:
6226 * @ctxt: the validation context
6227 * @doc: a document instance
6228 *
6229 * Does the final step for the dtds validation once all the
6230 * subsets have been parsed
6231 *
6232 * basically it does the following checks described by the XML Rec
6233 * - check that ENTITY and ENTITIES type attributes default or
6234 * possible values matches one of the defined entities.
6235 * - check that NOTATION type attributes default or
6236 * possible values matches one of the defined notations.
6237 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006238 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006239 */
6240
6241int
6242xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006243 xmlDtdPtr dtd;
6244 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006245 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006246
6247 if (doc == NULL) return(0);
6248 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6249 return(0);
6250 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006251 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006252 dtd = doc->intSubset;
6253 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6254 table = (xmlAttributeTablePtr) dtd->attributes;
6255 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006256 }
6257 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006258 entities = (xmlEntitiesTablePtr) dtd->entities;
6259 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6260 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006261 }
6262 dtd = doc->extSubset;
6263 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6264 table = (xmlAttributeTablePtr) dtd->attributes;
6265 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006266 }
6267 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006268 entities = (xmlEntitiesTablePtr) dtd->entities;
6269 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6270 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006271 }
6272 return(ctxt->valid);
6273}
6274
6275/**
6276 * xmlValidateDocument:
6277 * @ctxt: the validation context
6278 * @doc: a document instance
6279 *
6280 * Try to validate the document instance
6281 *
6282 * basically it does the all the checks described by the XML Rec
6283 * i.e. validates the internal and external subset (if present)
6284 * and validate the document tree.
6285 *
6286 * returns 1 if valid or 0 otherwise
6287 */
6288
6289int
6290xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6291 int ret;
6292 xmlNodePtr root;
6293
Daniel Veillard2fd85422002-10-16 14:32:41 +00006294 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6295 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00006296 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006297 }
Owen Taylor3473f882001-02-23 17:55:21 +00006298 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6299 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6300 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6301 doc->intSubset->SystemID);
6302 if (doc->extSubset == NULL) {
6303 if (doc->intSubset->SystemID != NULL) {
6304 VERROR(ctxt->userData,
6305 "Could not load the external subset \"%s\"\n",
6306 doc->intSubset->SystemID);
6307 } else {
6308 VERROR(ctxt->userData,
6309 "Could not load the external subset \"%s\"\n",
6310 doc->intSubset->ExternalID);
6311 }
6312 return(0);
6313 }
6314 }
6315
6316 if (doc->ids != NULL) {
6317 xmlFreeIDTable(doc->ids);
6318 doc->ids = NULL;
6319 }
6320 if (doc->refs != NULL) {
6321 xmlFreeRefTable(doc->refs);
6322 doc->refs = NULL;
6323 }
6324 ret = xmlValidateDtdFinal(ctxt, doc);
6325 if (!xmlValidateRoot(ctxt, doc)) return(0);
6326
6327 root = xmlDocGetRootElement(doc);
6328 ret &= xmlValidateElement(ctxt, doc, root);
6329 ret &= xmlValidateDocumentFinal(ctxt, doc);
6330 return(ret);
6331}
6332
Owen Taylor3473f882001-02-23 17:55:21 +00006333/************************************************************************
6334 * *
6335 * Routines for dynamic validation editing *
6336 * *
6337 ************************************************************************/
6338
6339/**
6340 * xmlValidGetPotentialChildren:
6341 * @ctree: an element content tree
6342 * @list: an array to store the list of child names
6343 * @len: a pointer to the number of element in the list
6344 * @max: the size of the array
6345 *
6346 * Build/extend a list of potential children allowed by the content tree
6347 *
6348 * returns the number of element in the list, or -1 in case of error.
6349 */
6350
6351int
6352xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6353 int *len, int max) {
6354 int i;
6355
6356 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6357 return(-1);
6358 if (*len >= max) return(*len);
6359
6360 switch (ctree->type) {
6361 case XML_ELEMENT_CONTENT_PCDATA:
6362 for (i = 0; i < *len;i++)
6363 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6364 list[(*len)++] = BAD_CAST "#PCDATA";
6365 break;
6366 case XML_ELEMENT_CONTENT_ELEMENT:
6367 for (i = 0; i < *len;i++)
6368 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6369 list[(*len)++] = ctree->name;
6370 break;
6371 case XML_ELEMENT_CONTENT_SEQ:
6372 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6373 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6374 break;
6375 case XML_ELEMENT_CONTENT_OR:
6376 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6377 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6378 break;
6379 }
6380
6381 return(*len);
6382}
6383
6384/**
6385 * xmlValidGetValidElements:
6386 * @prev: an element to insert after
6387 * @next: an element to insert next
6388 * @list: an array to store the list of child names
6389 * @max: the size of the array
6390 *
6391 * This function returns the list of authorized children to insert
6392 * within an existing tree while respecting the validity constraints
6393 * forced by the Dtd. The insertion point is defined using @prev and
6394 * @next in the following ways:
6395 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6396 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6397 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6398 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6399 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6400 *
6401 * pointers to the element names are inserted at the beginning of the array
6402 * and do not need to be freed.
6403 *
6404 * returns the number of element in the list, or -1 in case of error. If
6405 * the function returns the value @max the caller is invited to grow the
6406 * receiving array and retry.
6407 */
6408
6409int
6410xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6411 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006412 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006413 int nb_valid_elements = 0;
6414 const xmlChar *elements[256];
6415 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006416 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006417
6418 xmlNode *ref_node;
6419 xmlNode *parent;
6420 xmlNode *test_node;
6421
6422 xmlNode *prev_next;
6423 xmlNode *next_prev;
6424 xmlNode *parent_childs;
6425 xmlNode *parent_last;
6426
6427 xmlElement *element_desc;
6428
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006429 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006430
Owen Taylor3473f882001-02-23 17:55:21 +00006431 if (prev == NULL && next == NULL)
6432 return(-1);
6433
6434 if (list == NULL) return(-1);
6435 if (max <= 0) return(-1);
6436
6437 nb_valid_elements = 0;
6438 ref_node = prev ? prev : next;
6439 parent = ref_node->parent;
6440
6441 /*
6442 * Retrieves the parent element declaration
6443 */
6444 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6445 parent->name);
6446 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6447 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6448 parent->name);
6449 if (element_desc == NULL) return(-1);
6450
6451 /*
6452 * Do a backup of the current tree structure
6453 */
6454 prev_next = prev ? prev->next : NULL;
6455 next_prev = next ? next->prev : NULL;
6456 parent_childs = parent->children;
6457 parent_last = parent->last;
6458
6459 /*
6460 * Creates a dummy node and insert it into the tree
6461 */
6462 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6463 test_node->doc = ref_node->doc;
6464 test_node->parent = parent;
6465 test_node->prev = prev;
6466 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006467 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006468
6469 if (prev) prev->next = test_node;
6470 else parent->children = test_node;
6471
6472 if (next) next->prev = test_node;
6473 else parent->last = test_node;
6474
6475 /*
6476 * Insert each potential child node and check if the parent is
6477 * still valid
6478 */
6479 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6480 elements, &nb_elements, 256);
6481
6482 for (i = 0;i < nb_elements;i++) {
6483 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006484 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006485 int j;
6486
6487 for (j = 0; j < nb_valid_elements;j++)
6488 if (xmlStrEqual(elements[i], list[j])) break;
6489 list[nb_valid_elements++] = elements[i];
6490 if (nb_valid_elements >= max) break;
6491 }
6492 }
6493
6494 /*
6495 * Restore the tree structure
6496 */
6497 if (prev) prev->next = prev_next;
6498 if (next) next->prev = next_prev;
6499 parent->children = parent_childs;
6500 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006501
6502 /*
6503 * Free up the dummy node
6504 */
6505 test_node->name = name;
6506 xmlFreeNode(test_node);
6507
Owen Taylor3473f882001-02-23 17:55:21 +00006508 return(nb_valid_elements);
6509}
Daniel Veillard4432df22003-09-28 18:58:27 +00006510#endif /* LIBXML_VALID_ENABLED */
6511