blob: 7ed0e8a47ad8d23c7ca78133423d31c0881b6e29 [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) {
Owen Taylor3473f882001-02-23 17:55:21 +0000374 char expr[1000];
375
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 Veillarda646cfd2002-09-17 21:50:03 +0000564 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000565 xmlValidBuildAContentModel(content->c1, ctxt, name);
566 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000567 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
568 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
569 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000570 oldend = ctxt->state;
571 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000572 switch (ocur) {
573 case XML_ELEMENT_CONTENT_ONCE:
574 break;
575 case XML_ELEMENT_CONTENT_OPT:
576 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
577 break;
578 case XML_ELEMENT_CONTENT_MULT:
579 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000580 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000581 break;
582 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000583 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000584 break;
585 }
586 break;
587 }
588 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000589 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000590 xmlElementContentOccur ocur;
591
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000592 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000593 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
594 (ocur == XML_ELEMENT_CONTENT_MULT)) {
595 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
596 ctxt->state, NULL);
597 }
598 oldstate = ctxt->state;
599 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000600
601 /*
602 * iterate over the subtypes and remerge the end with an
603 * epsilon transition
604 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000605 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000606 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000607 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000608 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000609 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000610 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
611 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000612 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000613 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000614 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
615 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000616 switch (ocur) {
617 case XML_ELEMENT_CONTENT_ONCE:
618 break;
619 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000620 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000621 break;
622 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000623 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
624 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000625 break;
626 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000627 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000628 break;
629 }
630 break;
631 }
632 default:
633 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
634 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000635 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000636 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000637 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000638}
639/**
640 * xmlValidBuildContentModel:
641 * @ctxt: a validation context
642 * @elem: an element declaration node
643 *
644 * (Re)Build the automata associated to the content model of this
645 * element
646 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000647 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000648 */
649int
650xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
651 xmlAutomataStatePtr start;
652
653 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000654 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000655 if (elem->type != XML_ELEMENT_DECL)
656 return(0);
657 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
658 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000659 /* TODO: should we rebuild in this case ? */
Daniel Veillardec498e12003-02-05 11:01:50 +0000660 if (elem->contModel != NULL) {
661 if (!xmlRegexpIsDeterminist(elem->contModel)) {
662 ctxt->valid = 0;
663 return(0);
664 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000665 return(1);
Daniel Veillardec498e12003-02-05 11:01:50 +0000666 }
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000667
668 ctxt->am = xmlNewAutomata();
669 if (ctxt->am == NULL) {
670 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
671 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000672 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000673 }
674 start = ctxt->state = xmlAutomataGetInitState(ctxt->am);
675 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
676 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000677 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillard23e73572002-09-19 19:56:43 +0000678 if (!xmlRegexpIsDeterminist(elem->contModel)) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000679 char expr[5000];
680 expr[0] = 0;
681 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
682 VERROR(ctxt->userData, "Content model of %s is not determinist: %s\n",
683 elem->name, expr);
684#ifdef DEBUG_REGEXP_ALGO
685 xmlRegexpPrint(stderr, elem->contModel);
686#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000687 ctxt->valid = 0;
Daniel Veillardec498e12003-02-05 11:01:50 +0000688 ctxt->state = NULL;
689 xmlFreeAutomata(ctxt->am);
690 ctxt->am = NULL;
691 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000692 }
693 ctxt->state = NULL;
694 xmlFreeAutomata(ctxt->am);
695 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000696 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000697}
698
699#endif /* LIBXML_REGEXP_ENABLED */
700
Owen Taylor3473f882001-02-23 17:55:21 +0000701/************************************************************************
702 * *
703 * QName handling helper *
704 * *
705 ************************************************************************/
706
707/**
708 * xmlSplitQName2:
709 * @name: an XML parser context
710 * @prefix: a xmlChar **
711 *
712 * parse an XML qualified name string
713 *
714 * [NS 5] QName ::= (Prefix ':')? LocalPart
715 *
716 * [NS 6] Prefix ::= NCName
717 *
718 * [NS 7] LocalPart ::= NCName
719 *
720 * Returns NULL if not a QName, otherwise the local part, and prefix
721 * is updated to get the Prefix if any.
722 */
723
724xmlChar *
725xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
726 int len = 0;
727 xmlChar *ret = NULL;
728
729 *prefix = NULL;
730
Daniel Veillardf4309d72001-10-02 09:28:58 +0000731#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000732 /* xml: prefix is not really a namespace */
733 if ((name[0] == 'x') && (name[1] == 'm') &&
734 (name[2] == 'l') && (name[3] == ':'))
735 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000736#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000737
738 /* nasty but valid */
739 if (name[0] == ':')
740 return(NULL);
741
742 /*
743 * we are not trying to validate but just to cut, and yes it will
744 * work even if this is as set of UTF-8 encoded chars
745 */
746 while ((name[len] != 0) && (name[len] != ':'))
747 len++;
748
749 if (name[len] == 0)
750 return(NULL);
751
752 *prefix = xmlStrndup(name, len);
753 ret = xmlStrdup(&name[len + 1]);
754
755 return(ret);
756}
757
758/****************************************************************
759 * *
760 * Util functions for data allocation/deallocation *
761 * *
762 ****************************************************************/
763
764/**
765 * xmlNewElementContent:
766 * @name: the subelement name or NULL
767 * @type: the type of element content decl
768 *
769 * Allocate an element content structure.
770 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000771 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000772 */
773xmlElementContentPtr
774xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
775 xmlElementContentPtr ret;
776
777 switch(type) {
778 case XML_ELEMENT_CONTENT_ELEMENT:
779 if (name == NULL) {
780 xmlGenericError(xmlGenericErrorContext,
781 "xmlNewElementContent : name == NULL !\n");
782 }
783 break;
784 case XML_ELEMENT_CONTENT_PCDATA:
785 case XML_ELEMENT_CONTENT_SEQ:
786 case XML_ELEMENT_CONTENT_OR:
787 if (name != NULL) {
788 xmlGenericError(xmlGenericErrorContext,
789 "xmlNewElementContent : name != NULL !\n");
790 }
791 break;
792 default:
793 xmlGenericError(xmlGenericErrorContext,
794 "xmlNewElementContent: unknown type %d\n", type);
795 return(NULL);
796 }
797 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
798 if (ret == NULL) {
799 xmlGenericError(xmlGenericErrorContext,
800 "xmlNewElementContent : out of memory!\n");
801 return(NULL);
802 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000803 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000804 ret->type = type;
805 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000806 if (name != NULL) {
807 xmlChar *prefix = NULL;
808 ret->name = xmlSplitQName2(name, &prefix);
809 if (ret->name == NULL)
810 ret->name = xmlStrdup(name);
811 ret->prefix = prefix;
812 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000813 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000814 ret->prefix = NULL;
815 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000816 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000817 return(ret);
818}
819
820/**
821 * xmlCopyElementContent:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000822 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +0000823 *
824 * Build a copy of an element content description.
825 *
826 * Returns the new xmlElementContentPtr or NULL in case of error.
827 */
828xmlElementContentPtr
829xmlCopyElementContent(xmlElementContentPtr cur) {
830 xmlElementContentPtr ret;
831
832 if (cur == NULL) return(NULL);
833 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
834 if (ret == NULL) {
835 xmlGenericError(xmlGenericErrorContext,
836 "xmlCopyElementContent : out of memory\n");
837 return(NULL);
838 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000839 if (cur->prefix != NULL)
840 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000841 ret->ocur = cur->ocur;
842 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000843 if (ret->c1 != NULL)
844 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000845 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000846 if (ret->c2 != NULL)
847 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000848 return(ret);
849}
850
851/**
852 * xmlFreeElementContent:
853 * @cur: the element content tree to free
854 *
855 * Free an element content structure. This is a recursive call !
856 */
857void
858xmlFreeElementContent(xmlElementContentPtr cur) {
859 if (cur == NULL) return;
860 switch (cur->type) {
861 case XML_ELEMENT_CONTENT_PCDATA:
862 case XML_ELEMENT_CONTENT_ELEMENT:
863 case XML_ELEMENT_CONTENT_SEQ:
864 case XML_ELEMENT_CONTENT_OR:
865 break;
866 default:
867 xmlGenericError(xmlGenericErrorContext,
868 "xmlFreeElementContent : type %d\n", cur->type);
869 return;
870 }
871 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
872 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
873 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000874 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000875 xmlFree(cur);
876}
877
878/**
879 * xmlDumpElementContent:
880 * @buf: An XML buffer
881 * @content: An element table
882 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
883 *
884 * This will dump the content of the element table as an XML DTD definition
885 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000886static void
Owen Taylor3473f882001-02-23 17:55:21 +0000887xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
888 if (content == NULL) return;
889
890 if (glob) xmlBufferWriteChar(buf, "(");
891 switch (content->type) {
892 case XML_ELEMENT_CONTENT_PCDATA:
893 xmlBufferWriteChar(buf, "#PCDATA");
894 break;
895 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000896 if (content->prefix != NULL) {
897 xmlBufferWriteCHAR(buf, content->prefix);
898 xmlBufferWriteChar(buf, ":");
899 }
Owen Taylor3473f882001-02-23 17:55:21 +0000900 xmlBufferWriteCHAR(buf, content->name);
901 break;
902 case XML_ELEMENT_CONTENT_SEQ:
903 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
904 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
905 xmlDumpElementContent(buf, content->c1, 1);
906 else
907 xmlDumpElementContent(buf, content->c1, 0);
908 xmlBufferWriteChar(buf, " , ");
909 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
910 xmlDumpElementContent(buf, content->c2, 1);
911 else
912 xmlDumpElementContent(buf, content->c2, 0);
913 break;
914 case XML_ELEMENT_CONTENT_OR:
915 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
916 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
917 xmlDumpElementContent(buf, content->c1, 1);
918 else
919 xmlDumpElementContent(buf, content->c1, 0);
920 xmlBufferWriteChar(buf, " | ");
921 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
922 xmlDumpElementContent(buf, content->c2, 1);
923 else
924 xmlDumpElementContent(buf, content->c2, 0);
925 break;
926 default:
927 xmlGenericError(xmlGenericErrorContext,
928 "xmlDumpElementContent: unknown type %d\n",
929 content->type);
930 }
931 if (glob)
932 xmlBufferWriteChar(buf, ")");
933 switch (content->ocur) {
934 case XML_ELEMENT_CONTENT_ONCE:
935 break;
936 case XML_ELEMENT_CONTENT_OPT:
937 xmlBufferWriteChar(buf, "?");
938 break;
939 case XML_ELEMENT_CONTENT_MULT:
940 xmlBufferWriteChar(buf, "*");
941 break;
942 case XML_ELEMENT_CONTENT_PLUS:
943 xmlBufferWriteChar(buf, "+");
944 break;
945 }
946}
947
948/**
949 * xmlSprintfElementContent:
950 * @buf: an output buffer
951 * @content: An element table
952 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
953 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000954 * Deprecated, unsafe, use xmlSnprintfElementContent
955 */
956void
957xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
958 xmlElementContentPtr content ATTRIBUTE_UNUSED,
959 int glob ATTRIBUTE_UNUSED) {
960}
961
962/**
963 * xmlSnprintfElementContent:
964 * @buf: an output buffer
965 * @size: the buffer size
966 * @content: An element table
967 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
968 *
Owen Taylor3473f882001-02-23 17:55:21 +0000969 * This will dump the content of the element content definition
970 * Intended just for the debug routine
971 */
972void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000973xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
974 int len;
975
Owen Taylor3473f882001-02-23 17:55:21 +0000976 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000977 len = strlen(buf);
978 if (size - len < 50) {
979 if ((size - len > 4) && (buf[len - 1] != '.'))
980 strcat(buf, " ...");
981 return;
982 }
Owen Taylor3473f882001-02-23 17:55:21 +0000983 if (glob) strcat(buf, "(");
984 switch (content->type) {
985 case XML_ELEMENT_CONTENT_PCDATA:
986 strcat(buf, "#PCDATA");
987 break;
988 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000989 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000990 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000991 strcat(buf, " ...");
992 return;
993 }
994 strcat(buf, (char *) content->prefix);
995 strcat(buf, ":");
996 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000997 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000998 strcat(buf, " ...");
999 return;
1000 }
Owen Taylor3473f882001-02-23 17:55:21 +00001001 strcat(buf, (char *) content->name);
1002 break;
1003 case XML_ELEMENT_CONTENT_SEQ:
1004 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1005 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001006 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001007 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001008 xmlSnprintfElementContent(buf, size, content->c1, 0);
1009 len = strlen(buf);
1010 if (size - len < 50) {
1011 if ((size - len > 4) && (buf[len - 1] != '.'))
1012 strcat(buf, " ...");
1013 return;
1014 }
Owen Taylor3473f882001-02-23 17:55:21 +00001015 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00001016 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
1017 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1018 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001019 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001020 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001021 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001022 break;
1023 case XML_ELEMENT_CONTENT_OR:
1024 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1025 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001026 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001027 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001028 xmlSnprintfElementContent(buf, size, content->c1, 0);
1029 len = strlen(buf);
1030 if (size - len < 50) {
1031 if ((size - len > 4) && (buf[len - 1] != '.'))
1032 strcat(buf, " ...");
1033 return;
1034 }
Owen Taylor3473f882001-02-23 17:55:21 +00001035 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00001036 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
1037 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1038 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001039 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001040 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001041 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001042 break;
1043 }
1044 if (glob)
1045 strcat(buf, ")");
1046 switch (content->ocur) {
1047 case XML_ELEMENT_CONTENT_ONCE:
1048 break;
1049 case XML_ELEMENT_CONTENT_OPT:
1050 strcat(buf, "?");
1051 break;
1052 case XML_ELEMENT_CONTENT_MULT:
1053 strcat(buf, "*");
1054 break;
1055 case XML_ELEMENT_CONTENT_PLUS:
1056 strcat(buf, "+");
1057 break;
1058 }
1059}
1060
1061/****************************************************************
1062 * *
1063 * Registration of DTD declarations *
1064 * *
1065 ****************************************************************/
1066
1067/**
1068 * xmlCreateElementTable:
1069 *
1070 * create and initialize an empty element hash table.
1071 *
1072 * Returns the xmlElementTablePtr just created or NULL in case of error.
1073 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001074static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001075xmlCreateElementTable(void) {
1076 return(xmlHashCreate(0));
1077}
1078
1079/**
1080 * xmlFreeElement:
1081 * @elem: An element
1082 *
1083 * Deallocate the memory used by an element definition
1084 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001085static void
Owen Taylor3473f882001-02-23 17:55:21 +00001086xmlFreeElement(xmlElementPtr elem) {
1087 if (elem == NULL) return;
1088 xmlUnlinkNode((xmlNodePtr) elem);
1089 xmlFreeElementContent(elem->content);
1090 if (elem->name != NULL)
1091 xmlFree((xmlChar *) elem->name);
1092 if (elem->prefix != NULL)
1093 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001094#ifdef LIBXML_REGEXP_ENABLED
1095 if (elem->contModel != NULL)
1096 xmlRegFreeRegexp(elem->contModel);
1097#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001098 xmlFree(elem);
1099}
1100
1101
1102/**
1103 * xmlAddElementDecl:
1104 * @ctxt: the validation context
1105 * @dtd: pointer to the DTD
1106 * @name: the entity name
1107 * @type: the element type
1108 * @content: the element content tree or NULL
1109 *
1110 * Register a new element declaration
1111 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001112 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001113 */
1114xmlElementPtr
1115xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
1116 xmlElementTypeVal type,
1117 xmlElementContentPtr content) {
1118 xmlElementPtr ret;
1119 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001120 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001121 xmlChar *ns, *uqname;
1122
1123 if (dtd == NULL) {
1124 xmlGenericError(xmlGenericErrorContext,
1125 "xmlAddElementDecl: dtd == NULL\n");
1126 return(NULL);
1127 }
1128 if (name == NULL) {
1129 xmlGenericError(xmlGenericErrorContext,
1130 "xmlAddElementDecl: name == NULL\n");
1131 return(NULL);
1132 }
1133 switch (type) {
1134 case XML_ELEMENT_TYPE_EMPTY:
1135 if (content != NULL) {
1136 xmlGenericError(xmlGenericErrorContext,
1137 "xmlAddElementDecl: content != NULL for EMPTY\n");
1138 return(NULL);
1139 }
1140 break;
1141 case XML_ELEMENT_TYPE_ANY:
1142 if (content != NULL) {
1143 xmlGenericError(xmlGenericErrorContext,
1144 "xmlAddElementDecl: content != NULL for ANY\n");
1145 return(NULL);
1146 }
1147 break;
1148 case XML_ELEMENT_TYPE_MIXED:
1149 if (content == NULL) {
1150 xmlGenericError(xmlGenericErrorContext,
1151 "xmlAddElementDecl: content == NULL for MIXED\n");
1152 return(NULL);
1153 }
1154 break;
1155 case XML_ELEMENT_TYPE_ELEMENT:
1156 if (content == NULL) {
1157 xmlGenericError(xmlGenericErrorContext,
1158 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1159 return(NULL);
1160 }
1161 break;
1162 default:
1163 xmlGenericError(xmlGenericErrorContext,
1164 "xmlAddElementDecl: unknown type %d\n", type);
1165 return(NULL);
1166 }
1167
1168 /*
1169 * check if name is a QName
1170 */
1171 uqname = xmlSplitQName2(name, &ns);
1172 if (uqname != NULL)
1173 name = uqname;
1174
1175 /*
1176 * Create the Element table if needed.
1177 */
1178 table = (xmlElementTablePtr) dtd->elements;
1179 if (table == NULL) {
1180 table = xmlCreateElementTable();
1181 dtd->elements = (void *) table;
1182 }
1183 if (table == NULL) {
1184 xmlGenericError(xmlGenericErrorContext,
1185 "xmlAddElementDecl: Table creation failed!\n");
1186 return(NULL);
1187 }
1188
Daniel Veillarda10efa82001-04-18 13:09:01 +00001189 /*
1190 * lookup old attributes inserted on an undefined element in the
1191 * internal subset.
1192 */
1193 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1194 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1195 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1196 oldAttributes = ret->attributes;
1197 ret->attributes = NULL;
1198 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1199 xmlFreeElement(ret);
1200 }
Owen Taylor3473f882001-02-23 17:55:21 +00001201 }
Owen Taylor3473f882001-02-23 17:55:21 +00001202
1203 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001204 * The element may already be present if one of its attribute
1205 * was registered first
1206 */
1207 ret = xmlHashLookup2(table, name, ns);
1208 if (ret != NULL) {
1209 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1210 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001211 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001212 */
1213 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1214 if (uqname != NULL)
1215 xmlFree(uqname);
1216 return(NULL);
1217 }
1218 } else {
1219 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1220 if (ret == NULL) {
1221 xmlGenericError(xmlGenericErrorContext,
1222 "xmlAddElementDecl: out of memory\n");
1223 return(NULL);
1224 }
1225 memset(ret, 0, sizeof(xmlElement));
1226 ret->type = XML_ELEMENT_DECL;
1227
1228 /*
1229 * fill the structure.
1230 */
1231 ret->name = xmlStrdup(name);
1232 ret->prefix = ns;
1233
1234 /*
1235 * Validity Check:
1236 * Insertion must not fail
1237 */
1238 if (xmlHashAddEntry2(table, name, ns, ret)) {
1239 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001240 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001241 */
1242 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1243 xmlFreeElement(ret);
1244 if (uqname != NULL)
1245 xmlFree(uqname);
1246 return(NULL);
1247 }
1248 }
1249
1250 /*
1251 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001252 */
1253 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001254 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001255 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001256
1257 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001258 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001259 */
1260 ret->parent = dtd;
1261 ret->doc = dtd->doc;
1262 if (dtd->last == NULL) {
1263 dtd->children = dtd->last = (xmlNodePtr) ret;
1264 } else {
1265 dtd->last->next = (xmlNodePtr) ret;
1266 ret->prev = dtd->last;
1267 dtd->last = (xmlNodePtr) ret;
1268 }
1269 if (uqname != NULL)
1270 xmlFree(uqname);
1271 return(ret);
1272}
1273
1274/**
1275 * xmlFreeElementTable:
1276 * @table: An element table
1277 *
1278 * Deallocate the memory used by an element hash table.
1279 */
1280void
1281xmlFreeElementTable(xmlElementTablePtr table) {
1282 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1283}
1284
1285/**
1286 * xmlCopyElement:
1287 * @elem: An element
1288 *
1289 * Build a copy of an element.
1290 *
1291 * Returns the new xmlElementPtr or NULL in case of error.
1292 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001293static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001294xmlCopyElement(xmlElementPtr elem) {
1295 xmlElementPtr cur;
1296
1297 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1298 if (cur == NULL) {
1299 xmlGenericError(xmlGenericErrorContext,
1300 "xmlCopyElement: out of memory !\n");
1301 return(NULL);
1302 }
1303 memset(cur, 0, sizeof(xmlElement));
1304 cur->type = XML_ELEMENT_DECL;
1305 cur->etype = elem->etype;
1306 if (elem->name != NULL)
1307 cur->name = xmlStrdup(elem->name);
1308 else
1309 cur->name = NULL;
1310 if (elem->prefix != NULL)
1311 cur->prefix = xmlStrdup(elem->prefix);
1312 else
1313 cur->prefix = NULL;
1314 cur->content = xmlCopyElementContent(elem->content);
1315 /* TODO : rebuild the attribute list on the copy */
1316 cur->attributes = NULL;
1317 return(cur);
1318}
1319
1320/**
1321 * xmlCopyElementTable:
1322 * @table: An element table
1323 *
1324 * Build a copy of an element table.
1325 *
1326 * Returns the new xmlElementTablePtr or NULL in case of error.
1327 */
1328xmlElementTablePtr
1329xmlCopyElementTable(xmlElementTablePtr table) {
1330 return((xmlElementTablePtr) xmlHashCopy(table,
1331 (xmlHashCopier) xmlCopyElement));
1332}
1333
1334/**
1335 * xmlDumpElementDecl:
1336 * @buf: the XML buffer output
1337 * @elem: An element table
1338 *
1339 * This will dump the content of the element declaration as an XML
1340 * DTD definition
1341 */
1342void
1343xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1344 switch (elem->etype) {
1345 case XML_ELEMENT_TYPE_EMPTY:
1346 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001347 if (elem->prefix != NULL) {
1348 xmlBufferWriteCHAR(buf, elem->prefix);
1349 xmlBufferWriteChar(buf, ":");
1350 }
Owen Taylor3473f882001-02-23 17:55:21 +00001351 xmlBufferWriteCHAR(buf, elem->name);
1352 xmlBufferWriteChar(buf, " EMPTY>\n");
1353 break;
1354 case XML_ELEMENT_TYPE_ANY:
1355 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001356 if (elem->prefix != NULL) {
1357 xmlBufferWriteCHAR(buf, elem->prefix);
1358 xmlBufferWriteChar(buf, ":");
1359 }
Owen Taylor3473f882001-02-23 17:55:21 +00001360 xmlBufferWriteCHAR(buf, elem->name);
1361 xmlBufferWriteChar(buf, " ANY>\n");
1362 break;
1363 case XML_ELEMENT_TYPE_MIXED:
1364 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001365 if (elem->prefix != NULL) {
1366 xmlBufferWriteCHAR(buf, elem->prefix);
1367 xmlBufferWriteChar(buf, ":");
1368 }
Owen Taylor3473f882001-02-23 17:55:21 +00001369 xmlBufferWriteCHAR(buf, elem->name);
1370 xmlBufferWriteChar(buf, " ");
1371 xmlDumpElementContent(buf, elem->content, 1);
1372 xmlBufferWriteChar(buf, ">\n");
1373 break;
1374 case XML_ELEMENT_TYPE_ELEMENT:
1375 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001376 if (elem->prefix != NULL) {
1377 xmlBufferWriteCHAR(buf, elem->prefix);
1378 xmlBufferWriteChar(buf, ":");
1379 }
Owen Taylor3473f882001-02-23 17:55:21 +00001380 xmlBufferWriteCHAR(buf, elem->name);
1381 xmlBufferWriteChar(buf, " ");
1382 xmlDumpElementContent(buf, elem->content, 1);
1383 xmlBufferWriteChar(buf, ">\n");
1384 break;
1385 default:
1386 xmlGenericError(xmlGenericErrorContext,
1387 "xmlDumpElementDecl: internal: unknown type %d\n",
1388 elem->etype);
1389 }
1390}
1391
1392/**
1393 * xmlDumpElementTable:
1394 * @buf: the XML buffer output
1395 * @table: An element table
1396 *
1397 * This will dump the content of the element table as an XML DTD definition
1398 */
1399void
1400xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1401 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1402}
1403
1404/**
1405 * xmlCreateEnumeration:
1406 * @name: the enumeration name or NULL
1407 *
1408 * create and initialize an enumeration attribute node.
1409 *
1410 * Returns the xmlEnumerationPtr just created or NULL in case
1411 * of error.
1412 */
1413xmlEnumerationPtr
1414xmlCreateEnumeration(xmlChar *name) {
1415 xmlEnumerationPtr ret;
1416
1417 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1418 if (ret == NULL) {
1419 xmlGenericError(xmlGenericErrorContext,
1420 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1421 (long)sizeof(xmlEnumeration));
1422 return(NULL);
1423 }
1424 memset(ret, 0, sizeof(xmlEnumeration));
1425
1426 if (name != NULL)
1427 ret->name = xmlStrdup(name);
1428 return(ret);
1429}
1430
1431/**
1432 * xmlFreeEnumeration:
1433 * @cur: the tree to free.
1434 *
1435 * free an enumeration attribute node (recursive).
1436 */
1437void
1438xmlFreeEnumeration(xmlEnumerationPtr cur) {
1439 if (cur == NULL) return;
1440
1441 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1442
1443 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001444 xmlFree(cur);
1445}
1446
1447/**
1448 * xmlCopyEnumeration:
1449 * @cur: the tree to copy.
1450 *
1451 * Copy an enumeration attribute node (recursive).
1452 *
1453 * Returns the xmlEnumerationPtr just created or NULL in case
1454 * of error.
1455 */
1456xmlEnumerationPtr
1457xmlCopyEnumeration(xmlEnumerationPtr cur) {
1458 xmlEnumerationPtr ret;
1459
1460 if (cur == NULL) return(NULL);
1461 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1462
1463 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1464 else ret->next = NULL;
1465
1466 return(ret);
1467}
1468
1469/**
1470 * xmlDumpEnumeration:
1471 * @buf: the XML buffer output
1472 * @enum: An enumeration
1473 *
1474 * This will dump the content of the enumeration
1475 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001476static void
Owen Taylor3473f882001-02-23 17:55:21 +00001477xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1478 if (cur == NULL) return;
1479
1480 xmlBufferWriteCHAR(buf, cur->name);
1481 if (cur->next == NULL)
1482 xmlBufferWriteChar(buf, ")");
1483 else {
1484 xmlBufferWriteChar(buf, " | ");
1485 xmlDumpEnumeration(buf, cur->next);
1486 }
1487}
1488
1489/**
1490 * xmlCreateAttributeTable:
1491 *
1492 * create and initialize an empty attribute hash table.
1493 *
1494 * Returns the xmlAttributeTablePtr just created or NULL in case
1495 * of error.
1496 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001497static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001498xmlCreateAttributeTable(void) {
1499 return(xmlHashCreate(0));
1500}
1501
1502/**
1503 * xmlScanAttributeDeclCallback:
1504 * @attr: the attribute decl
1505 * @list: the list to update
1506 *
1507 * Callback called by xmlScanAttributeDecl when a new attribute
1508 * has to be entered in the list.
1509 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001510static void
Owen Taylor3473f882001-02-23 17:55:21 +00001511xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001512 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001513 attr->nexth = *list;
1514 *list = attr;
1515}
1516
1517/**
1518 * xmlScanAttributeDecl:
1519 * @dtd: pointer to the DTD
1520 * @elem: the element name
1521 *
1522 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001523 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001524 *
1525 * Returns the pointer to the first attribute decl in the chain,
1526 * possibly NULL.
1527 */
1528xmlAttributePtr
1529xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1530 xmlAttributePtr ret = NULL;
1531 xmlAttributeTablePtr table;
1532
1533 if (dtd == NULL) {
1534 xmlGenericError(xmlGenericErrorContext,
1535 "xmlScanAttributeDecl: dtd == NULL\n");
1536 return(NULL);
1537 }
1538 if (elem == NULL) {
1539 xmlGenericError(xmlGenericErrorContext,
1540 "xmlScanAttributeDecl: elem == NULL\n");
1541 return(NULL);
1542 }
1543 table = (xmlAttributeTablePtr) dtd->attributes;
1544 if (table == NULL)
1545 return(NULL);
1546
1547 /* WRONG !!! */
1548 xmlHashScan3(table, NULL, NULL, elem,
1549 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1550 return(ret);
1551}
1552
1553/**
1554 * xmlScanIDAttributeDecl:
1555 * @ctxt: the validation context
1556 * @elem: the element name
1557 *
1558 * Verify that the element don't have too many ID attributes
1559 * declared.
1560 *
1561 * Returns the number of ID attributes found.
1562 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001563static int
Owen Taylor3473f882001-02-23 17:55:21 +00001564xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1565 xmlAttributePtr cur;
1566 int ret = 0;
1567
1568 if (elem == NULL) return(0);
1569 cur = elem->attributes;
1570 while (cur != NULL) {
1571 if (cur->atype == XML_ATTRIBUTE_ID) {
1572 ret ++;
1573 if (ret > 1)
1574 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001575 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001576 elem->name, cur->name);
1577 }
1578 cur = cur->nexth;
1579 }
1580 return(ret);
1581}
1582
1583/**
1584 * xmlFreeAttribute:
1585 * @elem: An attribute
1586 *
1587 * Deallocate the memory used by an attribute definition
1588 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001589static void
Owen Taylor3473f882001-02-23 17:55:21 +00001590xmlFreeAttribute(xmlAttributePtr attr) {
1591 if (attr == NULL) return;
1592 xmlUnlinkNode((xmlNodePtr) attr);
1593 if (attr->tree != NULL)
1594 xmlFreeEnumeration(attr->tree);
1595 if (attr->elem != NULL)
1596 xmlFree((xmlChar *) attr->elem);
1597 if (attr->name != NULL)
1598 xmlFree((xmlChar *) attr->name);
1599 if (attr->defaultValue != NULL)
1600 xmlFree((xmlChar *) attr->defaultValue);
1601 if (attr->prefix != NULL)
1602 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001603 xmlFree(attr);
1604}
1605
1606
1607/**
1608 * xmlAddAttributeDecl:
1609 * @ctxt: the validation context
1610 * @dtd: pointer to the DTD
1611 * @elem: the element name
1612 * @name: the attribute name
1613 * @ns: the attribute namespace prefix
1614 * @type: the attribute type
1615 * @def: the attribute default type
1616 * @defaultValue: the attribute default value
1617 * @tree: if it's an enumeration, the associated list
1618 *
1619 * Register a new attribute declaration
1620 * Note that @tree becomes the ownership of the DTD
1621 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001622 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001623 */
1624xmlAttributePtr
1625xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1626 const xmlChar *name, const xmlChar *ns,
1627 xmlAttributeType type, xmlAttributeDefault def,
1628 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1629 xmlAttributePtr ret;
1630 xmlAttributeTablePtr table;
1631 xmlElementPtr elemDef;
1632
1633 if (dtd == NULL) {
1634 xmlGenericError(xmlGenericErrorContext,
1635 "xmlAddAttributeDecl: dtd == NULL\n");
1636 xmlFreeEnumeration(tree);
1637 return(NULL);
1638 }
1639 if (name == NULL) {
1640 xmlGenericError(xmlGenericErrorContext,
1641 "xmlAddAttributeDecl: name == NULL\n");
1642 xmlFreeEnumeration(tree);
1643 return(NULL);
1644 }
1645 if (elem == NULL) {
1646 xmlGenericError(xmlGenericErrorContext,
1647 "xmlAddAttributeDecl: elem == NULL\n");
1648 xmlFreeEnumeration(tree);
1649 return(NULL);
1650 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001651
Owen Taylor3473f882001-02-23 17:55:21 +00001652 /*
1653 * Check the type and possibly the default value.
1654 */
1655 switch (type) {
1656 case XML_ATTRIBUTE_CDATA:
1657 break;
1658 case XML_ATTRIBUTE_ID:
1659 break;
1660 case XML_ATTRIBUTE_IDREF:
1661 break;
1662 case XML_ATTRIBUTE_IDREFS:
1663 break;
1664 case XML_ATTRIBUTE_ENTITY:
1665 break;
1666 case XML_ATTRIBUTE_ENTITIES:
1667 break;
1668 case XML_ATTRIBUTE_NMTOKEN:
1669 break;
1670 case XML_ATTRIBUTE_NMTOKENS:
1671 break;
1672 case XML_ATTRIBUTE_ENUMERATION:
1673 break;
1674 case XML_ATTRIBUTE_NOTATION:
1675 break;
1676 default:
1677 xmlGenericError(xmlGenericErrorContext,
1678 "xmlAddAttributeDecl: unknown type %d\n", type);
1679 xmlFreeEnumeration(tree);
1680 return(NULL);
1681 }
1682 if ((defaultValue != NULL) &&
1683 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001684 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001685 elem, name, defaultValue);
1686 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001687 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001688 }
1689
1690 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001691 * Check first that an attribute defined in the external subset wasn't
1692 * already defined in the internal subset
1693 */
1694 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1695 (dtd->doc->intSubset != NULL) &&
1696 (dtd->doc->intSubset->attributes != NULL)) {
1697 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1698 if (ret != NULL)
1699 return(NULL);
1700 }
1701
1702 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001703 * Create the Attribute table if needed.
1704 */
1705 table = (xmlAttributeTablePtr) dtd->attributes;
1706 if (table == NULL) {
1707 table = xmlCreateAttributeTable();
1708 dtd->attributes = (void *) table;
1709 }
1710 if (table == NULL) {
1711 xmlGenericError(xmlGenericErrorContext,
1712 "xmlAddAttributeDecl: Table creation failed!\n");
1713 return(NULL);
1714 }
1715
1716
1717 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1718 if (ret == NULL) {
1719 xmlGenericError(xmlGenericErrorContext,
1720 "xmlAddAttributeDecl: out of memory\n");
1721 return(NULL);
1722 }
1723 memset(ret, 0, sizeof(xmlAttribute));
1724 ret->type = XML_ATTRIBUTE_DECL;
1725
1726 /*
1727 * fill the structure.
1728 */
1729 ret->atype = type;
1730 ret->name = xmlStrdup(name);
1731 ret->prefix = xmlStrdup(ns);
1732 ret->elem = xmlStrdup(elem);
1733 ret->def = def;
1734 ret->tree = tree;
1735 if (defaultValue != NULL)
1736 ret->defaultValue = xmlStrdup(defaultValue);
1737
1738 /*
1739 * Validity Check:
1740 * Search the DTD for previous declarations of the ATTLIST
1741 */
1742 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1743 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001744 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001745 */
1746 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001747 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001748 name, elem);
1749 xmlFreeAttribute(ret);
1750 return(NULL);
1751 }
1752
1753 /*
1754 * Validity Check:
1755 * Multiple ID per element
1756 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001757 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001758 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001759
Owen Taylor3473f882001-02-23 17:55:21 +00001760 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001761 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001762 VERROR(ctxt->userData,
1763 "Element %s has too may ID attributes defined : %s\n",
1764 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001765 ctxt->valid = 0;
1766 }
1767
Daniel Veillard48da9102001-08-07 01:10:10 +00001768 /*
1769 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001770 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001771 */
1772 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1773 ((ret->prefix != NULL &&
1774 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1775 ret->nexth = elemDef->attributes;
1776 elemDef->attributes = ret;
1777 } else {
1778 xmlAttributePtr tmp = elemDef->attributes;
1779
1780 while ((tmp != NULL) &&
1781 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1782 ((ret->prefix != NULL &&
1783 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1784 if (tmp->nexth == NULL)
1785 break;
1786 tmp = tmp->nexth;
1787 }
1788 if (tmp != NULL) {
1789 ret->nexth = tmp->nexth;
1790 tmp->nexth = ret;
1791 } else {
1792 ret->nexth = elemDef->attributes;
1793 elemDef->attributes = ret;
1794 }
1795 }
Owen Taylor3473f882001-02-23 17:55:21 +00001796 }
1797
1798 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001799 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001800 */
1801 ret->parent = dtd;
1802 ret->doc = dtd->doc;
1803 if (dtd->last == NULL) {
1804 dtd->children = dtd->last = (xmlNodePtr) ret;
1805 } else {
1806 dtd->last->next = (xmlNodePtr) ret;
1807 ret->prev = dtd->last;
1808 dtd->last = (xmlNodePtr) ret;
1809 }
1810 return(ret);
1811}
1812
1813/**
1814 * xmlFreeAttributeTable:
1815 * @table: An attribute table
1816 *
1817 * Deallocate the memory used by an entities hash table.
1818 */
1819void
1820xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1821 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1822}
1823
1824/**
1825 * xmlCopyAttribute:
1826 * @attr: An attribute
1827 *
1828 * Build a copy of an attribute.
1829 *
1830 * Returns the new xmlAttributePtr or NULL in case of error.
1831 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001832static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001833xmlCopyAttribute(xmlAttributePtr attr) {
1834 xmlAttributePtr cur;
1835
1836 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1837 if (cur == NULL) {
1838 xmlGenericError(xmlGenericErrorContext,
1839 "xmlCopyAttribute: out of memory !\n");
1840 return(NULL);
1841 }
1842 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001843 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001844 cur->atype = attr->atype;
1845 cur->def = attr->def;
1846 cur->tree = xmlCopyEnumeration(attr->tree);
1847 if (attr->elem != NULL)
1848 cur->elem = xmlStrdup(attr->elem);
1849 if (attr->name != NULL)
1850 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001851 if (attr->prefix != NULL)
1852 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001853 if (attr->defaultValue != NULL)
1854 cur->defaultValue = xmlStrdup(attr->defaultValue);
1855 return(cur);
1856}
1857
1858/**
1859 * xmlCopyAttributeTable:
1860 * @table: An attribute table
1861 *
1862 * Build a copy of an attribute table.
1863 *
1864 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1865 */
1866xmlAttributeTablePtr
1867xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1868 return((xmlAttributeTablePtr) xmlHashCopy(table,
1869 (xmlHashCopier) xmlCopyAttribute));
1870}
1871
1872/**
1873 * xmlDumpAttributeDecl:
1874 * @buf: the XML buffer output
1875 * @attr: An attribute declaration
1876 *
1877 * This will dump the content of the attribute declaration as an XML
1878 * DTD definition
1879 */
1880void
1881xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1882 xmlBufferWriteChar(buf, "<!ATTLIST ");
1883 xmlBufferWriteCHAR(buf, attr->elem);
1884 xmlBufferWriteChar(buf, " ");
1885 if (attr->prefix != NULL) {
1886 xmlBufferWriteCHAR(buf, attr->prefix);
1887 xmlBufferWriteChar(buf, ":");
1888 }
1889 xmlBufferWriteCHAR(buf, attr->name);
1890 switch (attr->atype) {
1891 case XML_ATTRIBUTE_CDATA:
1892 xmlBufferWriteChar(buf, " CDATA");
1893 break;
1894 case XML_ATTRIBUTE_ID:
1895 xmlBufferWriteChar(buf, " ID");
1896 break;
1897 case XML_ATTRIBUTE_IDREF:
1898 xmlBufferWriteChar(buf, " IDREF");
1899 break;
1900 case XML_ATTRIBUTE_IDREFS:
1901 xmlBufferWriteChar(buf, " IDREFS");
1902 break;
1903 case XML_ATTRIBUTE_ENTITY:
1904 xmlBufferWriteChar(buf, " ENTITY");
1905 break;
1906 case XML_ATTRIBUTE_ENTITIES:
1907 xmlBufferWriteChar(buf, " ENTITIES");
1908 break;
1909 case XML_ATTRIBUTE_NMTOKEN:
1910 xmlBufferWriteChar(buf, " NMTOKEN");
1911 break;
1912 case XML_ATTRIBUTE_NMTOKENS:
1913 xmlBufferWriteChar(buf, " NMTOKENS");
1914 break;
1915 case XML_ATTRIBUTE_ENUMERATION:
1916 xmlBufferWriteChar(buf, " (");
1917 xmlDumpEnumeration(buf, attr->tree);
1918 break;
1919 case XML_ATTRIBUTE_NOTATION:
1920 xmlBufferWriteChar(buf, " NOTATION (");
1921 xmlDumpEnumeration(buf, attr->tree);
1922 break;
1923 default:
1924 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001925 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001926 attr->atype);
1927 }
1928 switch (attr->def) {
1929 case XML_ATTRIBUTE_NONE:
1930 break;
1931 case XML_ATTRIBUTE_REQUIRED:
1932 xmlBufferWriteChar(buf, " #REQUIRED");
1933 break;
1934 case XML_ATTRIBUTE_IMPLIED:
1935 xmlBufferWriteChar(buf, " #IMPLIED");
1936 break;
1937 case XML_ATTRIBUTE_FIXED:
1938 xmlBufferWriteChar(buf, " #FIXED");
1939 break;
1940 default:
1941 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001942 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001943 attr->def);
1944 }
1945 if (attr->defaultValue != NULL) {
1946 xmlBufferWriteChar(buf, " ");
1947 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1948 }
1949 xmlBufferWriteChar(buf, ">\n");
1950}
1951
1952/**
1953 * xmlDumpAttributeTable:
1954 * @buf: the XML buffer output
1955 * @table: An attribute table
1956 *
1957 * This will dump the content of the attribute table as an XML DTD definition
1958 */
1959void
1960xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1961 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1962}
1963
1964/************************************************************************
1965 * *
1966 * NOTATIONs *
1967 * *
1968 ************************************************************************/
1969/**
1970 * xmlCreateNotationTable:
1971 *
1972 * create and initialize an empty notation hash table.
1973 *
1974 * Returns the xmlNotationTablePtr just created or NULL in case
1975 * of error.
1976 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001977static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001978xmlCreateNotationTable(void) {
1979 return(xmlHashCreate(0));
1980}
1981
1982/**
1983 * xmlFreeNotation:
1984 * @not: A notation
1985 *
1986 * Deallocate the memory used by an notation definition
1987 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001988static void
Owen Taylor3473f882001-02-23 17:55:21 +00001989xmlFreeNotation(xmlNotationPtr nota) {
1990 if (nota == NULL) return;
1991 if (nota->name != NULL)
1992 xmlFree((xmlChar *) nota->name);
1993 if (nota->PublicID != NULL)
1994 xmlFree((xmlChar *) nota->PublicID);
1995 if (nota->SystemID != NULL)
1996 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001997 xmlFree(nota);
1998}
1999
2000
2001/**
2002 * xmlAddNotationDecl:
2003 * @dtd: pointer to the DTD
2004 * @ctxt: the validation context
2005 * @name: the entity name
2006 * @PublicID: the public identifier or NULL
2007 * @SystemID: the system identifier or NULL
2008 *
2009 * Register a new notation declaration
2010 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002011 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00002012 */
2013xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002014xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002015 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00002016 const xmlChar *PublicID, const xmlChar *SystemID) {
2017 xmlNotationPtr ret;
2018 xmlNotationTablePtr table;
2019
2020 if (dtd == NULL) {
2021 xmlGenericError(xmlGenericErrorContext,
2022 "xmlAddNotationDecl: dtd == NULL\n");
2023 return(NULL);
2024 }
2025 if (name == NULL) {
2026 xmlGenericError(xmlGenericErrorContext,
2027 "xmlAddNotationDecl: name == NULL\n");
2028 return(NULL);
2029 }
2030 if ((PublicID == NULL) && (SystemID == NULL)) {
2031 xmlGenericError(xmlGenericErrorContext,
2032 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002033 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002034 }
2035
2036 /*
2037 * Create the Notation table if needed.
2038 */
2039 table = (xmlNotationTablePtr) dtd->notations;
2040 if (table == NULL)
2041 dtd->notations = table = xmlCreateNotationTable();
2042 if (table == NULL) {
2043 xmlGenericError(xmlGenericErrorContext,
2044 "xmlAddNotationDecl: Table creation failed!\n");
2045 return(NULL);
2046 }
2047
2048 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2049 if (ret == NULL) {
2050 xmlGenericError(xmlGenericErrorContext,
2051 "xmlAddNotationDecl: out of memory\n");
2052 return(NULL);
2053 }
2054 memset(ret, 0, sizeof(xmlNotation));
2055
2056 /*
2057 * fill the structure.
2058 */
2059 ret->name = xmlStrdup(name);
2060 if (SystemID != NULL)
2061 ret->SystemID = xmlStrdup(SystemID);
2062 if (PublicID != NULL)
2063 ret->PublicID = xmlStrdup(PublicID);
2064
2065 /*
2066 * Validity Check:
2067 * Check the DTD for previous declarations of the ATTLIST
2068 */
2069 if (xmlHashAddEntry(table, name, ret)) {
2070 xmlGenericError(xmlGenericErrorContext,
2071 "xmlAddNotationDecl: %s already defined\n", name);
2072 xmlFreeNotation(ret);
2073 return(NULL);
2074 }
2075 return(ret);
2076}
2077
2078/**
2079 * xmlFreeNotationTable:
2080 * @table: An notation table
2081 *
2082 * Deallocate the memory used by an entities hash table.
2083 */
2084void
2085xmlFreeNotationTable(xmlNotationTablePtr table) {
2086 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2087}
2088
2089/**
2090 * xmlCopyNotation:
2091 * @nota: A notation
2092 *
2093 * Build a copy of a notation.
2094 *
2095 * Returns the new xmlNotationPtr or NULL in case of error.
2096 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002097static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002098xmlCopyNotation(xmlNotationPtr nota) {
2099 xmlNotationPtr cur;
2100
2101 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2102 if (cur == NULL) {
2103 xmlGenericError(xmlGenericErrorContext,
2104 "xmlCopyNotation: out of memory !\n");
2105 return(NULL);
2106 }
2107 if (nota->name != NULL)
2108 cur->name = xmlStrdup(nota->name);
2109 else
2110 cur->name = NULL;
2111 if (nota->PublicID != NULL)
2112 cur->PublicID = xmlStrdup(nota->PublicID);
2113 else
2114 cur->PublicID = NULL;
2115 if (nota->SystemID != NULL)
2116 cur->SystemID = xmlStrdup(nota->SystemID);
2117 else
2118 cur->SystemID = NULL;
2119 return(cur);
2120}
2121
2122/**
2123 * xmlCopyNotationTable:
2124 * @table: A notation table
2125 *
2126 * Build a copy of a notation table.
2127 *
2128 * Returns the new xmlNotationTablePtr or NULL in case of error.
2129 */
2130xmlNotationTablePtr
2131xmlCopyNotationTable(xmlNotationTablePtr table) {
2132 return((xmlNotationTablePtr) xmlHashCopy(table,
2133 (xmlHashCopier) xmlCopyNotation));
2134}
2135
2136/**
2137 * xmlDumpNotationDecl:
2138 * @buf: the XML buffer output
2139 * @nota: A notation declaration
2140 *
2141 * This will dump the content the notation declaration as an XML DTD definition
2142 */
2143void
2144xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2145 xmlBufferWriteChar(buf, "<!NOTATION ");
2146 xmlBufferWriteCHAR(buf, nota->name);
2147 if (nota->PublicID != NULL) {
2148 xmlBufferWriteChar(buf, " PUBLIC ");
2149 xmlBufferWriteQuotedString(buf, nota->PublicID);
2150 if (nota->SystemID != NULL) {
2151 xmlBufferWriteChar(buf, " ");
2152 xmlBufferWriteCHAR(buf, nota->SystemID);
2153 }
2154 } else {
2155 xmlBufferWriteChar(buf, " SYSTEM ");
2156 xmlBufferWriteCHAR(buf, nota->SystemID);
2157 }
2158 xmlBufferWriteChar(buf, " >\n");
2159}
2160
2161/**
2162 * xmlDumpNotationTable:
2163 * @buf: the XML buffer output
2164 * @table: A notation table
2165 *
2166 * This will dump the content of the notation table as an XML DTD definition
2167 */
2168void
2169xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2170 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2171}
2172
2173/************************************************************************
2174 * *
2175 * IDs *
2176 * *
2177 ************************************************************************/
2178/**
2179 * xmlCreateIDTable:
2180 *
2181 * create and initialize an empty id hash table.
2182 *
2183 * Returns the xmlIDTablePtr just created or NULL in case
2184 * of error.
2185 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002186static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002187xmlCreateIDTable(void) {
2188 return(xmlHashCreate(0));
2189}
2190
2191/**
2192 * xmlFreeID:
2193 * @not: A id
2194 *
2195 * Deallocate the memory used by an id definition
2196 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002197static void
Owen Taylor3473f882001-02-23 17:55:21 +00002198xmlFreeID(xmlIDPtr id) {
2199 if (id == NULL) return;
2200 if (id->value != NULL)
2201 xmlFree((xmlChar *) id->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002202 if (id->name != NULL)
2203 xmlFree((xmlChar *) id->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002204 xmlFree(id);
2205}
2206
2207/**
2208 * xmlAddID:
2209 * @ctxt: the validation context
2210 * @doc: pointer to the document
2211 * @value: the value name
2212 * @attr: the attribute holding the ID
2213 *
2214 * Register a new id declaration
2215 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002216 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002217 */
2218xmlIDPtr
2219xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2220 xmlAttrPtr attr) {
2221 xmlIDPtr ret;
2222 xmlIDTablePtr table;
2223
2224 if (doc == NULL) {
2225 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002226 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002227 return(NULL);
2228 }
2229 if (value == NULL) {
2230 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002231 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002232 return(NULL);
2233 }
2234 if (attr == NULL) {
2235 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002236 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002237 return(NULL);
2238 }
2239
2240 /*
2241 * Create the ID table if needed.
2242 */
2243 table = (xmlIDTablePtr) doc->ids;
2244 if (table == NULL)
2245 doc->ids = table = xmlCreateIDTable();
2246 if (table == NULL) {
2247 xmlGenericError(xmlGenericErrorContext,
2248 "xmlAddID: Table creation failed!\n");
2249 return(NULL);
2250 }
2251
2252 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2253 if (ret == NULL) {
2254 xmlGenericError(xmlGenericErrorContext,
2255 "xmlAddID: out of memory\n");
2256 return(NULL);
2257 }
2258
2259 /*
2260 * fill the structure.
2261 */
2262 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002263 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2264 /*
2265 * Operating in streaming mode, attr is gonna disapear
2266 */
2267 ret->name = xmlStrdup(attr->name);
2268 ret->attr = NULL;
2269 } else {
2270 ret->attr = attr;
2271 ret->name = NULL;
2272 }
2273 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002274
2275 if (xmlHashAddEntry(table, value, ret) < 0) {
2276 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002277 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002278 */
Daniel Veillard76575762002-09-05 14:21:15 +00002279 if (ctxt != NULL) {
2280 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002281 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002282 }
Owen Taylor3473f882001-02-23 17:55:21 +00002283 xmlFreeID(ret);
2284 return(NULL);
2285 }
2286 return(ret);
2287}
2288
2289/**
2290 * xmlFreeIDTable:
2291 * @table: An id table
2292 *
2293 * Deallocate the memory used by an ID hash table.
2294 */
2295void
2296xmlFreeIDTable(xmlIDTablePtr table) {
2297 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2298}
2299
2300/**
2301 * xmlIsID:
2302 * @doc: the document
2303 * @elem: the element carrying the attribute
2304 * @attr: the attribute
2305 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002306 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002307 * then this is done if DTD loading has been requested. In the case
2308 * of HTML documents parsed with the HTML parser, then ID detection is
2309 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002310 *
2311 * Returns 0 or 1 depending on the lookup result
2312 */
2313int
2314xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2315 if (doc == NULL) return(0);
2316 if (attr == NULL) return(0);
2317 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2318 return(0);
2319 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2320 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2321 (xmlStrEqual(BAD_CAST "name", attr->name)))
2322 return(1);
2323 return(0);
2324 } else {
2325 xmlAttributePtr attrDecl;
2326
2327 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002328 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2329 /*
2330 * TODO: this sucks ... recomputing this every time is stupid
2331 */
2332 int len = xmlStrlen(elem->name) + xmlStrlen(elem->ns->prefix) + 2;
2333 xmlChar *fullname;
2334
2335 fullname = xmlMalloc(len);
2336 if (fullname == NULL)
2337 return(0);
2338 snprintf((char *) fullname, len, "%s:%s", (char *) elem->ns->prefix,
2339 (char *) elem->name);
2340 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2341 attr->name);
2342 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2343 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2344 attr->name);
2345 xmlFree(fullname);
2346 } else {
2347 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2348 attr->name);
2349 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2350 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2351 attr->name);
2352 }
Owen Taylor3473f882001-02-23 17:55:21 +00002353
2354 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2355 return(1);
2356 }
2357 return(0);
2358}
2359
2360/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002361 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002362 * @doc: the document
2363 * @attr: the attribute
2364 *
2365 * Remove the given attribute from the ID table maintained internally.
2366 *
2367 * Returns -1 if the lookup failed and 0 otherwise
2368 */
2369int
2370xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2371 xmlAttrPtr cur;
2372 xmlIDTablePtr table;
2373 xmlChar *ID;
2374
2375 if (doc == NULL) return(-1);
2376 if (attr == NULL) return(-1);
2377 table = (xmlIDTablePtr) doc->ids;
2378 if (table == NULL)
2379 return(-1);
2380
2381 if (attr == NULL)
2382 return(-1);
2383 ID = xmlNodeListGetString(doc, attr->children, 1);
2384 if (ID == NULL)
2385 return(-1);
2386 cur = xmlHashLookup(table, ID);
2387 if (cur != attr) {
2388 xmlFree(ID);
2389 return(-1);
2390 }
2391 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2392 xmlFree(ID);
2393 return(0);
2394}
2395
2396/**
2397 * xmlGetID:
2398 * @doc: pointer to the document
2399 * @ID: the ID value
2400 *
2401 * Search the attribute declaring the given ID
2402 *
2403 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2404 */
2405xmlAttrPtr
2406xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2407 xmlIDTablePtr table;
2408 xmlIDPtr id;
2409
2410 if (doc == NULL) {
2411 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2412 return(NULL);
2413 }
2414
2415 if (ID == NULL) {
2416 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2417 return(NULL);
2418 }
2419
2420 table = (xmlIDTablePtr) doc->ids;
2421 if (table == NULL)
2422 return(NULL);
2423
2424 id = xmlHashLookup(table, ID);
2425 if (id == NULL)
2426 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002427 if (id->attr == NULL) {
2428 /*
2429 * We are operating on a stream, return a well known reference
2430 * since the attribute node doesn't exist anymore
2431 */
2432 return((xmlAttrPtr) doc);
2433 }
Owen Taylor3473f882001-02-23 17:55:21 +00002434 return(id->attr);
2435}
2436
2437/************************************************************************
2438 * *
2439 * Refs *
2440 * *
2441 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002442typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002443{
2444 xmlListPtr l;
2445 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002446} xmlRemoveMemo;
2447
2448typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2449
2450typedef struct xmlValidateMemo_t
2451{
2452 xmlValidCtxtPtr ctxt;
2453 const xmlChar *name;
2454} xmlValidateMemo;
2455
2456typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002457
2458/**
2459 * xmlCreateRefTable:
2460 *
2461 * create and initialize an empty ref hash table.
2462 *
2463 * Returns the xmlRefTablePtr just created or NULL in case
2464 * of error.
2465 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002466static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002467xmlCreateRefTable(void) {
2468 return(xmlHashCreate(0));
2469}
2470
2471/**
2472 * xmlFreeRef:
2473 * @lk: A list link
2474 *
2475 * Deallocate the memory used by a ref definition
2476 */
2477static void
2478xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002479 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2480 if (ref == NULL) return;
2481 if (ref->value != NULL)
2482 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002483 if (ref->name != NULL)
2484 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002485 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002486}
2487
2488/**
2489 * xmlFreeRefList:
2490 * @list_ref: A list of references.
2491 *
2492 * Deallocate the memory used by a list of references
2493 */
2494static void
2495xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002496 if (list_ref == NULL) return;
2497 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002498}
2499
2500/**
2501 * xmlWalkRemoveRef:
2502 * @data: Contents of current link
2503 * @user: Value supplied by the user
2504 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002505 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002506 */
2507static int
2508xmlWalkRemoveRef(const void *data, const void *user)
2509{
Daniel Veillard37721922001-05-04 15:21:12 +00002510 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2511 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2512 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002513
Daniel Veillard37721922001-05-04 15:21:12 +00002514 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2515 xmlListRemoveFirst(ref_list, (void *)data);
2516 return 0;
2517 }
2518 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002519}
2520
2521/**
2522 * xmlAddRef:
2523 * @ctxt: the validation context
2524 * @doc: pointer to the document
2525 * @value: the value name
2526 * @attr: the attribute holding the Ref
2527 *
2528 * Register a new ref declaration
2529 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002530 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002531 */
2532xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002533xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002534 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002535 xmlRefPtr ret;
2536 xmlRefTablePtr table;
2537 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002538
Daniel Veillard37721922001-05-04 15:21:12 +00002539 if (doc == NULL) {
2540 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002541 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002542 return(NULL);
2543 }
2544 if (value == NULL) {
2545 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002546 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002547 return(NULL);
2548 }
2549 if (attr == NULL) {
2550 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002551 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002552 return(NULL);
2553 }
Owen Taylor3473f882001-02-23 17:55:21 +00002554
Daniel Veillard37721922001-05-04 15:21:12 +00002555 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002556 * Create the Ref table if needed.
2557 */
Daniel Veillard37721922001-05-04 15:21:12 +00002558 table = (xmlRefTablePtr) doc->refs;
2559 if (table == NULL)
2560 doc->refs = table = xmlCreateRefTable();
2561 if (table == NULL) {
2562 xmlGenericError(xmlGenericErrorContext,
2563 "xmlAddRef: Table creation failed!\n");
2564 return(NULL);
2565 }
Owen Taylor3473f882001-02-23 17:55:21 +00002566
Daniel Veillard37721922001-05-04 15:21:12 +00002567 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2568 if (ret == NULL) {
2569 xmlGenericError(xmlGenericErrorContext,
2570 "xmlAddRef: out of memory\n");
2571 return(NULL);
2572 }
Owen Taylor3473f882001-02-23 17:55:21 +00002573
Daniel Veillard37721922001-05-04 15:21:12 +00002574 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002575 * fill the structure.
2576 */
Daniel Veillard37721922001-05-04 15:21:12 +00002577 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002578 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2579 /*
2580 * Operating in streaming mode, attr is gonna disapear
2581 */
2582 ret->name = xmlStrdup(attr->name);
2583 ret->attr = NULL;
2584 } else {
2585 ret->name = NULL;
2586 ret->attr = attr;
2587 }
2588 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002589
Daniel Veillard37721922001-05-04 15:21:12 +00002590 /* To add a reference :-
2591 * References are maintained as a list of references,
2592 * Lookup the entry, if no entry create new nodelist
2593 * Add the owning node to the NodeList
2594 * Return the ref
2595 */
Owen Taylor3473f882001-02-23 17:55:21 +00002596
Daniel Veillard37721922001-05-04 15:21:12 +00002597 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2598 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2599 xmlGenericError(xmlGenericErrorContext,
2600 "xmlAddRef: Reference list creation failed!\n");
2601 return(NULL);
2602 }
2603 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2604 xmlListDelete(ref_list);
2605 xmlGenericError(xmlGenericErrorContext,
2606 "xmlAddRef: Reference list insertion failed!\n");
2607 return(NULL);
2608 }
2609 }
2610 xmlListInsert(ref_list, ret);
2611 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002612}
2613
2614/**
2615 * xmlFreeRefTable:
2616 * @table: An ref table
2617 *
2618 * Deallocate the memory used by an Ref hash table.
2619 */
2620void
2621xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002622 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002623}
2624
2625/**
2626 * xmlIsRef:
2627 * @doc: the document
2628 * @elem: the element carrying the attribute
2629 * @attr: the attribute
2630 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002631 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002632 * then this is simple, otherwise we use an heuristic: name Ref (upper
2633 * or lowercase).
2634 *
2635 * Returns 0 or 1 depending on the lookup result
2636 */
2637int
2638xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002639 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2640 return(0);
2641 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2642 /* TODO @@@ */
2643 return(0);
2644 } else {
2645 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002646
Daniel Veillard37721922001-05-04 15:21:12 +00002647 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2648 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2649 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2650 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002651
Daniel Veillard37721922001-05-04 15:21:12 +00002652 if ((attrDecl != NULL) &&
2653 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2654 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2655 return(1);
2656 }
2657 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002658}
2659
2660/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002661 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002662 * @doc: the document
2663 * @attr: the attribute
2664 *
2665 * Remove the given attribute from the Ref table maintained internally.
2666 *
2667 * Returns -1 if the lookup failed and 0 otherwise
2668 */
2669int
2670xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002671 xmlListPtr ref_list;
2672 xmlRefTablePtr table;
2673 xmlChar *ID;
2674 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002675
Daniel Veillard37721922001-05-04 15:21:12 +00002676 if (doc == NULL) return(-1);
2677 if (attr == NULL) return(-1);
2678 table = (xmlRefTablePtr) doc->refs;
2679 if (table == NULL)
2680 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002681
Daniel Veillard37721922001-05-04 15:21:12 +00002682 if (attr == NULL)
2683 return(-1);
2684 ID = xmlNodeListGetString(doc, attr->children, 1);
2685 if (ID == NULL)
2686 return(-1);
2687 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002688
Daniel Veillard37721922001-05-04 15:21:12 +00002689 if(ref_list == NULL) {
2690 xmlFree(ID);
2691 return (-1);
2692 }
2693 /* At this point, ref_list refers to a list of references which
2694 * have the same key as the supplied attr. Our list of references
2695 * is ordered by reference address and we don't have that information
2696 * here to use when removing. We'll have to walk the list and
2697 * check for a matching attribute, when we find one stop the walk
2698 * and remove the entry.
2699 * The list is ordered by reference, so that means we don't have the
2700 * key. Passing the list and the reference to the walker means we
2701 * will have enough data to be able to remove the entry.
2702 */
2703 target.l = ref_list;
2704 target.ap = attr;
2705
2706 /* Remove the supplied attr from our list */
2707 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002708
Daniel Veillard37721922001-05-04 15:21:12 +00002709 /*If the list is empty then remove the list entry in the hash */
2710 if (xmlListEmpty(ref_list))
2711 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2712 xmlFreeRefList);
2713 xmlFree(ID);
2714 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002715}
2716
2717/**
2718 * xmlGetRefs:
2719 * @doc: pointer to the document
2720 * @ID: the ID value
2721 *
2722 * Find the set of references for the supplied ID.
2723 *
2724 * Returns NULL if not found, otherwise node set for the ID.
2725 */
2726xmlListPtr
2727xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002728 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002729
Daniel Veillard37721922001-05-04 15:21:12 +00002730 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002731 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002732 return(NULL);
2733 }
Owen Taylor3473f882001-02-23 17:55:21 +00002734
Daniel Veillard37721922001-05-04 15:21:12 +00002735 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002736 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002737 return(NULL);
2738 }
Owen Taylor3473f882001-02-23 17:55:21 +00002739
Daniel Veillard37721922001-05-04 15:21:12 +00002740 table = (xmlRefTablePtr) doc->refs;
2741 if (table == NULL)
2742 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002743
Daniel Veillard37721922001-05-04 15:21:12 +00002744 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002745}
2746
2747/************************************************************************
2748 * *
2749 * Routines for validity checking *
2750 * *
2751 ************************************************************************/
2752
2753/**
2754 * xmlGetDtdElementDesc:
2755 * @dtd: a pointer to the DtD to search
2756 * @name: the element name
2757 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002758 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002759 *
2760 * returns the xmlElementPtr if found or NULL
2761 */
2762
2763xmlElementPtr
2764xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2765 xmlElementTablePtr table;
2766 xmlElementPtr cur;
2767 xmlChar *uqname = NULL, *prefix = NULL;
2768
2769 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002770 if (dtd->elements == NULL)
2771 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002772 table = (xmlElementTablePtr) dtd->elements;
2773
2774 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002775 if (uqname != NULL)
2776 name = uqname;
2777 cur = xmlHashLookup2(table, name, prefix);
2778 if (prefix != NULL) xmlFree(prefix);
2779 if (uqname != NULL) xmlFree(uqname);
2780 return(cur);
2781}
2782/**
2783 * xmlGetDtdElementDesc2:
2784 * @dtd: a pointer to the DtD to search
2785 * @name: the element name
2786 * @create: create an empty description if not found
2787 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002788 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002789 *
2790 * returns the xmlElementPtr if found or NULL
2791 */
2792
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002793static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002794xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2795 xmlElementTablePtr table;
2796 xmlElementPtr cur;
2797 xmlChar *uqname = NULL, *prefix = NULL;
2798
2799 if (dtd == NULL) return(NULL);
2800 if (dtd->elements == NULL) {
2801 if (!create)
2802 return(NULL);
2803 /*
2804 * Create the Element table if needed.
2805 */
2806 table = (xmlElementTablePtr) dtd->elements;
2807 if (table == NULL) {
2808 table = xmlCreateElementTable();
2809 dtd->elements = (void *) table;
2810 }
2811 if (table == NULL) {
2812 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002813 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002814 return(NULL);
2815 }
2816 }
2817 table = (xmlElementTablePtr) dtd->elements;
2818
2819 uqname = xmlSplitQName2(name, &prefix);
2820 if (uqname != NULL)
2821 name = uqname;
2822 cur = xmlHashLookup2(table, name, prefix);
2823 if ((cur == NULL) && (create)) {
2824 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2825 if (cur == NULL) {
2826 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002827 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002828 return(NULL);
2829 }
2830 memset(cur, 0, sizeof(xmlElement));
2831 cur->type = XML_ELEMENT_DECL;
2832
2833 /*
2834 * fill the structure.
2835 */
2836 cur->name = xmlStrdup(name);
2837 cur->prefix = xmlStrdup(prefix);
2838 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2839
2840 xmlHashAddEntry2(table, name, prefix, cur);
2841 }
2842 if (prefix != NULL) xmlFree(prefix);
2843 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002844 return(cur);
2845}
2846
2847/**
2848 * xmlGetDtdQElementDesc:
2849 * @dtd: a pointer to the DtD to search
2850 * @name: the element name
2851 * @prefix: the element namespace prefix
2852 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002853 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002854 *
2855 * returns the xmlElementPtr if found or NULL
2856 */
2857
Daniel Veillard48da9102001-08-07 01:10:10 +00002858xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002859xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2860 const xmlChar *prefix) {
2861 xmlElementTablePtr table;
2862
2863 if (dtd == NULL) return(NULL);
2864 if (dtd->elements == NULL) return(NULL);
2865 table = (xmlElementTablePtr) dtd->elements;
2866
2867 return(xmlHashLookup2(table, name, prefix));
2868}
2869
2870/**
2871 * xmlGetDtdAttrDesc:
2872 * @dtd: a pointer to the DtD to search
2873 * @elem: the element name
2874 * @name: the attribute name
2875 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002876 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002877 * this element.
2878 *
2879 * returns the xmlAttributePtr if found or NULL
2880 */
2881
2882xmlAttributePtr
2883xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2884 xmlAttributeTablePtr table;
2885 xmlAttributePtr cur;
2886 xmlChar *uqname = NULL, *prefix = NULL;
2887
2888 if (dtd == NULL) return(NULL);
2889 if (dtd->attributes == NULL) return(NULL);
2890
2891 table = (xmlAttributeTablePtr) dtd->attributes;
2892 if (table == NULL)
2893 return(NULL);
2894
2895 uqname = xmlSplitQName2(name, &prefix);
2896
2897 if (uqname != NULL) {
2898 cur = xmlHashLookup3(table, uqname, prefix, elem);
2899 if (prefix != NULL) xmlFree(prefix);
2900 if (uqname != NULL) xmlFree(uqname);
2901 } else
2902 cur = xmlHashLookup3(table, name, NULL, elem);
2903 return(cur);
2904}
2905
2906/**
2907 * xmlGetDtdQAttrDesc:
2908 * @dtd: a pointer to the DtD to search
2909 * @elem: the element name
2910 * @name: the attribute name
2911 * @prefix: the attribute namespace prefix
2912 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002913 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002914 * this element.
2915 *
2916 * returns the xmlAttributePtr if found or NULL
2917 */
2918
Daniel Veillard48da9102001-08-07 01:10:10 +00002919xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002920xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2921 const xmlChar *prefix) {
2922 xmlAttributeTablePtr table;
2923
2924 if (dtd == NULL) return(NULL);
2925 if (dtd->attributes == NULL) return(NULL);
2926 table = (xmlAttributeTablePtr) dtd->attributes;
2927
2928 return(xmlHashLookup3(table, name, prefix, elem));
2929}
2930
2931/**
2932 * xmlGetDtdNotationDesc:
2933 * @dtd: a pointer to the DtD to search
2934 * @name: the notation name
2935 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002936 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002937 *
2938 * returns the xmlNotationPtr if found or NULL
2939 */
2940
2941xmlNotationPtr
2942xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2943 xmlNotationTablePtr table;
2944
2945 if (dtd == NULL) return(NULL);
2946 if (dtd->notations == NULL) return(NULL);
2947 table = (xmlNotationTablePtr) dtd->notations;
2948
2949 return(xmlHashLookup(table, name));
2950}
2951
2952/**
2953 * xmlValidateNotationUse:
2954 * @ctxt: the validation context
2955 * @doc: the document
2956 * @notationName: the notation name to check
2957 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002958 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002959 * - [ VC: Notation Declared ]
2960 *
2961 * returns 1 if valid or 0 otherwise
2962 */
2963
2964int
2965xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2966 const xmlChar *notationName) {
2967 xmlNotationPtr notaDecl;
2968 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2969
2970 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2971 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2972 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2973
2974 if (notaDecl == NULL) {
2975 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2976 notationName);
2977 return(0);
2978 }
2979 return(1);
2980}
2981
2982/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002983 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00002984 * @doc: the document
2985 * @name: the element name
2986 *
2987 * Search in the DtDs whether an element accept Mixed content (or ANY)
2988 * basically if it is supposed to accept text childs
2989 *
2990 * returns 0 if no, 1 if yes, and -1 if no element description is available
2991 */
2992
2993int
2994xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2995 xmlElementPtr elemDecl;
2996
2997 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2998
2999 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
3000 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3001 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
3002 if (elemDecl == NULL) return(-1);
3003 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003004 case XML_ELEMENT_TYPE_UNDEFINED:
3005 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003006 case XML_ELEMENT_TYPE_ELEMENT:
3007 return(0);
3008 case XML_ELEMENT_TYPE_EMPTY:
3009 /*
3010 * return 1 for EMPTY since we want VC error to pop up
3011 * on <empty> </empty> for example
3012 */
3013 case XML_ELEMENT_TYPE_ANY:
3014 case XML_ELEMENT_TYPE_MIXED:
3015 return(1);
3016 }
3017 return(1);
3018}
3019
3020/**
3021 * xmlValidateNameValue:
3022 * @value: an Name value
3023 *
3024 * Validate that the given value match Name production
3025 *
3026 * returns 1 if valid or 0 otherwise
3027 */
3028
Daniel Veillard9b731d72002-04-14 12:56:08 +00003029int
Owen Taylor3473f882001-02-23 17:55:21 +00003030xmlValidateNameValue(const xmlChar *value) {
3031 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003032 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003033
3034 if (value == NULL) return(0);
3035 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003036 val = xmlStringCurrentChar(NULL, cur, &len);
3037 cur += len;
3038 if (!IS_LETTER(val) && (val != '_') &&
3039 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003040 return(0);
3041 }
3042
Daniel Veillardd8224e02002-01-13 15:43:22 +00003043 val = xmlStringCurrentChar(NULL, cur, &len);
3044 cur += len;
3045 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3046 (val == '.') || (val == '-') ||
3047 (val == '_') || (val == ':') ||
3048 (IS_COMBINING(val)) ||
3049 (IS_EXTENDER(val))) {
3050 val = xmlStringCurrentChar(NULL, cur, &len);
3051 cur += len;
3052 }
Owen Taylor3473f882001-02-23 17:55:21 +00003053
Daniel Veillardd8224e02002-01-13 15:43:22 +00003054 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003055
3056 return(1);
3057}
3058
3059/**
3060 * xmlValidateNamesValue:
3061 * @value: an Names value
3062 *
3063 * Validate that the given value match Names production
3064 *
3065 * returns 1 if valid or 0 otherwise
3066 */
3067
Daniel Veillard9b731d72002-04-14 12:56:08 +00003068int
Owen Taylor3473f882001-02-23 17:55:21 +00003069xmlValidateNamesValue(const xmlChar *value) {
3070 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003071 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003072
3073 if (value == NULL) return(0);
3074 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003075 val = xmlStringCurrentChar(NULL, cur, &len);
3076 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003077
Daniel Veillardd8224e02002-01-13 15:43:22 +00003078 if (!IS_LETTER(val) && (val != '_') &&
3079 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003080 return(0);
3081 }
3082
Daniel Veillardd8224e02002-01-13 15:43:22 +00003083 val = xmlStringCurrentChar(NULL, cur, &len);
3084 cur += len;
3085 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3086 (val == '.') || (val == '-') ||
3087 (val == '_') || (val == ':') ||
3088 (IS_COMBINING(val)) ||
3089 (IS_EXTENDER(val))) {
3090 val = xmlStringCurrentChar(NULL, cur, &len);
3091 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003092 }
3093
Daniel Veillardd8224e02002-01-13 15:43:22 +00003094 while (IS_BLANK(val)) {
3095 while (IS_BLANK(val)) {
3096 val = xmlStringCurrentChar(NULL, cur, &len);
3097 cur += len;
3098 }
3099
3100 if (!IS_LETTER(val) && (val != '_') &&
3101 (val != ':')) {
3102 return(0);
3103 }
3104 val = xmlStringCurrentChar(NULL, cur, &len);
3105 cur += len;
3106
3107 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3108 (val == '.') || (val == '-') ||
3109 (val == '_') || (val == ':') ||
3110 (IS_COMBINING(val)) ||
3111 (IS_EXTENDER(val))) {
3112 val = xmlStringCurrentChar(NULL, cur, &len);
3113 cur += len;
3114 }
3115 }
3116
3117 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003118
3119 return(1);
3120}
3121
3122/**
3123 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003124 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003125 *
3126 * Validate that the given value match Nmtoken production
3127 *
3128 * [ VC: Name Token ]
3129 *
3130 * returns 1 if valid or 0 otherwise
3131 */
3132
Daniel Veillard9b731d72002-04-14 12:56:08 +00003133int
Owen Taylor3473f882001-02-23 17:55:21 +00003134xmlValidateNmtokenValue(const xmlChar *value) {
3135 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003136 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003137
3138 if (value == NULL) return(0);
3139 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003140 val = xmlStringCurrentChar(NULL, cur, &len);
3141 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003142
Daniel Veillardd8224e02002-01-13 15:43:22 +00003143 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3144 (val != '.') && (val != '-') &&
3145 (val != '_') && (val != ':') &&
3146 (!IS_COMBINING(val)) &&
3147 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003148 return(0);
3149
Daniel Veillardd8224e02002-01-13 15:43:22 +00003150 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3151 (val == '.') || (val == '-') ||
3152 (val == '_') || (val == ':') ||
3153 (IS_COMBINING(val)) ||
3154 (IS_EXTENDER(val))) {
3155 val = xmlStringCurrentChar(NULL, cur, &len);
3156 cur += len;
3157 }
Owen Taylor3473f882001-02-23 17:55:21 +00003158
Daniel Veillardd8224e02002-01-13 15:43:22 +00003159 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003160
3161 return(1);
3162}
3163
3164/**
3165 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003166 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003167 *
3168 * Validate that the given value match Nmtokens production
3169 *
3170 * [ VC: Name Token ]
3171 *
3172 * returns 1 if valid or 0 otherwise
3173 */
3174
Daniel Veillard9b731d72002-04-14 12:56:08 +00003175int
Owen Taylor3473f882001-02-23 17:55:21 +00003176xmlValidateNmtokensValue(const xmlChar *value) {
3177 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003178 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003179
3180 if (value == NULL) return(0);
3181 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003182 val = xmlStringCurrentChar(NULL, cur, &len);
3183 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003184
Daniel Veillardd8224e02002-01-13 15:43:22 +00003185 while (IS_BLANK(val)) {
3186 val = xmlStringCurrentChar(NULL, cur, &len);
3187 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003188 }
3189
Daniel Veillardd8224e02002-01-13 15:43:22 +00003190 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3191 (val != '.') && (val != '-') &&
3192 (val != '_') && (val != ':') &&
3193 (!IS_COMBINING(val)) &&
3194 (!IS_EXTENDER(val)))
3195 return(0);
3196
3197 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3198 (val == '.') || (val == '-') ||
3199 (val == '_') || (val == ':') ||
3200 (IS_COMBINING(val)) ||
3201 (IS_EXTENDER(val))) {
3202 val = xmlStringCurrentChar(NULL, cur, &len);
3203 cur += len;
3204 }
3205
3206 while (IS_BLANK(val)) {
3207 while (IS_BLANK(val)) {
3208 val = xmlStringCurrentChar(NULL, cur, &len);
3209 cur += len;
3210 }
3211 if (val == 0) return(1);
3212
3213 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3214 (val != '.') && (val != '-') &&
3215 (val != '_') && (val != ':') &&
3216 (!IS_COMBINING(val)) &&
3217 (!IS_EXTENDER(val)))
3218 return(0);
3219
3220 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3221 (val == '.') || (val == '-') ||
3222 (val == '_') || (val == ':') ||
3223 (IS_COMBINING(val)) ||
3224 (IS_EXTENDER(val))) {
3225 val = xmlStringCurrentChar(NULL, cur, &len);
3226 cur += len;
3227 }
3228 }
3229
3230 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003231
3232 return(1);
3233}
3234
3235/**
3236 * xmlValidateNotationDecl:
3237 * @ctxt: the validation context
3238 * @doc: a document instance
3239 * @nota: a notation definition
3240 *
3241 * Try to validate a single notation definition
3242 * basically it does the following checks as described by the
3243 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003244 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003245 * But this function get called anyway ...
3246 *
3247 * returns 1 if valid or 0 otherwise
3248 */
3249
3250int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003251xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3252 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003253 int ret = 1;
3254
3255 return(ret);
3256}
3257
3258/**
3259 * xmlValidateAttributeValue:
3260 * @type: an attribute type
3261 * @value: an attribute value
3262 *
3263 * Validate that the given attribute value match the proper production
3264 *
3265 * [ VC: ID ]
3266 * Values of type ID must match the Name production....
3267 *
3268 * [ VC: IDREF ]
3269 * Values of type IDREF must match the Name production, and values
3270 * of type IDREFS must match Names ...
3271 *
3272 * [ VC: Entity Name ]
3273 * Values of type ENTITY must match the Name production, values
3274 * of type ENTITIES must match Names ...
3275 *
3276 * [ VC: Name Token ]
3277 * Values of type NMTOKEN must match the Nmtoken production; values
3278 * of type NMTOKENS must match Nmtokens.
3279 *
3280 * returns 1 if valid or 0 otherwise
3281 */
3282
3283int
3284xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3285 switch (type) {
3286 case XML_ATTRIBUTE_ENTITIES:
3287 case XML_ATTRIBUTE_IDREFS:
3288 return(xmlValidateNamesValue(value));
3289 case XML_ATTRIBUTE_ENTITY:
3290 case XML_ATTRIBUTE_IDREF:
3291 case XML_ATTRIBUTE_ID:
3292 case XML_ATTRIBUTE_NOTATION:
3293 return(xmlValidateNameValue(value));
3294 case XML_ATTRIBUTE_NMTOKENS:
3295 case XML_ATTRIBUTE_ENUMERATION:
3296 return(xmlValidateNmtokensValue(value));
3297 case XML_ATTRIBUTE_NMTOKEN:
3298 return(xmlValidateNmtokenValue(value));
3299 case XML_ATTRIBUTE_CDATA:
3300 break;
3301 }
3302 return(1);
3303}
3304
3305/**
3306 * xmlValidateAttributeValue2:
3307 * @ctxt: the validation context
3308 * @doc: the document
3309 * @name: the attribute name (used for error reporting only)
3310 * @type: the attribute type
3311 * @value: the attribute value
3312 *
3313 * Validate that the given attribute value match a given type.
3314 * This typically cannot be done before having finished parsing
3315 * the subsets.
3316 *
3317 * [ VC: IDREF ]
3318 * Values of type IDREF must match one of the declared IDs
3319 * Values of type IDREFS must match a sequence of the declared IDs
3320 * each Name must match the value of an ID attribute on some element
3321 * in the XML document; i.e. IDREF values must match the value of
3322 * some ID attribute
3323 *
3324 * [ VC: Entity Name ]
3325 * Values of type ENTITY must match one declared entity
3326 * Values of type ENTITIES must match a sequence of declared entities
3327 *
3328 * [ VC: Notation Attributes ]
3329 * all notation names in the declaration must be declared.
3330 *
3331 * returns 1 if valid or 0 otherwise
3332 */
3333
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003334static int
Owen Taylor3473f882001-02-23 17:55:21 +00003335xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3336 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3337 int ret = 1;
3338 switch (type) {
3339 case XML_ATTRIBUTE_IDREFS:
3340 case XML_ATTRIBUTE_IDREF:
3341 case XML_ATTRIBUTE_ID:
3342 case XML_ATTRIBUTE_NMTOKENS:
3343 case XML_ATTRIBUTE_ENUMERATION:
3344 case XML_ATTRIBUTE_NMTOKEN:
3345 case XML_ATTRIBUTE_CDATA:
3346 break;
3347 case XML_ATTRIBUTE_ENTITY: {
3348 xmlEntityPtr ent;
3349
3350 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003351 if ((ent == NULL) && (doc->standalone == 1)) {
3352 doc->standalone = 0;
3353 ent = xmlGetDocEntity(doc, value);
3354 if (ent != NULL) {
3355 VERROR(ctxt->userData,
3356"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3357 name, value);
3358 /* WAIT to get answer from the Core WG on this
3359 ret = 0;
3360 */
3361 }
3362 }
Owen Taylor3473f882001-02-23 17:55:21 +00003363 if (ent == NULL) {
3364 VERROR(ctxt->userData,
3365 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3366 name, value);
3367 ret = 0;
3368 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3369 VERROR(ctxt->userData,
3370 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3371 name, value);
3372 ret = 0;
3373 }
3374 break;
3375 }
3376 case XML_ATTRIBUTE_ENTITIES: {
3377 xmlChar *dup, *nam = NULL, *cur, save;
3378 xmlEntityPtr ent;
3379
3380 dup = xmlStrdup(value);
3381 if (dup == NULL)
3382 return(0);
3383 cur = dup;
3384 while (*cur != 0) {
3385 nam = cur;
3386 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3387 save = *cur;
3388 *cur = 0;
3389 ent = xmlGetDocEntity(doc, nam);
3390 if (ent == NULL) {
3391 VERROR(ctxt->userData,
3392 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3393 name, nam);
3394 ret = 0;
3395 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3396 VERROR(ctxt->userData,
3397 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3398 name, nam);
3399 ret = 0;
3400 }
3401 if (save == 0)
3402 break;
3403 *cur = save;
3404 while (IS_BLANK(*cur)) cur++;
3405 }
3406 xmlFree(dup);
3407 break;
3408 }
3409 case XML_ATTRIBUTE_NOTATION: {
3410 xmlNotationPtr nota;
3411
3412 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3413 if ((nota == NULL) && (doc->extSubset != NULL))
3414 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3415
3416 if (nota == NULL) {
3417 VERROR(ctxt->userData,
3418 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3419 name, value);
3420 ret = 0;
3421 }
3422 break;
3423 }
3424 }
3425 return(ret);
3426}
3427
3428/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003429 * xmlValidCtxtNormalizeAttributeValue:
3430 * @ctxt: the validation context
3431 * @doc: the document
3432 * @elem: the parent
3433 * @name: the attribute name
3434 * @value: the attribute value
3435 * @ctxt: the validation context or NULL
3436 *
3437 * Does the validation related extra step of the normalization of attribute
3438 * values:
3439 *
3440 * If the declared value is not CDATA, then the XML processor must further
3441 * process the normalized attribute value by discarding any leading and
3442 * trailing space (#x20) characters, and by replacing sequences of space
3443 * (#x20) characters by single space (#x20) character.
3444 *
3445 * Also check VC: Standalone Document Declaration in P32, and update
3446 * ctxt->valid accordingly
3447 *
3448 * returns a new normalized string if normalization is needed, NULL otherwise
3449 * the caller must free the returned value.
3450 */
3451
3452xmlChar *
3453xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3454 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3455 xmlChar *ret, *dst;
3456 const xmlChar *src;
3457 xmlAttributePtr attrDecl = NULL;
3458 int extsubset = 0;
3459
3460 if (doc == NULL) return(NULL);
3461 if (elem == NULL) return(NULL);
3462 if (name == NULL) return(NULL);
3463 if (value == NULL) return(NULL);
3464
3465 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3466 xmlChar qname[500];
3467 snprintf((char *) qname, sizeof(qname), "%s:%s",
3468 elem->ns->prefix, elem->name);
3469 qname[sizeof(qname) - 1] = 0;
3470 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3471 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3472 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3473 if (attrDecl != NULL)
3474 extsubset = 1;
3475 }
3476 }
3477 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3478 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3479 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3480 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3481 if (attrDecl != NULL)
3482 extsubset = 1;
3483 }
3484
3485 if (attrDecl == NULL)
3486 return(NULL);
3487 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3488 return(NULL);
3489
3490 ret = xmlStrdup(value);
3491 if (ret == NULL)
3492 return(NULL);
3493 src = value;
3494 dst = ret;
3495 while (*src == 0x20) src++;
3496 while (*src != 0) {
3497 if (*src == 0x20) {
3498 while (*src == 0x20) src++;
3499 if (*src != 0)
3500 *dst++ = 0x20;
3501 } else {
3502 *dst++ = *src++;
3503 }
3504 }
3505 *dst = 0;
3506 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3507 VERROR(ctxt->userData,
3508"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3509 name, elem->name);
3510 ctxt->valid = 0;
3511 }
3512 return(ret);
3513}
3514
3515/**
Owen Taylor3473f882001-02-23 17:55:21 +00003516 * xmlValidNormalizeAttributeValue:
3517 * @doc: the document
3518 * @elem: the parent
3519 * @name: the attribute name
3520 * @value: the attribute value
3521 *
3522 * Does the validation related extra step of the normalization of attribute
3523 * values:
3524 *
3525 * If the declared value is not CDATA, then the XML processor must further
3526 * process the normalized attribute value by discarding any leading and
3527 * trailing space (#x20) characters, and by replacing sequences of space
3528 * (#x20) characters by single space (#x20) character.
3529 *
3530 * returns a new normalized string if normalization is needed, NULL otherwise
3531 * the caller must free the returned value.
3532 */
3533
3534xmlChar *
3535xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3536 const xmlChar *name, const xmlChar *value) {
3537 xmlChar *ret, *dst;
3538 const xmlChar *src;
3539 xmlAttributePtr attrDecl = NULL;
3540
3541 if (doc == NULL) return(NULL);
3542 if (elem == NULL) return(NULL);
3543 if (name == NULL) return(NULL);
3544 if (value == NULL) return(NULL);
3545
3546 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3547 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003548 snprintf((char *) qname, sizeof(qname), "%s:%s",
3549 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003550 qname[sizeof(qname) - 1] = 0;
3551 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3552 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3553 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3554 }
3555 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3556 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3557 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3558
3559 if (attrDecl == NULL)
3560 return(NULL);
3561 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3562 return(NULL);
3563
3564 ret = xmlStrdup(value);
3565 if (ret == NULL)
3566 return(NULL);
3567 src = value;
3568 dst = ret;
3569 while (*src == 0x20) src++;
3570 while (*src != 0) {
3571 if (*src == 0x20) {
3572 while (*src == 0x20) src++;
3573 if (*src != 0)
3574 *dst++ = 0x20;
3575 } else {
3576 *dst++ = *src++;
3577 }
3578 }
3579 *dst = 0;
3580 return(ret);
3581}
3582
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003583static void
Owen Taylor3473f882001-02-23 17:55:21 +00003584xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003585 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003586 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3587}
3588
3589/**
3590 * xmlValidateAttributeDecl:
3591 * @ctxt: the validation context
3592 * @doc: a document instance
3593 * @attr: an attribute definition
3594 *
3595 * Try to validate a single attribute definition
3596 * basically it does the following checks as described by the
3597 * XML-1.0 recommendation:
3598 * - [ VC: Attribute Default Legal ]
3599 * - [ VC: Enumeration ]
3600 * - [ VC: ID Attribute Default ]
3601 *
3602 * The ID/IDREF uniqueness and matching are done separately
3603 *
3604 * returns 1 if valid or 0 otherwise
3605 */
3606
3607int
3608xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3609 xmlAttributePtr attr) {
3610 int ret = 1;
3611 int val;
3612 CHECK_DTD;
3613 if(attr == NULL) return(1);
3614
3615 /* Attribute Default Legal */
3616 /* Enumeration */
3617 if (attr->defaultValue != NULL) {
3618 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3619 if (val == 0) {
3620 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003621 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003622 attr->name, attr->elem);
3623 }
3624 ret &= val;
3625 }
3626
3627 /* ID Attribute Default */
3628 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3629 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3630 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3631 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003632 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003633 attr->name, attr->elem);
3634 ret = 0;
3635 }
3636
3637 /* One ID per Element Type */
3638 if (attr->atype == XML_ATTRIBUTE_ID) {
3639 int nbId;
3640
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003641 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003642 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3643 attr->elem);
3644 if (elem != NULL) {
3645 nbId = xmlScanIDAttributeDecl(NULL, elem);
3646 } else {
3647 xmlAttributeTablePtr table;
3648
3649 /*
3650 * The attribute may be declared in the internal subset and the
3651 * element in the external subset.
3652 */
3653 nbId = 0;
3654 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3655 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3656 xmlValidateAttributeIdCallback, &nbId);
3657 }
3658 if (nbId > 1) {
3659 VERROR(ctxt->userData,
3660 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3661 attr->elem, nbId, attr->name);
3662 } else if (doc->extSubset != NULL) {
3663 int extId = 0;
3664 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3665 if (elem != NULL) {
3666 extId = xmlScanIDAttributeDecl(NULL, elem);
3667 }
3668 if (extId > 1) {
3669 VERROR(ctxt->userData,
3670 "Element %s has %d ID attribute defined in the external subset : %s\n",
3671 attr->elem, extId, attr->name);
3672 } else if (extId + nbId > 1) {
3673 VERROR(ctxt->userData,
3674"Element %s has ID attributes defined in the internal and external subset : %s\n",
3675 attr->elem, attr->name);
3676 }
3677 }
3678 }
3679
3680 /* Validity Constraint: Enumeration */
3681 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3682 xmlEnumerationPtr tree = attr->tree;
3683 while (tree != NULL) {
3684 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3685 tree = tree->next;
3686 }
3687 if (tree == NULL) {
3688 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003689"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003690 attr->defaultValue, attr->name, attr->elem);
3691 ret = 0;
3692 }
3693 }
3694
3695 return(ret);
3696}
3697
3698/**
3699 * xmlValidateElementDecl:
3700 * @ctxt: the validation context
3701 * @doc: a document instance
3702 * @elem: an element definition
3703 *
3704 * Try to validate a single element definition
3705 * basically it does the following checks as described by the
3706 * XML-1.0 recommendation:
3707 * - [ VC: One ID per Element Type ]
3708 * - [ VC: No Duplicate Types ]
3709 * - [ VC: Unique Element Type Declaration ]
3710 *
3711 * returns 1 if valid or 0 otherwise
3712 */
3713
3714int
3715xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3716 xmlElementPtr elem) {
3717 int ret = 1;
3718 xmlElementPtr tst;
3719
3720 CHECK_DTD;
3721
3722 if (elem == NULL) return(1);
3723
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003724#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003725#ifdef LIBXML_REGEXP_ENABLED
3726 /* Build the regexp associated to the content model */
3727 ret = xmlValidBuildContentModel(ctxt, elem);
3728#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003729#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003730
Owen Taylor3473f882001-02-23 17:55:21 +00003731 /* No Duplicate Types */
3732 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3733 xmlElementContentPtr cur, next;
3734 const xmlChar *name;
3735
3736 cur = elem->content;
3737 while (cur != NULL) {
3738 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3739 if (cur->c1 == NULL) break;
3740 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3741 name = cur->c1->name;
3742 next = cur->c2;
3743 while (next != NULL) {
3744 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3745 if (xmlStrEqual(next->name, name)) {
3746 VERROR(ctxt->userData,
3747 "Definition of %s has duplicate references of %s\n",
3748 elem->name, name);
3749 ret = 0;
3750 }
3751 break;
3752 }
3753 if (next->c1 == NULL) break;
3754 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3755 if (xmlStrEqual(next->c1->name, name)) {
3756 VERROR(ctxt->userData,
3757 "Definition of %s has duplicate references of %s\n",
3758 elem->name, name);
3759 ret = 0;
3760 }
3761 next = next->c2;
3762 }
3763 }
3764 cur = cur->c2;
3765 }
3766 }
3767
3768 /* VC: Unique Element Type Declaration */
3769 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003770 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003771 ((tst->prefix == elem->prefix) ||
3772 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003773 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003774 VERROR(ctxt->userData, "Redefinition of element %s\n",
3775 elem->name);
3776 ret = 0;
3777 }
3778 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003779 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003780 ((tst->prefix == elem->prefix) ||
3781 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003782 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003783 VERROR(ctxt->userData, "Redefinition of element %s\n",
3784 elem->name);
3785 ret = 0;
3786 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003787 /* One ID per Element Type
3788 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003789 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3790 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003791 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003792 return(ret);
3793}
3794
3795/**
3796 * xmlValidateOneAttribute:
3797 * @ctxt: the validation context
3798 * @doc: a document instance
3799 * @elem: an element instance
3800 * @attr: an attribute instance
3801 * @value: the attribute value (without entities processing)
3802 *
3803 * Try to validate a single attribute for an element
3804 * basically it does the following checks as described by the
3805 * XML-1.0 recommendation:
3806 * - [ VC: Attribute Value Type ]
3807 * - [ VC: Fixed Attribute Default ]
3808 * - [ VC: Entity Name ]
3809 * - [ VC: Name Token ]
3810 * - [ VC: ID ]
3811 * - [ VC: IDREF ]
3812 * - [ VC: Entity Name ]
3813 * - [ VC: Notation Attributes ]
3814 *
3815 * The ID/IDREF uniqueness and matching are done separately
3816 *
3817 * returns 1 if valid or 0 otherwise
3818 */
3819
3820int
3821xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3822 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3823 /* xmlElementPtr elemDecl; */
3824 xmlAttributePtr attrDecl = NULL;
3825 int val;
3826 int ret = 1;
3827
3828 CHECK_DTD;
3829 if ((elem == NULL) || (elem->name == NULL)) return(0);
3830 if ((attr == NULL) || (attr->name == NULL)) return(0);
3831
3832 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3833 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003834 snprintf((char *) qname, sizeof(qname), "%s:%s",
3835 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003836 qname[sizeof(qname) - 1] = 0;
3837 if (attr->ns != NULL) {
3838 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3839 attr->name, attr->ns->prefix);
3840 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3841 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3842 attr->name, attr->ns->prefix);
3843 } else {
3844 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3845 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3846 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3847 qname, attr->name);
3848 }
3849 }
3850 if (attrDecl == NULL) {
3851 if (attr->ns != NULL) {
3852 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3853 attr->name, attr->ns->prefix);
3854 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3855 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3856 attr->name, attr->ns->prefix);
3857 } else {
3858 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3859 elem->name, attr->name);
3860 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3861 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3862 elem->name, attr->name);
3863 }
3864 }
3865
3866
3867 /* Validity Constraint: Attribute Value Type */
3868 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003869 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003870 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003871 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003872 attr->name, elem->name);
3873 return(0);
3874 }
3875 attr->atype = attrDecl->atype;
3876
3877 val = xmlValidateAttributeValue(attrDecl->atype, value);
3878 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003879 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003880 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003881 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003882 attr->name, elem->name);
3883 ret = 0;
3884 }
3885
3886 /* Validity constraint: Fixed Attribute Default */
3887 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3888 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003889 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003890 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003891 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003892 attr->name, elem->name, attrDecl->defaultValue);
3893 ret = 0;
3894 }
3895 }
3896
3897 /* Validity Constraint: ID uniqueness */
3898 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3899 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3900 ret = 0;
3901 }
3902
3903 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3904 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3905 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3906 ret = 0;
3907 }
3908
3909 /* Validity Constraint: Notation Attributes */
3910 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3911 xmlEnumerationPtr tree = attrDecl->tree;
3912 xmlNotationPtr nota;
3913
3914 /* First check that the given NOTATION was declared */
3915 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3916 if (nota == NULL)
3917 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3918
3919 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003920 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003921 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003922 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003923 value, attr->name, elem->name);
3924 ret = 0;
3925 }
3926
3927 /* Second, verify that it's among the list */
3928 while (tree != NULL) {
3929 if (xmlStrEqual(tree->name, value)) break;
3930 tree = tree->next;
3931 }
3932 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003933 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003934 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003935"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003936 value, attr->name, elem->name);
3937 ret = 0;
3938 }
3939 }
3940
3941 /* Validity Constraint: Enumeration */
3942 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3943 xmlEnumerationPtr tree = attrDecl->tree;
3944 while (tree != NULL) {
3945 if (xmlStrEqual(tree->name, value)) break;
3946 tree = tree->next;
3947 }
3948 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003949 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003950 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003951 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003952 value, attr->name, elem->name);
3953 ret = 0;
3954 }
3955 }
3956
3957 /* Fixed Attribute Default */
3958 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3959 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003960 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003961 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003962 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003963 attr->name, elem->name, attrDecl->defaultValue);
3964 ret = 0;
3965 }
3966
3967 /* Extra check for the attribute value */
3968 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3969 attrDecl->atype, value);
3970
3971 return(ret);
3972}
3973
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003974/**
3975 * xmlValidateOneNamespace:
3976 * @ctxt: the validation context
3977 * @doc: a document instance
3978 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00003979 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003980 * @ns: an namespace declaration instance
3981 * @value: the attribute value (without entities processing)
3982 *
3983 * Try to validate a single namespace declaration for an element
3984 * basically it does the following checks as described by the
3985 * XML-1.0 recommendation:
3986 * - [ VC: Attribute Value Type ]
3987 * - [ VC: Fixed Attribute Default ]
3988 * - [ VC: Entity Name ]
3989 * - [ VC: Name Token ]
3990 * - [ VC: ID ]
3991 * - [ VC: IDREF ]
3992 * - [ VC: Entity Name ]
3993 * - [ VC: Notation Attributes ]
3994 *
3995 * The ID/IDREF uniqueness and matching are done separately
3996 *
3997 * returns 1 if valid or 0 otherwise
3998 */
3999
4000int
4001xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4002xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
4003 /* xmlElementPtr elemDecl; */
4004 xmlAttributePtr attrDecl = NULL;
4005 int val;
4006 int ret = 1;
4007
4008 CHECK_DTD;
4009 if ((elem == NULL) || (elem->name == NULL)) return(0);
4010 if ((ns == NULL) || (ns->href == NULL)) return(0);
4011
4012 if (prefix != NULL) {
4013 xmlChar qname[500];
4014 snprintf((char *) qname, sizeof(qname), "%s:%s",
4015 prefix, elem->name);
4016 qname[sizeof(qname) - 1] = 0;
4017 if (ns->prefix != NULL) {
4018 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
4019 ns->prefix, BAD_CAST "xmlns");
4020 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4021 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
4022 ns->prefix, BAD_CAST "xmlns");
4023 } else {
4024 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname,
4025 BAD_CAST "xmlns");
4026 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4027 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname,
4028 BAD_CAST "xmlns");
4029 }
4030 }
4031 if (attrDecl == NULL) {
4032 if (ns->prefix != NULL) {
4033 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4034 ns->prefix, BAD_CAST "xmlns");
4035 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4036 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4037 ns->prefix, BAD_CAST "xmlns");
4038 } else {
4039 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4040 elem->name, BAD_CAST "xmlns");
4041 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4042 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4043 elem->name, BAD_CAST "xmlns");
4044 }
4045 }
4046
4047
4048 /* Validity Constraint: Attribute Value Type */
4049 if (attrDecl == NULL) {
4050 VECTXT(ctxt, elem);
4051 if (ns->prefix != NULL) {
4052 VERROR(ctxt->userData,
4053 "No declaration for attribute xmlns:%s of element %s\n",
4054 ns->prefix, elem->name);
4055 } else {
4056 VERROR(ctxt->userData,
4057 "No declaration for attribute xmlns of element %s\n",
4058 elem->name);
4059 }
4060 return(0);
4061 }
4062
4063 val = xmlValidateAttributeValue(attrDecl->atype, value);
4064 if (val == 0) {
4065 VECTXT(ctxt, elem);
4066 if (ns->prefix != NULL) {
4067 VERROR(ctxt->userData,
4068 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
4069 ns->prefix, elem->name);
4070 } else {
4071 VERROR(ctxt->userData,
4072 "Syntax of value for attribute xmlns of %s is not valid\n",
4073 elem->name);
4074 }
4075 ret = 0;
4076 }
4077
4078 /* Validity constraint: Fixed Attribute Default */
4079 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4080 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4081 VECTXT(ctxt, elem);
4082 if (ns->prefix != NULL) {
4083 VERROR(ctxt->userData,
4084 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4085 ns->prefix, elem->name, attrDecl->defaultValue);
4086 } else {
4087 VERROR(ctxt->userData,
4088 "Value for attribute xmlns of %s is different from default \"%s\"\n",
4089 elem->name, attrDecl->defaultValue);
4090 }
4091 ret = 0;
4092 }
4093 }
4094
4095 /* Validity Constraint: ID uniqueness */
4096 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4097 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4098 ret = 0;
4099 }
4100
4101 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4102 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4103 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4104 ret = 0;
4105 }
4106
4107 /* Validity Constraint: Notation Attributes */
4108 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4109 xmlEnumerationPtr tree = attrDecl->tree;
4110 xmlNotationPtr nota;
4111
4112 /* First check that the given NOTATION was declared */
4113 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4114 if (nota == NULL)
4115 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4116
4117 if (nota == NULL) {
4118 VECTXT(ctxt, elem);
4119 if (ns->prefix != NULL) {
4120 VERROR(ctxt->userData,
4121 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4122 value, ns->prefix, elem->name);
4123 } else {
4124 VERROR(ctxt->userData,
4125 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
4126 value, elem->name);
4127 }
4128 ret = 0;
4129 }
4130
4131 /* Second, verify that it's among the list */
4132 while (tree != NULL) {
4133 if (xmlStrEqual(tree->name, value)) break;
4134 tree = tree->next;
4135 }
4136 if (tree == NULL) {
4137 VECTXT(ctxt, elem);
4138 if (ns->prefix != NULL) {
4139 VERROR(ctxt->userData,
4140"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4141 value, ns->prefix, elem->name);
4142 } else {
4143 VERROR(ctxt->userData,
4144"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
4145 value, elem->name);
4146 }
4147 ret = 0;
4148 }
4149 }
4150
4151 /* Validity Constraint: Enumeration */
4152 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4153 xmlEnumerationPtr tree = attrDecl->tree;
4154 while (tree != NULL) {
4155 if (xmlStrEqual(tree->name, value)) break;
4156 tree = tree->next;
4157 }
4158 if (tree == NULL) {
4159 VECTXT(ctxt, elem);
4160 if (ns->prefix != NULL) {
4161 VERROR(ctxt->userData,
4162"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4163 value, ns->prefix, elem->name);
4164 } else {
4165 VERROR(ctxt->userData,
4166"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4167 value, elem->name);
4168 }
4169 ret = 0;
4170 }
4171 }
4172
4173 /* Fixed Attribute Default */
4174 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4175 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4176 VECTXT(ctxt, elem);
4177 if (ns->prefix != NULL) {
4178 VERROR(ctxt->userData,
4179 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4180 ns->prefix, elem->name, attrDecl->defaultValue);
4181 } else {
4182 VERROR(ctxt->userData,
4183 "Value for attribute xmlns of %s must be \"%s\"\n",
4184 elem->name, attrDecl->defaultValue);
4185 }
4186 ret = 0;
4187 }
4188
4189 /* Extra check for the attribute value */
4190 if (ns->prefix != NULL) {
4191 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4192 attrDecl->atype, value);
4193 } else {
4194 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4195 attrDecl->atype, value);
4196 }
4197
4198 return(ret);
4199}
4200
Daniel Veillard118aed72002-09-24 14:13:13 +00004201#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004202/**
4203 * xmlValidateSkipIgnorable:
4204 * @ctxt: the validation context
4205 * @child: the child list
4206 *
4207 * Skip ignorable elements w.r.t. the validation process
4208 *
4209 * returns the first element to consider for validation of the content model
4210 */
4211
4212static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004213xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004214 while (child != NULL) {
4215 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004216 /* These things are ignored (skipped) during validation. */
4217 case XML_PI_NODE:
4218 case XML_COMMENT_NODE:
4219 case XML_XINCLUDE_START:
4220 case XML_XINCLUDE_END:
4221 child = child->next;
4222 break;
4223 case XML_TEXT_NODE:
4224 if (xmlIsBlankNode(child))
4225 child = child->next;
4226 else
4227 return(child);
4228 break;
4229 /* keep current node */
4230 default:
4231 return(child);
4232 }
4233 }
4234 return(child);
4235}
4236
4237/**
4238 * xmlValidateElementType:
4239 * @ctxt: the validation context
4240 *
4241 * Try to validate the content model of an element internal function
4242 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004243 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4244 * reference is found and -3 if the validation succeeded but
4245 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004246 */
4247
4248static int
4249xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004250 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004251 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004252
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004253 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004254 if ((NODE == NULL) && (CONT == NULL))
4255 return(1);
4256 if ((NODE == NULL) &&
4257 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4258 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4259 return(1);
4260 }
4261 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004262 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004263 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004264
4265 /*
4266 * We arrive here when more states need to be examined
4267 */
4268cont:
4269
4270 /*
4271 * We just recovered from a rollback generated by a possible
4272 * epsilon transition, go directly to the analysis phase
4273 */
4274 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004275 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004276 DEBUG_VALID_STATE(NODE, CONT)
4277 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004278 goto analyze;
4279 }
4280
4281 DEBUG_VALID_STATE(NODE, CONT)
4282 /*
4283 * we may have to save a backup state here. This is the equivalent
4284 * of handling epsilon transition in NFAs.
4285 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004286 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004287 ((CONT->parent == NULL) ||
4288 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004289 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004290 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004291 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004292 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004293 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4294 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004295 }
4296
4297
4298 /*
4299 * Check first if the content matches
4300 */
4301 switch (CONT->type) {
4302 case XML_ELEMENT_CONTENT_PCDATA:
4303 if (NODE == NULL) {
4304 DEBUG_VALID_MSG("pcdata failed no node");
4305 ret = 0;
4306 break;
4307 }
4308 if (NODE->type == XML_TEXT_NODE) {
4309 DEBUG_VALID_MSG("pcdata found, skip to next");
4310 /*
4311 * go to next element in the content model
4312 * skipping ignorable elems
4313 */
4314 do {
4315 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004316 NODE = xmlValidateSkipIgnorable(NODE);
4317 if ((NODE != NULL) &&
4318 (NODE->type == XML_ENTITY_REF_NODE))
4319 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004320 } while ((NODE != NULL) &&
4321 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004322 (NODE->type != XML_TEXT_NODE) &&
4323 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004324 ret = 1;
4325 break;
4326 } else {
4327 DEBUG_VALID_MSG("pcdata failed");
4328 ret = 0;
4329 break;
4330 }
4331 break;
4332 case XML_ELEMENT_CONTENT_ELEMENT:
4333 if (NODE == NULL) {
4334 DEBUG_VALID_MSG("element failed no node");
4335 ret = 0;
4336 break;
4337 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004338 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4339 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004340 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004341 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4342 ret = (CONT->prefix == NULL);
4343 } else if (CONT->prefix == NULL) {
4344 ret = 0;
4345 } else {
4346 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4347 }
4348 }
4349 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004350 DEBUG_VALID_MSG("element found, skip to next");
4351 /*
4352 * go to next element in the content model
4353 * skipping ignorable elems
4354 */
4355 do {
4356 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004357 NODE = xmlValidateSkipIgnorable(NODE);
4358 if ((NODE != NULL) &&
4359 (NODE->type == XML_ENTITY_REF_NODE))
4360 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004361 } while ((NODE != NULL) &&
4362 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004363 (NODE->type != XML_TEXT_NODE) &&
4364 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004365 } else {
4366 DEBUG_VALID_MSG("element failed");
4367 ret = 0;
4368 break;
4369 }
4370 break;
4371 case XML_ELEMENT_CONTENT_OR:
4372 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004373 * Small optimization.
4374 */
4375 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4376 if ((NODE == NULL) ||
4377 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4378 DEPTH++;
4379 CONT = CONT->c2;
4380 goto cont;
4381 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004382 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4383 ret = (CONT->c1->prefix == NULL);
4384 } else if (CONT->c1->prefix == NULL) {
4385 ret = 0;
4386 } else {
4387 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4388 }
4389 if (ret == 0) {
4390 DEPTH++;
4391 CONT = CONT->c2;
4392 goto cont;
4393 }
Daniel Veillard85349052001-04-20 13:48:21 +00004394 }
4395
4396 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004397 * save the second branch 'or' branch
4398 */
4399 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004400 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4401 OCCURS, ROLLBACK_OR) < 0)
4402 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004403 DEPTH++;
4404 CONT = CONT->c1;
4405 goto cont;
4406 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004407 /*
4408 * Small optimization.
4409 */
4410 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4411 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4412 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4413 if ((NODE == NULL) ||
4414 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4415 DEPTH++;
4416 CONT = CONT->c2;
4417 goto cont;
4418 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004419 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4420 ret = (CONT->c1->prefix == NULL);
4421 } else if (CONT->c1->prefix == NULL) {
4422 ret = 0;
4423 } else {
4424 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4425 }
4426 if (ret == 0) {
4427 DEPTH++;
4428 CONT = CONT->c2;
4429 goto cont;
4430 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004431 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004432 DEPTH++;
4433 CONT = CONT->c1;
4434 goto cont;
4435 }
4436
4437 /*
4438 * At this point handle going up in the tree
4439 */
4440 if (ret == -1) {
4441 DEBUG_VALID_MSG("error found returning");
4442 return(ret);
4443 }
4444analyze:
4445 while (CONT != NULL) {
4446 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004447 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004448 * this level.
4449 */
4450 if (ret == 0) {
4451 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004452 xmlNodePtr cur;
4453
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004454 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004455 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004456 DEBUG_VALID_MSG("Once branch failed, rollback");
4457 if (vstateVPop(ctxt) < 0 ) {
4458 DEBUG_VALID_MSG("exhaustion, failed");
4459 return(0);
4460 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004461 if (cur != ctxt->vstate->node)
4462 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004463 goto cont;
4464 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004465 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004466 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004467 DEBUG_VALID_MSG("Plus branch failed, rollback");
4468 if (vstateVPop(ctxt) < 0 ) {
4469 DEBUG_VALID_MSG("exhaustion, failed");
4470 return(0);
4471 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004472 if (cur != ctxt->vstate->node)
4473 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004474 goto cont;
4475 }
4476 DEBUG_VALID_MSG("Plus branch found");
4477 ret = 1;
4478 break;
4479 case XML_ELEMENT_CONTENT_MULT:
4480#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004481 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004482 DEBUG_VALID_MSG("Mult branch failed");
4483 } else {
4484 DEBUG_VALID_MSG("Mult branch found");
4485 }
4486#endif
4487 ret = 1;
4488 break;
4489 case XML_ELEMENT_CONTENT_OPT:
4490 DEBUG_VALID_MSG("Option branch failed");
4491 ret = 1;
4492 break;
4493 }
4494 } else {
4495 switch (CONT->ocur) {
4496 case XML_ELEMENT_CONTENT_OPT:
4497 DEBUG_VALID_MSG("Option branch succeeded");
4498 ret = 1;
4499 break;
4500 case XML_ELEMENT_CONTENT_ONCE:
4501 DEBUG_VALID_MSG("Once branch succeeded");
4502 ret = 1;
4503 break;
4504 case XML_ELEMENT_CONTENT_PLUS:
4505 if (STATE == ROLLBACK_PARENT) {
4506 DEBUG_VALID_MSG("Plus branch rollback");
4507 ret = 1;
4508 break;
4509 }
4510 if (NODE == NULL) {
4511 DEBUG_VALID_MSG("Plus branch exhausted");
4512 ret = 1;
4513 break;
4514 }
4515 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004516 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004517 goto cont;
4518 case XML_ELEMENT_CONTENT_MULT:
4519 if (STATE == ROLLBACK_PARENT) {
4520 DEBUG_VALID_MSG("Mult branch rollback");
4521 ret = 1;
4522 break;
4523 }
4524 if (NODE == NULL) {
4525 DEBUG_VALID_MSG("Mult branch exhausted");
4526 ret = 1;
4527 break;
4528 }
4529 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004530 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004531 goto cont;
4532 }
4533 }
4534 STATE = 0;
4535
4536 /*
4537 * Then act accordingly at the parent level
4538 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004539 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004540 if (CONT->parent == NULL)
4541 break;
4542
4543 switch (CONT->parent->type) {
4544 case XML_ELEMENT_CONTENT_PCDATA:
4545 DEBUG_VALID_MSG("Error: parent pcdata");
4546 return(-1);
4547 case XML_ELEMENT_CONTENT_ELEMENT:
4548 DEBUG_VALID_MSG("Error: parent element");
4549 return(-1);
4550 case XML_ELEMENT_CONTENT_OR:
4551 if (ret == 1) {
4552 DEBUG_VALID_MSG("Or succeeded");
4553 CONT = CONT->parent;
4554 DEPTH--;
4555 } else {
4556 DEBUG_VALID_MSG("Or failed");
4557 CONT = CONT->parent;
4558 DEPTH--;
4559 }
4560 break;
4561 case XML_ELEMENT_CONTENT_SEQ:
4562 if (ret == 0) {
4563 DEBUG_VALID_MSG("Sequence failed");
4564 CONT = CONT->parent;
4565 DEPTH--;
4566 } else if (CONT == CONT->parent->c1) {
4567 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4568 CONT = CONT->parent->c2;
4569 goto cont;
4570 } else {
4571 DEBUG_VALID_MSG("Sequence succeeded");
4572 CONT = CONT->parent;
4573 DEPTH--;
4574 }
4575 }
4576 }
4577 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004578 xmlNodePtr cur;
4579
4580 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004581 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4582 if (vstateVPop(ctxt) < 0 ) {
4583 DEBUG_VALID_MSG("exhaustion, failed");
4584 return(0);
4585 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004586 if (cur != ctxt->vstate->node)
4587 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004588 goto cont;
4589 }
4590 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004591 xmlNodePtr cur;
4592
4593 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004594 DEBUG_VALID_MSG("Failure, rollback");
4595 if (vstateVPop(ctxt) < 0 ) {
4596 DEBUG_VALID_MSG("exhaustion, failed");
4597 return(0);
4598 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004599 if (cur != ctxt->vstate->node)
4600 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004601 goto cont;
4602 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004603 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004604}
Daniel Veillard23e73572002-09-19 19:56:43 +00004605#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004606
4607/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004608 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004609 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004610 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004611 * @content: An element
4612 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4613 *
4614 * This will dump the list of elements to the buffer
4615 * Intended just for the debug routine
4616 */
4617static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004618xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004619 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004620 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004621
4622 if (node == NULL) return;
4623 if (glob) strcat(buf, "(");
4624 cur = node;
4625 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004626 len = strlen(buf);
4627 if (size - len < 50) {
4628 if ((size - len > 4) && (buf[len - 1] != '.'))
4629 strcat(buf, " ...");
4630 return;
4631 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004632 switch (cur->type) {
4633 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004634 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004635 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004636 if ((size - len > 4) && (buf[len - 1] != '.'))
4637 strcat(buf, " ...");
4638 return;
4639 }
4640 strcat(buf, (char *) cur->ns->prefix);
4641 strcat(buf, ":");
4642 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004643 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004644 if ((size - len > 4) && (buf[len - 1] != '.'))
4645 strcat(buf, " ...");
4646 return;
4647 }
4648 strcat(buf, (char *) cur->name);
4649 if (cur->next != NULL)
4650 strcat(buf, " ");
4651 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004652 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004653 if (xmlIsBlankNode(cur))
4654 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004655 case XML_CDATA_SECTION_NODE:
4656 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004657 strcat(buf, "CDATA");
4658 if (cur->next != NULL)
4659 strcat(buf, " ");
4660 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004661 case XML_ATTRIBUTE_NODE:
4662 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004663#ifdef LIBXML_DOCB_ENABLED
4664 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004665#endif
4666 case XML_HTML_DOCUMENT_NODE:
4667 case XML_DOCUMENT_TYPE_NODE:
4668 case XML_DOCUMENT_FRAG_NODE:
4669 case XML_NOTATION_NODE:
4670 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004671 strcat(buf, "???");
4672 if (cur->next != NULL)
4673 strcat(buf, " ");
4674 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004675 case XML_ENTITY_NODE:
4676 case XML_PI_NODE:
4677 case XML_DTD_NODE:
4678 case XML_COMMENT_NODE:
4679 case XML_ELEMENT_DECL:
4680 case XML_ATTRIBUTE_DECL:
4681 case XML_ENTITY_DECL:
4682 case XML_XINCLUDE_START:
4683 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004684 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004685 }
4686 cur = cur->next;
4687 }
4688 if (glob) strcat(buf, ")");
4689}
4690
4691/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004692 * xmlValidateElementContent:
4693 * @ctxt: the validation context
4694 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004695 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004696 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004697 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004698 *
4699 * Try to validate the content model of an element
4700 *
4701 * returns 1 if valid or 0 if not and -1 in case of error
4702 */
4703
4704static int
4705xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004706 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004707 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004708#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004709 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004710#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004711 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004712 xmlElementContentPtr cont;
4713 const xmlChar *name;
4714
4715 if (elemDecl == NULL)
4716 return(-1);
4717 cont = elemDecl->content;
4718 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004719
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004720#ifdef LIBXML_REGEXP_ENABLED
4721 /* Build the regexp associated to the content model */
4722 if (elemDecl->contModel == NULL)
4723 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4724 if (elemDecl->contModel == NULL) {
4725 ret = -1;
4726 } else {
4727 xmlRegExecCtxtPtr exec;
4728
Daniel Veillardec498e12003-02-05 11:01:50 +00004729 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
4730 return(-1);
4731 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004732 ctxt->nodeMax = 0;
4733 ctxt->nodeNr = 0;
4734 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004735 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4736 if (exec != NULL) {
4737 cur = child;
4738 while (cur != NULL) {
4739 switch (cur->type) {
4740 case XML_ENTITY_REF_NODE:
4741 /*
4742 * Push the current node to be able to roll back
4743 * and process within the entity
4744 */
4745 if ((cur->children != NULL) &&
4746 (cur->children->children != NULL)) {
4747 nodeVPush(ctxt, cur);
4748 cur = cur->children->children;
4749 continue;
4750 }
4751 break;
4752 case XML_TEXT_NODE:
4753 if (xmlIsBlankNode(cur))
4754 break;
4755 ret = 0;
4756 goto fail;
4757 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004758 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004759 ret = 0;
4760 goto fail;
4761 case XML_ELEMENT_NODE:
4762 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
4763 xmlChar *QName;
4764 int len;
4765
4766 len = xmlStrlen(cur->name) +
4767 xmlStrlen(cur->ns->prefix) + 2;
4768 QName = xmlMalloc(len);
4769 if (QName == NULL) {
4770 ret = -1;
4771 goto fail;
4772 }
4773 snprintf((char *) QName, len, "%s:%s",
4774 (char *)cur->ns->prefix,
4775 (char *)cur->name);
4776 ret = xmlRegExecPushString(exec, QName, NULL);
4777 xmlFree(QName);
4778 } else {
4779 ret = xmlRegExecPushString(exec, cur->name, NULL);
4780 }
4781 break;
4782 default:
4783 break;
4784 }
4785 /*
4786 * Switch to next element
4787 */
4788 cur = cur->next;
4789 while (cur == NULL) {
4790 cur = nodeVPop(ctxt);
4791 if (cur == NULL)
4792 break;
4793 cur = cur->next;
4794 }
4795 }
4796 ret = xmlRegExecPushString(exec, NULL, NULL);
4797fail:
4798 xmlRegFreeExecCtxt(exec);
4799 }
4800 }
4801#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004802 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004803 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004804 */
4805 ctxt->vstateMax = 8;
4806 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4807 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4808 if (ctxt->vstateTab == NULL) {
4809 xmlGenericError(xmlGenericErrorContext,
4810 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004811 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004812 }
4813 /*
4814 * The first entry in the stack is reserved to the current state
4815 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004816 ctxt->nodeMax = 0;
4817 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004818 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004819 ctxt->vstate = &ctxt->vstateTab[0];
4820 ctxt->vstateNr = 1;
4821 CONT = cont;
4822 NODE = child;
4823 DEPTH = 0;
4824 OCCURS = 0;
4825 STATE = 0;
4826 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004827 if ((ret == -3) && (warn)) {
4828 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004829 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004830 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004831 /*
4832 * An entities reference appeared at this level.
4833 * Buid a minimal representation of this node content
4834 * sufficient to run the validation process on it
4835 */
4836 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004837 cur = child;
4838 while (cur != NULL) {
4839 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004840 case XML_ENTITY_REF_NODE:
4841 /*
4842 * Push the current node to be able to roll back
4843 * and process within the entity
4844 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004845 if ((cur->children != NULL) &&
4846 (cur->children->children != NULL)) {
4847 nodeVPush(ctxt, cur);
4848 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004849 continue;
4850 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004851 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004852 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004853 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004854 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004855 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004856 case XML_CDATA_SECTION_NODE:
4857 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004858 case XML_ELEMENT_NODE:
4859 /*
4860 * Allocate a new node and minimally fills in
4861 * what's required
4862 */
4863 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4864 if (tmp == NULL) {
4865 xmlGenericError(xmlGenericErrorContext,
4866 "xmlValidateElementContent : malloc failed\n");
4867 xmlFreeNodeList(repl);
4868 ret = -1;
4869 goto done;
4870 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004871 tmp->type = cur->type;
4872 tmp->name = cur->name;
4873 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004874 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004875 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004876 if (repl == NULL)
4877 repl = last = tmp;
4878 else {
4879 last->next = tmp;
4880 last = tmp;
4881 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004882 if (cur->type == XML_CDATA_SECTION_NODE) {
4883 /*
4884 * E59 spaces in CDATA does not match the
4885 * nonterminal S
4886 */
4887 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4888 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004889 break;
4890 default:
4891 break;
4892 }
4893 /*
4894 * Switch to next element
4895 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004896 cur = cur->next;
4897 while (cur == NULL) {
4898 cur = nodeVPop(ctxt);
4899 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004900 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004901 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004902 }
4903 }
4904
4905 /*
4906 * Relaunch the validation
4907 */
4908 ctxt->vstate = &ctxt->vstateTab[0];
4909 ctxt->vstateNr = 1;
4910 CONT = cont;
4911 NODE = repl;
4912 DEPTH = 0;
4913 OCCURS = 0;
4914 STATE = 0;
4915 ret = xmlValidateElementType(ctxt);
4916 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004917#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004918 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004919 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4920 char expr[5000];
4921 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004922
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004923 expr[0] = 0;
4924 xmlSnprintfElementContent(expr, 5000, cont, 1);
4925 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004926#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004927 if (repl != NULL)
4928 xmlSnprintfElements(list, 5000, repl, 1);
4929 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004930#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004931 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004932
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004933 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004934 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004935 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004936 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004937 name, expr, list);
4938 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004939 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004940 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004941 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004942 expr, list);
4943 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004944 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004945 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004946 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004947 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004948 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004949 name);
4950 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004951 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004952 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004953 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004954 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004955 }
4956 ret = 0;
4957 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004958 if (ret == -3)
4959 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004960
Daniel Veillard23e73572002-09-19 19:56:43 +00004961#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004962done:
4963 /*
4964 * Deallocate the copy if done, and free up the validation stack
4965 */
4966 while (repl != NULL) {
4967 tmp = repl->next;
4968 xmlFree(repl);
4969 repl = tmp;
4970 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004971 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004972 if (ctxt->vstateTab != NULL) {
4973 xmlFree(ctxt->vstateTab);
4974 ctxt->vstateTab = NULL;
4975 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004976#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004977 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004978 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004979 if (ctxt->nodeTab != NULL) {
4980 xmlFree(ctxt->nodeTab);
4981 ctxt->nodeTab = NULL;
4982 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004983 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004984
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004985}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004986
Owen Taylor3473f882001-02-23 17:55:21 +00004987/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004988 * xmlValidateCdataElement:
4989 * @ctxt: the validation context
4990 * @doc: a document instance
4991 * @elem: an element instance
4992 *
4993 * Check that an element follows #CDATA
4994 *
4995 * returns 1 if valid or 0 otherwise
4996 */
4997static int
4998xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4999 xmlNodePtr elem) {
5000 int ret = 1;
5001 xmlNodePtr cur, child;
5002
Daniel Veillardceb09b92002-10-04 11:46:37 +00005003 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005004 return(0);
5005
5006 child = elem->children;
5007
5008 cur = child;
5009 while (cur != NULL) {
5010 switch (cur->type) {
5011 case XML_ENTITY_REF_NODE:
5012 /*
5013 * Push the current node to be able to roll back
5014 * and process within the entity
5015 */
5016 if ((cur->children != NULL) &&
5017 (cur->children->children != NULL)) {
5018 nodeVPush(ctxt, cur);
5019 cur = cur->children->children;
5020 continue;
5021 }
5022 break;
5023 case XML_COMMENT_NODE:
5024 case XML_PI_NODE:
5025 case XML_TEXT_NODE:
5026 case XML_CDATA_SECTION_NODE:
5027 break;
5028 default:
5029 ret = 0;
5030 goto done;
5031 }
5032 /*
5033 * Switch to next element
5034 */
5035 cur = cur->next;
5036 while (cur == NULL) {
5037 cur = nodeVPop(ctxt);
5038 if (cur == NULL)
5039 break;
5040 cur = cur->next;
5041 }
5042 }
5043done:
5044 ctxt->nodeMax = 0;
5045 ctxt->nodeNr = 0;
5046 if (ctxt->nodeTab != NULL) {
5047 xmlFree(ctxt->nodeTab);
5048 ctxt->nodeTab = NULL;
5049 }
5050 return(ret);
5051}
5052
5053/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005054 * xmlValidateCheckMixed:
5055 * @ctxt: the validation context
5056 * @cont: the mixed content model
5057 * @qname: the qualified name as appearing in the serialization
5058 *
5059 * Check if the given node is part of the content model.
5060 *
5061 * Returns 1 if yes, 0 if no, -1 in case of error
5062 */
5063static int
5064xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5065 xmlElementContentPtr cont, const xmlChar *qname) {
5066 while (cont != NULL) {
5067 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5068 if (xmlStrEqual(cont->name, qname))
5069 return(1);
5070 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5071 (cont->c1 != NULL) &&
5072 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5073 if (xmlStrEqual(cont->c1->name, qname))
5074 return(1);
5075 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5076 (cont->c1 == NULL) ||
5077 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5078 /* Internal error !!! */
5079 xmlGenericError(xmlGenericErrorContext,
5080 "Internal: MIXED struct bad\n");
5081 break;
5082 }
5083 cont = cont->c2;
5084 }
5085 return(0);
5086}
5087
5088/**
5089 * xmlValidGetElemDecl:
5090 * @ctxt: the validation context
5091 * @doc: a document instance
5092 * @elem: an element instance
5093 * @extsubset: pointer, (out) indicate if the declaration was found
5094 * in the external subset.
5095 *
5096 * Finds a declaration associated to an element in the document.
5097 *
5098 * returns the pointer to the declaration or NULL if not found.
5099 */
5100static xmlElementPtr
5101xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5102 xmlNodePtr elem, int *extsubset) {
5103 xmlElementPtr elemDecl = NULL;
5104 const xmlChar *prefix = NULL;
5105
5106 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5107 if (extsubset != NULL)
5108 *extsubset = 0;
5109
5110 /*
5111 * Fetch the declaration for the qualified name
5112 */
5113 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5114 prefix = elem->ns->prefix;
5115
5116 if (prefix != NULL) {
5117 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5118 elem->name, prefix);
5119 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5120 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5121 elem->name, prefix);
5122 if ((elemDecl != NULL) && (extsubset != NULL))
5123 *extsubset = 1;
5124 }
5125 }
5126
5127 /*
5128 * Fetch the declaration for the non qualified name
5129 * This is "non-strict" validation should be done on the
5130 * full QName but in that case being flexible makes sense.
5131 */
5132 if (elemDecl == NULL) {
5133 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5134 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5135 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5136 if ((elemDecl != NULL) && (extsubset != NULL))
5137 *extsubset = 1;
5138 }
5139 }
5140 if (elemDecl == NULL) {
5141 VECTXT(ctxt, elem);
5142 VERROR(ctxt->userData, "No declaration for element %s\n",
5143 elem->name);
5144 }
5145 return(elemDecl);
5146}
5147
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005148#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005149/**
5150 * xmlValidatePushElement:
5151 * @ctxt: the validation context
5152 * @doc: a document instance
5153 * @elem: an element instance
5154 * @qname: the qualified name as appearing in the serialization
5155 *
5156 * Push a new element start on the validation stack.
5157 *
5158 * returns 1 if no validation problem was found or 0 otherwise
5159 */
5160int
5161xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5162 xmlNodePtr elem, const xmlChar *qname) {
5163 int ret = 1;
5164 xmlElementPtr eDecl;
5165 int extsubset = 0;
5166
5167 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5168 xmlValidStatePtr state = ctxt->vstate;
5169 xmlElementPtr elemDecl;
5170
5171 /*
5172 * Check the new element agaisnt the content model of the new elem.
5173 */
5174 if (state->elemDecl != NULL) {
5175 elemDecl = state->elemDecl;
5176
5177 switch(elemDecl->etype) {
5178 case XML_ELEMENT_TYPE_UNDEFINED:
5179 ret = 0;
5180 break;
5181 case XML_ELEMENT_TYPE_EMPTY:
5182 VECTXT(ctxt, state->node);
5183 VERROR(ctxt->userData,
5184 "Element %s was declared EMPTY this one has content\n",
5185 state->node->name);
5186 ret = 0;
5187 break;
5188 case XML_ELEMENT_TYPE_ANY:
5189 /* I don't think anything is required then */
5190 break;
5191 case XML_ELEMENT_TYPE_MIXED:
5192 /* simple case of declared as #PCDATA */
5193 if ((elemDecl->content != NULL) &&
5194 (elemDecl->content->type ==
5195 XML_ELEMENT_CONTENT_PCDATA)) {
5196 VECTXT(ctxt, state->node);
5197 VERROR(ctxt->userData,
5198 "Element %s was declared #PCDATA but contains non text nodes\n",
5199 state->node->name);
5200 ret = 0;
5201 } else {
5202 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5203 qname);
5204 if (ret != 1) {
5205 VECTXT(ctxt, state->node);
5206 VERROR(ctxt->userData,
5207 "Element %s is not declared in %s list of possible children\n",
5208 qname, state->node->name);
5209 }
5210 }
5211 break;
5212 case XML_ELEMENT_TYPE_ELEMENT:
5213 /*
5214 * TODO:
5215 * VC: Standalone Document Declaration
5216 * - element types with element content, if white space
5217 * occurs directly within any instance of those types.
5218 */
5219 if (state->exec != NULL) {
5220 ret = xmlRegExecPushString(state->exec, qname, NULL);
5221 if (ret < 0) {
5222 VECTXT(ctxt, state->node);
5223 VERROR(ctxt->userData,
5224 "Element %s content does not follow the DTD\nMisplaced %s\n",
5225 state->node->name, qname);
5226 ret = 0;
5227 } else {
5228 ret = 1;
5229 }
5230 }
5231 break;
5232 }
5233 }
5234 }
5235 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5236 vstateVPush(ctxt, eDecl, elem);
5237 return(ret);
5238}
5239
5240/**
5241 * xmlValidatePushCData:
5242 * @ctxt: the validation context
5243 * @data: some character data read
5244 * @len: the lenght of the data
5245 *
5246 * check the CData parsed for validation in the current stack
5247 *
5248 * returns 1 if no validation problem was found or 0 otherwise
5249 */
5250int
5251xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5252 int ret = 1;
5253
5254 if (len <= 0)
5255 return(ret);
5256 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5257 xmlValidStatePtr state = ctxt->vstate;
5258 xmlElementPtr elemDecl;
5259
5260 /*
5261 * Check the new element agaisnt the content model of the new elem.
5262 */
5263 if (state->elemDecl != NULL) {
5264 elemDecl = state->elemDecl;
5265
5266 switch(elemDecl->etype) {
5267 case XML_ELEMENT_TYPE_UNDEFINED:
5268 ret = 0;
5269 break;
5270 case XML_ELEMENT_TYPE_EMPTY:
5271 VECTXT(ctxt, state->node);
5272 VERROR(ctxt->userData,
5273 "Element %s was declared EMPTY this one has content\n",
5274 state->node->name);
5275 ret = 0;
5276 break;
5277 case XML_ELEMENT_TYPE_ANY:
5278 break;
5279 case XML_ELEMENT_TYPE_MIXED:
5280 break;
5281 case XML_ELEMENT_TYPE_ELEMENT:
5282 if (len > 0) {
5283 int i;
5284
5285 for (i = 0;i < len;i++) {
5286 if (!IS_BLANK(data[i])) {
5287 VECTXT(ctxt, state->node);
5288 VERROR(ctxt->userData,
5289 "Element %s content does not follow the DTD\nText not allowed\n",
5290 state->node->name);
5291 ret = 0;
5292 goto done;
5293 }
5294 }
5295 /*
5296 * TODO:
5297 * VC: Standalone Document Declaration
5298 * element types with element content, if white space
5299 * occurs directly within any instance of those types.
5300 */
5301 }
5302 break;
5303 }
5304 }
5305 }
5306done:
5307 return(ret);
5308}
5309
5310/**
5311 * xmlValidatePopElement:
5312 * @ctxt: the validation context
5313 * @doc: a document instance
5314 * @elem: an element instance
5315 * @qname: the qualified name as appearing in the serialization
5316 *
5317 * Pop the element end from the validation stack.
5318 *
5319 * returns 1 if no validation problem was found or 0 otherwise
5320 */
5321int
5322xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
5323 xmlNodePtr elem, const xmlChar *qname ATTRIBUTE_UNUSED) {
5324 int ret = 1;
5325
5326 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5327 xmlValidStatePtr state = ctxt->vstate;
5328 xmlElementPtr elemDecl;
5329
5330 /*
5331 * Check the new element agaisnt the content model of the new elem.
5332 */
5333 if (state->elemDecl != NULL) {
5334 elemDecl = state->elemDecl;
5335
5336 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5337 if (state->exec != NULL) {
5338 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5339 if (ret == 0) {
5340 VECTXT(ctxt, state->node);
5341 VERROR(ctxt->userData,
5342 "Element %s content does not follow the DTD\nExpecting more child\n",
5343 state->node->name);
5344 } else {
5345 /*
5346 * previous validation errors should not generate
5347 * a new one here
5348 */
5349 ret = 1;
5350 }
5351 }
5352 }
5353 }
5354 vstateVPop(ctxt);
5355 }
5356 return(ret);
5357}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005358#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005359
5360/**
Owen Taylor3473f882001-02-23 17:55:21 +00005361 * xmlValidateOneElement:
5362 * @ctxt: the validation context
5363 * @doc: a document instance
5364 * @elem: an element instance
5365 *
5366 * Try to validate a single element and it's attributes,
5367 * basically it does the following checks as described by the
5368 * XML-1.0 recommendation:
5369 * - [ VC: Element Valid ]
5370 * - [ VC: Required Attribute ]
5371 * Then call xmlValidateOneAttribute() for each attribute present.
5372 *
5373 * The ID/IDREF checkings are done separately
5374 *
5375 * returns 1 if valid or 0 otherwise
5376 */
5377
5378int
5379xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5380 xmlNodePtr elem) {
5381 xmlElementPtr elemDecl = NULL;
5382 xmlElementContentPtr cont;
5383 xmlAttributePtr attr;
5384 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005385 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005386 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005387 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005388
5389 CHECK_DTD;
5390
5391 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005392 switch (elem->type) {
5393 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005394 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005395 VERROR(ctxt->userData,
5396 "Attribute element not expected here\n");
5397 return(0);
5398 case XML_TEXT_NODE:
5399 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005400 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005401 VERROR(ctxt->userData, "Text element has childs !\n");
5402 return(0);
5403 }
5404 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005405 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005406 VERROR(ctxt->userData, "Text element has attributes !\n");
5407 return(0);
5408 }
5409 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005410 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005411 VERROR(ctxt->userData, "Text element has namespace !\n");
5412 return(0);
5413 }
5414 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005415 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005416 VERROR(ctxt->userData,
5417 "Text element carries namespace definitions !\n");
5418 return(0);
5419 }
5420 if (elem->content == 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 has no content !\n");
5424 return(0);
5425 }
5426 return(1);
5427 case XML_XINCLUDE_START:
5428 case XML_XINCLUDE_END:
5429 return(1);
5430 case XML_CDATA_SECTION_NODE:
5431 case XML_ENTITY_REF_NODE:
5432 case XML_PI_NODE:
5433 case XML_COMMENT_NODE:
5434 return(1);
5435 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005436 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005437 VERROR(ctxt->userData,
5438 "Entity element not expected here\n");
5439 return(0);
5440 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005441 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005442 VERROR(ctxt->userData,
5443 "Notation element not expected here\n");
5444 return(0);
5445 case XML_DOCUMENT_NODE:
5446 case XML_DOCUMENT_TYPE_NODE:
5447 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005448 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005449 VERROR(ctxt->userData,
5450 "Document element not expected here\n");
5451 return(0);
5452 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005453 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005454 VERROR(ctxt->userData,
5455 "\n");
5456 return(0);
5457 case XML_ELEMENT_NODE:
5458 break;
5459 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005460 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005461 VERROR(ctxt->userData,
5462 "unknown element type %d\n", elem->type);
5463 return(0);
5464 }
Owen Taylor3473f882001-02-23 17:55:21 +00005465
5466 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005467 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005468 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005469 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5470 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005471 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005472
Daniel Veillardea7751d2002-12-20 00:16:24 +00005473 /*
5474 * If vstateNr is not zero that means continuous validation is
5475 * activated, do not try to check the content model at that level.
5476 */
5477 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005478 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005479 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005480 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005481 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005482 VERROR(ctxt->userData, "No declaration for element %s\n",
5483 elem->name);
5484 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005485 case XML_ELEMENT_TYPE_EMPTY:
5486 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005487 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005488 VERROR(ctxt->userData,
5489 "Element %s was declared EMPTY this one has content\n",
5490 elem->name);
5491 ret = 0;
5492 }
5493 break;
5494 case XML_ELEMENT_TYPE_ANY:
5495 /* I don't think anything is required then */
5496 break;
5497 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005498
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005499 /* simple case of declared as #PCDATA */
5500 if ((elemDecl->content != NULL) &&
5501 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5502 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5503 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005504 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005505 VERROR(ctxt->userData,
5506 "Element %s was declared #PCDATA but contains non text nodes\n",
5507 elem->name);
5508 }
5509 break;
5510 }
Owen Taylor3473f882001-02-23 17:55:21 +00005511 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005512 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005513 while (child != NULL) {
5514 if (child->type == XML_ELEMENT_NODE) {
5515 name = child->name;
5516 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
5517 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005518 snprintf((char *) qname, sizeof(qname), "%s:%s",
5519 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005520 qname[sizeof(qname) - 1] = 0;
5521 cont = elemDecl->content;
5522 while (cont != NULL) {
5523 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5524 if (xmlStrEqual(cont->name, qname)) break;
5525 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5526 (cont->c1 != NULL) &&
5527 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5528 if (xmlStrEqual(cont->c1->name, qname)) break;
5529 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5530 (cont->c1 == NULL) ||
5531 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5532 /* Internal error !!! */
5533 xmlGenericError(xmlGenericErrorContext,
5534 "Internal: MIXED struct bad\n");
5535 break;
5536 }
5537 cont = cont->c2;
5538 }
5539 if (cont != NULL)
5540 goto child_ok;
5541 }
5542 cont = elemDecl->content;
5543 while (cont != NULL) {
5544 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5545 if (xmlStrEqual(cont->name, name)) break;
5546 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5547 (cont->c1 != NULL) &&
5548 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5549 if (xmlStrEqual(cont->c1->name, name)) break;
5550 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5551 (cont->c1 == NULL) ||
5552 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5553 /* Internal error !!! */
5554 xmlGenericError(xmlGenericErrorContext,
5555 "Internal: MIXED struct bad\n");
5556 break;
5557 }
5558 cont = cont->c2;
5559 }
5560 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005561 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005562 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005563 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005564 name, elem->name);
5565 ret = 0;
5566 }
5567 }
5568child_ok:
5569 child = child->next;
5570 }
5571 break;
5572 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005573 if ((doc->standalone == 1) && (extsubset == 1)) {
5574 /*
5575 * VC: Standalone Document Declaration
5576 * - element types with element content, if white space
5577 * occurs directly within any instance of those types.
5578 */
5579 child = elem->children;
5580 while (child != NULL) {
5581 if (child->type == XML_TEXT_NODE) {
5582 const xmlChar *content = child->content;
5583
5584 while (IS_BLANK(*content))
5585 content++;
5586 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005587 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005588 VERROR(ctxt->userData,
5589"standalone: %s declared in the external subset contains white spaces nodes\n",
5590 elem->name);
5591 ret = 0;
5592 break;
5593 }
5594 }
5595 child =child->next;
5596 }
5597 }
Owen Taylor3473f882001-02-23 17:55:21 +00005598 child = elem->children;
5599 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005600 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005601 if (tmp <= 0)
5602 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005603 break;
5604 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005605 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005606
5607 /* [ VC: Required Attribute ] */
5608 attr = elemDecl->attributes;
5609 while (attr != NULL) {
5610 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005611 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005612
Daniel Veillarde4301c82002-02-13 13:32:35 +00005613 if ((attr->prefix == NULL) &&
5614 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5615 xmlNsPtr ns;
5616
5617 ns = elem->nsDef;
5618 while (ns != NULL) {
5619 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005620 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005621 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005622 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005623 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5624 xmlNsPtr ns;
5625
5626 ns = elem->nsDef;
5627 while (ns != NULL) {
5628 if (xmlStrEqual(attr->name, ns->prefix))
5629 goto found;
5630 ns = ns->next;
5631 }
5632 } else {
5633 xmlAttrPtr attrib;
5634
5635 attrib = elem->properties;
5636 while (attrib != NULL) {
5637 if (xmlStrEqual(attrib->name, attr->name)) {
5638 if (attr->prefix != NULL) {
5639 xmlNsPtr nameSpace = attrib->ns;
5640
5641 if (nameSpace == NULL)
5642 nameSpace = elem->ns;
5643 /*
5644 * qualified names handling is problematic, having a
5645 * different prefix should be possible but DTDs don't
5646 * allow to define the URI instead of the prefix :-(
5647 */
5648 if (nameSpace == NULL) {
5649 if (qualified < 0)
5650 qualified = 0;
5651 } else if (!xmlStrEqual(nameSpace->prefix,
5652 attr->prefix)) {
5653 if (qualified < 1)
5654 qualified = 1;
5655 } else
5656 goto found;
5657 } else {
5658 /*
5659 * We should allow applications to define namespaces
5660 * for their application even if the DTD doesn't
5661 * carry one, otherwise, basically we would always
5662 * break.
5663 */
5664 goto found;
5665 }
5666 }
5667 attrib = attrib->next;
5668 }
Owen Taylor3473f882001-02-23 17:55:21 +00005669 }
5670 if (qualified == -1) {
5671 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005672 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005673 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005674 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005675 elem->name, attr->name);
5676 ret = 0;
5677 } else {
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:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005681 elem->name, attr->prefix,attr->name);
5682 ret = 0;
5683 }
5684 } else if (qualified == 0) {
5685 VWARNING(ctxt->userData,
5686 "Element %s required attribute %s:%s has no prefix\n",
5687 elem->name, attr->prefix,attr->name);
5688 } else if (qualified == 1) {
5689 VWARNING(ctxt->userData,
5690 "Element %s required attribute %s:%s has different prefix\n",
5691 elem->name, attr->prefix,attr->name);
5692 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005693 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5694 /*
5695 * Special tests checking #FIXED namespace declarations
5696 * have the right value since this is not done as an
5697 * attribute checking
5698 */
5699 if ((attr->prefix == NULL) &&
5700 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5701 xmlNsPtr ns;
5702
5703 ns = elem->nsDef;
5704 while (ns != NULL) {
5705 if (ns->prefix == NULL) {
5706 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005707 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005708 VERROR(ctxt->userData,
5709 "Element %s namespace name for default namespace does not match the DTD\n",
5710 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005711 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005712 }
5713 goto found;
5714 }
5715 ns = ns->next;
5716 }
5717 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5718 xmlNsPtr ns;
5719
5720 ns = elem->nsDef;
5721 while (ns != NULL) {
5722 if (xmlStrEqual(attr->name, ns->prefix)) {
5723 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005724 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005725 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005726 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005727 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005728 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005729 }
5730 goto found;
5731 }
5732 ns = ns->next;
5733 }
5734 }
Owen Taylor3473f882001-02-23 17:55:21 +00005735 }
5736found:
5737 attr = attr->nexth;
5738 }
5739 return(ret);
5740}
5741
5742/**
5743 * xmlValidateRoot:
5744 * @ctxt: the validation context
5745 * @doc: a document instance
5746 *
5747 * Try to validate a the root element
5748 * basically it does the following check as described by the
5749 * XML-1.0 recommendation:
5750 * - [ VC: Root Element Type ]
5751 * it doesn't try to recurse or apply other check to the element
5752 *
5753 * returns 1 if valid or 0 otherwise
5754 */
5755
5756int
5757xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5758 xmlNodePtr root;
5759 if (doc == NULL) return(0);
5760
5761 root = xmlDocGetRootElement(doc);
5762 if ((root == NULL) || (root->name == NULL)) {
5763 VERROR(ctxt->userData, "Not valid: no root element\n");
5764 return(0);
5765 }
5766
5767 /*
5768 * When doing post validation against a separate DTD, those may
5769 * no internal subset has been generated
5770 */
5771 if ((doc->intSubset != NULL) &&
5772 (doc->intSubset->name != NULL)) {
5773 /*
5774 * Check first the document root against the NQName
5775 */
5776 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5777 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
5778 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005779 snprintf((char *) qname, sizeof(qname), "%s:%s",
5780 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005781 qname[sizeof(qname) - 1] = 0;
5782 if (xmlStrEqual(doc->intSubset->name, qname))
5783 goto name_ok;
5784 }
5785 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5786 (xmlStrEqual(root->name, BAD_CAST "html")))
5787 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005788 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005789 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005790 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005791 root->name, doc->intSubset->name);
5792 return(0);
5793
5794 }
5795 }
5796name_ok:
5797 return(1);
5798}
5799
5800
5801/**
5802 * xmlValidateElement:
5803 * @ctxt: the validation context
5804 * @doc: a document instance
5805 * @elem: an element instance
5806 *
5807 * Try to validate the subtree under an element
5808 *
5809 * returns 1 if valid or 0 otherwise
5810 */
5811
5812int
5813xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5814 xmlNodePtr child;
5815 xmlAttrPtr attr;
5816 xmlChar *value;
5817 int ret = 1;
5818
5819 if (elem == NULL) return(0);
5820
5821 /*
5822 * XInclude elements were added after parsing in the infoset,
5823 * they don't really mean anything validation wise.
5824 */
5825 if ((elem->type == XML_XINCLUDE_START) ||
5826 (elem->type == XML_XINCLUDE_END))
5827 return(1);
5828
5829 CHECK_DTD;
5830
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005831 /*
5832 * Entities references have to be handled separately
5833 */
5834 if (elem->type == XML_ENTITY_REF_NODE) {
5835 return(1);
5836 }
5837
Owen Taylor3473f882001-02-23 17:55:21 +00005838 ret &= xmlValidateOneElement(ctxt, doc, elem);
5839 attr = elem->properties;
5840 while(attr != NULL) {
5841 value = xmlNodeListGetString(doc, attr->children, 0);
5842 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5843 if (value != NULL)
5844 xmlFree(value);
5845 attr= attr->next;
5846 }
5847 child = elem->children;
5848 while (child != NULL) {
5849 ret &= xmlValidateElement(ctxt, doc, child);
5850 child = child->next;
5851 }
5852
5853 return(ret);
5854}
5855
Daniel Veillard8730c562001-02-26 10:49:57 +00005856/**
5857 * xmlValidateRef:
5858 * @ref: A reference to be validated
5859 * @ctxt: Validation context
5860 * @name: Name of ID we are searching for
5861 *
5862 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005863static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005864xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005865 const xmlChar *name) {
5866 xmlAttrPtr id;
5867 xmlAttrPtr attr;
5868
5869 if (ref == NULL)
5870 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005871 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00005872 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005873 attr = ref->attr;
5874 if (attr == NULL) {
5875 xmlChar *dup, *str = NULL, *cur, save;
5876
5877 dup = xmlStrdup(name);
5878 if (dup == NULL) {
5879 ctxt->valid = 0;
5880 return;
5881 }
5882 cur = dup;
5883 while (*cur != 0) {
5884 str = cur;
5885 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5886 save = *cur;
5887 *cur = 0;
5888 id = xmlGetID(ctxt->doc, str);
5889 if (id == NULL) {
5890 VERROR(ctxt->userData,
5891 "attribute %s line %d references an unknown ID \"%s\"\n",
5892 ref->name, ref->lineno, str);
5893 ctxt->valid = 0;
5894 }
5895 if (save == 0)
5896 break;
5897 *cur = save;
5898 while (IS_BLANK(*cur)) cur++;
5899 }
5900 xmlFree(dup);
5901 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00005902 id = xmlGetID(ctxt->doc, name);
5903 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005904 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005905 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005906 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005907 attr->name, name);
5908 ctxt->valid = 0;
5909 }
5910 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5911 xmlChar *dup, *str = NULL, *cur, save;
5912
5913 dup = xmlStrdup(name);
5914 if (dup == NULL) {
5915 ctxt->valid = 0;
5916 return;
5917 }
5918 cur = dup;
5919 while (*cur != 0) {
5920 str = cur;
5921 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5922 save = *cur;
5923 *cur = 0;
5924 id = xmlGetID(ctxt->doc, str);
5925 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005926 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005927 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005928 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005929 attr->name, str);
5930 ctxt->valid = 0;
5931 }
5932 if (save == 0)
5933 break;
5934 *cur = save;
5935 while (IS_BLANK(*cur)) cur++;
5936 }
5937 xmlFree(dup);
5938 }
5939}
5940
5941/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005942 * xmlWalkValidateList:
5943 * @data: Contents of current link
5944 * @user: Value supplied by the user
5945 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005946 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005947 */
5948static int
5949xmlWalkValidateList(const void *data, const void *user)
5950{
5951 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5952 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5953 return 1;
5954}
5955
5956/**
5957 * xmlValidateCheckRefCallback:
5958 * @ref_list: List of references
5959 * @ctxt: Validation context
5960 * @name: Name of ID we are searching for
5961 *
5962 */
5963static void
5964xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5965 const xmlChar *name) {
5966 xmlValidateMemo memo;
5967
5968 if (ref_list == NULL)
5969 return;
5970 memo.ctxt = ctxt;
5971 memo.name = name;
5972
5973 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5974
5975}
5976
5977/**
Owen Taylor3473f882001-02-23 17:55:21 +00005978 * xmlValidateDocumentFinal:
5979 * @ctxt: the validation context
5980 * @doc: a document instance
5981 *
5982 * Does the final step for the document validation once all the
5983 * incremental validation steps have been completed
5984 *
5985 * basically it does the following checks described by the XML Rec
5986 *
5987 *
5988 * returns 1 if valid or 0 otherwise
5989 */
5990
5991int
5992xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5993 xmlRefTablePtr table;
5994
5995 if (doc == NULL) {
5996 xmlGenericError(xmlGenericErrorContext,
5997 "xmlValidateDocumentFinal: doc == NULL\n");
5998 return(0);
5999 }
6000
6001 /*
6002 * Check all the NOTATION/NOTATIONS attributes
6003 */
6004 /*
6005 * Check all the ENTITY/ENTITIES attributes definition for validity
6006 */
6007 /*
6008 * Check all the IDREF/IDREFS attributes definition for validity
6009 */
6010 table = (xmlRefTablePtr) doc->refs;
6011 ctxt->doc = doc;
6012 ctxt->valid = 1;
6013 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
6014 return(ctxt->valid);
6015}
6016
6017/**
6018 * xmlValidateDtd:
6019 * @ctxt: the validation context
6020 * @doc: a document instance
6021 * @dtd: a dtd instance
6022 *
6023 * Try to validate the document against the dtd instance
6024 *
6025 * basically it does check all the definitions in the DtD.
6026 *
6027 * returns 1 if valid or 0 otherwise
6028 */
6029
6030int
6031xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6032 int ret;
6033 xmlDtdPtr oldExt;
6034 xmlNodePtr root;
6035
6036 if (dtd == NULL) return(0);
6037 if (doc == NULL) return(0);
6038 oldExt = doc->extSubset;
6039 doc->extSubset = dtd;
6040 ret = xmlValidateRoot(ctxt, doc);
6041 if (ret == 0) {
6042 doc->extSubset = oldExt;
6043 return(ret);
6044 }
6045 if (doc->ids != NULL) {
6046 xmlFreeIDTable(doc->ids);
6047 doc->ids = NULL;
6048 }
6049 if (doc->refs != NULL) {
6050 xmlFreeRefTable(doc->refs);
6051 doc->refs = NULL;
6052 }
6053 root = xmlDocGetRootElement(doc);
6054 ret = xmlValidateElement(ctxt, doc, root);
6055 ret &= xmlValidateDocumentFinal(ctxt, doc);
6056 doc->extSubset = oldExt;
6057 return(ret);
6058}
6059
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006060static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006061xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6062 const xmlChar *name ATTRIBUTE_UNUSED) {
6063 if (cur == NULL)
6064 return;
6065 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6066 xmlChar *notation = cur->content;
6067
Daniel Veillard878eab02002-02-19 13:46:09 +00006068 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006069 int ret;
6070
6071 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6072 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006073 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006074 }
6075 }
6076 }
6077}
6078
6079static void
Owen Taylor3473f882001-02-23 17:55:21 +00006080xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006081 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006082 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006083 xmlDocPtr doc;
6084 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006085
Owen Taylor3473f882001-02-23 17:55:21 +00006086 if (cur == NULL)
6087 return;
6088 switch (cur->atype) {
6089 case XML_ATTRIBUTE_CDATA:
6090 case XML_ATTRIBUTE_ID:
6091 case XML_ATTRIBUTE_IDREF :
6092 case XML_ATTRIBUTE_IDREFS:
6093 case XML_ATTRIBUTE_NMTOKEN:
6094 case XML_ATTRIBUTE_NMTOKENS:
6095 case XML_ATTRIBUTE_ENUMERATION:
6096 break;
6097 case XML_ATTRIBUTE_ENTITY:
6098 case XML_ATTRIBUTE_ENTITIES:
6099 case XML_ATTRIBUTE_NOTATION:
6100 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006101
6102 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6103 cur->atype, cur->defaultValue);
6104 if ((ret == 0) && (ctxt->valid == 1))
6105 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006106 }
6107 if (cur->tree != NULL) {
6108 xmlEnumerationPtr tree = cur->tree;
6109 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006110 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006111 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006112 if ((ret == 0) && (ctxt->valid == 1))
6113 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006114 tree = tree->next;
6115 }
6116 }
6117 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006118 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6119 doc = cur->doc;
6120 if ((doc == NULL) || (cur->elem == NULL)) {
6121 VERROR(ctxt->userData,
6122 "xmlValidateAttributeCallback(%s): internal error\n",
6123 cur->name);
6124 return;
6125 }
6126 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6127 if (elem == NULL)
6128 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6129 if (elem == NULL) {
6130 VERROR(ctxt->userData,
6131 "attribute %s: could not find decl for element %s\n",
6132 cur->name, cur->elem);
6133 return;
6134 }
6135 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6136 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006137 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00006138 cur->name, cur->elem);
6139 ctxt->valid = 0;
6140 }
6141 }
Owen Taylor3473f882001-02-23 17:55:21 +00006142}
6143
6144/**
6145 * xmlValidateDtdFinal:
6146 * @ctxt: the validation context
6147 * @doc: a document instance
6148 *
6149 * Does the final step for the dtds validation once all the
6150 * subsets have been parsed
6151 *
6152 * basically it does the following checks described by the XML Rec
6153 * - check that ENTITY and ENTITIES type attributes default or
6154 * possible values matches one of the defined entities.
6155 * - check that NOTATION type attributes default or
6156 * possible values matches one of the defined notations.
6157 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006158 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006159 */
6160
6161int
6162xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006163 xmlDtdPtr dtd;
6164 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006165 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006166
6167 if (doc == NULL) return(0);
6168 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6169 return(0);
6170 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006171 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006172 dtd = doc->intSubset;
6173 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6174 table = (xmlAttributeTablePtr) dtd->attributes;
6175 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006176 }
6177 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006178 entities = (xmlEntitiesTablePtr) dtd->entities;
6179 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6180 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006181 }
6182 dtd = doc->extSubset;
6183 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6184 table = (xmlAttributeTablePtr) dtd->attributes;
6185 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006186 }
6187 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006188 entities = (xmlEntitiesTablePtr) dtd->entities;
6189 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6190 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006191 }
6192 return(ctxt->valid);
6193}
6194
6195/**
6196 * xmlValidateDocument:
6197 * @ctxt: the validation context
6198 * @doc: a document instance
6199 *
6200 * Try to validate the document instance
6201 *
6202 * basically it does the all the checks described by the XML Rec
6203 * i.e. validates the internal and external subset (if present)
6204 * and validate the document tree.
6205 *
6206 * returns 1 if valid or 0 otherwise
6207 */
6208
6209int
6210xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6211 int ret;
6212 xmlNodePtr root;
6213
Daniel Veillard2fd85422002-10-16 14:32:41 +00006214 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6215 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00006216 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006217 }
Owen Taylor3473f882001-02-23 17:55:21 +00006218 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6219 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6220 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6221 doc->intSubset->SystemID);
6222 if (doc->extSubset == NULL) {
6223 if (doc->intSubset->SystemID != NULL) {
6224 VERROR(ctxt->userData,
6225 "Could not load the external subset \"%s\"\n",
6226 doc->intSubset->SystemID);
6227 } else {
6228 VERROR(ctxt->userData,
6229 "Could not load the external subset \"%s\"\n",
6230 doc->intSubset->ExternalID);
6231 }
6232 return(0);
6233 }
6234 }
6235
6236 if (doc->ids != NULL) {
6237 xmlFreeIDTable(doc->ids);
6238 doc->ids = NULL;
6239 }
6240 if (doc->refs != NULL) {
6241 xmlFreeRefTable(doc->refs);
6242 doc->refs = NULL;
6243 }
6244 ret = xmlValidateDtdFinal(ctxt, doc);
6245 if (!xmlValidateRoot(ctxt, doc)) return(0);
6246
6247 root = xmlDocGetRootElement(doc);
6248 ret &= xmlValidateElement(ctxt, doc, root);
6249 ret &= xmlValidateDocumentFinal(ctxt, doc);
6250 return(ret);
6251}
6252
6253
6254/************************************************************************
6255 * *
6256 * Routines for dynamic validation editing *
6257 * *
6258 ************************************************************************/
6259
6260/**
6261 * xmlValidGetPotentialChildren:
6262 * @ctree: an element content tree
6263 * @list: an array to store the list of child names
6264 * @len: a pointer to the number of element in the list
6265 * @max: the size of the array
6266 *
6267 * Build/extend a list of potential children allowed by the content tree
6268 *
6269 * returns the number of element in the list, or -1 in case of error.
6270 */
6271
6272int
6273xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6274 int *len, int max) {
6275 int i;
6276
6277 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6278 return(-1);
6279 if (*len >= max) return(*len);
6280
6281 switch (ctree->type) {
6282 case XML_ELEMENT_CONTENT_PCDATA:
6283 for (i = 0; i < *len;i++)
6284 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6285 list[(*len)++] = BAD_CAST "#PCDATA";
6286 break;
6287 case XML_ELEMENT_CONTENT_ELEMENT:
6288 for (i = 0; i < *len;i++)
6289 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6290 list[(*len)++] = ctree->name;
6291 break;
6292 case XML_ELEMENT_CONTENT_SEQ:
6293 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6294 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6295 break;
6296 case XML_ELEMENT_CONTENT_OR:
6297 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6298 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6299 break;
6300 }
6301
6302 return(*len);
6303}
6304
6305/**
6306 * xmlValidGetValidElements:
6307 * @prev: an element to insert after
6308 * @next: an element to insert next
6309 * @list: an array to store the list of child names
6310 * @max: the size of the array
6311 *
6312 * This function returns the list of authorized children to insert
6313 * within an existing tree while respecting the validity constraints
6314 * forced by the Dtd. The insertion point is defined using @prev and
6315 * @next in the following ways:
6316 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6317 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6318 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6319 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6320 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6321 *
6322 * pointers to the element names are inserted at the beginning of the array
6323 * and do not need to be freed.
6324 *
6325 * returns the number of element in the list, or -1 in case of error. If
6326 * the function returns the value @max the caller is invited to grow the
6327 * receiving array and retry.
6328 */
6329
6330int
6331xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6332 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006333 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006334 int nb_valid_elements = 0;
6335 const xmlChar *elements[256];
6336 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006337 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006338
6339 xmlNode *ref_node;
6340 xmlNode *parent;
6341 xmlNode *test_node;
6342
6343 xmlNode *prev_next;
6344 xmlNode *next_prev;
6345 xmlNode *parent_childs;
6346 xmlNode *parent_last;
6347
6348 xmlElement *element_desc;
6349
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006350 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006351
Owen Taylor3473f882001-02-23 17:55:21 +00006352 if (prev == NULL && next == NULL)
6353 return(-1);
6354
6355 if (list == NULL) return(-1);
6356 if (max <= 0) return(-1);
6357
6358 nb_valid_elements = 0;
6359 ref_node = prev ? prev : next;
6360 parent = ref_node->parent;
6361
6362 /*
6363 * Retrieves the parent element declaration
6364 */
6365 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6366 parent->name);
6367 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6368 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6369 parent->name);
6370 if (element_desc == NULL) return(-1);
6371
6372 /*
6373 * Do a backup of the current tree structure
6374 */
6375 prev_next = prev ? prev->next : NULL;
6376 next_prev = next ? next->prev : NULL;
6377 parent_childs = parent->children;
6378 parent_last = parent->last;
6379
6380 /*
6381 * Creates a dummy node and insert it into the tree
6382 */
6383 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6384 test_node->doc = ref_node->doc;
6385 test_node->parent = parent;
6386 test_node->prev = prev;
6387 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006388 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006389
6390 if (prev) prev->next = test_node;
6391 else parent->children = test_node;
6392
6393 if (next) next->prev = test_node;
6394 else parent->last = test_node;
6395
6396 /*
6397 * Insert each potential child node and check if the parent is
6398 * still valid
6399 */
6400 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6401 elements, &nb_elements, 256);
6402
6403 for (i = 0;i < nb_elements;i++) {
6404 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006405 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006406 int j;
6407
6408 for (j = 0; j < nb_valid_elements;j++)
6409 if (xmlStrEqual(elements[i], list[j])) break;
6410 list[nb_valid_elements++] = elements[i];
6411 if (nb_valid_elements >= max) break;
6412 }
6413 }
6414
6415 /*
6416 * Restore the tree structure
6417 */
6418 if (prev) prev->next = prev_next;
6419 if (next) next->prev = next_prev;
6420 parent->children = parent_childs;
6421 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006422
6423 /*
6424 * Free up the dummy node
6425 */
6426 test_node->name = name;
6427 xmlFreeNode(test_node);
6428
Owen Taylor3473f882001-02-23 17:55:21 +00006429 return(nb_valid_elements);
6430}