blob: b3b52c9f4374ceabc585c295b43288fa413cc03b [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * valid.c : part of the code use to do the DTD handling and the validity
3 * checking
4 *
5 * See Copyright for the status of this software.
6 *
Daniel Veillardc5d64342001-06-24 12:13:24 +00007 * daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +00008 */
9
Daniel Veillard34ce8be2002-03-18 19:37:11 +000010#define IN_LIBXML
Bjorn Reese70a9da52001-04-21 16:57:29 +000011#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000012
Owen Taylor3473f882001-02-23 17:55:21 +000013#include <string.h>
14
15#ifdef HAVE_STDLIB_H
16#include <stdlib.h>
17#endif
18
19#include <libxml/xmlmemory.h>
20#include <libxml/hash.h>
21#include <libxml/valid.h>
22#include <libxml/parser.h>
23#include <libxml/parserInternals.h>
24#include <libxml/xmlerror.h>
25#include <libxml/list.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000026#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000027
Daniel Veillarde62d36c2001-05-15 08:53:16 +000028/* #define DEBUG_VALID_ALGO */
Daniel Veillard5acfd6b2002-09-18 16:29:02 +000029/* #define DEBUG_REGEXP_ALGO */
Daniel Veillarde62d36c2001-05-15 08:53:16 +000030
Daniel Veillarda646cfd2002-09-17 21:50:03 +000031#define TODO \
32 xmlGenericError(xmlGenericErrorContext, \
33 "Unimplemented block at %s:%d\n", \
34 __FILE__, __LINE__);
Owen Taylor3473f882001-02-23 17:55:21 +000035
Daniel Veillardea7751d2002-12-20 00:16:24 +000036#define VERROR \
37 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
Owen Taylor3473f882001-02-23 17:55:21 +000038
Daniel Veillardea7751d2002-12-20 00:16:24 +000039#define VWARNING \
40 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
41
42
43#ifdef LIBXML_REGEXP_ENABLED
44/*
45 * If regexp are enabled we can do continuous validation without the
46 * need of a tree to validate the content model. this is done in each
47 * callbacks.
48 * Each xmlValidState represent the validation state associated to the
49 * set of nodes currently open from the document root to the current element.
50 */
51
52
53typedef struct _xmlValidState {
54 xmlElementPtr elemDecl; /* pointer to the content model */
55 xmlNodePtr node; /* pointer to the current node */
56 xmlRegExecCtxtPtr exec; /* regexp runtime */
57} _xmlValidState;
58
59
60static int
61vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) {
62 if (ctxt->vstateMax == 0) {
63 ctxt->vstateMax = 10;
64 ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax *
65 sizeof(ctxt->vstateTab[0]));
66 if (ctxt->vstateTab == NULL) {
67 VERROR(ctxt->userData, "realloc failed !n");
68 return(-1);
69 }
70 }
71
72 if (ctxt->vstateNr >= ctxt->vstateMax) {
73 ctxt->vstateMax *= 2;
74 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
75 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
76 if (ctxt->vstateTab == NULL) {
77 VERROR(ctxt->userData, "realloc failed !n");
78 return(-1);
79 }
80 }
81 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr];
82 ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl;
83 ctxt->vstateTab[ctxt->vstateNr].node = node;
84 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
85 if (elemDecl->contModel == NULL)
86 xmlValidBuildContentModel(ctxt, elemDecl);
87 if (elemDecl->contModel != NULL) {
88 ctxt->vstateTab[ctxt->vstateNr].exec =
89 xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
90 } else {
91 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
92 VERROR(ctxt->userData,
93 "Failed to build content model regexp for %s", node->name);
94 }
95 }
96 return(ctxt->vstateNr++);
97}
98
99static int
100vstateVPop(xmlValidCtxtPtr ctxt) {
101 xmlElementPtr elemDecl;
102
Daniel Veillard336fc7d2002-12-27 19:37:04 +0000103 if (ctxt->vstateNr < 1) return(-1);
Daniel Veillardea7751d2002-12-20 00:16:24 +0000104 ctxt->vstateNr--;
105 elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
106 ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
107 ctxt->vstateTab[ctxt->vstateNr].node = NULL;
108 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
109 xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
110 }
111 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
112 if (ctxt->vstateNr >= 1)
113 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
114 else
115 ctxt->vstate = NULL;
116 return(ctxt->vstateNr);
117}
118
119#else /* not LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000120/*
Daniel Veillard1c732d22002-11-30 11:22:59 +0000121 * If regexp are not enabled, it uses a home made algorithm less
122 * complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000123 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000124 * only restriction is on the deepness of the tree limited by the
125 * size of the occurs bitfield
126 *
127 * this is the content of a saved state for rollbacks
128 */
129
130#define ROLLBACK_OR 0
131#define ROLLBACK_PARENT 1
132
Daniel Veillardb44025c2001-10-11 22:55:55 +0000133typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000134 xmlElementContentPtr cont; /* pointer to the content model subtree */
135 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000136 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000137 unsigned char depth; /* current depth in the overall tree */
138 unsigned char state; /* ROLLBACK_XXX */
139} _xmlValidState;
140
Daniel Veillardfc57b412002-04-29 15:50:14 +0000141#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000142#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
143#define CONT ctxt->vstate->cont
144#define NODE ctxt->vstate->node
145#define DEPTH ctxt->vstate->depth
146#define OCCURS ctxt->vstate->occurs
147#define STATE ctxt->vstate->state
148
Daniel Veillard5344c602001-12-31 16:37:34 +0000149#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
150#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000151
Daniel Veillard5344c602001-12-31 16:37:34 +0000152#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
153#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000154
155static int
156vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
157 xmlNodePtr node, unsigned char depth, long occurs,
158 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000159 int i = ctxt->vstateNr - 1;
160
Daniel Veillard940492d2002-04-15 10:15:25 +0000161 if (ctxt->vstateNr > MAX_RECURSE) {
162 return(-1);
163 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000164 if (ctxt->vstateNr >= ctxt->vstateMax) {
165 ctxt->vstateMax *= 2;
166 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
167 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
168 if (ctxt->vstateTab == NULL) {
169 xmlGenericError(xmlGenericErrorContext,
170 "realloc failed !n");
Daniel Veillard940492d2002-04-15 10:15:25 +0000171 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000172 }
Daniel Veillard06803992001-04-22 10:35:56 +0000173 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000174 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000175 /*
176 * Don't push on the stack a state already here
177 */
178 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
179 (ctxt->vstateTab[i].node == node) &&
180 (ctxt->vstateTab[i].depth == depth) &&
181 (ctxt->vstateTab[i].occurs == occurs) &&
182 (ctxt->vstateTab[i].state == state))
183 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000184 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
185 ctxt->vstateTab[ctxt->vstateNr].node = node;
186 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
187 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
188 ctxt->vstateTab[ctxt->vstateNr].state = state;
189 return(ctxt->vstateNr++);
190}
191
192static int
193vstateVPop(xmlValidCtxtPtr ctxt) {
194 if (ctxt->vstateNr <= 1) return(-1);
195 ctxt->vstateNr--;
196 ctxt->vstate = &ctxt->vstateTab[0];
197 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
198 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
199 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
200 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
201 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
202 return(ctxt->vstateNr);
203}
204
Daniel Veillard118aed72002-09-24 14:13:13 +0000205#endif /* LIBXML_REGEXP_ENABLED */
206
Daniel Veillard1c732d22002-11-30 11:22:59 +0000207static int
208nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value)
209{
210 if (ctxt->nodeMax <= 0) {
211 ctxt->nodeMax = 4;
212 ctxt->nodeTab =
213 (xmlNodePtr *) xmlMalloc(ctxt->nodeMax *
214 sizeof(ctxt->nodeTab[0]));
215 if (ctxt->nodeTab == NULL) {
216 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
217 ctxt->nodeMax = 0;
218 return (0);
219 }
220 }
221 if (ctxt->nodeNr >= ctxt->nodeMax) {
222 ctxt->nodeMax *= 2;
223 ctxt->nodeTab =
224 (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
225 ctxt->nodeMax *
226 sizeof(ctxt->nodeTab[0]));
227 if (ctxt->nodeTab == NULL) {
228 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
229 return (0);
230 }
231 }
232 ctxt->nodeTab[ctxt->nodeNr] = value;
233 ctxt->node = value;
234 return (ctxt->nodeNr++);
235}
236static xmlNodePtr
237nodeVPop(xmlValidCtxtPtr ctxt)
238{
239 xmlNodePtr ret;
240
241 if (ctxt->nodeNr <= 0)
242 return (0);
243 ctxt->nodeNr--;
244 if (ctxt->nodeNr > 0)
245 ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1];
246 else
247 ctxt->node = NULL;
248 ret = ctxt->nodeTab[ctxt->nodeNr];
249 ctxt->nodeTab[ctxt->nodeNr] = 0;
250 return (ret);
251}
Owen Taylor3473f882001-02-23 17:55:21 +0000252
Daniel Veillard336fc7d2002-12-27 19:37:04 +0000253#if 0
254/**
255 * xmlFreeValidCtxt:
256 * @ctxt: a validation context
257 *
258 * Free the memory allocated for a validation context
259 */
260void
261xmlFreeValidCtxt(xmlValidCtxtPtr ctxt) {
262 if (ctxt == NULL)
263 return;
264#ifdef LIBXML_REGEXP_ENABLED
265 while (ctxt->vstateNr >= 0)
266 vstateVPop(ctxt);
267 if (ctxt->vstateNr <= 1) return(-1);
268 ctxt->vstateNr--;
269 elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
270 ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
271 ctxt->vstateTab[ctxt->vstateNr].node = NULL;
272 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
273 xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
274 }
275 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
276 if (ctxt->vstateNr >= 1)
277 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
278 else
279 ctxt->vstate = NULL;
280 return(ctxt->vstateNr);
281#else /* ! LIBXML_REGEXP_ENABLED */
282#endif /* LIBXML_REGEXP_ENABLED */
283}
284#endif
285
Owen Taylor3473f882001-02-23 17:55:21 +0000286#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000287static void
288xmlValidPrintNode(xmlNodePtr cur) {
289 if (cur == NULL) {
290 xmlGenericError(xmlGenericErrorContext, "null");
291 return;
292 }
293 switch (cur->type) {
294 case XML_ELEMENT_NODE:
295 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
296 break;
297 case XML_TEXT_NODE:
298 xmlGenericError(xmlGenericErrorContext, "text ");
299 break;
300 case XML_CDATA_SECTION_NODE:
301 xmlGenericError(xmlGenericErrorContext, "cdata ");
302 break;
303 case XML_ENTITY_REF_NODE:
304 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
305 break;
306 case XML_PI_NODE:
307 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
308 break;
309 case XML_COMMENT_NODE:
310 xmlGenericError(xmlGenericErrorContext, "comment ");
311 break;
312 case XML_ATTRIBUTE_NODE:
313 xmlGenericError(xmlGenericErrorContext, "?attr? ");
314 break;
315 case XML_ENTITY_NODE:
316 xmlGenericError(xmlGenericErrorContext, "?ent? ");
317 break;
318 case XML_DOCUMENT_NODE:
319 xmlGenericError(xmlGenericErrorContext, "?doc? ");
320 break;
321 case XML_DOCUMENT_TYPE_NODE:
322 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
323 break;
324 case XML_DOCUMENT_FRAG_NODE:
325 xmlGenericError(xmlGenericErrorContext, "?frag? ");
326 break;
327 case XML_NOTATION_NODE:
328 xmlGenericError(xmlGenericErrorContext, "?nota? ");
329 break;
330 case XML_HTML_DOCUMENT_NODE:
331 xmlGenericError(xmlGenericErrorContext, "?html? ");
332 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000333#ifdef LIBXML_DOCB_ENABLED
334 case XML_DOCB_DOCUMENT_NODE:
335 xmlGenericError(xmlGenericErrorContext, "?docb? ");
336 break;
337#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000338 case XML_DTD_NODE:
339 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
340 break;
341 case XML_ELEMENT_DECL:
342 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
343 break;
344 case XML_ATTRIBUTE_DECL:
345 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
346 break;
347 case XML_ENTITY_DECL:
348 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
349 break;
350 case XML_NAMESPACE_DECL:
351 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
352 break;
353 case XML_XINCLUDE_START:
354 xmlGenericError(xmlGenericErrorContext, "incstart ");
355 break;
356 case XML_XINCLUDE_END:
357 xmlGenericError(xmlGenericErrorContext, "incend ");
358 break;
359 }
360}
361
362static void
363xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000364 if (cur == NULL)
365 xmlGenericError(xmlGenericErrorContext, "null ");
366 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000367 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000368 cur = cur->next;
369 }
370}
371
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000372static void
373xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Daniel Veillard83391282003-03-06 21:37:30 +0000374 char expr[5000];
Owen Taylor3473f882001-02-23 17:55:21 +0000375
376 expr[0] = 0;
377 xmlGenericError(xmlGenericErrorContext, "valid: ");
378 xmlValidPrintNodeList(cur);
379 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000380 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000381 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
382}
383
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000384static void
385xmlValidDebugState(xmlValidStatePtr state) {
386 xmlGenericError(xmlGenericErrorContext, "(");
387 if (state->cont == NULL)
388 xmlGenericError(xmlGenericErrorContext, "null,");
389 else
390 switch (state->cont->type) {
391 case XML_ELEMENT_CONTENT_PCDATA:
392 xmlGenericError(xmlGenericErrorContext, "pcdata,");
393 break;
394 case XML_ELEMENT_CONTENT_ELEMENT:
395 xmlGenericError(xmlGenericErrorContext, "%s,",
396 state->cont->name);
397 break;
398 case XML_ELEMENT_CONTENT_SEQ:
399 xmlGenericError(xmlGenericErrorContext, "seq,");
400 break;
401 case XML_ELEMENT_CONTENT_OR:
402 xmlGenericError(xmlGenericErrorContext, "or,");
403 break;
404 }
405 xmlValidPrintNode(state->node);
406 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
407 state->depth, state->occurs, state->state);
408}
409
410static void
411xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
412 int i, j;
413
414 xmlGenericError(xmlGenericErrorContext, "state: ");
415 xmlValidDebugState(ctxt->vstate);
416 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
417 ctxt->vstateNr - 1);
418 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
419 xmlValidDebugState(&ctxt->vstateTab[j]);
420 xmlGenericError(xmlGenericErrorContext, "\n");
421}
422
423/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000424#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000425 *****/
426
427#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000428#define DEBUG_VALID_MSG(m) \
429 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
430
Owen Taylor3473f882001-02-23 17:55:21 +0000431#else
432#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000433#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000434#endif
435
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000436/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000437
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000438#define VECTXT(ctxt, node) \
439 if ((ctxt != NULL) && (ctxt->error != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000440 (node != NULL)) { \
441 xmlChar *base = xmlNodeGetBase(NULL,node); \
442 if (base != NULL) { \
443 ctxt->error(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->error(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000448 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000449 }
450
451#define VWCTXT(ctxt, node) \
452 if ((ctxt != NULL) && (ctxt->warning != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000453 (node != NULL)) { \
454 xmlChar *base = xmlNodeGetBase(NULL,node); \
455 if (base != NULL) { \
456 ctxt->warning(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000457 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000458 xmlFree(base); \
459 } else \
460 ctxt->warning(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000461 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000462 }
463
Owen Taylor3473f882001-02-23 17:55:21 +0000464#define CHECK_DTD \
465 if (doc == NULL) return(0); \
466 else if ((doc->intSubset == NULL) && \
467 (doc->extSubset == NULL)) return(0)
468
Daniel Veillarda10efa82001-04-18 13:09:01 +0000469static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
470 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000471xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
472
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000473#ifdef LIBXML_REGEXP_ENABLED
474
475/************************************************************************
476 * *
477 * Content model validation based on the regexps *
478 * *
479 ************************************************************************/
480
481/**
482 * xmlValidBuildAContentModel:
483 * @content: the content model
484 * @ctxt: the schema parser context
485 * @name: the element name whose content is being built
486 *
487 * Generate the automata sequence needed for that type
488 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000489 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000490 */
491static int
492xmlValidBuildAContentModel(xmlElementContentPtr content,
493 xmlValidCtxtPtr ctxt,
494 const xmlChar *name) {
495 if (content == NULL) {
496 VERROR(ctxt->userData,
497 "Found unexpected type = NULL in %s content model\n", name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000498 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000499 }
500 switch (content->type) {
501 case XML_ELEMENT_CONTENT_PCDATA:
502 VERROR(ctxt->userData, "ContentModel found PCDATA for element %s\n",
503 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000504 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000505 break;
506 case XML_ELEMENT_CONTENT_ELEMENT: {
507 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000508 xmlChar *QName = NULL;
509 const xmlChar *fname = content->name;
510
511 if (content->prefix != NULL) {
512 int len;
513
514 len = xmlStrlen(content->name) +
515 xmlStrlen(content->prefix) + 2;
516 QName = xmlMalloc(len);
517 if (QName == NULL) {
518 VERROR(ctxt->userData,
519 "ContentModel %s : alloc failed\n", name);
520 return(0);
521 }
522 snprintf((char *) QName, len, "%s:%s",
523 (char *)content->prefix,
524 (char *)content->name);
525 fname = QName;
526 }
527
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000528 switch (content->ocur) {
529 case XML_ELEMENT_CONTENT_ONCE:
530 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000531 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000532 break;
533 case XML_ELEMENT_CONTENT_OPT:
534 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000535 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000536 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
537 break;
538 case XML_ELEMENT_CONTENT_PLUS:
539 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000540 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000541 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000542 ctxt->state, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000543 break;
544 case XML_ELEMENT_CONTENT_MULT:
545 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000546 ctxt->state, fname, NULL);
Daniel Veillard57e79b32003-02-04 15:33:12 +0000547 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, ctxt->state,
548 NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000549 break;
550 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000551 if (QName != NULL)
552 xmlFree(QName);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000553 break;
554 }
555 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000556 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000557 xmlElementContentOccur ocur;
558
559 /*
560 * Simply iterate over the content
561 */
562 oldstate = ctxt->state;
563 ocur = content->ocur;
Daniel Veillardf4be0182003-02-24 19:54:33 +0000564 if (ocur != XML_ELEMENT_CONTENT_ONCE) {
565 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
566 oldstate = ctxt->state;
567 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000568 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000569 xmlValidBuildAContentModel(content->c1, ctxt, name);
570 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000571 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
572 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
573 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000574 oldend = ctxt->state;
575 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000576 switch (ocur) {
577 case XML_ELEMENT_CONTENT_ONCE:
578 break;
579 case XML_ELEMENT_CONTENT_OPT:
580 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
581 break;
582 case XML_ELEMENT_CONTENT_MULT:
583 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000584 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000585 break;
586 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000587 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000588 break;
589 }
590 break;
591 }
592 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000593 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000594 xmlElementContentOccur ocur;
595
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000596 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000597 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
598 (ocur == XML_ELEMENT_CONTENT_MULT)) {
599 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
600 ctxt->state, NULL);
601 }
602 oldstate = ctxt->state;
603 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000604
605 /*
606 * iterate over the subtypes and remerge the end with an
607 * epsilon transition
608 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000609 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000610 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000611 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000612 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000613 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000614 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
615 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000616 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000617 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000618 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
619 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000620 switch (ocur) {
621 case XML_ELEMENT_CONTENT_ONCE:
622 break;
623 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000624 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000625 break;
626 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000627 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
628 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000629 break;
630 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000631 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000632 break;
633 }
634 break;
635 }
636 default:
637 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
638 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000639 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000640 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000641 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000642}
643/**
644 * xmlValidBuildContentModel:
645 * @ctxt: a validation context
646 * @elem: an element declaration node
647 *
648 * (Re)Build the automata associated to the content model of this
649 * element
650 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000651 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000652 */
653int
654xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
655 xmlAutomataStatePtr start;
656
657 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000658 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000659 if (elem->type != XML_ELEMENT_DECL)
660 return(0);
661 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
662 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000663 /* TODO: should we rebuild in this case ? */
Daniel Veillardec498e12003-02-05 11:01:50 +0000664 if (elem->contModel != NULL) {
665 if (!xmlRegexpIsDeterminist(elem->contModel)) {
666 ctxt->valid = 0;
667 return(0);
668 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000669 return(1);
Daniel Veillardec498e12003-02-05 11:01:50 +0000670 }
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000671
672 ctxt->am = xmlNewAutomata();
673 if (ctxt->am == NULL) {
674 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
675 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000676 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000677 }
678 start = ctxt->state = xmlAutomataGetInitState(ctxt->am);
679 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
680 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000681 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillard23e73572002-09-19 19:56:43 +0000682 if (!xmlRegexpIsDeterminist(elem->contModel)) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000683 char expr[5000];
684 expr[0] = 0;
685 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
686 VERROR(ctxt->userData, "Content model of %s is not determinist: %s\n",
687 elem->name, expr);
688#ifdef DEBUG_REGEXP_ALGO
689 xmlRegexpPrint(stderr, elem->contModel);
690#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000691 ctxt->valid = 0;
Daniel Veillardec498e12003-02-05 11:01:50 +0000692 ctxt->state = NULL;
693 xmlFreeAutomata(ctxt->am);
694 ctxt->am = NULL;
695 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000696 }
697 ctxt->state = NULL;
698 xmlFreeAutomata(ctxt->am);
699 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000700 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000701}
702
703#endif /* LIBXML_REGEXP_ENABLED */
704
Owen Taylor3473f882001-02-23 17:55:21 +0000705/************************************************************************
706 * *
707 * QName handling helper *
708 * *
709 ************************************************************************/
710
711/**
712 * xmlSplitQName2:
713 * @name: an XML parser context
714 * @prefix: a xmlChar **
715 *
716 * parse an XML qualified name string
717 *
718 * [NS 5] QName ::= (Prefix ':')? LocalPart
719 *
720 * [NS 6] Prefix ::= NCName
721 *
722 * [NS 7] LocalPart ::= NCName
723 *
724 * Returns NULL if not a QName, otherwise the local part, and prefix
725 * is updated to get the Prefix if any.
726 */
727
728xmlChar *
729xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
730 int len = 0;
731 xmlChar *ret = NULL;
732
733 *prefix = NULL;
734
Daniel Veillardf4309d72001-10-02 09:28:58 +0000735#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000736 /* xml: prefix is not really a namespace */
737 if ((name[0] == 'x') && (name[1] == 'm') &&
738 (name[2] == 'l') && (name[3] == ':'))
739 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000740#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000741
742 /* nasty but valid */
743 if (name[0] == ':')
744 return(NULL);
745
746 /*
747 * we are not trying to validate but just to cut, and yes it will
748 * work even if this is as set of UTF-8 encoded chars
749 */
750 while ((name[len] != 0) && (name[len] != ':'))
751 len++;
752
753 if (name[len] == 0)
754 return(NULL);
755
756 *prefix = xmlStrndup(name, len);
757 ret = xmlStrdup(&name[len + 1]);
758
759 return(ret);
760}
761
762/****************************************************************
763 * *
764 * Util functions for data allocation/deallocation *
765 * *
766 ****************************************************************/
767
768/**
769 * xmlNewElementContent:
770 * @name: the subelement name or NULL
771 * @type: the type of element content decl
772 *
773 * Allocate an element content structure.
774 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000775 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000776 */
777xmlElementContentPtr
778xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
779 xmlElementContentPtr ret;
780
781 switch(type) {
782 case XML_ELEMENT_CONTENT_ELEMENT:
783 if (name == NULL) {
784 xmlGenericError(xmlGenericErrorContext,
785 "xmlNewElementContent : name == NULL !\n");
786 }
787 break;
788 case XML_ELEMENT_CONTENT_PCDATA:
789 case XML_ELEMENT_CONTENT_SEQ:
790 case XML_ELEMENT_CONTENT_OR:
791 if (name != NULL) {
792 xmlGenericError(xmlGenericErrorContext,
793 "xmlNewElementContent : name != NULL !\n");
794 }
795 break;
796 default:
797 xmlGenericError(xmlGenericErrorContext,
798 "xmlNewElementContent: unknown type %d\n", type);
799 return(NULL);
800 }
801 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
802 if (ret == NULL) {
803 xmlGenericError(xmlGenericErrorContext,
804 "xmlNewElementContent : out of memory!\n");
805 return(NULL);
806 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000807 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000808 ret->type = type;
809 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000810 if (name != NULL) {
811 xmlChar *prefix = NULL;
812 ret->name = xmlSplitQName2(name, &prefix);
813 if (ret->name == NULL)
814 ret->name = xmlStrdup(name);
815 ret->prefix = prefix;
816 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000817 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000818 ret->prefix = NULL;
819 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000820 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000821 return(ret);
822}
823
824/**
825 * xmlCopyElementContent:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000826 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +0000827 *
828 * Build a copy of an element content description.
829 *
830 * Returns the new xmlElementContentPtr or NULL in case of error.
831 */
832xmlElementContentPtr
833xmlCopyElementContent(xmlElementContentPtr cur) {
834 xmlElementContentPtr ret;
835
836 if (cur == NULL) return(NULL);
837 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
838 if (ret == NULL) {
839 xmlGenericError(xmlGenericErrorContext,
840 "xmlCopyElementContent : out of memory\n");
841 return(NULL);
842 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000843 if (cur->prefix != NULL)
844 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000845 ret->ocur = cur->ocur;
846 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000847 if (ret->c1 != NULL)
848 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000849 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000850 if (ret->c2 != NULL)
851 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000852 return(ret);
853}
854
855/**
856 * xmlFreeElementContent:
857 * @cur: the element content tree to free
858 *
859 * Free an element content structure. This is a recursive call !
860 */
861void
862xmlFreeElementContent(xmlElementContentPtr cur) {
863 if (cur == NULL) return;
864 switch (cur->type) {
865 case XML_ELEMENT_CONTENT_PCDATA:
866 case XML_ELEMENT_CONTENT_ELEMENT:
867 case XML_ELEMENT_CONTENT_SEQ:
868 case XML_ELEMENT_CONTENT_OR:
869 break;
870 default:
871 xmlGenericError(xmlGenericErrorContext,
872 "xmlFreeElementContent : type %d\n", cur->type);
873 return;
874 }
875 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
876 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
877 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000878 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000879 xmlFree(cur);
880}
881
882/**
883 * xmlDumpElementContent:
884 * @buf: An XML buffer
885 * @content: An element table
886 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
887 *
888 * This will dump the content of the element table as an XML DTD definition
889 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000890static void
Owen Taylor3473f882001-02-23 17:55:21 +0000891xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
892 if (content == NULL) return;
893
894 if (glob) xmlBufferWriteChar(buf, "(");
895 switch (content->type) {
896 case XML_ELEMENT_CONTENT_PCDATA:
897 xmlBufferWriteChar(buf, "#PCDATA");
898 break;
899 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000900 if (content->prefix != NULL) {
901 xmlBufferWriteCHAR(buf, content->prefix);
902 xmlBufferWriteChar(buf, ":");
903 }
Owen Taylor3473f882001-02-23 17:55:21 +0000904 xmlBufferWriteCHAR(buf, content->name);
905 break;
906 case XML_ELEMENT_CONTENT_SEQ:
907 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
908 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
909 xmlDumpElementContent(buf, content->c1, 1);
910 else
911 xmlDumpElementContent(buf, content->c1, 0);
912 xmlBufferWriteChar(buf, " , ");
913 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
914 xmlDumpElementContent(buf, content->c2, 1);
915 else
916 xmlDumpElementContent(buf, content->c2, 0);
917 break;
918 case XML_ELEMENT_CONTENT_OR:
919 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
920 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
921 xmlDumpElementContent(buf, content->c1, 1);
922 else
923 xmlDumpElementContent(buf, content->c1, 0);
924 xmlBufferWriteChar(buf, " | ");
925 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
926 xmlDumpElementContent(buf, content->c2, 1);
927 else
928 xmlDumpElementContent(buf, content->c2, 0);
929 break;
930 default:
931 xmlGenericError(xmlGenericErrorContext,
932 "xmlDumpElementContent: unknown type %d\n",
933 content->type);
934 }
935 if (glob)
936 xmlBufferWriteChar(buf, ")");
937 switch (content->ocur) {
938 case XML_ELEMENT_CONTENT_ONCE:
939 break;
940 case XML_ELEMENT_CONTENT_OPT:
941 xmlBufferWriteChar(buf, "?");
942 break;
943 case XML_ELEMENT_CONTENT_MULT:
944 xmlBufferWriteChar(buf, "*");
945 break;
946 case XML_ELEMENT_CONTENT_PLUS:
947 xmlBufferWriteChar(buf, "+");
948 break;
949 }
950}
951
952/**
953 * xmlSprintfElementContent:
954 * @buf: an output buffer
955 * @content: An element table
956 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
957 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000958 * Deprecated, unsafe, use xmlSnprintfElementContent
959 */
960void
961xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
962 xmlElementContentPtr content ATTRIBUTE_UNUSED,
963 int glob ATTRIBUTE_UNUSED) {
964}
965
966/**
967 * xmlSnprintfElementContent:
968 * @buf: an output buffer
969 * @size: the buffer size
970 * @content: An element table
971 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
972 *
Owen Taylor3473f882001-02-23 17:55:21 +0000973 * This will dump the content of the element content definition
974 * Intended just for the debug routine
975 */
976void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000977xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
978 int len;
979
Owen Taylor3473f882001-02-23 17:55:21 +0000980 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000981 len = strlen(buf);
982 if (size - len < 50) {
983 if ((size - len > 4) && (buf[len - 1] != '.'))
984 strcat(buf, " ...");
985 return;
986 }
Owen Taylor3473f882001-02-23 17:55:21 +0000987 if (glob) strcat(buf, "(");
988 switch (content->type) {
989 case XML_ELEMENT_CONTENT_PCDATA:
990 strcat(buf, "#PCDATA");
991 break;
992 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000993 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000994 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000995 strcat(buf, " ...");
996 return;
997 }
998 strcat(buf, (char *) content->prefix);
999 strcat(buf, ":");
1000 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00001001 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00001002 strcat(buf, " ...");
1003 return;
1004 }
Owen Taylor3473f882001-02-23 17:55:21 +00001005 strcat(buf, (char *) content->name);
1006 break;
1007 case XML_ELEMENT_CONTENT_SEQ:
1008 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1009 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001010 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001011 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001012 xmlSnprintfElementContent(buf, size, content->c1, 0);
1013 len = strlen(buf);
1014 if (size - len < 50) {
1015 if ((size - len > 4) && (buf[len - 1] != '.'))
1016 strcat(buf, " ...");
1017 return;
1018 }
Owen Taylor3473f882001-02-23 17:55:21 +00001019 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00001020 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
1021 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1022 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001023 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001024 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001025 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001026 break;
1027 case XML_ELEMENT_CONTENT_OR:
1028 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1029 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001030 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001031 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001032 xmlSnprintfElementContent(buf, size, content->c1, 0);
1033 len = strlen(buf);
1034 if (size - len < 50) {
1035 if ((size - len > 4) && (buf[len - 1] != '.'))
1036 strcat(buf, " ...");
1037 return;
1038 }
Owen Taylor3473f882001-02-23 17:55:21 +00001039 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00001040 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
1041 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1042 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001043 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001044 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001045 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001046 break;
1047 }
1048 if (glob)
1049 strcat(buf, ")");
1050 switch (content->ocur) {
1051 case XML_ELEMENT_CONTENT_ONCE:
1052 break;
1053 case XML_ELEMENT_CONTENT_OPT:
1054 strcat(buf, "?");
1055 break;
1056 case XML_ELEMENT_CONTENT_MULT:
1057 strcat(buf, "*");
1058 break;
1059 case XML_ELEMENT_CONTENT_PLUS:
1060 strcat(buf, "+");
1061 break;
1062 }
1063}
1064
1065/****************************************************************
1066 * *
1067 * Registration of DTD declarations *
1068 * *
1069 ****************************************************************/
1070
1071/**
1072 * xmlCreateElementTable:
1073 *
1074 * create and initialize an empty element hash table.
1075 *
1076 * Returns the xmlElementTablePtr just created or NULL in case of error.
1077 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001078static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001079xmlCreateElementTable(void) {
1080 return(xmlHashCreate(0));
1081}
1082
1083/**
1084 * xmlFreeElement:
1085 * @elem: An element
1086 *
1087 * Deallocate the memory used by an element definition
1088 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001089static void
Owen Taylor3473f882001-02-23 17:55:21 +00001090xmlFreeElement(xmlElementPtr elem) {
1091 if (elem == NULL) return;
1092 xmlUnlinkNode((xmlNodePtr) elem);
1093 xmlFreeElementContent(elem->content);
1094 if (elem->name != NULL)
1095 xmlFree((xmlChar *) elem->name);
1096 if (elem->prefix != NULL)
1097 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001098#ifdef LIBXML_REGEXP_ENABLED
1099 if (elem->contModel != NULL)
1100 xmlRegFreeRegexp(elem->contModel);
1101#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001102 xmlFree(elem);
1103}
1104
1105
1106/**
1107 * xmlAddElementDecl:
1108 * @ctxt: the validation context
1109 * @dtd: pointer to the DTD
1110 * @name: the entity name
1111 * @type: the element type
1112 * @content: the element content tree or NULL
1113 *
1114 * Register a new element declaration
1115 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001116 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001117 */
1118xmlElementPtr
1119xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
1120 xmlElementTypeVal type,
1121 xmlElementContentPtr content) {
1122 xmlElementPtr ret;
1123 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001124 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001125 xmlChar *ns, *uqname;
1126
1127 if (dtd == NULL) {
1128 xmlGenericError(xmlGenericErrorContext,
1129 "xmlAddElementDecl: dtd == NULL\n");
1130 return(NULL);
1131 }
1132 if (name == NULL) {
1133 xmlGenericError(xmlGenericErrorContext,
1134 "xmlAddElementDecl: name == NULL\n");
1135 return(NULL);
1136 }
1137 switch (type) {
1138 case XML_ELEMENT_TYPE_EMPTY:
1139 if (content != NULL) {
1140 xmlGenericError(xmlGenericErrorContext,
1141 "xmlAddElementDecl: content != NULL for EMPTY\n");
1142 return(NULL);
1143 }
1144 break;
1145 case XML_ELEMENT_TYPE_ANY:
1146 if (content != NULL) {
1147 xmlGenericError(xmlGenericErrorContext,
1148 "xmlAddElementDecl: content != NULL for ANY\n");
1149 return(NULL);
1150 }
1151 break;
1152 case XML_ELEMENT_TYPE_MIXED:
1153 if (content == NULL) {
1154 xmlGenericError(xmlGenericErrorContext,
1155 "xmlAddElementDecl: content == NULL for MIXED\n");
1156 return(NULL);
1157 }
1158 break;
1159 case XML_ELEMENT_TYPE_ELEMENT:
1160 if (content == NULL) {
1161 xmlGenericError(xmlGenericErrorContext,
1162 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1163 return(NULL);
1164 }
1165 break;
1166 default:
1167 xmlGenericError(xmlGenericErrorContext,
1168 "xmlAddElementDecl: unknown type %d\n", type);
1169 return(NULL);
1170 }
1171
1172 /*
1173 * check if name is a QName
1174 */
1175 uqname = xmlSplitQName2(name, &ns);
1176 if (uqname != NULL)
1177 name = uqname;
1178
1179 /*
1180 * Create the Element table if needed.
1181 */
1182 table = (xmlElementTablePtr) dtd->elements;
1183 if (table == NULL) {
1184 table = xmlCreateElementTable();
1185 dtd->elements = (void *) table;
1186 }
1187 if (table == NULL) {
1188 xmlGenericError(xmlGenericErrorContext,
1189 "xmlAddElementDecl: Table creation failed!\n");
1190 return(NULL);
1191 }
1192
Daniel Veillarda10efa82001-04-18 13:09:01 +00001193 /*
1194 * lookup old attributes inserted on an undefined element in the
1195 * internal subset.
1196 */
1197 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1198 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1199 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1200 oldAttributes = ret->attributes;
1201 ret->attributes = NULL;
1202 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1203 xmlFreeElement(ret);
1204 }
Owen Taylor3473f882001-02-23 17:55:21 +00001205 }
Owen Taylor3473f882001-02-23 17:55:21 +00001206
1207 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001208 * The element may already be present if one of its attribute
1209 * was registered first
1210 */
1211 ret = xmlHashLookup2(table, name, ns);
1212 if (ret != NULL) {
1213 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1214 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001215 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001216 */
1217 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1218 if (uqname != NULL)
1219 xmlFree(uqname);
1220 return(NULL);
1221 }
1222 } else {
1223 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1224 if (ret == NULL) {
1225 xmlGenericError(xmlGenericErrorContext,
1226 "xmlAddElementDecl: out of memory\n");
1227 return(NULL);
1228 }
1229 memset(ret, 0, sizeof(xmlElement));
1230 ret->type = XML_ELEMENT_DECL;
1231
1232 /*
1233 * fill the structure.
1234 */
1235 ret->name = xmlStrdup(name);
1236 ret->prefix = ns;
1237
1238 /*
1239 * Validity Check:
1240 * Insertion must not fail
1241 */
1242 if (xmlHashAddEntry2(table, name, ns, ret)) {
1243 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001244 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001245 */
1246 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1247 xmlFreeElement(ret);
1248 if (uqname != NULL)
1249 xmlFree(uqname);
1250 return(NULL);
1251 }
1252 }
1253
1254 /*
1255 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001256 */
1257 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001258 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001259 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001260
1261 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001262 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001263 */
1264 ret->parent = dtd;
1265 ret->doc = dtd->doc;
1266 if (dtd->last == NULL) {
1267 dtd->children = dtd->last = (xmlNodePtr) ret;
1268 } else {
1269 dtd->last->next = (xmlNodePtr) ret;
1270 ret->prev = dtd->last;
1271 dtd->last = (xmlNodePtr) ret;
1272 }
1273 if (uqname != NULL)
1274 xmlFree(uqname);
1275 return(ret);
1276}
1277
1278/**
1279 * xmlFreeElementTable:
1280 * @table: An element table
1281 *
1282 * Deallocate the memory used by an element hash table.
1283 */
1284void
1285xmlFreeElementTable(xmlElementTablePtr table) {
1286 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1287}
1288
1289/**
1290 * xmlCopyElement:
1291 * @elem: An element
1292 *
1293 * Build a copy of an element.
1294 *
1295 * Returns the new xmlElementPtr or NULL in case of error.
1296 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001297static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001298xmlCopyElement(xmlElementPtr elem) {
1299 xmlElementPtr cur;
1300
1301 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1302 if (cur == NULL) {
1303 xmlGenericError(xmlGenericErrorContext,
1304 "xmlCopyElement: out of memory !\n");
1305 return(NULL);
1306 }
1307 memset(cur, 0, sizeof(xmlElement));
1308 cur->type = XML_ELEMENT_DECL;
1309 cur->etype = elem->etype;
1310 if (elem->name != NULL)
1311 cur->name = xmlStrdup(elem->name);
1312 else
1313 cur->name = NULL;
1314 if (elem->prefix != NULL)
1315 cur->prefix = xmlStrdup(elem->prefix);
1316 else
1317 cur->prefix = NULL;
1318 cur->content = xmlCopyElementContent(elem->content);
1319 /* TODO : rebuild the attribute list on the copy */
1320 cur->attributes = NULL;
1321 return(cur);
1322}
1323
1324/**
1325 * xmlCopyElementTable:
1326 * @table: An element table
1327 *
1328 * Build a copy of an element table.
1329 *
1330 * Returns the new xmlElementTablePtr or NULL in case of error.
1331 */
1332xmlElementTablePtr
1333xmlCopyElementTable(xmlElementTablePtr table) {
1334 return((xmlElementTablePtr) xmlHashCopy(table,
1335 (xmlHashCopier) xmlCopyElement));
1336}
1337
1338/**
1339 * xmlDumpElementDecl:
1340 * @buf: the XML buffer output
1341 * @elem: An element table
1342 *
1343 * This will dump the content of the element declaration as an XML
1344 * DTD definition
1345 */
1346void
1347xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1348 switch (elem->etype) {
1349 case XML_ELEMENT_TYPE_EMPTY:
1350 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001351 if (elem->prefix != NULL) {
1352 xmlBufferWriteCHAR(buf, elem->prefix);
1353 xmlBufferWriteChar(buf, ":");
1354 }
Owen Taylor3473f882001-02-23 17:55:21 +00001355 xmlBufferWriteCHAR(buf, elem->name);
1356 xmlBufferWriteChar(buf, " EMPTY>\n");
1357 break;
1358 case XML_ELEMENT_TYPE_ANY:
1359 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001360 if (elem->prefix != NULL) {
1361 xmlBufferWriteCHAR(buf, elem->prefix);
1362 xmlBufferWriteChar(buf, ":");
1363 }
Owen Taylor3473f882001-02-23 17:55:21 +00001364 xmlBufferWriteCHAR(buf, elem->name);
1365 xmlBufferWriteChar(buf, " ANY>\n");
1366 break;
1367 case XML_ELEMENT_TYPE_MIXED:
1368 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001369 if (elem->prefix != NULL) {
1370 xmlBufferWriteCHAR(buf, elem->prefix);
1371 xmlBufferWriteChar(buf, ":");
1372 }
Owen Taylor3473f882001-02-23 17:55:21 +00001373 xmlBufferWriteCHAR(buf, elem->name);
1374 xmlBufferWriteChar(buf, " ");
1375 xmlDumpElementContent(buf, elem->content, 1);
1376 xmlBufferWriteChar(buf, ">\n");
1377 break;
1378 case XML_ELEMENT_TYPE_ELEMENT:
1379 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001380 if (elem->prefix != NULL) {
1381 xmlBufferWriteCHAR(buf, elem->prefix);
1382 xmlBufferWriteChar(buf, ":");
1383 }
Owen Taylor3473f882001-02-23 17:55:21 +00001384 xmlBufferWriteCHAR(buf, elem->name);
1385 xmlBufferWriteChar(buf, " ");
1386 xmlDumpElementContent(buf, elem->content, 1);
1387 xmlBufferWriteChar(buf, ">\n");
1388 break;
1389 default:
1390 xmlGenericError(xmlGenericErrorContext,
1391 "xmlDumpElementDecl: internal: unknown type %d\n",
1392 elem->etype);
1393 }
1394}
1395
1396/**
1397 * xmlDumpElementTable:
1398 * @buf: the XML buffer output
1399 * @table: An element table
1400 *
1401 * This will dump the content of the element table as an XML DTD definition
1402 */
1403void
1404xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1405 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1406}
1407
1408/**
1409 * xmlCreateEnumeration:
1410 * @name: the enumeration name or NULL
1411 *
1412 * create and initialize an enumeration attribute node.
1413 *
1414 * Returns the xmlEnumerationPtr just created or NULL in case
1415 * of error.
1416 */
1417xmlEnumerationPtr
1418xmlCreateEnumeration(xmlChar *name) {
1419 xmlEnumerationPtr ret;
1420
1421 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1422 if (ret == NULL) {
1423 xmlGenericError(xmlGenericErrorContext,
1424 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1425 (long)sizeof(xmlEnumeration));
1426 return(NULL);
1427 }
1428 memset(ret, 0, sizeof(xmlEnumeration));
1429
1430 if (name != NULL)
1431 ret->name = xmlStrdup(name);
1432 return(ret);
1433}
1434
1435/**
1436 * xmlFreeEnumeration:
1437 * @cur: the tree to free.
1438 *
1439 * free an enumeration attribute node (recursive).
1440 */
1441void
1442xmlFreeEnumeration(xmlEnumerationPtr cur) {
1443 if (cur == NULL) return;
1444
1445 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1446
1447 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001448 xmlFree(cur);
1449}
1450
1451/**
1452 * xmlCopyEnumeration:
1453 * @cur: the tree to copy.
1454 *
1455 * Copy an enumeration attribute node (recursive).
1456 *
1457 * Returns the xmlEnumerationPtr just created or NULL in case
1458 * of error.
1459 */
1460xmlEnumerationPtr
1461xmlCopyEnumeration(xmlEnumerationPtr cur) {
1462 xmlEnumerationPtr ret;
1463
1464 if (cur == NULL) return(NULL);
1465 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1466
1467 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1468 else ret->next = NULL;
1469
1470 return(ret);
1471}
1472
1473/**
1474 * xmlDumpEnumeration:
1475 * @buf: the XML buffer output
1476 * @enum: An enumeration
1477 *
1478 * This will dump the content of the enumeration
1479 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001480static void
Owen Taylor3473f882001-02-23 17:55:21 +00001481xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1482 if (cur == NULL) return;
1483
1484 xmlBufferWriteCHAR(buf, cur->name);
1485 if (cur->next == NULL)
1486 xmlBufferWriteChar(buf, ")");
1487 else {
1488 xmlBufferWriteChar(buf, " | ");
1489 xmlDumpEnumeration(buf, cur->next);
1490 }
1491}
1492
1493/**
1494 * xmlCreateAttributeTable:
1495 *
1496 * create and initialize an empty attribute hash table.
1497 *
1498 * Returns the xmlAttributeTablePtr just created or NULL in case
1499 * of error.
1500 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001501static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001502xmlCreateAttributeTable(void) {
1503 return(xmlHashCreate(0));
1504}
1505
1506/**
1507 * xmlScanAttributeDeclCallback:
1508 * @attr: the attribute decl
1509 * @list: the list to update
1510 *
1511 * Callback called by xmlScanAttributeDecl when a new attribute
1512 * has to be entered in the list.
1513 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001514static void
Owen Taylor3473f882001-02-23 17:55:21 +00001515xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001516 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001517 attr->nexth = *list;
1518 *list = attr;
1519}
1520
1521/**
1522 * xmlScanAttributeDecl:
1523 * @dtd: pointer to the DTD
1524 * @elem: the element name
1525 *
1526 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001527 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001528 *
1529 * Returns the pointer to the first attribute decl in the chain,
1530 * possibly NULL.
1531 */
1532xmlAttributePtr
1533xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1534 xmlAttributePtr ret = NULL;
1535 xmlAttributeTablePtr table;
1536
1537 if (dtd == NULL) {
1538 xmlGenericError(xmlGenericErrorContext,
1539 "xmlScanAttributeDecl: dtd == NULL\n");
1540 return(NULL);
1541 }
1542 if (elem == NULL) {
1543 xmlGenericError(xmlGenericErrorContext,
1544 "xmlScanAttributeDecl: elem == NULL\n");
1545 return(NULL);
1546 }
1547 table = (xmlAttributeTablePtr) dtd->attributes;
1548 if (table == NULL)
1549 return(NULL);
1550
1551 /* WRONG !!! */
1552 xmlHashScan3(table, NULL, NULL, elem,
1553 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1554 return(ret);
1555}
1556
1557/**
1558 * xmlScanIDAttributeDecl:
1559 * @ctxt: the validation context
1560 * @elem: the element name
1561 *
1562 * Verify that the element don't have too many ID attributes
1563 * declared.
1564 *
1565 * Returns the number of ID attributes found.
1566 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001567static int
Owen Taylor3473f882001-02-23 17:55:21 +00001568xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1569 xmlAttributePtr cur;
1570 int ret = 0;
1571
1572 if (elem == NULL) return(0);
1573 cur = elem->attributes;
1574 while (cur != NULL) {
1575 if (cur->atype == XML_ATTRIBUTE_ID) {
1576 ret ++;
1577 if (ret > 1)
1578 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001579 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001580 elem->name, cur->name);
1581 }
1582 cur = cur->nexth;
1583 }
1584 return(ret);
1585}
1586
1587/**
1588 * xmlFreeAttribute:
1589 * @elem: An attribute
1590 *
1591 * Deallocate the memory used by an attribute definition
1592 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001593static void
Owen Taylor3473f882001-02-23 17:55:21 +00001594xmlFreeAttribute(xmlAttributePtr attr) {
1595 if (attr == NULL) return;
1596 xmlUnlinkNode((xmlNodePtr) attr);
1597 if (attr->tree != NULL)
1598 xmlFreeEnumeration(attr->tree);
1599 if (attr->elem != NULL)
1600 xmlFree((xmlChar *) attr->elem);
1601 if (attr->name != NULL)
1602 xmlFree((xmlChar *) attr->name);
1603 if (attr->defaultValue != NULL)
1604 xmlFree((xmlChar *) attr->defaultValue);
1605 if (attr->prefix != NULL)
1606 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001607 xmlFree(attr);
1608}
1609
1610
1611/**
1612 * xmlAddAttributeDecl:
1613 * @ctxt: the validation context
1614 * @dtd: pointer to the DTD
1615 * @elem: the element name
1616 * @name: the attribute name
1617 * @ns: the attribute namespace prefix
1618 * @type: the attribute type
1619 * @def: the attribute default type
1620 * @defaultValue: the attribute default value
1621 * @tree: if it's an enumeration, the associated list
1622 *
1623 * Register a new attribute declaration
1624 * Note that @tree becomes the ownership of the DTD
1625 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001626 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001627 */
1628xmlAttributePtr
1629xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1630 const xmlChar *name, const xmlChar *ns,
1631 xmlAttributeType type, xmlAttributeDefault def,
1632 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1633 xmlAttributePtr ret;
1634 xmlAttributeTablePtr table;
1635 xmlElementPtr elemDef;
1636
1637 if (dtd == NULL) {
1638 xmlGenericError(xmlGenericErrorContext,
1639 "xmlAddAttributeDecl: dtd == NULL\n");
1640 xmlFreeEnumeration(tree);
1641 return(NULL);
1642 }
1643 if (name == NULL) {
1644 xmlGenericError(xmlGenericErrorContext,
1645 "xmlAddAttributeDecl: name == NULL\n");
1646 xmlFreeEnumeration(tree);
1647 return(NULL);
1648 }
1649 if (elem == NULL) {
1650 xmlGenericError(xmlGenericErrorContext,
1651 "xmlAddAttributeDecl: elem == NULL\n");
1652 xmlFreeEnumeration(tree);
1653 return(NULL);
1654 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001655
Owen Taylor3473f882001-02-23 17:55:21 +00001656 /*
1657 * Check the type and possibly the default value.
1658 */
1659 switch (type) {
1660 case XML_ATTRIBUTE_CDATA:
1661 break;
1662 case XML_ATTRIBUTE_ID:
1663 break;
1664 case XML_ATTRIBUTE_IDREF:
1665 break;
1666 case XML_ATTRIBUTE_IDREFS:
1667 break;
1668 case XML_ATTRIBUTE_ENTITY:
1669 break;
1670 case XML_ATTRIBUTE_ENTITIES:
1671 break;
1672 case XML_ATTRIBUTE_NMTOKEN:
1673 break;
1674 case XML_ATTRIBUTE_NMTOKENS:
1675 break;
1676 case XML_ATTRIBUTE_ENUMERATION:
1677 break;
1678 case XML_ATTRIBUTE_NOTATION:
1679 break;
1680 default:
1681 xmlGenericError(xmlGenericErrorContext,
1682 "xmlAddAttributeDecl: unknown type %d\n", type);
1683 xmlFreeEnumeration(tree);
1684 return(NULL);
1685 }
1686 if ((defaultValue != NULL) &&
1687 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001688 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001689 elem, name, defaultValue);
1690 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001691 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001692 }
1693
1694 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001695 * Check first that an attribute defined in the external subset wasn't
1696 * already defined in the internal subset
1697 */
1698 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1699 (dtd->doc->intSubset != NULL) &&
1700 (dtd->doc->intSubset->attributes != NULL)) {
1701 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1702 if (ret != NULL)
1703 return(NULL);
1704 }
1705
1706 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001707 * Create the Attribute table if needed.
1708 */
1709 table = (xmlAttributeTablePtr) dtd->attributes;
1710 if (table == NULL) {
1711 table = xmlCreateAttributeTable();
1712 dtd->attributes = (void *) table;
1713 }
1714 if (table == NULL) {
1715 xmlGenericError(xmlGenericErrorContext,
1716 "xmlAddAttributeDecl: Table creation failed!\n");
1717 return(NULL);
1718 }
1719
1720
1721 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1722 if (ret == NULL) {
1723 xmlGenericError(xmlGenericErrorContext,
1724 "xmlAddAttributeDecl: out of memory\n");
1725 return(NULL);
1726 }
1727 memset(ret, 0, sizeof(xmlAttribute));
1728 ret->type = XML_ATTRIBUTE_DECL;
1729
1730 /*
1731 * fill the structure.
1732 */
1733 ret->atype = type;
1734 ret->name = xmlStrdup(name);
1735 ret->prefix = xmlStrdup(ns);
1736 ret->elem = xmlStrdup(elem);
1737 ret->def = def;
1738 ret->tree = tree;
1739 if (defaultValue != NULL)
1740 ret->defaultValue = xmlStrdup(defaultValue);
1741
1742 /*
1743 * Validity Check:
1744 * Search the DTD for previous declarations of the ATTLIST
1745 */
1746 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1747 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001748 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001749 */
1750 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001751 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001752 name, elem);
1753 xmlFreeAttribute(ret);
1754 return(NULL);
1755 }
1756
1757 /*
1758 * Validity Check:
1759 * Multiple ID per element
1760 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001761 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001762 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001763
Owen Taylor3473f882001-02-23 17:55:21 +00001764 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001765 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001766 VERROR(ctxt->userData,
1767 "Element %s has too may ID attributes defined : %s\n",
1768 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001769 ctxt->valid = 0;
1770 }
1771
Daniel Veillard48da9102001-08-07 01:10:10 +00001772 /*
1773 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001774 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001775 */
1776 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1777 ((ret->prefix != NULL &&
1778 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1779 ret->nexth = elemDef->attributes;
1780 elemDef->attributes = ret;
1781 } else {
1782 xmlAttributePtr tmp = elemDef->attributes;
1783
1784 while ((tmp != NULL) &&
1785 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1786 ((ret->prefix != NULL &&
1787 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1788 if (tmp->nexth == NULL)
1789 break;
1790 tmp = tmp->nexth;
1791 }
1792 if (tmp != NULL) {
1793 ret->nexth = tmp->nexth;
1794 tmp->nexth = ret;
1795 } else {
1796 ret->nexth = elemDef->attributes;
1797 elemDef->attributes = ret;
1798 }
1799 }
Owen Taylor3473f882001-02-23 17:55:21 +00001800 }
1801
1802 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001803 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001804 */
1805 ret->parent = dtd;
1806 ret->doc = dtd->doc;
1807 if (dtd->last == NULL) {
1808 dtd->children = dtd->last = (xmlNodePtr) ret;
1809 } else {
1810 dtd->last->next = (xmlNodePtr) ret;
1811 ret->prev = dtd->last;
1812 dtd->last = (xmlNodePtr) ret;
1813 }
1814 return(ret);
1815}
1816
1817/**
1818 * xmlFreeAttributeTable:
1819 * @table: An attribute table
1820 *
1821 * Deallocate the memory used by an entities hash table.
1822 */
1823void
1824xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1825 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1826}
1827
1828/**
1829 * xmlCopyAttribute:
1830 * @attr: An attribute
1831 *
1832 * Build a copy of an attribute.
1833 *
1834 * Returns the new xmlAttributePtr or NULL in case of error.
1835 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001836static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001837xmlCopyAttribute(xmlAttributePtr attr) {
1838 xmlAttributePtr cur;
1839
1840 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1841 if (cur == NULL) {
1842 xmlGenericError(xmlGenericErrorContext,
1843 "xmlCopyAttribute: out of memory !\n");
1844 return(NULL);
1845 }
1846 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001847 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001848 cur->atype = attr->atype;
1849 cur->def = attr->def;
1850 cur->tree = xmlCopyEnumeration(attr->tree);
1851 if (attr->elem != NULL)
1852 cur->elem = xmlStrdup(attr->elem);
1853 if (attr->name != NULL)
1854 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001855 if (attr->prefix != NULL)
1856 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001857 if (attr->defaultValue != NULL)
1858 cur->defaultValue = xmlStrdup(attr->defaultValue);
1859 return(cur);
1860}
1861
1862/**
1863 * xmlCopyAttributeTable:
1864 * @table: An attribute table
1865 *
1866 * Build a copy of an attribute table.
1867 *
1868 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1869 */
1870xmlAttributeTablePtr
1871xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1872 return((xmlAttributeTablePtr) xmlHashCopy(table,
1873 (xmlHashCopier) xmlCopyAttribute));
1874}
1875
1876/**
1877 * xmlDumpAttributeDecl:
1878 * @buf: the XML buffer output
1879 * @attr: An attribute declaration
1880 *
1881 * This will dump the content of the attribute declaration as an XML
1882 * DTD definition
1883 */
1884void
1885xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1886 xmlBufferWriteChar(buf, "<!ATTLIST ");
1887 xmlBufferWriteCHAR(buf, attr->elem);
1888 xmlBufferWriteChar(buf, " ");
1889 if (attr->prefix != NULL) {
1890 xmlBufferWriteCHAR(buf, attr->prefix);
1891 xmlBufferWriteChar(buf, ":");
1892 }
1893 xmlBufferWriteCHAR(buf, attr->name);
1894 switch (attr->atype) {
1895 case XML_ATTRIBUTE_CDATA:
1896 xmlBufferWriteChar(buf, " CDATA");
1897 break;
1898 case XML_ATTRIBUTE_ID:
1899 xmlBufferWriteChar(buf, " ID");
1900 break;
1901 case XML_ATTRIBUTE_IDREF:
1902 xmlBufferWriteChar(buf, " IDREF");
1903 break;
1904 case XML_ATTRIBUTE_IDREFS:
1905 xmlBufferWriteChar(buf, " IDREFS");
1906 break;
1907 case XML_ATTRIBUTE_ENTITY:
1908 xmlBufferWriteChar(buf, " ENTITY");
1909 break;
1910 case XML_ATTRIBUTE_ENTITIES:
1911 xmlBufferWriteChar(buf, " ENTITIES");
1912 break;
1913 case XML_ATTRIBUTE_NMTOKEN:
1914 xmlBufferWriteChar(buf, " NMTOKEN");
1915 break;
1916 case XML_ATTRIBUTE_NMTOKENS:
1917 xmlBufferWriteChar(buf, " NMTOKENS");
1918 break;
1919 case XML_ATTRIBUTE_ENUMERATION:
1920 xmlBufferWriteChar(buf, " (");
1921 xmlDumpEnumeration(buf, attr->tree);
1922 break;
1923 case XML_ATTRIBUTE_NOTATION:
1924 xmlBufferWriteChar(buf, " NOTATION (");
1925 xmlDumpEnumeration(buf, attr->tree);
1926 break;
1927 default:
1928 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001929 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001930 attr->atype);
1931 }
1932 switch (attr->def) {
1933 case XML_ATTRIBUTE_NONE:
1934 break;
1935 case XML_ATTRIBUTE_REQUIRED:
1936 xmlBufferWriteChar(buf, " #REQUIRED");
1937 break;
1938 case XML_ATTRIBUTE_IMPLIED:
1939 xmlBufferWriteChar(buf, " #IMPLIED");
1940 break;
1941 case XML_ATTRIBUTE_FIXED:
1942 xmlBufferWriteChar(buf, " #FIXED");
1943 break;
1944 default:
1945 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001946 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001947 attr->def);
1948 }
1949 if (attr->defaultValue != NULL) {
1950 xmlBufferWriteChar(buf, " ");
1951 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1952 }
1953 xmlBufferWriteChar(buf, ">\n");
1954}
1955
1956/**
1957 * xmlDumpAttributeTable:
1958 * @buf: the XML buffer output
1959 * @table: An attribute table
1960 *
1961 * This will dump the content of the attribute table as an XML DTD definition
1962 */
1963void
1964xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1965 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1966}
1967
1968/************************************************************************
1969 * *
1970 * NOTATIONs *
1971 * *
1972 ************************************************************************/
1973/**
1974 * xmlCreateNotationTable:
1975 *
1976 * create and initialize an empty notation hash table.
1977 *
1978 * Returns the xmlNotationTablePtr just created or NULL in case
1979 * of error.
1980 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001981static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001982xmlCreateNotationTable(void) {
1983 return(xmlHashCreate(0));
1984}
1985
1986/**
1987 * xmlFreeNotation:
1988 * @not: A notation
1989 *
1990 * Deallocate the memory used by an notation definition
1991 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001992static void
Owen Taylor3473f882001-02-23 17:55:21 +00001993xmlFreeNotation(xmlNotationPtr nota) {
1994 if (nota == NULL) return;
1995 if (nota->name != NULL)
1996 xmlFree((xmlChar *) nota->name);
1997 if (nota->PublicID != NULL)
1998 xmlFree((xmlChar *) nota->PublicID);
1999 if (nota->SystemID != NULL)
2000 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00002001 xmlFree(nota);
2002}
2003
2004
2005/**
2006 * xmlAddNotationDecl:
2007 * @dtd: pointer to the DTD
2008 * @ctxt: the validation context
2009 * @name: the entity name
2010 * @PublicID: the public identifier or NULL
2011 * @SystemID: the system identifier or NULL
2012 *
2013 * Register a new notation declaration
2014 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002015 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00002016 */
2017xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002018xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002019 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00002020 const xmlChar *PublicID, const xmlChar *SystemID) {
2021 xmlNotationPtr ret;
2022 xmlNotationTablePtr table;
2023
2024 if (dtd == NULL) {
2025 xmlGenericError(xmlGenericErrorContext,
2026 "xmlAddNotationDecl: dtd == NULL\n");
2027 return(NULL);
2028 }
2029 if (name == NULL) {
2030 xmlGenericError(xmlGenericErrorContext,
2031 "xmlAddNotationDecl: name == NULL\n");
2032 return(NULL);
2033 }
2034 if ((PublicID == NULL) && (SystemID == NULL)) {
2035 xmlGenericError(xmlGenericErrorContext,
2036 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002037 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002038 }
2039
2040 /*
2041 * Create the Notation table if needed.
2042 */
2043 table = (xmlNotationTablePtr) dtd->notations;
2044 if (table == NULL)
2045 dtd->notations = table = xmlCreateNotationTable();
2046 if (table == NULL) {
2047 xmlGenericError(xmlGenericErrorContext,
2048 "xmlAddNotationDecl: Table creation failed!\n");
2049 return(NULL);
2050 }
2051
2052 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2053 if (ret == NULL) {
2054 xmlGenericError(xmlGenericErrorContext,
2055 "xmlAddNotationDecl: out of memory\n");
2056 return(NULL);
2057 }
2058 memset(ret, 0, sizeof(xmlNotation));
2059
2060 /*
2061 * fill the structure.
2062 */
2063 ret->name = xmlStrdup(name);
2064 if (SystemID != NULL)
2065 ret->SystemID = xmlStrdup(SystemID);
2066 if (PublicID != NULL)
2067 ret->PublicID = xmlStrdup(PublicID);
2068
2069 /*
2070 * Validity Check:
2071 * Check the DTD for previous declarations of the ATTLIST
2072 */
2073 if (xmlHashAddEntry(table, name, ret)) {
2074 xmlGenericError(xmlGenericErrorContext,
2075 "xmlAddNotationDecl: %s already defined\n", name);
2076 xmlFreeNotation(ret);
2077 return(NULL);
2078 }
2079 return(ret);
2080}
2081
2082/**
2083 * xmlFreeNotationTable:
2084 * @table: An notation table
2085 *
2086 * Deallocate the memory used by an entities hash table.
2087 */
2088void
2089xmlFreeNotationTable(xmlNotationTablePtr table) {
2090 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2091}
2092
2093/**
2094 * xmlCopyNotation:
2095 * @nota: A notation
2096 *
2097 * Build a copy of a notation.
2098 *
2099 * Returns the new xmlNotationPtr or NULL in case of error.
2100 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002101static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002102xmlCopyNotation(xmlNotationPtr nota) {
2103 xmlNotationPtr cur;
2104
2105 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2106 if (cur == NULL) {
2107 xmlGenericError(xmlGenericErrorContext,
2108 "xmlCopyNotation: out of memory !\n");
2109 return(NULL);
2110 }
2111 if (nota->name != NULL)
2112 cur->name = xmlStrdup(nota->name);
2113 else
2114 cur->name = NULL;
2115 if (nota->PublicID != NULL)
2116 cur->PublicID = xmlStrdup(nota->PublicID);
2117 else
2118 cur->PublicID = NULL;
2119 if (nota->SystemID != NULL)
2120 cur->SystemID = xmlStrdup(nota->SystemID);
2121 else
2122 cur->SystemID = NULL;
2123 return(cur);
2124}
2125
2126/**
2127 * xmlCopyNotationTable:
2128 * @table: A notation table
2129 *
2130 * Build a copy of a notation table.
2131 *
2132 * Returns the new xmlNotationTablePtr or NULL in case of error.
2133 */
2134xmlNotationTablePtr
2135xmlCopyNotationTable(xmlNotationTablePtr table) {
2136 return((xmlNotationTablePtr) xmlHashCopy(table,
2137 (xmlHashCopier) xmlCopyNotation));
2138}
2139
2140/**
2141 * xmlDumpNotationDecl:
2142 * @buf: the XML buffer output
2143 * @nota: A notation declaration
2144 *
2145 * This will dump the content the notation declaration as an XML DTD definition
2146 */
2147void
2148xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2149 xmlBufferWriteChar(buf, "<!NOTATION ");
2150 xmlBufferWriteCHAR(buf, nota->name);
2151 if (nota->PublicID != NULL) {
2152 xmlBufferWriteChar(buf, " PUBLIC ");
2153 xmlBufferWriteQuotedString(buf, nota->PublicID);
2154 if (nota->SystemID != NULL) {
2155 xmlBufferWriteChar(buf, " ");
2156 xmlBufferWriteCHAR(buf, nota->SystemID);
2157 }
2158 } else {
2159 xmlBufferWriteChar(buf, " SYSTEM ");
2160 xmlBufferWriteCHAR(buf, nota->SystemID);
2161 }
2162 xmlBufferWriteChar(buf, " >\n");
2163}
2164
2165/**
2166 * xmlDumpNotationTable:
2167 * @buf: the XML buffer output
2168 * @table: A notation table
2169 *
2170 * This will dump the content of the notation table as an XML DTD definition
2171 */
2172void
2173xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2174 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2175}
2176
2177/************************************************************************
2178 * *
2179 * IDs *
2180 * *
2181 ************************************************************************/
2182/**
2183 * xmlCreateIDTable:
2184 *
2185 * create and initialize an empty id hash table.
2186 *
2187 * Returns the xmlIDTablePtr just created or NULL in case
2188 * of error.
2189 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002190static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002191xmlCreateIDTable(void) {
2192 return(xmlHashCreate(0));
2193}
2194
2195/**
2196 * xmlFreeID:
2197 * @not: A id
2198 *
2199 * Deallocate the memory used by an id definition
2200 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002201static void
Owen Taylor3473f882001-02-23 17:55:21 +00002202xmlFreeID(xmlIDPtr id) {
2203 if (id == NULL) return;
2204 if (id->value != NULL)
2205 xmlFree((xmlChar *) id->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002206 if (id->name != NULL)
2207 xmlFree((xmlChar *) id->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002208 xmlFree(id);
2209}
2210
2211/**
2212 * xmlAddID:
2213 * @ctxt: the validation context
2214 * @doc: pointer to the document
2215 * @value: the value name
2216 * @attr: the attribute holding the ID
2217 *
2218 * Register a new id declaration
2219 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002220 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002221 */
2222xmlIDPtr
2223xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2224 xmlAttrPtr attr) {
2225 xmlIDPtr ret;
2226 xmlIDTablePtr table;
2227
2228 if (doc == NULL) {
2229 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002230 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002231 return(NULL);
2232 }
2233 if (value == NULL) {
2234 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002235 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002236 return(NULL);
2237 }
2238 if (attr == NULL) {
2239 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002240 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002241 return(NULL);
2242 }
2243
2244 /*
2245 * Create the ID table if needed.
2246 */
2247 table = (xmlIDTablePtr) doc->ids;
2248 if (table == NULL)
2249 doc->ids = table = xmlCreateIDTable();
2250 if (table == NULL) {
2251 xmlGenericError(xmlGenericErrorContext,
2252 "xmlAddID: Table creation failed!\n");
2253 return(NULL);
2254 }
2255
2256 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2257 if (ret == NULL) {
2258 xmlGenericError(xmlGenericErrorContext,
2259 "xmlAddID: out of memory\n");
2260 return(NULL);
2261 }
2262
2263 /*
2264 * fill the structure.
2265 */
2266 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002267 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2268 /*
2269 * Operating in streaming mode, attr is gonna disapear
2270 */
2271 ret->name = xmlStrdup(attr->name);
2272 ret->attr = NULL;
2273 } else {
2274 ret->attr = attr;
2275 ret->name = NULL;
2276 }
2277 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002278
2279 if (xmlHashAddEntry(table, value, ret) < 0) {
2280 /*
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 }
Owen Taylor3473f882001-02-23 17:55:21 +00002287 xmlFreeID(ret);
2288 return(NULL);
2289 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002290 if (attr != NULL)
2291 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002292 return(ret);
2293}
2294
2295/**
2296 * xmlFreeIDTable:
2297 * @table: An id table
2298 *
2299 * Deallocate the memory used by an ID hash table.
2300 */
2301void
2302xmlFreeIDTable(xmlIDTablePtr table) {
2303 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2304}
2305
2306/**
2307 * xmlIsID:
2308 * @doc: the document
2309 * @elem: the element carrying the attribute
2310 * @attr: the attribute
2311 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002312 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002313 * then this is done if DTD loading has been requested. In the case
2314 * of HTML documents parsed with the HTML parser, then ID detection is
2315 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002316 *
2317 * Returns 0 or 1 depending on the lookup result
2318 */
2319int
2320xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2321 if (doc == NULL) return(0);
2322 if (attr == NULL) return(0);
2323 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2324 return(0);
2325 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2326 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2327 (xmlStrEqual(BAD_CAST "name", attr->name)))
2328 return(1);
2329 return(0);
2330 } else {
2331 xmlAttributePtr attrDecl;
2332
2333 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002334 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2335 /*
2336 * TODO: this sucks ... recomputing this every time is stupid
2337 */
2338 int len = xmlStrlen(elem->name) + xmlStrlen(elem->ns->prefix) + 2;
2339 xmlChar *fullname;
2340
2341 fullname = xmlMalloc(len);
2342 if (fullname == NULL)
2343 return(0);
2344 snprintf((char *) fullname, len, "%s:%s", (char *) elem->ns->prefix,
2345 (char *) elem->name);
2346 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2347 attr->name);
2348 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2349 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2350 attr->name);
2351 xmlFree(fullname);
2352 } else {
2353 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2354 attr->name);
2355 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2356 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2357 attr->name);
2358 }
Owen Taylor3473f882001-02-23 17:55:21 +00002359
2360 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2361 return(1);
2362 }
2363 return(0);
2364}
2365
2366/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002367 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002368 * @doc: the document
2369 * @attr: the attribute
2370 *
2371 * Remove the given attribute from the ID table maintained internally.
2372 *
2373 * Returns -1 if the lookup failed and 0 otherwise
2374 */
2375int
2376xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2377 xmlAttrPtr cur;
2378 xmlIDTablePtr table;
2379 xmlChar *ID;
2380
2381 if (doc == NULL) return(-1);
2382 if (attr == NULL) return(-1);
2383 table = (xmlIDTablePtr) doc->ids;
2384 if (table == NULL)
2385 return(-1);
2386
2387 if (attr == NULL)
2388 return(-1);
2389 ID = xmlNodeListGetString(doc, attr->children, 1);
2390 if (ID == NULL)
2391 return(-1);
2392 cur = xmlHashLookup(table, ID);
2393 if (cur != attr) {
2394 xmlFree(ID);
2395 return(-1);
2396 }
2397 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2398 xmlFree(ID);
2399 return(0);
2400}
2401
2402/**
2403 * xmlGetID:
2404 * @doc: pointer to the document
2405 * @ID: the ID value
2406 *
2407 * Search the attribute declaring the given ID
2408 *
2409 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2410 */
2411xmlAttrPtr
2412xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2413 xmlIDTablePtr table;
2414 xmlIDPtr id;
2415
2416 if (doc == NULL) {
2417 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2418 return(NULL);
2419 }
2420
2421 if (ID == NULL) {
2422 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2423 return(NULL);
2424 }
2425
2426 table = (xmlIDTablePtr) doc->ids;
2427 if (table == NULL)
2428 return(NULL);
2429
2430 id = xmlHashLookup(table, ID);
2431 if (id == NULL)
2432 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002433 if (id->attr == NULL) {
2434 /*
2435 * We are operating on a stream, return a well known reference
2436 * since the attribute node doesn't exist anymore
2437 */
2438 return((xmlAttrPtr) doc);
2439 }
Owen Taylor3473f882001-02-23 17:55:21 +00002440 return(id->attr);
2441}
2442
2443/************************************************************************
2444 * *
2445 * Refs *
2446 * *
2447 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002448typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002449{
2450 xmlListPtr l;
2451 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002452} xmlRemoveMemo;
2453
2454typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2455
2456typedef struct xmlValidateMemo_t
2457{
2458 xmlValidCtxtPtr ctxt;
2459 const xmlChar *name;
2460} xmlValidateMemo;
2461
2462typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002463
2464/**
2465 * xmlCreateRefTable:
2466 *
2467 * create and initialize an empty ref hash table.
2468 *
2469 * Returns the xmlRefTablePtr just created or NULL in case
2470 * of error.
2471 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002472static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002473xmlCreateRefTable(void) {
2474 return(xmlHashCreate(0));
2475}
2476
2477/**
2478 * xmlFreeRef:
2479 * @lk: A list link
2480 *
2481 * Deallocate the memory used by a ref definition
2482 */
2483static void
2484xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002485 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2486 if (ref == NULL) return;
2487 if (ref->value != NULL)
2488 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002489 if (ref->name != NULL)
2490 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002491 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002492}
2493
2494/**
2495 * xmlFreeRefList:
2496 * @list_ref: A list of references.
2497 *
2498 * Deallocate the memory used by a list of references
2499 */
2500static void
2501xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002502 if (list_ref == NULL) return;
2503 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002504}
2505
2506/**
2507 * xmlWalkRemoveRef:
2508 * @data: Contents of current link
2509 * @user: Value supplied by the user
2510 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002511 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002512 */
2513static int
2514xmlWalkRemoveRef(const void *data, const void *user)
2515{
Daniel Veillard37721922001-05-04 15:21:12 +00002516 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2517 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2518 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002519
Daniel Veillard37721922001-05-04 15:21:12 +00002520 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2521 xmlListRemoveFirst(ref_list, (void *)data);
2522 return 0;
2523 }
2524 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002525}
2526
2527/**
2528 * xmlAddRef:
2529 * @ctxt: the validation context
2530 * @doc: pointer to the document
2531 * @value: the value name
2532 * @attr: the attribute holding the Ref
2533 *
2534 * Register a new ref declaration
2535 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002536 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002537 */
2538xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002539xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002540 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002541 xmlRefPtr ret;
2542 xmlRefTablePtr table;
2543 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002544
Daniel Veillard37721922001-05-04 15:21:12 +00002545 if (doc == NULL) {
2546 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002547 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002548 return(NULL);
2549 }
2550 if (value == NULL) {
2551 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002552 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002553 return(NULL);
2554 }
2555 if (attr == NULL) {
2556 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002557 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002558 return(NULL);
2559 }
Owen Taylor3473f882001-02-23 17:55:21 +00002560
Daniel Veillard37721922001-05-04 15:21:12 +00002561 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002562 * Create the Ref table if needed.
2563 */
Daniel Veillard37721922001-05-04 15:21:12 +00002564 table = (xmlRefTablePtr) doc->refs;
2565 if (table == NULL)
2566 doc->refs = table = xmlCreateRefTable();
2567 if (table == NULL) {
2568 xmlGenericError(xmlGenericErrorContext,
2569 "xmlAddRef: Table creation failed!\n");
2570 return(NULL);
2571 }
Owen Taylor3473f882001-02-23 17:55:21 +00002572
Daniel Veillard37721922001-05-04 15:21:12 +00002573 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2574 if (ret == NULL) {
2575 xmlGenericError(xmlGenericErrorContext,
2576 "xmlAddRef: out of memory\n");
2577 return(NULL);
2578 }
Owen Taylor3473f882001-02-23 17:55:21 +00002579
Daniel Veillard37721922001-05-04 15:21:12 +00002580 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002581 * fill the structure.
2582 */
Daniel Veillard37721922001-05-04 15:21:12 +00002583 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002584 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2585 /*
2586 * Operating in streaming mode, attr is gonna disapear
2587 */
2588 ret->name = xmlStrdup(attr->name);
2589 ret->attr = NULL;
2590 } else {
2591 ret->name = NULL;
2592 ret->attr = attr;
2593 }
2594 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002595
Daniel Veillard37721922001-05-04 15:21:12 +00002596 /* To add a reference :-
2597 * References are maintained as a list of references,
2598 * Lookup the entry, if no entry create new nodelist
2599 * Add the owning node to the NodeList
2600 * Return the ref
2601 */
Owen Taylor3473f882001-02-23 17:55:21 +00002602
Daniel Veillard37721922001-05-04 15:21:12 +00002603 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2604 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2605 xmlGenericError(xmlGenericErrorContext,
2606 "xmlAddRef: Reference list creation failed!\n");
2607 return(NULL);
2608 }
2609 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2610 xmlListDelete(ref_list);
2611 xmlGenericError(xmlGenericErrorContext,
2612 "xmlAddRef: Reference list insertion failed!\n");
2613 return(NULL);
2614 }
2615 }
2616 xmlListInsert(ref_list, ret);
2617 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002618}
2619
2620/**
2621 * xmlFreeRefTable:
2622 * @table: An ref table
2623 *
2624 * Deallocate the memory used by an Ref hash table.
2625 */
2626void
2627xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002628 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002629}
2630
2631/**
2632 * xmlIsRef:
2633 * @doc: the document
2634 * @elem: the element carrying the attribute
2635 * @attr: the attribute
2636 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002637 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002638 * then this is simple, otherwise we use an heuristic: name Ref (upper
2639 * or lowercase).
2640 *
2641 * Returns 0 or 1 depending on the lookup result
2642 */
2643int
2644xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002645 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2646 return(0);
2647 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2648 /* TODO @@@ */
2649 return(0);
2650 } else {
2651 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002652
Daniel Veillard37721922001-05-04 15:21:12 +00002653 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2654 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2655 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2656 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002657
Daniel Veillard37721922001-05-04 15:21:12 +00002658 if ((attrDecl != NULL) &&
2659 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2660 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2661 return(1);
2662 }
2663 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002664}
2665
2666/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002667 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002668 * @doc: the document
2669 * @attr: the attribute
2670 *
2671 * Remove the given attribute from the Ref table maintained internally.
2672 *
2673 * Returns -1 if the lookup failed and 0 otherwise
2674 */
2675int
2676xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002677 xmlListPtr ref_list;
2678 xmlRefTablePtr table;
2679 xmlChar *ID;
2680 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002681
Daniel Veillard37721922001-05-04 15:21:12 +00002682 if (doc == NULL) return(-1);
2683 if (attr == NULL) return(-1);
2684 table = (xmlRefTablePtr) doc->refs;
2685 if (table == NULL)
2686 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002687
Daniel Veillard37721922001-05-04 15:21:12 +00002688 if (attr == NULL)
2689 return(-1);
2690 ID = xmlNodeListGetString(doc, attr->children, 1);
2691 if (ID == NULL)
2692 return(-1);
2693 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002694
Daniel Veillard37721922001-05-04 15:21:12 +00002695 if(ref_list == NULL) {
2696 xmlFree(ID);
2697 return (-1);
2698 }
2699 /* At this point, ref_list refers to a list of references which
2700 * have the same key as the supplied attr. Our list of references
2701 * is ordered by reference address and we don't have that information
2702 * here to use when removing. We'll have to walk the list and
2703 * check for a matching attribute, when we find one stop the walk
2704 * and remove the entry.
2705 * The list is ordered by reference, so that means we don't have the
2706 * key. Passing the list and the reference to the walker means we
2707 * will have enough data to be able to remove the entry.
2708 */
2709 target.l = ref_list;
2710 target.ap = attr;
2711
2712 /* Remove the supplied attr from our list */
2713 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002714
Daniel Veillard37721922001-05-04 15:21:12 +00002715 /*If the list is empty then remove the list entry in the hash */
2716 if (xmlListEmpty(ref_list))
2717 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2718 xmlFreeRefList);
2719 xmlFree(ID);
2720 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002721}
2722
2723/**
2724 * xmlGetRefs:
2725 * @doc: pointer to the document
2726 * @ID: the ID value
2727 *
2728 * Find the set of references for the supplied ID.
2729 *
2730 * Returns NULL if not found, otherwise node set for the ID.
2731 */
2732xmlListPtr
2733xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002734 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002735
Daniel Veillard37721922001-05-04 15:21:12 +00002736 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002737 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002738 return(NULL);
2739 }
Owen Taylor3473f882001-02-23 17:55:21 +00002740
Daniel Veillard37721922001-05-04 15:21:12 +00002741 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002742 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002743 return(NULL);
2744 }
Owen Taylor3473f882001-02-23 17:55:21 +00002745
Daniel Veillard37721922001-05-04 15:21:12 +00002746 table = (xmlRefTablePtr) doc->refs;
2747 if (table == NULL)
2748 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002749
Daniel Veillard37721922001-05-04 15:21:12 +00002750 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002751}
2752
2753/************************************************************************
2754 * *
2755 * Routines for validity checking *
2756 * *
2757 ************************************************************************/
2758
2759/**
2760 * xmlGetDtdElementDesc:
2761 * @dtd: a pointer to the DtD to search
2762 * @name: the element name
2763 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002764 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002765 *
2766 * returns the xmlElementPtr if found or NULL
2767 */
2768
2769xmlElementPtr
2770xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2771 xmlElementTablePtr table;
2772 xmlElementPtr cur;
2773 xmlChar *uqname = NULL, *prefix = NULL;
2774
2775 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002776 if (dtd->elements == NULL)
2777 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002778 table = (xmlElementTablePtr) dtd->elements;
2779
2780 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002781 if (uqname != NULL)
2782 name = uqname;
2783 cur = xmlHashLookup2(table, name, prefix);
2784 if (prefix != NULL) xmlFree(prefix);
2785 if (uqname != NULL) xmlFree(uqname);
2786 return(cur);
2787}
2788/**
2789 * xmlGetDtdElementDesc2:
2790 * @dtd: a pointer to the DtD to search
2791 * @name: the element name
2792 * @create: create an empty description if not found
2793 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002794 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002795 *
2796 * returns the xmlElementPtr if found or NULL
2797 */
2798
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002799static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002800xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2801 xmlElementTablePtr table;
2802 xmlElementPtr cur;
2803 xmlChar *uqname = NULL, *prefix = NULL;
2804
2805 if (dtd == NULL) return(NULL);
2806 if (dtd->elements == NULL) {
2807 if (!create)
2808 return(NULL);
2809 /*
2810 * Create the Element table if needed.
2811 */
2812 table = (xmlElementTablePtr) dtd->elements;
2813 if (table == NULL) {
2814 table = xmlCreateElementTable();
2815 dtd->elements = (void *) table;
2816 }
2817 if (table == NULL) {
2818 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002819 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002820 return(NULL);
2821 }
2822 }
2823 table = (xmlElementTablePtr) dtd->elements;
2824
2825 uqname = xmlSplitQName2(name, &prefix);
2826 if (uqname != NULL)
2827 name = uqname;
2828 cur = xmlHashLookup2(table, name, prefix);
2829 if ((cur == NULL) && (create)) {
2830 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2831 if (cur == NULL) {
2832 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002833 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002834 return(NULL);
2835 }
2836 memset(cur, 0, sizeof(xmlElement));
2837 cur->type = XML_ELEMENT_DECL;
2838
2839 /*
2840 * fill the structure.
2841 */
2842 cur->name = xmlStrdup(name);
2843 cur->prefix = xmlStrdup(prefix);
2844 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2845
2846 xmlHashAddEntry2(table, name, prefix, cur);
2847 }
2848 if (prefix != NULL) xmlFree(prefix);
2849 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002850 return(cur);
2851}
2852
2853/**
2854 * xmlGetDtdQElementDesc:
2855 * @dtd: a pointer to the DtD to search
2856 * @name: the element name
2857 * @prefix: the element namespace prefix
2858 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002859 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002860 *
2861 * returns the xmlElementPtr if found or NULL
2862 */
2863
Daniel Veillard48da9102001-08-07 01:10:10 +00002864xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002865xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2866 const xmlChar *prefix) {
2867 xmlElementTablePtr table;
2868
2869 if (dtd == NULL) return(NULL);
2870 if (dtd->elements == NULL) return(NULL);
2871 table = (xmlElementTablePtr) dtd->elements;
2872
2873 return(xmlHashLookup2(table, name, prefix));
2874}
2875
2876/**
2877 * xmlGetDtdAttrDesc:
2878 * @dtd: a pointer to the DtD to search
2879 * @elem: the element name
2880 * @name: the attribute name
2881 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002882 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002883 * this element.
2884 *
2885 * returns the xmlAttributePtr if found or NULL
2886 */
2887
2888xmlAttributePtr
2889xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2890 xmlAttributeTablePtr table;
2891 xmlAttributePtr cur;
2892 xmlChar *uqname = NULL, *prefix = NULL;
2893
2894 if (dtd == NULL) return(NULL);
2895 if (dtd->attributes == NULL) return(NULL);
2896
2897 table = (xmlAttributeTablePtr) dtd->attributes;
2898 if (table == NULL)
2899 return(NULL);
2900
2901 uqname = xmlSplitQName2(name, &prefix);
2902
2903 if (uqname != NULL) {
2904 cur = xmlHashLookup3(table, uqname, prefix, elem);
2905 if (prefix != NULL) xmlFree(prefix);
2906 if (uqname != NULL) xmlFree(uqname);
2907 } else
2908 cur = xmlHashLookup3(table, name, NULL, elem);
2909 return(cur);
2910}
2911
2912/**
2913 * xmlGetDtdQAttrDesc:
2914 * @dtd: a pointer to the DtD to search
2915 * @elem: the element name
2916 * @name: the attribute name
2917 * @prefix: the attribute namespace prefix
2918 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002919 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002920 * this element.
2921 *
2922 * returns the xmlAttributePtr if found or NULL
2923 */
2924
Daniel Veillard48da9102001-08-07 01:10:10 +00002925xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002926xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2927 const xmlChar *prefix) {
2928 xmlAttributeTablePtr table;
2929
2930 if (dtd == NULL) return(NULL);
2931 if (dtd->attributes == NULL) return(NULL);
2932 table = (xmlAttributeTablePtr) dtd->attributes;
2933
2934 return(xmlHashLookup3(table, name, prefix, elem));
2935}
2936
2937/**
2938 * xmlGetDtdNotationDesc:
2939 * @dtd: a pointer to the DtD to search
2940 * @name: the notation name
2941 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002942 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002943 *
2944 * returns the xmlNotationPtr if found or NULL
2945 */
2946
2947xmlNotationPtr
2948xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2949 xmlNotationTablePtr table;
2950
2951 if (dtd == NULL) return(NULL);
2952 if (dtd->notations == NULL) return(NULL);
2953 table = (xmlNotationTablePtr) dtd->notations;
2954
2955 return(xmlHashLookup(table, name));
2956}
2957
2958/**
2959 * xmlValidateNotationUse:
2960 * @ctxt: the validation context
2961 * @doc: the document
2962 * @notationName: the notation name to check
2963 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002964 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002965 * - [ VC: Notation Declared ]
2966 *
2967 * returns 1 if valid or 0 otherwise
2968 */
2969
2970int
2971xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2972 const xmlChar *notationName) {
2973 xmlNotationPtr notaDecl;
2974 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2975
2976 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2977 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2978 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2979
2980 if (notaDecl == NULL) {
2981 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2982 notationName);
2983 return(0);
2984 }
2985 return(1);
2986}
2987
2988/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002989 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00002990 * @doc: the document
2991 * @name: the element name
2992 *
2993 * Search in the DtDs whether an element accept Mixed content (or ANY)
2994 * basically if it is supposed to accept text childs
2995 *
2996 * returns 0 if no, 1 if yes, and -1 if no element description is available
2997 */
2998
2999int
3000xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
3001 xmlElementPtr elemDecl;
3002
3003 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
3004
3005 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
3006 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3007 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
3008 if (elemDecl == NULL) return(-1);
3009 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003010 case XML_ELEMENT_TYPE_UNDEFINED:
3011 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003012 case XML_ELEMENT_TYPE_ELEMENT:
3013 return(0);
3014 case XML_ELEMENT_TYPE_EMPTY:
3015 /*
3016 * return 1 for EMPTY since we want VC error to pop up
3017 * on <empty> </empty> for example
3018 */
3019 case XML_ELEMENT_TYPE_ANY:
3020 case XML_ELEMENT_TYPE_MIXED:
3021 return(1);
3022 }
3023 return(1);
3024}
3025
3026/**
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 Veillard878eab02002-02-19 13:46:09 +00003357 if ((ent == NULL) && (doc->standalone == 1)) {
3358 doc->standalone = 0;
3359 ent = xmlGetDocEntity(doc, value);
3360 if (ent != NULL) {
3361 VERROR(ctxt->userData,
3362"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3363 name, value);
3364 /* WAIT to get answer from the Core WG on this
3365 ret = 0;
3366 */
3367 }
3368 }
Owen Taylor3473f882001-02-23 17:55:21 +00003369 if (ent == NULL) {
3370 VERROR(ctxt->userData,
3371 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3372 name, value);
3373 ret = 0;
3374 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3375 VERROR(ctxt->userData,
3376 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3377 name, value);
3378 ret = 0;
3379 }
3380 break;
3381 }
3382 case XML_ATTRIBUTE_ENTITIES: {
3383 xmlChar *dup, *nam = NULL, *cur, save;
3384 xmlEntityPtr ent;
3385
3386 dup = xmlStrdup(value);
3387 if (dup == NULL)
3388 return(0);
3389 cur = dup;
3390 while (*cur != 0) {
3391 nam = cur;
3392 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3393 save = *cur;
3394 *cur = 0;
3395 ent = xmlGetDocEntity(doc, nam);
3396 if (ent == NULL) {
3397 VERROR(ctxt->userData,
3398 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3399 name, nam);
3400 ret = 0;
3401 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3402 VERROR(ctxt->userData,
3403 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3404 name, nam);
3405 ret = 0;
3406 }
3407 if (save == 0)
3408 break;
3409 *cur = save;
3410 while (IS_BLANK(*cur)) cur++;
3411 }
3412 xmlFree(dup);
3413 break;
3414 }
3415 case XML_ATTRIBUTE_NOTATION: {
3416 xmlNotationPtr nota;
3417
3418 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3419 if ((nota == NULL) && (doc->extSubset != NULL))
3420 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3421
3422 if (nota == NULL) {
3423 VERROR(ctxt->userData,
3424 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3425 name, value);
3426 ret = 0;
3427 }
3428 break;
3429 }
3430 }
3431 return(ret);
3432}
3433
3434/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003435 * xmlValidCtxtNormalizeAttributeValue:
3436 * @ctxt: the validation context
3437 * @doc: the document
3438 * @elem: the parent
3439 * @name: the attribute name
3440 * @value: the attribute value
3441 * @ctxt: the validation context or NULL
3442 *
3443 * Does the validation related extra step of the normalization of attribute
3444 * values:
3445 *
3446 * If the declared value is not CDATA, then the XML processor must further
3447 * process the normalized attribute value by discarding any leading and
3448 * trailing space (#x20) characters, and by replacing sequences of space
3449 * (#x20) characters by single space (#x20) character.
3450 *
3451 * Also check VC: Standalone Document Declaration in P32, and update
3452 * ctxt->valid accordingly
3453 *
3454 * returns a new normalized string if normalization is needed, NULL otherwise
3455 * the caller must free the returned value.
3456 */
3457
3458xmlChar *
3459xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3460 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3461 xmlChar *ret, *dst;
3462 const xmlChar *src;
3463 xmlAttributePtr attrDecl = NULL;
3464 int extsubset = 0;
3465
3466 if (doc == NULL) return(NULL);
3467 if (elem == NULL) return(NULL);
3468 if (name == NULL) return(NULL);
3469 if (value == NULL) return(NULL);
3470
3471 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3472 xmlChar qname[500];
3473 snprintf((char *) qname, sizeof(qname), "%s:%s",
3474 elem->ns->prefix, elem->name);
3475 qname[sizeof(qname) - 1] = 0;
3476 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3477 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3478 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3479 if (attrDecl != NULL)
3480 extsubset = 1;
3481 }
3482 }
3483 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3484 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3485 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3486 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3487 if (attrDecl != NULL)
3488 extsubset = 1;
3489 }
3490
3491 if (attrDecl == NULL)
3492 return(NULL);
3493 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3494 return(NULL);
3495
3496 ret = xmlStrdup(value);
3497 if (ret == NULL)
3498 return(NULL);
3499 src = value;
3500 dst = ret;
3501 while (*src == 0x20) src++;
3502 while (*src != 0) {
3503 if (*src == 0x20) {
3504 while (*src == 0x20) src++;
3505 if (*src != 0)
3506 *dst++ = 0x20;
3507 } else {
3508 *dst++ = *src++;
3509 }
3510 }
3511 *dst = 0;
3512 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3513 VERROR(ctxt->userData,
3514"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3515 name, elem->name);
3516 ctxt->valid = 0;
3517 }
3518 return(ret);
3519}
3520
3521/**
Owen Taylor3473f882001-02-23 17:55:21 +00003522 * xmlValidNormalizeAttributeValue:
3523 * @doc: the document
3524 * @elem: the parent
3525 * @name: the attribute name
3526 * @value: the attribute value
3527 *
3528 * Does the validation related extra step of the normalization of attribute
3529 * values:
3530 *
3531 * If the declared value is not CDATA, then the XML processor must further
3532 * process the normalized attribute value by discarding any leading and
3533 * trailing space (#x20) characters, and by replacing sequences of space
3534 * (#x20) characters by single space (#x20) character.
3535 *
3536 * returns a new normalized string if normalization is needed, NULL otherwise
3537 * the caller must free the returned value.
3538 */
3539
3540xmlChar *
3541xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3542 const xmlChar *name, const xmlChar *value) {
3543 xmlChar *ret, *dst;
3544 const xmlChar *src;
3545 xmlAttributePtr attrDecl = NULL;
3546
3547 if (doc == NULL) return(NULL);
3548 if (elem == NULL) return(NULL);
3549 if (name == NULL) return(NULL);
3550 if (value == NULL) return(NULL);
3551
3552 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3553 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003554 snprintf((char *) qname, sizeof(qname), "%s:%s",
3555 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003556 qname[sizeof(qname) - 1] = 0;
3557 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3558 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3559 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3560 }
3561 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3562 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3563 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3564
3565 if (attrDecl == NULL)
3566 return(NULL);
3567 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3568 return(NULL);
3569
3570 ret = xmlStrdup(value);
3571 if (ret == NULL)
3572 return(NULL);
3573 src = value;
3574 dst = ret;
3575 while (*src == 0x20) src++;
3576 while (*src != 0) {
3577 if (*src == 0x20) {
3578 while (*src == 0x20) src++;
3579 if (*src != 0)
3580 *dst++ = 0x20;
3581 } else {
3582 *dst++ = *src++;
3583 }
3584 }
3585 *dst = 0;
3586 return(ret);
3587}
3588
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003589static void
Owen Taylor3473f882001-02-23 17:55:21 +00003590xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003591 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003592 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3593}
3594
3595/**
3596 * xmlValidateAttributeDecl:
3597 * @ctxt: the validation context
3598 * @doc: a document instance
3599 * @attr: an attribute definition
3600 *
3601 * Try to validate a single attribute definition
3602 * basically it does the following checks as described by the
3603 * XML-1.0 recommendation:
3604 * - [ VC: Attribute Default Legal ]
3605 * - [ VC: Enumeration ]
3606 * - [ VC: ID Attribute Default ]
3607 *
3608 * The ID/IDREF uniqueness and matching are done separately
3609 *
3610 * returns 1 if valid or 0 otherwise
3611 */
3612
3613int
3614xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3615 xmlAttributePtr attr) {
3616 int ret = 1;
3617 int val;
3618 CHECK_DTD;
3619 if(attr == NULL) return(1);
3620
3621 /* Attribute Default Legal */
3622 /* Enumeration */
3623 if (attr->defaultValue != NULL) {
3624 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3625 if (val == 0) {
3626 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003627 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003628 attr->name, attr->elem);
3629 }
3630 ret &= val;
3631 }
3632
3633 /* ID Attribute Default */
3634 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3635 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3636 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3637 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003638 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003639 attr->name, attr->elem);
3640 ret = 0;
3641 }
3642
3643 /* One ID per Element Type */
3644 if (attr->atype == XML_ATTRIBUTE_ID) {
3645 int nbId;
3646
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003647 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003648 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3649 attr->elem);
3650 if (elem != NULL) {
3651 nbId = xmlScanIDAttributeDecl(NULL, elem);
3652 } else {
3653 xmlAttributeTablePtr table;
3654
3655 /*
3656 * The attribute may be declared in the internal subset and the
3657 * element in the external subset.
3658 */
3659 nbId = 0;
3660 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3661 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3662 xmlValidateAttributeIdCallback, &nbId);
3663 }
3664 if (nbId > 1) {
3665 VERROR(ctxt->userData,
3666 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3667 attr->elem, nbId, attr->name);
3668 } else if (doc->extSubset != NULL) {
3669 int extId = 0;
3670 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3671 if (elem != NULL) {
3672 extId = xmlScanIDAttributeDecl(NULL, elem);
3673 }
3674 if (extId > 1) {
3675 VERROR(ctxt->userData,
3676 "Element %s has %d ID attribute defined in the external subset : %s\n",
3677 attr->elem, extId, attr->name);
3678 } else if (extId + nbId > 1) {
3679 VERROR(ctxt->userData,
3680"Element %s has ID attributes defined in the internal and external subset : %s\n",
3681 attr->elem, attr->name);
3682 }
3683 }
3684 }
3685
3686 /* Validity Constraint: Enumeration */
3687 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3688 xmlEnumerationPtr tree = attr->tree;
3689 while (tree != NULL) {
3690 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3691 tree = tree->next;
3692 }
3693 if (tree == NULL) {
3694 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003695"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003696 attr->defaultValue, attr->name, attr->elem);
3697 ret = 0;
3698 }
3699 }
3700
3701 return(ret);
3702}
3703
3704/**
3705 * xmlValidateElementDecl:
3706 * @ctxt: the validation context
3707 * @doc: a document instance
3708 * @elem: an element definition
3709 *
3710 * Try to validate a single element definition
3711 * basically it does the following checks as described by the
3712 * XML-1.0 recommendation:
3713 * - [ VC: One ID per Element Type ]
3714 * - [ VC: No Duplicate Types ]
3715 * - [ VC: Unique Element Type Declaration ]
3716 *
3717 * returns 1 if valid or 0 otherwise
3718 */
3719
3720int
3721xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3722 xmlElementPtr elem) {
3723 int ret = 1;
3724 xmlElementPtr tst;
3725
3726 CHECK_DTD;
3727
3728 if (elem == NULL) return(1);
3729
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003730#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003731#ifdef LIBXML_REGEXP_ENABLED
3732 /* Build the regexp associated to the content model */
3733 ret = xmlValidBuildContentModel(ctxt, elem);
3734#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003735#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003736
Owen Taylor3473f882001-02-23 17:55:21 +00003737 /* No Duplicate Types */
3738 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3739 xmlElementContentPtr cur, next;
3740 const xmlChar *name;
3741
3742 cur = elem->content;
3743 while (cur != NULL) {
3744 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3745 if (cur->c1 == NULL) break;
3746 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3747 name = cur->c1->name;
3748 next = cur->c2;
3749 while (next != NULL) {
3750 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3751 if (xmlStrEqual(next->name, name)) {
3752 VERROR(ctxt->userData,
3753 "Definition of %s has duplicate references of %s\n",
3754 elem->name, name);
3755 ret = 0;
3756 }
3757 break;
3758 }
3759 if (next->c1 == NULL) break;
3760 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3761 if (xmlStrEqual(next->c1->name, name)) {
3762 VERROR(ctxt->userData,
3763 "Definition of %s has duplicate references of %s\n",
3764 elem->name, name);
3765 ret = 0;
3766 }
3767 next = next->c2;
3768 }
3769 }
3770 cur = cur->c2;
3771 }
3772 }
3773
3774 /* VC: Unique Element Type Declaration */
3775 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003776 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003777 ((tst->prefix == elem->prefix) ||
3778 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003779 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003780 VERROR(ctxt->userData, "Redefinition of element %s\n",
3781 elem->name);
3782 ret = 0;
3783 }
3784 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003785 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003786 ((tst->prefix == elem->prefix) ||
3787 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003788 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003789 VERROR(ctxt->userData, "Redefinition of element %s\n",
3790 elem->name);
3791 ret = 0;
3792 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003793 /* One ID per Element Type
3794 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003795 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3796 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003797 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003798 return(ret);
3799}
3800
3801/**
3802 * xmlValidateOneAttribute:
3803 * @ctxt: the validation context
3804 * @doc: a document instance
3805 * @elem: an element instance
3806 * @attr: an attribute instance
3807 * @value: the attribute value (without entities processing)
3808 *
3809 * Try to validate a single attribute for an element
3810 * basically it does the following checks as described by the
3811 * XML-1.0 recommendation:
3812 * - [ VC: Attribute Value Type ]
3813 * - [ VC: Fixed Attribute Default ]
3814 * - [ VC: Entity Name ]
3815 * - [ VC: Name Token ]
3816 * - [ VC: ID ]
3817 * - [ VC: IDREF ]
3818 * - [ VC: Entity Name ]
3819 * - [ VC: Notation Attributes ]
3820 *
3821 * The ID/IDREF uniqueness and matching are done separately
3822 *
3823 * returns 1 if valid or 0 otherwise
3824 */
3825
3826int
3827xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3828 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3829 /* xmlElementPtr elemDecl; */
3830 xmlAttributePtr attrDecl = NULL;
3831 int val;
3832 int ret = 1;
3833
3834 CHECK_DTD;
3835 if ((elem == NULL) || (elem->name == NULL)) return(0);
3836 if ((attr == NULL) || (attr->name == NULL)) return(0);
3837
3838 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3839 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003840 snprintf((char *) qname, sizeof(qname), "%s:%s",
3841 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003842 qname[sizeof(qname) - 1] = 0;
3843 if (attr->ns != NULL) {
3844 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3845 attr->name, attr->ns->prefix);
3846 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3847 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3848 attr->name, attr->ns->prefix);
3849 } else {
3850 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3851 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3852 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3853 qname, attr->name);
3854 }
3855 }
3856 if (attrDecl == NULL) {
3857 if (attr->ns != NULL) {
3858 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3859 attr->name, attr->ns->prefix);
3860 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3861 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3862 attr->name, attr->ns->prefix);
3863 } else {
3864 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3865 elem->name, attr->name);
3866 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3867 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3868 elem->name, attr->name);
3869 }
3870 }
3871
3872
3873 /* Validity Constraint: Attribute Value Type */
3874 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003875 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003876 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003877 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003878 attr->name, elem->name);
3879 return(0);
3880 }
3881 attr->atype = attrDecl->atype;
3882
3883 val = xmlValidateAttributeValue(attrDecl->atype, value);
3884 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003885 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003886 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003887 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003888 attr->name, elem->name);
3889 ret = 0;
3890 }
3891
3892 /* Validity constraint: Fixed Attribute Default */
3893 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3894 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003895 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003896 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003897 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003898 attr->name, elem->name, attrDecl->defaultValue);
3899 ret = 0;
3900 }
3901 }
3902
3903 /* Validity Constraint: ID uniqueness */
3904 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3905 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3906 ret = 0;
3907 }
3908
3909 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3910 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3911 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3912 ret = 0;
3913 }
3914
3915 /* Validity Constraint: Notation Attributes */
3916 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3917 xmlEnumerationPtr tree = attrDecl->tree;
3918 xmlNotationPtr nota;
3919
3920 /* First check that the given NOTATION was declared */
3921 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3922 if (nota == NULL)
3923 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3924
3925 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003926 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003927 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003928 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003929 value, attr->name, elem->name);
3930 ret = 0;
3931 }
3932
3933 /* Second, verify that it's among the list */
3934 while (tree != NULL) {
3935 if (xmlStrEqual(tree->name, value)) break;
3936 tree = tree->next;
3937 }
3938 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003939 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003940 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003941"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003942 value, attr->name, elem->name);
3943 ret = 0;
3944 }
3945 }
3946
3947 /* Validity Constraint: Enumeration */
3948 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3949 xmlEnumerationPtr tree = attrDecl->tree;
3950 while (tree != NULL) {
3951 if (xmlStrEqual(tree->name, value)) break;
3952 tree = tree->next;
3953 }
3954 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003955 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003956 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003957 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003958 value, attr->name, elem->name);
3959 ret = 0;
3960 }
3961 }
3962
3963 /* Fixed Attribute Default */
3964 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3965 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003966 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003967 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003968 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003969 attr->name, elem->name, attrDecl->defaultValue);
3970 ret = 0;
3971 }
3972
3973 /* Extra check for the attribute value */
3974 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3975 attrDecl->atype, value);
3976
3977 return(ret);
3978}
3979
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003980/**
3981 * xmlValidateOneNamespace:
3982 * @ctxt: the validation context
3983 * @doc: a document instance
3984 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00003985 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003986 * @ns: an namespace declaration instance
3987 * @value: the attribute value (without entities processing)
3988 *
3989 * Try to validate a single namespace declaration for an element
3990 * basically it does the following checks as described by the
3991 * XML-1.0 recommendation:
3992 * - [ VC: Attribute Value Type ]
3993 * - [ VC: Fixed Attribute Default ]
3994 * - [ VC: Entity Name ]
3995 * - [ VC: Name Token ]
3996 * - [ VC: ID ]
3997 * - [ VC: IDREF ]
3998 * - [ VC: Entity Name ]
3999 * - [ VC: Notation Attributes ]
4000 *
4001 * The ID/IDREF uniqueness and matching are done separately
4002 *
4003 * returns 1 if valid or 0 otherwise
4004 */
4005
4006int
4007xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4008xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
4009 /* xmlElementPtr elemDecl; */
4010 xmlAttributePtr attrDecl = NULL;
4011 int val;
4012 int ret = 1;
4013
4014 CHECK_DTD;
4015 if ((elem == NULL) || (elem->name == NULL)) return(0);
4016 if ((ns == NULL) || (ns->href == NULL)) return(0);
4017
4018 if (prefix != NULL) {
4019 xmlChar qname[500];
4020 snprintf((char *) qname, sizeof(qname), "%s:%s",
4021 prefix, elem->name);
4022 qname[sizeof(qname) - 1] = 0;
4023 if (ns->prefix != NULL) {
4024 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
4025 ns->prefix, BAD_CAST "xmlns");
4026 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4027 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
4028 ns->prefix, BAD_CAST "xmlns");
4029 } else {
4030 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname,
4031 BAD_CAST "xmlns");
4032 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4033 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname,
4034 BAD_CAST "xmlns");
4035 }
4036 }
4037 if (attrDecl == NULL) {
4038 if (ns->prefix != NULL) {
4039 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4040 ns->prefix, BAD_CAST "xmlns");
4041 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4042 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4043 ns->prefix, BAD_CAST "xmlns");
4044 } else {
4045 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4046 elem->name, BAD_CAST "xmlns");
4047 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4048 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4049 elem->name, BAD_CAST "xmlns");
4050 }
4051 }
4052
4053
4054 /* Validity Constraint: Attribute Value Type */
4055 if (attrDecl == NULL) {
4056 VECTXT(ctxt, elem);
4057 if (ns->prefix != NULL) {
4058 VERROR(ctxt->userData,
4059 "No declaration for attribute xmlns:%s of element %s\n",
4060 ns->prefix, elem->name);
4061 } else {
4062 VERROR(ctxt->userData,
4063 "No declaration for attribute xmlns of element %s\n",
4064 elem->name);
4065 }
4066 return(0);
4067 }
4068
4069 val = xmlValidateAttributeValue(attrDecl->atype, value);
4070 if (val == 0) {
4071 VECTXT(ctxt, elem);
4072 if (ns->prefix != NULL) {
4073 VERROR(ctxt->userData,
4074 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
4075 ns->prefix, elem->name);
4076 } else {
4077 VERROR(ctxt->userData,
4078 "Syntax of value for attribute xmlns of %s is not valid\n",
4079 elem->name);
4080 }
4081 ret = 0;
4082 }
4083
4084 /* Validity constraint: Fixed Attribute Default */
4085 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4086 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4087 VECTXT(ctxt, elem);
4088 if (ns->prefix != NULL) {
4089 VERROR(ctxt->userData,
4090 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4091 ns->prefix, elem->name, attrDecl->defaultValue);
4092 } else {
4093 VERROR(ctxt->userData,
4094 "Value for attribute xmlns of %s is different from default \"%s\"\n",
4095 elem->name, attrDecl->defaultValue);
4096 }
4097 ret = 0;
4098 }
4099 }
4100
4101 /* Validity Constraint: ID uniqueness */
4102 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4103 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4104 ret = 0;
4105 }
4106
4107 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4108 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4109 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4110 ret = 0;
4111 }
4112
4113 /* Validity Constraint: Notation Attributes */
4114 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4115 xmlEnumerationPtr tree = attrDecl->tree;
4116 xmlNotationPtr nota;
4117
4118 /* First check that the given NOTATION was declared */
4119 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4120 if (nota == NULL)
4121 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4122
4123 if (nota == NULL) {
4124 VECTXT(ctxt, elem);
4125 if (ns->prefix != NULL) {
4126 VERROR(ctxt->userData,
4127 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4128 value, ns->prefix, elem->name);
4129 } else {
4130 VERROR(ctxt->userData,
4131 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
4132 value, elem->name);
4133 }
4134 ret = 0;
4135 }
4136
4137 /* Second, verify that it's among the list */
4138 while (tree != NULL) {
4139 if (xmlStrEqual(tree->name, value)) break;
4140 tree = tree->next;
4141 }
4142 if (tree == NULL) {
4143 VECTXT(ctxt, elem);
4144 if (ns->prefix != NULL) {
4145 VERROR(ctxt->userData,
4146"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4147 value, ns->prefix, elem->name);
4148 } else {
4149 VERROR(ctxt->userData,
4150"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
4151 value, elem->name);
4152 }
4153 ret = 0;
4154 }
4155 }
4156
4157 /* Validity Constraint: Enumeration */
4158 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4159 xmlEnumerationPtr tree = attrDecl->tree;
4160 while (tree != NULL) {
4161 if (xmlStrEqual(tree->name, value)) break;
4162 tree = tree->next;
4163 }
4164 if (tree == NULL) {
4165 VECTXT(ctxt, elem);
4166 if (ns->prefix != NULL) {
4167 VERROR(ctxt->userData,
4168"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4169 value, ns->prefix, elem->name);
4170 } else {
4171 VERROR(ctxt->userData,
4172"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4173 value, elem->name);
4174 }
4175 ret = 0;
4176 }
4177 }
4178
4179 /* Fixed Attribute Default */
4180 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4181 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4182 VECTXT(ctxt, elem);
4183 if (ns->prefix != NULL) {
4184 VERROR(ctxt->userData,
4185 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4186 ns->prefix, elem->name, attrDecl->defaultValue);
4187 } else {
4188 VERROR(ctxt->userData,
4189 "Value for attribute xmlns of %s must be \"%s\"\n",
4190 elem->name, attrDecl->defaultValue);
4191 }
4192 ret = 0;
4193 }
4194
4195 /* Extra check for the attribute value */
4196 if (ns->prefix != NULL) {
4197 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4198 attrDecl->atype, value);
4199 } else {
4200 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4201 attrDecl->atype, value);
4202 }
4203
4204 return(ret);
4205}
4206
Daniel Veillard118aed72002-09-24 14:13:13 +00004207#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004208/**
4209 * xmlValidateSkipIgnorable:
4210 * @ctxt: the validation context
4211 * @child: the child list
4212 *
4213 * Skip ignorable elements w.r.t. the validation process
4214 *
4215 * returns the first element to consider for validation of the content model
4216 */
4217
4218static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004219xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004220 while (child != NULL) {
4221 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004222 /* These things are ignored (skipped) during validation. */
4223 case XML_PI_NODE:
4224 case XML_COMMENT_NODE:
4225 case XML_XINCLUDE_START:
4226 case XML_XINCLUDE_END:
4227 child = child->next;
4228 break;
4229 case XML_TEXT_NODE:
4230 if (xmlIsBlankNode(child))
4231 child = child->next;
4232 else
4233 return(child);
4234 break;
4235 /* keep current node */
4236 default:
4237 return(child);
4238 }
4239 }
4240 return(child);
4241}
4242
4243/**
4244 * xmlValidateElementType:
4245 * @ctxt: the validation context
4246 *
4247 * Try to validate the content model of an element internal function
4248 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004249 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4250 * reference is found and -3 if the validation succeeded but
4251 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004252 */
4253
4254static int
4255xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004256 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004257 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004258
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004259 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004260 if ((NODE == NULL) && (CONT == NULL))
4261 return(1);
4262 if ((NODE == NULL) &&
4263 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4264 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4265 return(1);
4266 }
4267 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004268 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004269 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004270
4271 /*
4272 * We arrive here when more states need to be examined
4273 */
4274cont:
4275
4276 /*
4277 * We just recovered from a rollback generated by a possible
4278 * epsilon transition, go directly to the analysis phase
4279 */
4280 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004281 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004282 DEBUG_VALID_STATE(NODE, CONT)
4283 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004284 goto analyze;
4285 }
4286
4287 DEBUG_VALID_STATE(NODE, CONT)
4288 /*
4289 * we may have to save a backup state here. This is the equivalent
4290 * of handling epsilon transition in NFAs.
4291 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004292 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004293 ((CONT->parent == NULL) ||
4294 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004295 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004296 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004297 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004298 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004299 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4300 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004301 }
4302
4303
4304 /*
4305 * Check first if the content matches
4306 */
4307 switch (CONT->type) {
4308 case XML_ELEMENT_CONTENT_PCDATA:
4309 if (NODE == NULL) {
4310 DEBUG_VALID_MSG("pcdata failed no node");
4311 ret = 0;
4312 break;
4313 }
4314 if (NODE->type == XML_TEXT_NODE) {
4315 DEBUG_VALID_MSG("pcdata found, skip to next");
4316 /*
4317 * go to next element in the content model
4318 * skipping ignorable elems
4319 */
4320 do {
4321 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004322 NODE = xmlValidateSkipIgnorable(NODE);
4323 if ((NODE != NULL) &&
4324 (NODE->type == XML_ENTITY_REF_NODE))
4325 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004326 } while ((NODE != NULL) &&
4327 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004328 (NODE->type != XML_TEXT_NODE) &&
4329 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004330 ret = 1;
4331 break;
4332 } else {
4333 DEBUG_VALID_MSG("pcdata failed");
4334 ret = 0;
4335 break;
4336 }
4337 break;
4338 case XML_ELEMENT_CONTENT_ELEMENT:
4339 if (NODE == NULL) {
4340 DEBUG_VALID_MSG("element failed no node");
4341 ret = 0;
4342 break;
4343 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004344 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4345 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004346 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004347 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4348 ret = (CONT->prefix == NULL);
4349 } else if (CONT->prefix == NULL) {
4350 ret = 0;
4351 } else {
4352 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4353 }
4354 }
4355 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004356 DEBUG_VALID_MSG("element found, skip to next");
4357 /*
4358 * go to next element in the content model
4359 * skipping ignorable elems
4360 */
4361 do {
4362 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004363 NODE = xmlValidateSkipIgnorable(NODE);
4364 if ((NODE != NULL) &&
4365 (NODE->type == XML_ENTITY_REF_NODE))
4366 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004367 } while ((NODE != NULL) &&
4368 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004369 (NODE->type != XML_TEXT_NODE) &&
4370 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004371 } else {
4372 DEBUG_VALID_MSG("element failed");
4373 ret = 0;
4374 break;
4375 }
4376 break;
4377 case XML_ELEMENT_CONTENT_OR:
4378 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004379 * Small optimization.
4380 */
4381 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4382 if ((NODE == NULL) ||
4383 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4384 DEPTH++;
4385 CONT = CONT->c2;
4386 goto cont;
4387 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004388 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4389 ret = (CONT->c1->prefix == NULL);
4390 } else if (CONT->c1->prefix == NULL) {
4391 ret = 0;
4392 } else {
4393 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4394 }
4395 if (ret == 0) {
4396 DEPTH++;
4397 CONT = CONT->c2;
4398 goto cont;
4399 }
Daniel Veillard85349052001-04-20 13:48:21 +00004400 }
4401
4402 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004403 * save the second branch 'or' branch
4404 */
4405 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004406 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4407 OCCURS, ROLLBACK_OR) < 0)
4408 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004409 DEPTH++;
4410 CONT = CONT->c1;
4411 goto cont;
4412 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004413 /*
4414 * Small optimization.
4415 */
4416 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4417 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4418 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4419 if ((NODE == NULL) ||
4420 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4421 DEPTH++;
4422 CONT = CONT->c2;
4423 goto cont;
4424 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004425 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4426 ret = (CONT->c1->prefix == NULL);
4427 } else if (CONT->c1->prefix == NULL) {
4428 ret = 0;
4429 } else {
4430 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4431 }
4432 if (ret == 0) {
4433 DEPTH++;
4434 CONT = CONT->c2;
4435 goto cont;
4436 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004437 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004438 DEPTH++;
4439 CONT = CONT->c1;
4440 goto cont;
4441 }
4442
4443 /*
4444 * At this point handle going up in the tree
4445 */
4446 if (ret == -1) {
4447 DEBUG_VALID_MSG("error found returning");
4448 return(ret);
4449 }
4450analyze:
4451 while (CONT != NULL) {
4452 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004453 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004454 * this level.
4455 */
4456 if (ret == 0) {
4457 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004458 xmlNodePtr cur;
4459
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004460 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004461 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004462 DEBUG_VALID_MSG("Once branch failed, rollback");
4463 if (vstateVPop(ctxt) < 0 ) {
4464 DEBUG_VALID_MSG("exhaustion, failed");
4465 return(0);
4466 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004467 if (cur != ctxt->vstate->node)
4468 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004469 goto cont;
4470 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004471 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004472 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004473 DEBUG_VALID_MSG("Plus branch failed, rollback");
4474 if (vstateVPop(ctxt) < 0 ) {
4475 DEBUG_VALID_MSG("exhaustion, failed");
4476 return(0);
4477 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004478 if (cur != ctxt->vstate->node)
4479 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004480 goto cont;
4481 }
4482 DEBUG_VALID_MSG("Plus branch found");
4483 ret = 1;
4484 break;
4485 case XML_ELEMENT_CONTENT_MULT:
4486#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004487 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004488 DEBUG_VALID_MSG("Mult branch failed");
4489 } else {
4490 DEBUG_VALID_MSG("Mult branch found");
4491 }
4492#endif
4493 ret = 1;
4494 break;
4495 case XML_ELEMENT_CONTENT_OPT:
4496 DEBUG_VALID_MSG("Option branch failed");
4497 ret = 1;
4498 break;
4499 }
4500 } else {
4501 switch (CONT->ocur) {
4502 case XML_ELEMENT_CONTENT_OPT:
4503 DEBUG_VALID_MSG("Option branch succeeded");
4504 ret = 1;
4505 break;
4506 case XML_ELEMENT_CONTENT_ONCE:
4507 DEBUG_VALID_MSG("Once branch succeeded");
4508 ret = 1;
4509 break;
4510 case XML_ELEMENT_CONTENT_PLUS:
4511 if (STATE == ROLLBACK_PARENT) {
4512 DEBUG_VALID_MSG("Plus branch rollback");
4513 ret = 1;
4514 break;
4515 }
4516 if (NODE == NULL) {
4517 DEBUG_VALID_MSG("Plus branch exhausted");
4518 ret = 1;
4519 break;
4520 }
4521 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004522 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004523 goto cont;
4524 case XML_ELEMENT_CONTENT_MULT:
4525 if (STATE == ROLLBACK_PARENT) {
4526 DEBUG_VALID_MSG("Mult branch rollback");
4527 ret = 1;
4528 break;
4529 }
4530 if (NODE == NULL) {
4531 DEBUG_VALID_MSG("Mult branch exhausted");
4532 ret = 1;
4533 break;
4534 }
4535 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004536 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004537 goto cont;
4538 }
4539 }
4540 STATE = 0;
4541
4542 /*
4543 * Then act accordingly at the parent level
4544 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004545 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004546 if (CONT->parent == NULL)
4547 break;
4548
4549 switch (CONT->parent->type) {
4550 case XML_ELEMENT_CONTENT_PCDATA:
4551 DEBUG_VALID_MSG("Error: parent pcdata");
4552 return(-1);
4553 case XML_ELEMENT_CONTENT_ELEMENT:
4554 DEBUG_VALID_MSG("Error: parent element");
4555 return(-1);
4556 case XML_ELEMENT_CONTENT_OR:
4557 if (ret == 1) {
4558 DEBUG_VALID_MSG("Or succeeded");
4559 CONT = CONT->parent;
4560 DEPTH--;
4561 } else {
4562 DEBUG_VALID_MSG("Or failed");
4563 CONT = CONT->parent;
4564 DEPTH--;
4565 }
4566 break;
4567 case XML_ELEMENT_CONTENT_SEQ:
4568 if (ret == 0) {
4569 DEBUG_VALID_MSG("Sequence failed");
4570 CONT = CONT->parent;
4571 DEPTH--;
4572 } else if (CONT == CONT->parent->c1) {
4573 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4574 CONT = CONT->parent->c2;
4575 goto cont;
4576 } else {
4577 DEBUG_VALID_MSG("Sequence succeeded");
4578 CONT = CONT->parent;
4579 DEPTH--;
4580 }
4581 }
4582 }
4583 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004584 xmlNodePtr cur;
4585
4586 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004587 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4588 if (vstateVPop(ctxt) < 0 ) {
4589 DEBUG_VALID_MSG("exhaustion, failed");
4590 return(0);
4591 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004592 if (cur != ctxt->vstate->node)
4593 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004594 goto cont;
4595 }
4596 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004597 xmlNodePtr cur;
4598
4599 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004600 DEBUG_VALID_MSG("Failure, rollback");
4601 if (vstateVPop(ctxt) < 0 ) {
4602 DEBUG_VALID_MSG("exhaustion, failed");
4603 return(0);
4604 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004605 if (cur != ctxt->vstate->node)
4606 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004607 goto cont;
4608 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004609 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004610}
Daniel Veillard23e73572002-09-19 19:56:43 +00004611#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004612
4613/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004614 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004615 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004616 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004617 * @content: An element
4618 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4619 *
4620 * This will dump the list of elements to the buffer
4621 * Intended just for the debug routine
4622 */
4623static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004624xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004625 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004626 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004627
4628 if (node == NULL) return;
4629 if (glob) strcat(buf, "(");
4630 cur = node;
4631 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004632 len = strlen(buf);
4633 if (size - len < 50) {
4634 if ((size - len > 4) && (buf[len - 1] != '.'))
4635 strcat(buf, " ...");
4636 return;
4637 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004638 switch (cur->type) {
4639 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004640 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004641 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004642 if ((size - len > 4) && (buf[len - 1] != '.'))
4643 strcat(buf, " ...");
4644 return;
4645 }
4646 strcat(buf, (char *) cur->ns->prefix);
4647 strcat(buf, ":");
4648 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004649 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004650 if ((size - len > 4) && (buf[len - 1] != '.'))
4651 strcat(buf, " ...");
4652 return;
4653 }
4654 strcat(buf, (char *) cur->name);
4655 if (cur->next != NULL)
4656 strcat(buf, " ");
4657 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004658 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004659 if (xmlIsBlankNode(cur))
4660 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004661 case XML_CDATA_SECTION_NODE:
4662 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004663 strcat(buf, "CDATA");
4664 if (cur->next != NULL)
4665 strcat(buf, " ");
4666 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004667 case XML_ATTRIBUTE_NODE:
4668 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004669#ifdef LIBXML_DOCB_ENABLED
4670 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004671#endif
4672 case XML_HTML_DOCUMENT_NODE:
4673 case XML_DOCUMENT_TYPE_NODE:
4674 case XML_DOCUMENT_FRAG_NODE:
4675 case XML_NOTATION_NODE:
4676 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004677 strcat(buf, "???");
4678 if (cur->next != NULL)
4679 strcat(buf, " ");
4680 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004681 case XML_ENTITY_NODE:
4682 case XML_PI_NODE:
4683 case XML_DTD_NODE:
4684 case XML_COMMENT_NODE:
4685 case XML_ELEMENT_DECL:
4686 case XML_ATTRIBUTE_DECL:
4687 case XML_ENTITY_DECL:
4688 case XML_XINCLUDE_START:
4689 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004690 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004691 }
4692 cur = cur->next;
4693 }
4694 if (glob) strcat(buf, ")");
4695}
4696
4697/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004698 * xmlValidateElementContent:
4699 * @ctxt: the validation context
4700 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004701 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004702 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004703 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004704 *
4705 * Try to validate the content model of an element
4706 *
4707 * returns 1 if valid or 0 if not and -1 in case of error
4708 */
4709
4710static int
4711xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004712 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004713 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004714#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004715 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004716#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004717 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004718 xmlElementContentPtr cont;
4719 const xmlChar *name;
4720
4721 if (elemDecl == NULL)
4722 return(-1);
4723 cont = elemDecl->content;
4724 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004725
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004726#ifdef LIBXML_REGEXP_ENABLED
4727 /* Build the regexp associated to the content model */
4728 if (elemDecl->contModel == NULL)
4729 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4730 if (elemDecl->contModel == NULL) {
4731 ret = -1;
4732 } else {
4733 xmlRegExecCtxtPtr exec;
4734
Daniel Veillardec498e12003-02-05 11:01:50 +00004735 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
4736 return(-1);
4737 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004738 ctxt->nodeMax = 0;
4739 ctxt->nodeNr = 0;
4740 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004741 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4742 if (exec != NULL) {
4743 cur = child;
4744 while (cur != NULL) {
4745 switch (cur->type) {
4746 case XML_ENTITY_REF_NODE:
4747 /*
4748 * Push the current node to be able to roll back
4749 * and process within the entity
4750 */
4751 if ((cur->children != NULL) &&
4752 (cur->children->children != NULL)) {
4753 nodeVPush(ctxt, cur);
4754 cur = cur->children->children;
4755 continue;
4756 }
4757 break;
4758 case XML_TEXT_NODE:
4759 if (xmlIsBlankNode(cur))
4760 break;
4761 ret = 0;
4762 goto fail;
4763 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004764 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004765 ret = 0;
4766 goto fail;
4767 case XML_ELEMENT_NODE:
4768 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
4769 xmlChar *QName;
4770 int len;
4771
4772 len = xmlStrlen(cur->name) +
4773 xmlStrlen(cur->ns->prefix) + 2;
4774 QName = xmlMalloc(len);
4775 if (QName == NULL) {
4776 ret = -1;
4777 goto fail;
4778 }
4779 snprintf((char *) QName, len, "%s:%s",
4780 (char *)cur->ns->prefix,
4781 (char *)cur->name);
4782 ret = xmlRegExecPushString(exec, QName, NULL);
4783 xmlFree(QName);
4784 } else {
4785 ret = xmlRegExecPushString(exec, cur->name, NULL);
4786 }
4787 break;
4788 default:
4789 break;
4790 }
4791 /*
4792 * Switch to next element
4793 */
4794 cur = cur->next;
4795 while (cur == NULL) {
4796 cur = nodeVPop(ctxt);
4797 if (cur == NULL)
4798 break;
4799 cur = cur->next;
4800 }
4801 }
4802 ret = xmlRegExecPushString(exec, NULL, NULL);
4803fail:
4804 xmlRegFreeExecCtxt(exec);
4805 }
4806 }
4807#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004808 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004809 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004810 */
4811 ctxt->vstateMax = 8;
4812 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4813 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4814 if (ctxt->vstateTab == NULL) {
4815 xmlGenericError(xmlGenericErrorContext,
4816 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004817 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004818 }
4819 /*
4820 * The first entry in the stack is reserved to the current state
4821 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004822 ctxt->nodeMax = 0;
4823 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004824 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004825 ctxt->vstate = &ctxt->vstateTab[0];
4826 ctxt->vstateNr = 1;
4827 CONT = cont;
4828 NODE = child;
4829 DEPTH = 0;
4830 OCCURS = 0;
4831 STATE = 0;
4832 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004833 if ((ret == -3) && (warn)) {
4834 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004835 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004836 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004837 /*
4838 * An entities reference appeared at this level.
4839 * Buid a minimal representation of this node content
4840 * sufficient to run the validation process on it
4841 */
4842 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004843 cur = child;
4844 while (cur != NULL) {
4845 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004846 case XML_ENTITY_REF_NODE:
4847 /*
4848 * Push the current node to be able to roll back
4849 * and process within the entity
4850 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004851 if ((cur->children != NULL) &&
4852 (cur->children->children != NULL)) {
4853 nodeVPush(ctxt, cur);
4854 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004855 continue;
4856 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004857 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004858 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004859 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004860 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004861 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004862 case XML_CDATA_SECTION_NODE:
4863 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004864 case XML_ELEMENT_NODE:
4865 /*
4866 * Allocate a new node and minimally fills in
4867 * what's required
4868 */
4869 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4870 if (tmp == NULL) {
4871 xmlGenericError(xmlGenericErrorContext,
4872 "xmlValidateElementContent : malloc failed\n");
4873 xmlFreeNodeList(repl);
4874 ret = -1;
4875 goto done;
4876 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004877 tmp->type = cur->type;
4878 tmp->name = cur->name;
4879 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004880 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004881 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004882 if (repl == NULL)
4883 repl = last = tmp;
4884 else {
4885 last->next = tmp;
4886 last = tmp;
4887 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004888 if (cur->type == XML_CDATA_SECTION_NODE) {
4889 /*
4890 * E59 spaces in CDATA does not match the
4891 * nonterminal S
4892 */
4893 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4894 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004895 break;
4896 default:
4897 break;
4898 }
4899 /*
4900 * Switch to next element
4901 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004902 cur = cur->next;
4903 while (cur == NULL) {
4904 cur = nodeVPop(ctxt);
4905 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004906 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004907 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004908 }
4909 }
4910
4911 /*
4912 * Relaunch the validation
4913 */
4914 ctxt->vstate = &ctxt->vstateTab[0];
4915 ctxt->vstateNr = 1;
4916 CONT = cont;
4917 NODE = repl;
4918 DEPTH = 0;
4919 OCCURS = 0;
4920 STATE = 0;
4921 ret = xmlValidateElementType(ctxt);
4922 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004923#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004924 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004925 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4926 char expr[5000];
4927 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004928
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004929 expr[0] = 0;
4930 xmlSnprintfElementContent(expr, 5000, cont, 1);
4931 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004932#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004933 if (repl != NULL)
4934 xmlSnprintfElements(list, 5000, repl, 1);
4935 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004936#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004937 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004938
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004939 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004940 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004941 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004942 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004943 name, expr, list);
4944 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004945 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004946 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004947 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004948 expr, list);
4949 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004950 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004951 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004952 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004953 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004954 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004955 name);
4956 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004957 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004958 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004959 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004960 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004961 }
4962 ret = 0;
4963 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004964 if (ret == -3)
4965 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004966
Daniel Veillard23e73572002-09-19 19:56:43 +00004967#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004968done:
4969 /*
4970 * Deallocate the copy if done, and free up the validation stack
4971 */
4972 while (repl != NULL) {
4973 tmp = repl->next;
4974 xmlFree(repl);
4975 repl = tmp;
4976 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004977 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004978 if (ctxt->vstateTab != NULL) {
4979 xmlFree(ctxt->vstateTab);
4980 ctxt->vstateTab = NULL;
4981 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004982#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004983 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004984 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004985 if (ctxt->nodeTab != NULL) {
4986 xmlFree(ctxt->nodeTab);
4987 ctxt->nodeTab = NULL;
4988 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004989 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004990
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004991}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004992
Owen Taylor3473f882001-02-23 17:55:21 +00004993/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004994 * xmlValidateCdataElement:
4995 * @ctxt: the validation context
4996 * @doc: a document instance
4997 * @elem: an element instance
4998 *
4999 * Check that an element follows #CDATA
5000 *
5001 * returns 1 if valid or 0 otherwise
5002 */
5003static int
5004xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5005 xmlNodePtr elem) {
5006 int ret = 1;
5007 xmlNodePtr cur, child;
5008
Daniel Veillardceb09b92002-10-04 11:46:37 +00005009 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005010 return(0);
5011
5012 child = elem->children;
5013
5014 cur = child;
5015 while (cur != NULL) {
5016 switch (cur->type) {
5017 case XML_ENTITY_REF_NODE:
5018 /*
5019 * Push the current node to be able to roll back
5020 * and process within the entity
5021 */
5022 if ((cur->children != NULL) &&
5023 (cur->children->children != NULL)) {
5024 nodeVPush(ctxt, cur);
5025 cur = cur->children->children;
5026 continue;
5027 }
5028 break;
5029 case XML_COMMENT_NODE:
5030 case XML_PI_NODE:
5031 case XML_TEXT_NODE:
5032 case XML_CDATA_SECTION_NODE:
5033 break;
5034 default:
5035 ret = 0;
5036 goto done;
5037 }
5038 /*
5039 * Switch to next element
5040 */
5041 cur = cur->next;
5042 while (cur == NULL) {
5043 cur = nodeVPop(ctxt);
5044 if (cur == NULL)
5045 break;
5046 cur = cur->next;
5047 }
5048 }
5049done:
5050 ctxt->nodeMax = 0;
5051 ctxt->nodeNr = 0;
5052 if (ctxt->nodeTab != NULL) {
5053 xmlFree(ctxt->nodeTab);
5054 ctxt->nodeTab = NULL;
5055 }
5056 return(ret);
5057}
5058
5059/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005060 * xmlValidateCheckMixed:
5061 * @ctxt: the validation context
5062 * @cont: the mixed content model
5063 * @qname: the qualified name as appearing in the serialization
5064 *
5065 * Check if the given node is part of the content model.
5066 *
5067 * Returns 1 if yes, 0 if no, -1 in case of error
5068 */
5069static int
5070xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5071 xmlElementContentPtr cont, const xmlChar *qname) {
5072 while (cont != NULL) {
5073 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5074 if (xmlStrEqual(cont->name, qname))
5075 return(1);
5076 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5077 (cont->c1 != NULL) &&
5078 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5079 if (xmlStrEqual(cont->c1->name, qname))
5080 return(1);
5081 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5082 (cont->c1 == NULL) ||
5083 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5084 /* Internal error !!! */
5085 xmlGenericError(xmlGenericErrorContext,
5086 "Internal: MIXED struct bad\n");
5087 break;
5088 }
5089 cont = cont->c2;
5090 }
5091 return(0);
5092}
5093
5094/**
5095 * xmlValidGetElemDecl:
5096 * @ctxt: the validation context
5097 * @doc: a document instance
5098 * @elem: an element instance
5099 * @extsubset: pointer, (out) indicate if the declaration was found
5100 * in the external subset.
5101 *
5102 * Finds a declaration associated to an element in the document.
5103 *
5104 * returns the pointer to the declaration or NULL if not found.
5105 */
5106static xmlElementPtr
5107xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5108 xmlNodePtr elem, int *extsubset) {
5109 xmlElementPtr elemDecl = NULL;
5110 const xmlChar *prefix = NULL;
5111
5112 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5113 if (extsubset != NULL)
5114 *extsubset = 0;
5115
5116 /*
5117 * Fetch the declaration for the qualified name
5118 */
5119 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5120 prefix = elem->ns->prefix;
5121
5122 if (prefix != NULL) {
5123 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5124 elem->name, prefix);
5125 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5126 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5127 elem->name, prefix);
5128 if ((elemDecl != NULL) && (extsubset != NULL))
5129 *extsubset = 1;
5130 }
5131 }
5132
5133 /*
5134 * Fetch the declaration for the non qualified name
5135 * This is "non-strict" validation should be done on the
5136 * full QName but in that case being flexible makes sense.
5137 */
5138 if (elemDecl == NULL) {
5139 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5140 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5141 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5142 if ((elemDecl != NULL) && (extsubset != NULL))
5143 *extsubset = 1;
5144 }
5145 }
5146 if (elemDecl == NULL) {
5147 VECTXT(ctxt, elem);
5148 VERROR(ctxt->userData, "No declaration for element %s\n",
5149 elem->name);
5150 }
5151 return(elemDecl);
5152}
5153
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005154#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005155/**
5156 * xmlValidatePushElement:
5157 * @ctxt: the validation context
5158 * @doc: a document instance
5159 * @elem: an element instance
5160 * @qname: the qualified name as appearing in the serialization
5161 *
5162 * Push a new element start on the validation stack.
5163 *
5164 * returns 1 if no validation problem was found or 0 otherwise
5165 */
5166int
5167xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5168 xmlNodePtr elem, const xmlChar *qname) {
5169 int ret = 1;
5170 xmlElementPtr eDecl;
5171 int extsubset = 0;
5172
5173 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5174 xmlValidStatePtr state = ctxt->vstate;
5175 xmlElementPtr elemDecl;
5176
5177 /*
5178 * Check the new element agaisnt the content model of the new elem.
5179 */
5180 if (state->elemDecl != NULL) {
5181 elemDecl = state->elemDecl;
5182
5183 switch(elemDecl->etype) {
5184 case XML_ELEMENT_TYPE_UNDEFINED:
5185 ret = 0;
5186 break;
5187 case XML_ELEMENT_TYPE_EMPTY:
5188 VECTXT(ctxt, state->node);
5189 VERROR(ctxt->userData,
5190 "Element %s was declared EMPTY this one has content\n",
5191 state->node->name);
5192 ret = 0;
5193 break;
5194 case XML_ELEMENT_TYPE_ANY:
5195 /* I don't think anything is required then */
5196 break;
5197 case XML_ELEMENT_TYPE_MIXED:
5198 /* simple case of declared as #PCDATA */
5199 if ((elemDecl->content != NULL) &&
5200 (elemDecl->content->type ==
5201 XML_ELEMENT_CONTENT_PCDATA)) {
5202 VECTXT(ctxt, state->node);
5203 VERROR(ctxt->userData,
5204 "Element %s was declared #PCDATA but contains non text nodes\n",
5205 state->node->name);
5206 ret = 0;
5207 } else {
5208 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5209 qname);
5210 if (ret != 1) {
5211 VECTXT(ctxt, state->node);
5212 VERROR(ctxt->userData,
5213 "Element %s is not declared in %s list of possible children\n",
5214 qname, state->node->name);
5215 }
5216 }
5217 break;
5218 case XML_ELEMENT_TYPE_ELEMENT:
5219 /*
5220 * TODO:
5221 * VC: Standalone Document Declaration
5222 * - element types with element content, if white space
5223 * occurs directly within any instance of those types.
5224 */
5225 if (state->exec != NULL) {
5226 ret = xmlRegExecPushString(state->exec, qname, NULL);
5227 if (ret < 0) {
5228 VECTXT(ctxt, state->node);
5229 VERROR(ctxt->userData,
5230 "Element %s content does not follow the DTD\nMisplaced %s\n",
5231 state->node->name, qname);
5232 ret = 0;
5233 } else {
5234 ret = 1;
5235 }
5236 }
5237 break;
5238 }
5239 }
5240 }
5241 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5242 vstateVPush(ctxt, eDecl, elem);
5243 return(ret);
5244}
5245
5246/**
5247 * xmlValidatePushCData:
5248 * @ctxt: the validation context
5249 * @data: some character data read
5250 * @len: the lenght of the data
5251 *
5252 * check the CData parsed for validation in the current stack
5253 *
5254 * returns 1 if no validation problem was found or 0 otherwise
5255 */
5256int
5257xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5258 int ret = 1;
5259
5260 if (len <= 0)
5261 return(ret);
5262 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5263 xmlValidStatePtr state = ctxt->vstate;
5264 xmlElementPtr elemDecl;
5265
5266 /*
5267 * Check the new element agaisnt the content model of the new elem.
5268 */
5269 if (state->elemDecl != NULL) {
5270 elemDecl = state->elemDecl;
5271
5272 switch(elemDecl->etype) {
5273 case XML_ELEMENT_TYPE_UNDEFINED:
5274 ret = 0;
5275 break;
5276 case XML_ELEMENT_TYPE_EMPTY:
5277 VECTXT(ctxt, state->node);
5278 VERROR(ctxt->userData,
5279 "Element %s was declared EMPTY this one has content\n",
5280 state->node->name);
5281 ret = 0;
5282 break;
5283 case XML_ELEMENT_TYPE_ANY:
5284 break;
5285 case XML_ELEMENT_TYPE_MIXED:
5286 break;
5287 case XML_ELEMENT_TYPE_ELEMENT:
5288 if (len > 0) {
5289 int i;
5290
5291 for (i = 0;i < len;i++) {
5292 if (!IS_BLANK(data[i])) {
5293 VECTXT(ctxt, state->node);
5294 VERROR(ctxt->userData,
5295 "Element %s content does not follow the DTD\nText not allowed\n",
5296 state->node->name);
5297 ret = 0;
5298 goto done;
5299 }
5300 }
5301 /*
5302 * TODO:
5303 * VC: Standalone Document Declaration
5304 * element types with element content, if white space
5305 * occurs directly within any instance of those types.
5306 */
5307 }
5308 break;
5309 }
5310 }
5311 }
5312done:
5313 return(ret);
5314}
5315
5316/**
5317 * xmlValidatePopElement:
5318 * @ctxt: the validation context
5319 * @doc: a document instance
5320 * @elem: an element instance
5321 * @qname: the qualified name as appearing in the serialization
5322 *
5323 * Pop the element end from the validation stack.
5324 *
5325 * returns 1 if no validation problem was found or 0 otherwise
5326 */
5327int
5328xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
5329 xmlNodePtr elem, const xmlChar *qname ATTRIBUTE_UNUSED) {
5330 int ret = 1;
5331
5332 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5333 xmlValidStatePtr state = ctxt->vstate;
5334 xmlElementPtr elemDecl;
5335
5336 /*
5337 * Check the new element agaisnt the content model of the new elem.
5338 */
5339 if (state->elemDecl != NULL) {
5340 elemDecl = state->elemDecl;
5341
5342 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5343 if (state->exec != NULL) {
5344 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5345 if (ret == 0) {
5346 VECTXT(ctxt, state->node);
5347 VERROR(ctxt->userData,
5348 "Element %s content does not follow the DTD\nExpecting more child\n",
5349 state->node->name);
5350 } else {
5351 /*
5352 * previous validation errors should not generate
5353 * a new one here
5354 */
5355 ret = 1;
5356 }
5357 }
5358 }
5359 }
5360 vstateVPop(ctxt);
5361 }
5362 return(ret);
5363}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005364#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005365
5366/**
Owen Taylor3473f882001-02-23 17:55:21 +00005367 * xmlValidateOneElement:
5368 * @ctxt: the validation context
5369 * @doc: a document instance
5370 * @elem: an element instance
5371 *
5372 * Try to validate a single element and it's attributes,
5373 * basically it does the following checks as described by the
5374 * XML-1.0 recommendation:
5375 * - [ VC: Element Valid ]
5376 * - [ VC: Required Attribute ]
5377 * Then call xmlValidateOneAttribute() for each attribute present.
5378 *
5379 * The ID/IDREF checkings are done separately
5380 *
5381 * returns 1 if valid or 0 otherwise
5382 */
5383
5384int
5385xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5386 xmlNodePtr elem) {
5387 xmlElementPtr elemDecl = NULL;
5388 xmlElementContentPtr cont;
5389 xmlAttributePtr attr;
5390 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005391 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005392 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005393 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005394
5395 CHECK_DTD;
5396
5397 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005398 switch (elem->type) {
5399 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005400 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005401 VERROR(ctxt->userData,
5402 "Attribute element not expected here\n");
5403 return(0);
5404 case XML_TEXT_NODE:
5405 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005406 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005407 VERROR(ctxt->userData, "Text element has childs !\n");
5408 return(0);
5409 }
5410 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005411 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005412 VERROR(ctxt->userData, "Text element has attributes !\n");
5413 return(0);
5414 }
5415 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005416 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005417 VERROR(ctxt->userData, "Text element has namespace !\n");
5418 return(0);
5419 }
5420 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005421 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005422 VERROR(ctxt->userData,
5423 "Text element carries namespace definitions !\n");
5424 return(0);
5425 }
5426 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005427 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005428 VERROR(ctxt->userData,
5429 "Text element has no content !\n");
5430 return(0);
5431 }
5432 return(1);
5433 case XML_XINCLUDE_START:
5434 case XML_XINCLUDE_END:
5435 return(1);
5436 case XML_CDATA_SECTION_NODE:
5437 case XML_ENTITY_REF_NODE:
5438 case XML_PI_NODE:
5439 case XML_COMMENT_NODE:
5440 return(1);
5441 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005442 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005443 VERROR(ctxt->userData,
5444 "Entity element not expected here\n");
5445 return(0);
5446 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005447 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005448 VERROR(ctxt->userData,
5449 "Notation element not expected here\n");
5450 return(0);
5451 case XML_DOCUMENT_NODE:
5452 case XML_DOCUMENT_TYPE_NODE:
5453 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005454 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005455 VERROR(ctxt->userData,
5456 "Document element not expected here\n");
5457 return(0);
5458 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005459 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005460 VERROR(ctxt->userData,
5461 "\n");
5462 return(0);
5463 case XML_ELEMENT_NODE:
5464 break;
5465 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005466 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005467 VERROR(ctxt->userData,
5468 "unknown element type %d\n", elem->type);
5469 return(0);
5470 }
Owen Taylor3473f882001-02-23 17:55:21 +00005471
5472 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005473 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005474 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005475 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5476 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005477 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005478
Daniel Veillardea7751d2002-12-20 00:16:24 +00005479 /*
5480 * If vstateNr is not zero that means continuous validation is
5481 * activated, do not try to check the content model at that level.
5482 */
5483 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005484 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005485 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005486 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005487 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005488 VERROR(ctxt->userData, "No declaration for element %s\n",
5489 elem->name);
5490 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005491 case XML_ELEMENT_TYPE_EMPTY:
5492 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005493 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005494 VERROR(ctxt->userData,
5495 "Element %s was declared EMPTY this one has content\n",
5496 elem->name);
5497 ret = 0;
5498 }
5499 break;
5500 case XML_ELEMENT_TYPE_ANY:
5501 /* I don't think anything is required then */
5502 break;
5503 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005504
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005505 /* simple case of declared as #PCDATA */
5506 if ((elemDecl->content != NULL) &&
5507 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5508 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5509 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005510 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005511 VERROR(ctxt->userData,
5512 "Element %s was declared #PCDATA but contains non text nodes\n",
5513 elem->name);
5514 }
5515 break;
5516 }
Owen Taylor3473f882001-02-23 17:55:21 +00005517 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005518 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005519 while (child != NULL) {
5520 if (child->type == XML_ELEMENT_NODE) {
5521 name = child->name;
5522 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
5523 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005524 snprintf((char *) qname, sizeof(qname), "%s:%s",
5525 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005526 qname[sizeof(qname) - 1] = 0;
5527 cont = elemDecl->content;
5528 while (cont != NULL) {
5529 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5530 if (xmlStrEqual(cont->name, qname)) break;
5531 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5532 (cont->c1 != NULL) &&
5533 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5534 if (xmlStrEqual(cont->c1->name, qname)) break;
5535 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5536 (cont->c1 == NULL) ||
5537 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5538 /* Internal error !!! */
5539 xmlGenericError(xmlGenericErrorContext,
5540 "Internal: MIXED struct bad\n");
5541 break;
5542 }
5543 cont = cont->c2;
5544 }
5545 if (cont != NULL)
5546 goto child_ok;
5547 }
5548 cont = elemDecl->content;
5549 while (cont != NULL) {
5550 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5551 if (xmlStrEqual(cont->name, name)) break;
5552 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5553 (cont->c1 != NULL) &&
5554 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5555 if (xmlStrEqual(cont->c1->name, name)) break;
5556 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5557 (cont->c1 == NULL) ||
5558 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5559 /* Internal error !!! */
5560 xmlGenericError(xmlGenericErrorContext,
5561 "Internal: MIXED struct bad\n");
5562 break;
5563 }
5564 cont = cont->c2;
5565 }
5566 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005567 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005568 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005569 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005570 name, elem->name);
5571 ret = 0;
5572 }
5573 }
5574child_ok:
5575 child = child->next;
5576 }
5577 break;
5578 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005579 if ((doc->standalone == 1) && (extsubset == 1)) {
5580 /*
5581 * VC: Standalone Document Declaration
5582 * - element types with element content, if white space
5583 * occurs directly within any instance of those types.
5584 */
5585 child = elem->children;
5586 while (child != NULL) {
5587 if (child->type == XML_TEXT_NODE) {
5588 const xmlChar *content = child->content;
5589
5590 while (IS_BLANK(*content))
5591 content++;
5592 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005593 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005594 VERROR(ctxt->userData,
5595"standalone: %s declared in the external subset contains white spaces nodes\n",
5596 elem->name);
5597 ret = 0;
5598 break;
5599 }
5600 }
5601 child =child->next;
5602 }
5603 }
Owen Taylor3473f882001-02-23 17:55:21 +00005604 child = elem->children;
5605 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005606 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005607 if (tmp <= 0)
5608 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005609 break;
5610 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005611 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005612
5613 /* [ VC: Required Attribute ] */
5614 attr = elemDecl->attributes;
5615 while (attr != NULL) {
5616 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005617 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005618
Daniel Veillarde4301c82002-02-13 13:32:35 +00005619 if ((attr->prefix == NULL) &&
5620 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5621 xmlNsPtr ns;
5622
5623 ns = elem->nsDef;
5624 while (ns != NULL) {
5625 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005626 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005627 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005628 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005629 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5630 xmlNsPtr ns;
5631
5632 ns = elem->nsDef;
5633 while (ns != NULL) {
5634 if (xmlStrEqual(attr->name, ns->prefix))
5635 goto found;
5636 ns = ns->next;
5637 }
5638 } else {
5639 xmlAttrPtr attrib;
5640
5641 attrib = elem->properties;
5642 while (attrib != NULL) {
5643 if (xmlStrEqual(attrib->name, attr->name)) {
5644 if (attr->prefix != NULL) {
5645 xmlNsPtr nameSpace = attrib->ns;
5646
5647 if (nameSpace == NULL)
5648 nameSpace = elem->ns;
5649 /*
5650 * qualified names handling is problematic, having a
5651 * different prefix should be possible but DTDs don't
5652 * allow to define the URI instead of the prefix :-(
5653 */
5654 if (nameSpace == NULL) {
5655 if (qualified < 0)
5656 qualified = 0;
5657 } else if (!xmlStrEqual(nameSpace->prefix,
5658 attr->prefix)) {
5659 if (qualified < 1)
5660 qualified = 1;
5661 } else
5662 goto found;
5663 } else {
5664 /*
5665 * We should allow applications to define namespaces
5666 * for their application even if the DTD doesn't
5667 * carry one, otherwise, basically we would always
5668 * break.
5669 */
5670 goto found;
5671 }
5672 }
5673 attrib = attrib->next;
5674 }
Owen Taylor3473f882001-02-23 17:55:21 +00005675 }
5676 if (qualified == -1) {
5677 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005678 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005679 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005680 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005681 elem->name, attr->name);
5682 ret = 0;
5683 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005684 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005685 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005686 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005687 elem->name, attr->prefix,attr->name);
5688 ret = 0;
5689 }
5690 } else if (qualified == 0) {
5691 VWARNING(ctxt->userData,
5692 "Element %s required attribute %s:%s has no prefix\n",
5693 elem->name, attr->prefix,attr->name);
5694 } else if (qualified == 1) {
5695 VWARNING(ctxt->userData,
5696 "Element %s required attribute %s:%s has different prefix\n",
5697 elem->name, attr->prefix,attr->name);
5698 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005699 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5700 /*
5701 * Special tests checking #FIXED namespace declarations
5702 * have the right value since this is not done as an
5703 * attribute checking
5704 */
5705 if ((attr->prefix == NULL) &&
5706 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5707 xmlNsPtr ns;
5708
5709 ns = elem->nsDef;
5710 while (ns != NULL) {
5711 if (ns->prefix == NULL) {
5712 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005713 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005714 VERROR(ctxt->userData,
5715 "Element %s namespace name for default namespace does not match the DTD\n",
5716 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005717 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005718 }
5719 goto found;
5720 }
5721 ns = ns->next;
5722 }
5723 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5724 xmlNsPtr ns;
5725
5726 ns = elem->nsDef;
5727 while (ns != NULL) {
5728 if (xmlStrEqual(attr->name, ns->prefix)) {
5729 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005730 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005731 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005732 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005733 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005734 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005735 }
5736 goto found;
5737 }
5738 ns = ns->next;
5739 }
5740 }
Owen Taylor3473f882001-02-23 17:55:21 +00005741 }
5742found:
5743 attr = attr->nexth;
5744 }
5745 return(ret);
5746}
5747
5748/**
5749 * xmlValidateRoot:
5750 * @ctxt: the validation context
5751 * @doc: a document instance
5752 *
5753 * Try to validate a the root element
5754 * basically it does the following check as described by the
5755 * XML-1.0 recommendation:
5756 * - [ VC: Root Element Type ]
5757 * it doesn't try to recurse or apply other check to the element
5758 *
5759 * returns 1 if valid or 0 otherwise
5760 */
5761
5762int
5763xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5764 xmlNodePtr root;
5765 if (doc == NULL) return(0);
5766
5767 root = xmlDocGetRootElement(doc);
5768 if ((root == NULL) || (root->name == NULL)) {
5769 VERROR(ctxt->userData, "Not valid: no root element\n");
5770 return(0);
5771 }
5772
5773 /*
5774 * When doing post validation against a separate DTD, those may
5775 * no internal subset has been generated
5776 */
5777 if ((doc->intSubset != NULL) &&
5778 (doc->intSubset->name != NULL)) {
5779 /*
5780 * Check first the document root against the NQName
5781 */
5782 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5783 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
5784 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005785 snprintf((char *) qname, sizeof(qname), "%s:%s",
5786 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005787 qname[sizeof(qname) - 1] = 0;
5788 if (xmlStrEqual(doc->intSubset->name, qname))
5789 goto name_ok;
5790 }
5791 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5792 (xmlStrEqual(root->name, BAD_CAST "html")))
5793 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005794 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005795 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005796 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005797 root->name, doc->intSubset->name);
5798 return(0);
5799
5800 }
5801 }
5802name_ok:
5803 return(1);
5804}
5805
5806
5807/**
5808 * xmlValidateElement:
5809 * @ctxt: the validation context
5810 * @doc: a document instance
5811 * @elem: an element instance
5812 *
5813 * Try to validate the subtree under an element
5814 *
5815 * returns 1 if valid or 0 otherwise
5816 */
5817
5818int
5819xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5820 xmlNodePtr child;
5821 xmlAttrPtr attr;
5822 xmlChar *value;
5823 int ret = 1;
5824
5825 if (elem == NULL) return(0);
5826
5827 /*
5828 * XInclude elements were added after parsing in the infoset,
5829 * they don't really mean anything validation wise.
5830 */
5831 if ((elem->type == XML_XINCLUDE_START) ||
5832 (elem->type == XML_XINCLUDE_END))
5833 return(1);
5834
5835 CHECK_DTD;
5836
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005837 /*
5838 * Entities references have to be handled separately
5839 */
5840 if (elem->type == XML_ENTITY_REF_NODE) {
5841 return(1);
5842 }
5843
Owen Taylor3473f882001-02-23 17:55:21 +00005844 ret &= xmlValidateOneElement(ctxt, doc, elem);
5845 attr = elem->properties;
5846 while(attr != NULL) {
5847 value = xmlNodeListGetString(doc, attr->children, 0);
5848 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5849 if (value != NULL)
5850 xmlFree(value);
5851 attr= attr->next;
5852 }
5853 child = elem->children;
5854 while (child != NULL) {
5855 ret &= xmlValidateElement(ctxt, doc, child);
5856 child = child->next;
5857 }
5858
5859 return(ret);
5860}
5861
Daniel Veillard8730c562001-02-26 10:49:57 +00005862/**
5863 * xmlValidateRef:
5864 * @ref: A reference to be validated
5865 * @ctxt: Validation context
5866 * @name: Name of ID we are searching for
5867 *
5868 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005869static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005870xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005871 const xmlChar *name) {
5872 xmlAttrPtr id;
5873 xmlAttrPtr attr;
5874
5875 if (ref == NULL)
5876 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005877 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00005878 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005879 attr = ref->attr;
5880 if (attr == NULL) {
5881 xmlChar *dup, *str = NULL, *cur, save;
5882
5883 dup = xmlStrdup(name);
5884 if (dup == NULL) {
5885 ctxt->valid = 0;
5886 return;
5887 }
5888 cur = dup;
5889 while (*cur != 0) {
5890 str = cur;
5891 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5892 save = *cur;
5893 *cur = 0;
5894 id = xmlGetID(ctxt->doc, str);
5895 if (id == NULL) {
5896 VERROR(ctxt->userData,
5897 "attribute %s line %d references an unknown ID \"%s\"\n",
5898 ref->name, ref->lineno, str);
5899 ctxt->valid = 0;
5900 }
5901 if (save == 0)
5902 break;
5903 *cur = save;
5904 while (IS_BLANK(*cur)) cur++;
5905 }
5906 xmlFree(dup);
5907 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00005908 id = xmlGetID(ctxt->doc, name);
5909 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005910 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005911 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005912 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005913 attr->name, name);
5914 ctxt->valid = 0;
5915 }
5916 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5917 xmlChar *dup, *str = NULL, *cur, save;
5918
5919 dup = xmlStrdup(name);
5920 if (dup == NULL) {
5921 ctxt->valid = 0;
5922 return;
5923 }
5924 cur = dup;
5925 while (*cur != 0) {
5926 str = cur;
5927 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5928 save = *cur;
5929 *cur = 0;
5930 id = xmlGetID(ctxt->doc, str);
5931 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005932 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005933 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005934 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005935 attr->name, str);
5936 ctxt->valid = 0;
5937 }
5938 if (save == 0)
5939 break;
5940 *cur = save;
5941 while (IS_BLANK(*cur)) cur++;
5942 }
5943 xmlFree(dup);
5944 }
5945}
5946
5947/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005948 * xmlWalkValidateList:
5949 * @data: Contents of current link
5950 * @user: Value supplied by the user
5951 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005952 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005953 */
5954static int
5955xmlWalkValidateList(const void *data, const void *user)
5956{
5957 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5958 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5959 return 1;
5960}
5961
5962/**
5963 * xmlValidateCheckRefCallback:
5964 * @ref_list: List of references
5965 * @ctxt: Validation context
5966 * @name: Name of ID we are searching for
5967 *
5968 */
5969static void
5970xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5971 const xmlChar *name) {
5972 xmlValidateMemo memo;
5973
5974 if (ref_list == NULL)
5975 return;
5976 memo.ctxt = ctxt;
5977 memo.name = name;
5978
5979 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5980
5981}
5982
5983/**
Owen Taylor3473f882001-02-23 17:55:21 +00005984 * xmlValidateDocumentFinal:
5985 * @ctxt: the validation context
5986 * @doc: a document instance
5987 *
5988 * Does the final step for the document validation once all the
5989 * incremental validation steps have been completed
5990 *
5991 * basically it does the following checks described by the XML Rec
5992 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00005993 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00005994 *
5995 * returns 1 if valid or 0 otherwise
5996 */
5997
5998int
5999xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6000 xmlRefTablePtr table;
6001
6002 if (doc == NULL) {
6003 xmlGenericError(xmlGenericErrorContext,
6004 "xmlValidateDocumentFinal: doc == NULL\n");
6005 return(0);
6006 }
6007
6008 /*
6009 * Check all the NOTATION/NOTATIONS attributes
6010 */
6011 /*
6012 * Check all the ENTITY/ENTITIES attributes definition for validity
6013 */
6014 /*
6015 * Check all the IDREF/IDREFS attributes definition for validity
6016 */
6017 table = (xmlRefTablePtr) doc->refs;
6018 ctxt->doc = doc;
6019 ctxt->valid = 1;
6020 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6021 return(ctxt->valid);
6022}
6023
6024/**
6025 * xmlValidateDtd:
6026 * @ctxt: the validation context
6027 * @doc: a document instance
6028 * @dtd: a dtd instance
6029 *
6030 * Try to validate the document against the dtd instance
6031 *
6032 * basically it does check all the definitions in the DtD.
6033 *
6034 * returns 1 if valid or 0 otherwise
6035 */
6036
6037int
6038xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6039 int ret;
6040 xmlDtdPtr oldExt;
6041 xmlNodePtr root;
6042
6043 if (dtd == NULL) return(0);
6044 if (doc == NULL) return(0);
6045 oldExt = doc->extSubset;
6046 doc->extSubset = dtd;
6047 ret = xmlValidateRoot(ctxt, doc);
6048 if (ret == 0) {
6049 doc->extSubset = oldExt;
6050 return(ret);
6051 }
6052 if (doc->ids != NULL) {
6053 xmlFreeIDTable(doc->ids);
6054 doc->ids = NULL;
6055 }
6056 if (doc->refs != NULL) {
6057 xmlFreeRefTable(doc->refs);
6058 doc->refs = NULL;
6059 }
6060 root = xmlDocGetRootElement(doc);
6061 ret = xmlValidateElement(ctxt, doc, root);
6062 ret &= xmlValidateDocumentFinal(ctxt, doc);
6063 doc->extSubset = oldExt;
6064 return(ret);
6065}
6066
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006067static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006068xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6069 const xmlChar *name ATTRIBUTE_UNUSED) {
6070 if (cur == NULL)
6071 return;
6072 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6073 xmlChar *notation = cur->content;
6074
Daniel Veillard878eab02002-02-19 13:46:09 +00006075 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006076 int ret;
6077
6078 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6079 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006080 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006081 }
6082 }
6083 }
6084}
6085
6086static void
Owen Taylor3473f882001-02-23 17:55:21 +00006087xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006088 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006089 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006090 xmlDocPtr doc;
6091 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006092
Owen Taylor3473f882001-02-23 17:55:21 +00006093 if (cur == NULL)
6094 return;
6095 switch (cur->atype) {
6096 case XML_ATTRIBUTE_CDATA:
6097 case XML_ATTRIBUTE_ID:
6098 case XML_ATTRIBUTE_IDREF :
6099 case XML_ATTRIBUTE_IDREFS:
6100 case XML_ATTRIBUTE_NMTOKEN:
6101 case XML_ATTRIBUTE_NMTOKENS:
6102 case XML_ATTRIBUTE_ENUMERATION:
6103 break;
6104 case XML_ATTRIBUTE_ENTITY:
6105 case XML_ATTRIBUTE_ENTITIES:
6106 case XML_ATTRIBUTE_NOTATION:
6107 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006108
6109 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6110 cur->atype, cur->defaultValue);
6111 if ((ret == 0) && (ctxt->valid == 1))
6112 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006113 }
6114 if (cur->tree != NULL) {
6115 xmlEnumerationPtr tree = cur->tree;
6116 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006117 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006118 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006119 if ((ret == 0) && (ctxt->valid == 1))
6120 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006121 tree = tree->next;
6122 }
6123 }
6124 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006125 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6126 doc = cur->doc;
6127 if ((doc == NULL) || (cur->elem == NULL)) {
6128 VERROR(ctxt->userData,
6129 "xmlValidateAttributeCallback(%s): internal error\n",
6130 cur->name);
6131 return;
6132 }
6133 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6134 if (elem == NULL)
6135 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6136 if (elem == NULL) {
6137 VERROR(ctxt->userData,
6138 "attribute %s: could not find decl for element %s\n",
6139 cur->name, cur->elem);
6140 return;
6141 }
6142 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6143 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006144 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00006145 cur->name, cur->elem);
6146 ctxt->valid = 0;
6147 }
6148 }
Owen Taylor3473f882001-02-23 17:55:21 +00006149}
6150
6151/**
6152 * xmlValidateDtdFinal:
6153 * @ctxt: the validation context
6154 * @doc: a document instance
6155 *
6156 * Does the final step for the dtds validation once all the
6157 * subsets have been parsed
6158 *
6159 * basically it does the following checks described by the XML Rec
6160 * - check that ENTITY and ENTITIES type attributes default or
6161 * possible values matches one of the defined entities.
6162 * - check that NOTATION type attributes default or
6163 * possible values matches one of the defined notations.
6164 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006165 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006166 */
6167
6168int
6169xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006170 xmlDtdPtr dtd;
6171 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006172 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006173
6174 if (doc == NULL) return(0);
6175 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6176 return(0);
6177 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006178 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006179 dtd = doc->intSubset;
6180 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6181 table = (xmlAttributeTablePtr) dtd->attributes;
6182 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006183 }
6184 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006185 entities = (xmlEntitiesTablePtr) dtd->entities;
6186 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6187 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006188 }
6189 dtd = doc->extSubset;
6190 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6191 table = (xmlAttributeTablePtr) dtd->attributes;
6192 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006193 }
6194 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006195 entities = (xmlEntitiesTablePtr) dtd->entities;
6196 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6197 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006198 }
6199 return(ctxt->valid);
6200}
6201
6202/**
6203 * xmlValidateDocument:
6204 * @ctxt: the validation context
6205 * @doc: a document instance
6206 *
6207 * Try to validate the document instance
6208 *
6209 * basically it does the all the checks described by the XML Rec
6210 * i.e. validates the internal and external subset (if present)
6211 * and validate the document tree.
6212 *
6213 * returns 1 if valid or 0 otherwise
6214 */
6215
6216int
6217xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6218 int ret;
6219 xmlNodePtr root;
6220
Daniel Veillard2fd85422002-10-16 14:32:41 +00006221 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6222 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00006223 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006224 }
Owen Taylor3473f882001-02-23 17:55:21 +00006225 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6226 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6227 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6228 doc->intSubset->SystemID);
6229 if (doc->extSubset == NULL) {
6230 if (doc->intSubset->SystemID != NULL) {
6231 VERROR(ctxt->userData,
6232 "Could not load the external subset \"%s\"\n",
6233 doc->intSubset->SystemID);
6234 } else {
6235 VERROR(ctxt->userData,
6236 "Could not load the external subset \"%s\"\n",
6237 doc->intSubset->ExternalID);
6238 }
6239 return(0);
6240 }
6241 }
6242
6243 if (doc->ids != NULL) {
6244 xmlFreeIDTable(doc->ids);
6245 doc->ids = NULL;
6246 }
6247 if (doc->refs != NULL) {
6248 xmlFreeRefTable(doc->refs);
6249 doc->refs = NULL;
6250 }
6251 ret = xmlValidateDtdFinal(ctxt, doc);
6252 if (!xmlValidateRoot(ctxt, doc)) return(0);
6253
6254 root = xmlDocGetRootElement(doc);
6255 ret &= xmlValidateElement(ctxt, doc, root);
6256 ret &= xmlValidateDocumentFinal(ctxt, doc);
6257 return(ret);
6258}
6259
6260
6261/************************************************************************
6262 * *
6263 * Routines for dynamic validation editing *
6264 * *
6265 ************************************************************************/
6266
6267/**
6268 * xmlValidGetPotentialChildren:
6269 * @ctree: an element content tree
6270 * @list: an array to store the list of child names
6271 * @len: a pointer to the number of element in the list
6272 * @max: the size of the array
6273 *
6274 * Build/extend a list of potential children allowed by the content tree
6275 *
6276 * returns the number of element in the list, or -1 in case of error.
6277 */
6278
6279int
6280xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6281 int *len, int max) {
6282 int i;
6283
6284 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6285 return(-1);
6286 if (*len >= max) return(*len);
6287
6288 switch (ctree->type) {
6289 case XML_ELEMENT_CONTENT_PCDATA:
6290 for (i = 0; i < *len;i++)
6291 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6292 list[(*len)++] = BAD_CAST "#PCDATA";
6293 break;
6294 case XML_ELEMENT_CONTENT_ELEMENT:
6295 for (i = 0; i < *len;i++)
6296 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6297 list[(*len)++] = ctree->name;
6298 break;
6299 case XML_ELEMENT_CONTENT_SEQ:
6300 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6301 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6302 break;
6303 case XML_ELEMENT_CONTENT_OR:
6304 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6305 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6306 break;
6307 }
6308
6309 return(*len);
6310}
6311
6312/**
6313 * xmlValidGetValidElements:
6314 * @prev: an element to insert after
6315 * @next: an element to insert next
6316 * @list: an array to store the list of child names
6317 * @max: the size of the array
6318 *
6319 * This function returns the list of authorized children to insert
6320 * within an existing tree while respecting the validity constraints
6321 * forced by the Dtd. The insertion point is defined using @prev and
6322 * @next in the following ways:
6323 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6324 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6325 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6326 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6327 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6328 *
6329 * pointers to the element names are inserted at the beginning of the array
6330 * and do not need to be freed.
6331 *
6332 * returns the number of element in the list, or -1 in case of error. If
6333 * the function returns the value @max the caller is invited to grow the
6334 * receiving array and retry.
6335 */
6336
6337int
6338xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6339 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006340 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006341 int nb_valid_elements = 0;
6342 const xmlChar *elements[256];
6343 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006344 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006345
6346 xmlNode *ref_node;
6347 xmlNode *parent;
6348 xmlNode *test_node;
6349
6350 xmlNode *prev_next;
6351 xmlNode *next_prev;
6352 xmlNode *parent_childs;
6353 xmlNode *parent_last;
6354
6355 xmlElement *element_desc;
6356
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006357 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006358
Owen Taylor3473f882001-02-23 17:55:21 +00006359 if (prev == NULL && next == NULL)
6360 return(-1);
6361
6362 if (list == NULL) return(-1);
6363 if (max <= 0) return(-1);
6364
6365 nb_valid_elements = 0;
6366 ref_node = prev ? prev : next;
6367 parent = ref_node->parent;
6368
6369 /*
6370 * Retrieves the parent element declaration
6371 */
6372 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6373 parent->name);
6374 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6375 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6376 parent->name);
6377 if (element_desc == NULL) return(-1);
6378
6379 /*
6380 * Do a backup of the current tree structure
6381 */
6382 prev_next = prev ? prev->next : NULL;
6383 next_prev = next ? next->prev : NULL;
6384 parent_childs = parent->children;
6385 parent_last = parent->last;
6386
6387 /*
6388 * Creates a dummy node and insert it into the tree
6389 */
6390 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6391 test_node->doc = ref_node->doc;
6392 test_node->parent = parent;
6393 test_node->prev = prev;
6394 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006395 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006396
6397 if (prev) prev->next = test_node;
6398 else parent->children = test_node;
6399
6400 if (next) next->prev = test_node;
6401 else parent->last = test_node;
6402
6403 /*
6404 * Insert each potential child node and check if the parent is
6405 * still valid
6406 */
6407 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6408 elements, &nb_elements, 256);
6409
6410 for (i = 0;i < nb_elements;i++) {
6411 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006412 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006413 int j;
6414
6415 for (j = 0; j < nb_valid_elements;j++)
6416 if (xmlStrEqual(elements[i], list[j])) break;
6417 list[nb_valid_elements++] = elements[i];
6418 if (nb_valid_elements >= max) break;
6419 }
6420 }
6421
6422 /*
6423 * Restore the tree structure
6424 */
6425 if (prev) prev->next = prev_next;
6426 if (next) next->prev = next_prev;
6427 parent->children = parent_childs;
6428 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006429
6430 /*
6431 * Free up the dummy node
6432 */
6433 test_node->name = name;
6434 xmlFreeNode(test_node);
6435
Owen Taylor3473f882001-02-23 17:55:21 +00006436 return(nb_valid_elements);
6437}