blob: dcf72d4b4022185c2f215300c32685d819ded139 [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 Veillardaeb258a2002-09-13 14:48:12 +0000547 break;
548 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000549 if (QName != NULL)
550 xmlFree(QName);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000551 break;
552 }
553 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000554 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000555 xmlElementContentOccur ocur;
556
557 /*
558 * Simply iterate over the content
559 */
560 oldstate = ctxt->state;
561 ocur = content->ocur;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000562 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000563 xmlValidBuildAContentModel(content->c1, ctxt, name);
564 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000565 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
566 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
567 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000568 oldend = ctxt->state;
569 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000570 switch (ocur) {
571 case XML_ELEMENT_CONTENT_ONCE:
572 break;
573 case XML_ELEMENT_CONTENT_OPT:
574 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
575 break;
576 case XML_ELEMENT_CONTENT_MULT:
577 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000578 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000579 break;
580 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000581 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000582 break;
583 }
584 break;
585 }
586 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000587 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000588 xmlElementContentOccur ocur;
589
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000590 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000591 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
592 (ocur == XML_ELEMENT_CONTENT_MULT)) {
593 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
594 ctxt->state, NULL);
595 }
596 oldstate = ctxt->state;
597 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000598
599 /*
600 * iterate over the subtypes and remerge the end with an
601 * epsilon transition
602 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000603 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000604 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000605 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000606 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000607 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000608 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
609 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000610 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000611 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000612 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
613 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000614 switch (ocur) {
615 case XML_ELEMENT_CONTENT_ONCE:
616 break;
617 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000618 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000619 break;
620 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000621 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
622 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000623 break;
624 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000625 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000626 break;
627 }
628 break;
629 }
630 default:
631 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
632 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000633 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000634 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000635 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000636}
637/**
638 * xmlValidBuildContentModel:
639 * @ctxt: a validation context
640 * @elem: an element declaration node
641 *
642 * (Re)Build the automata associated to the content model of this
643 * element
644 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000645 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000646 */
647int
648xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
649 xmlAutomataStatePtr start;
650
651 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000652 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000653 if (elem->type != XML_ELEMENT_DECL)
654 return(0);
655 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
656 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000657 /* TODO: should we rebuild in this case ? */
658 if (elem->contModel != NULL)
Daniel Veillard84d70a42002-09-16 10:51:38 +0000659 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000660
661 ctxt->am = xmlNewAutomata();
662 if (ctxt->am == NULL) {
663 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
664 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000665 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000666 }
667 start = ctxt->state = xmlAutomataGetInitState(ctxt->am);
668 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
669 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000670 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillard23e73572002-09-19 19:56:43 +0000671 if (!xmlRegexpIsDeterminist(elem->contModel)) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000672 char expr[5000];
673 expr[0] = 0;
674 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
675 VERROR(ctxt->userData, "Content model of %s is not determinist: %s\n",
676 elem->name, expr);
677#ifdef DEBUG_REGEXP_ALGO
678 xmlRegexpPrint(stderr, elem->contModel);
679#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000680 ctxt->valid = 0;
681 }
682 ctxt->state = NULL;
683 xmlFreeAutomata(ctxt->am);
684 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000685 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000686}
687
688#endif /* LIBXML_REGEXP_ENABLED */
689
Owen Taylor3473f882001-02-23 17:55:21 +0000690/************************************************************************
691 * *
692 * QName handling helper *
693 * *
694 ************************************************************************/
695
696/**
697 * xmlSplitQName2:
698 * @name: an XML parser context
699 * @prefix: a xmlChar **
700 *
701 * parse an XML qualified name string
702 *
703 * [NS 5] QName ::= (Prefix ':')? LocalPart
704 *
705 * [NS 6] Prefix ::= NCName
706 *
707 * [NS 7] LocalPart ::= NCName
708 *
709 * Returns NULL if not a QName, otherwise the local part, and prefix
710 * is updated to get the Prefix if any.
711 */
712
713xmlChar *
714xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
715 int len = 0;
716 xmlChar *ret = NULL;
717
718 *prefix = NULL;
719
Daniel Veillardf4309d72001-10-02 09:28:58 +0000720#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000721 /* xml: prefix is not really a namespace */
722 if ((name[0] == 'x') && (name[1] == 'm') &&
723 (name[2] == 'l') && (name[3] == ':'))
724 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000725#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000726
727 /* nasty but valid */
728 if (name[0] == ':')
729 return(NULL);
730
731 /*
732 * we are not trying to validate but just to cut, and yes it will
733 * work even if this is as set of UTF-8 encoded chars
734 */
735 while ((name[len] != 0) && (name[len] != ':'))
736 len++;
737
738 if (name[len] == 0)
739 return(NULL);
740
741 *prefix = xmlStrndup(name, len);
742 ret = xmlStrdup(&name[len + 1]);
743
744 return(ret);
745}
746
747/****************************************************************
748 * *
749 * Util functions for data allocation/deallocation *
750 * *
751 ****************************************************************/
752
753/**
754 * xmlNewElementContent:
755 * @name: the subelement name or NULL
756 * @type: the type of element content decl
757 *
758 * Allocate an element content structure.
759 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000760 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000761 */
762xmlElementContentPtr
763xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
764 xmlElementContentPtr ret;
765
766 switch(type) {
767 case XML_ELEMENT_CONTENT_ELEMENT:
768 if (name == NULL) {
769 xmlGenericError(xmlGenericErrorContext,
770 "xmlNewElementContent : name == NULL !\n");
771 }
772 break;
773 case XML_ELEMENT_CONTENT_PCDATA:
774 case XML_ELEMENT_CONTENT_SEQ:
775 case XML_ELEMENT_CONTENT_OR:
776 if (name != NULL) {
777 xmlGenericError(xmlGenericErrorContext,
778 "xmlNewElementContent : name != NULL !\n");
779 }
780 break;
781 default:
782 xmlGenericError(xmlGenericErrorContext,
783 "xmlNewElementContent: unknown type %d\n", type);
784 return(NULL);
785 }
786 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
787 if (ret == NULL) {
788 xmlGenericError(xmlGenericErrorContext,
789 "xmlNewElementContent : out of memory!\n");
790 return(NULL);
791 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000792 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000793 ret->type = type;
794 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000795 if (name != NULL) {
796 xmlChar *prefix = NULL;
797 ret->name = xmlSplitQName2(name, &prefix);
798 if (ret->name == NULL)
799 ret->name = xmlStrdup(name);
800 ret->prefix = prefix;
801 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000802 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000803 ret->prefix = NULL;
804 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000805 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000806 return(ret);
807}
808
809/**
810 * xmlCopyElementContent:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000811 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +0000812 *
813 * Build a copy of an element content description.
814 *
815 * Returns the new xmlElementContentPtr or NULL in case of error.
816 */
817xmlElementContentPtr
818xmlCopyElementContent(xmlElementContentPtr cur) {
819 xmlElementContentPtr ret;
820
821 if (cur == NULL) return(NULL);
822 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
823 if (ret == NULL) {
824 xmlGenericError(xmlGenericErrorContext,
825 "xmlCopyElementContent : out of memory\n");
826 return(NULL);
827 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000828 if (cur->prefix != NULL)
829 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000830 ret->ocur = cur->ocur;
831 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000832 if (ret->c1 != NULL)
833 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000834 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000835 if (ret->c2 != NULL)
836 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000837 return(ret);
838}
839
840/**
841 * xmlFreeElementContent:
842 * @cur: the element content tree to free
843 *
844 * Free an element content structure. This is a recursive call !
845 */
846void
847xmlFreeElementContent(xmlElementContentPtr cur) {
848 if (cur == NULL) return;
849 switch (cur->type) {
850 case XML_ELEMENT_CONTENT_PCDATA:
851 case XML_ELEMENT_CONTENT_ELEMENT:
852 case XML_ELEMENT_CONTENT_SEQ:
853 case XML_ELEMENT_CONTENT_OR:
854 break;
855 default:
856 xmlGenericError(xmlGenericErrorContext,
857 "xmlFreeElementContent : type %d\n", cur->type);
858 return;
859 }
860 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
861 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
862 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000863 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000864 xmlFree(cur);
865}
866
867/**
868 * xmlDumpElementContent:
869 * @buf: An XML buffer
870 * @content: An element table
871 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
872 *
873 * This will dump the content of the element table as an XML DTD definition
874 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000875static void
Owen Taylor3473f882001-02-23 17:55:21 +0000876xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
877 if (content == NULL) return;
878
879 if (glob) xmlBufferWriteChar(buf, "(");
880 switch (content->type) {
881 case XML_ELEMENT_CONTENT_PCDATA:
882 xmlBufferWriteChar(buf, "#PCDATA");
883 break;
884 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000885 if (content->prefix != NULL) {
886 xmlBufferWriteCHAR(buf, content->prefix);
887 xmlBufferWriteChar(buf, ":");
888 }
Owen Taylor3473f882001-02-23 17:55:21 +0000889 xmlBufferWriteCHAR(buf, content->name);
890 break;
891 case XML_ELEMENT_CONTENT_SEQ:
892 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
893 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
894 xmlDumpElementContent(buf, content->c1, 1);
895 else
896 xmlDumpElementContent(buf, content->c1, 0);
897 xmlBufferWriteChar(buf, " , ");
898 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
899 xmlDumpElementContent(buf, content->c2, 1);
900 else
901 xmlDumpElementContent(buf, content->c2, 0);
902 break;
903 case XML_ELEMENT_CONTENT_OR:
904 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
905 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
906 xmlDumpElementContent(buf, content->c1, 1);
907 else
908 xmlDumpElementContent(buf, content->c1, 0);
909 xmlBufferWriteChar(buf, " | ");
910 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
911 xmlDumpElementContent(buf, content->c2, 1);
912 else
913 xmlDumpElementContent(buf, content->c2, 0);
914 break;
915 default:
916 xmlGenericError(xmlGenericErrorContext,
917 "xmlDumpElementContent: unknown type %d\n",
918 content->type);
919 }
920 if (glob)
921 xmlBufferWriteChar(buf, ")");
922 switch (content->ocur) {
923 case XML_ELEMENT_CONTENT_ONCE:
924 break;
925 case XML_ELEMENT_CONTENT_OPT:
926 xmlBufferWriteChar(buf, "?");
927 break;
928 case XML_ELEMENT_CONTENT_MULT:
929 xmlBufferWriteChar(buf, "*");
930 break;
931 case XML_ELEMENT_CONTENT_PLUS:
932 xmlBufferWriteChar(buf, "+");
933 break;
934 }
935}
936
937/**
938 * xmlSprintfElementContent:
939 * @buf: an output buffer
940 * @content: An element table
941 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
942 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000943 * Deprecated, unsafe, use xmlSnprintfElementContent
944 */
945void
946xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
947 xmlElementContentPtr content ATTRIBUTE_UNUSED,
948 int glob ATTRIBUTE_UNUSED) {
949}
950
951/**
952 * xmlSnprintfElementContent:
953 * @buf: an output buffer
954 * @size: the buffer size
955 * @content: An element table
956 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
957 *
Owen Taylor3473f882001-02-23 17:55:21 +0000958 * This will dump the content of the element content definition
959 * Intended just for the debug routine
960 */
961void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000962xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
963 int len;
964
Owen Taylor3473f882001-02-23 17:55:21 +0000965 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000966 len = strlen(buf);
967 if (size - len < 50) {
968 if ((size - len > 4) && (buf[len - 1] != '.'))
969 strcat(buf, " ...");
970 return;
971 }
Owen Taylor3473f882001-02-23 17:55:21 +0000972 if (glob) strcat(buf, "(");
973 switch (content->type) {
974 case XML_ELEMENT_CONTENT_PCDATA:
975 strcat(buf, "#PCDATA");
976 break;
977 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000978 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000979 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000980 strcat(buf, " ...");
981 return;
982 }
983 strcat(buf, (char *) content->prefix);
984 strcat(buf, ":");
985 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000986 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000987 strcat(buf, " ...");
988 return;
989 }
Owen Taylor3473f882001-02-23 17:55:21 +0000990 strcat(buf, (char *) content->name);
991 break;
992 case XML_ELEMENT_CONTENT_SEQ:
993 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
994 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000995 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000996 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000997 xmlSnprintfElementContent(buf, size, content->c1, 0);
998 len = strlen(buf);
999 if (size - len < 50) {
1000 if ((size - len > 4) && (buf[len - 1] != '.'))
1001 strcat(buf, " ...");
1002 return;
1003 }
Owen Taylor3473f882001-02-23 17:55:21 +00001004 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00001005 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
1006 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1007 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001008 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001009 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001010 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001011 break;
1012 case XML_ELEMENT_CONTENT_OR:
1013 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
1014 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001015 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001016 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001017 xmlSnprintfElementContent(buf, size, content->c1, 0);
1018 len = strlen(buf);
1019 if (size - len < 50) {
1020 if ((size - len > 4) && (buf[len - 1] != '.'))
1021 strcat(buf, " ...");
1022 return;
1023 }
Owen Taylor3473f882001-02-23 17:55:21 +00001024 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00001025 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
1026 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
1027 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +00001028 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001029 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00001030 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +00001031 break;
1032 }
1033 if (glob)
1034 strcat(buf, ")");
1035 switch (content->ocur) {
1036 case XML_ELEMENT_CONTENT_ONCE:
1037 break;
1038 case XML_ELEMENT_CONTENT_OPT:
1039 strcat(buf, "?");
1040 break;
1041 case XML_ELEMENT_CONTENT_MULT:
1042 strcat(buf, "*");
1043 break;
1044 case XML_ELEMENT_CONTENT_PLUS:
1045 strcat(buf, "+");
1046 break;
1047 }
1048}
1049
1050/****************************************************************
1051 * *
1052 * Registration of DTD declarations *
1053 * *
1054 ****************************************************************/
1055
1056/**
1057 * xmlCreateElementTable:
1058 *
1059 * create and initialize an empty element hash table.
1060 *
1061 * Returns the xmlElementTablePtr just created or NULL in case of error.
1062 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001063static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001064xmlCreateElementTable(void) {
1065 return(xmlHashCreate(0));
1066}
1067
1068/**
1069 * xmlFreeElement:
1070 * @elem: An element
1071 *
1072 * Deallocate the memory used by an element definition
1073 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001074static void
Owen Taylor3473f882001-02-23 17:55:21 +00001075xmlFreeElement(xmlElementPtr elem) {
1076 if (elem == NULL) return;
1077 xmlUnlinkNode((xmlNodePtr) elem);
1078 xmlFreeElementContent(elem->content);
1079 if (elem->name != NULL)
1080 xmlFree((xmlChar *) elem->name);
1081 if (elem->prefix != NULL)
1082 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001083#ifdef LIBXML_REGEXP_ENABLED
1084 if (elem->contModel != NULL)
1085 xmlRegFreeRegexp(elem->contModel);
1086#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001087 xmlFree(elem);
1088}
1089
1090
1091/**
1092 * xmlAddElementDecl:
1093 * @ctxt: the validation context
1094 * @dtd: pointer to the DTD
1095 * @name: the entity name
1096 * @type: the element type
1097 * @content: the element content tree or NULL
1098 *
1099 * Register a new element declaration
1100 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001101 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001102 */
1103xmlElementPtr
1104xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
1105 xmlElementTypeVal type,
1106 xmlElementContentPtr content) {
1107 xmlElementPtr ret;
1108 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001109 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001110 xmlChar *ns, *uqname;
1111
1112 if (dtd == NULL) {
1113 xmlGenericError(xmlGenericErrorContext,
1114 "xmlAddElementDecl: dtd == NULL\n");
1115 return(NULL);
1116 }
1117 if (name == NULL) {
1118 xmlGenericError(xmlGenericErrorContext,
1119 "xmlAddElementDecl: name == NULL\n");
1120 return(NULL);
1121 }
1122 switch (type) {
1123 case XML_ELEMENT_TYPE_EMPTY:
1124 if (content != NULL) {
1125 xmlGenericError(xmlGenericErrorContext,
1126 "xmlAddElementDecl: content != NULL for EMPTY\n");
1127 return(NULL);
1128 }
1129 break;
1130 case XML_ELEMENT_TYPE_ANY:
1131 if (content != NULL) {
1132 xmlGenericError(xmlGenericErrorContext,
1133 "xmlAddElementDecl: content != NULL for ANY\n");
1134 return(NULL);
1135 }
1136 break;
1137 case XML_ELEMENT_TYPE_MIXED:
1138 if (content == NULL) {
1139 xmlGenericError(xmlGenericErrorContext,
1140 "xmlAddElementDecl: content == NULL for MIXED\n");
1141 return(NULL);
1142 }
1143 break;
1144 case XML_ELEMENT_TYPE_ELEMENT:
1145 if (content == NULL) {
1146 xmlGenericError(xmlGenericErrorContext,
1147 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1148 return(NULL);
1149 }
1150 break;
1151 default:
1152 xmlGenericError(xmlGenericErrorContext,
1153 "xmlAddElementDecl: unknown type %d\n", type);
1154 return(NULL);
1155 }
1156
1157 /*
1158 * check if name is a QName
1159 */
1160 uqname = xmlSplitQName2(name, &ns);
1161 if (uqname != NULL)
1162 name = uqname;
1163
1164 /*
1165 * Create the Element table if needed.
1166 */
1167 table = (xmlElementTablePtr) dtd->elements;
1168 if (table == NULL) {
1169 table = xmlCreateElementTable();
1170 dtd->elements = (void *) table;
1171 }
1172 if (table == NULL) {
1173 xmlGenericError(xmlGenericErrorContext,
1174 "xmlAddElementDecl: Table creation failed!\n");
1175 return(NULL);
1176 }
1177
Daniel Veillarda10efa82001-04-18 13:09:01 +00001178 /*
1179 * lookup old attributes inserted on an undefined element in the
1180 * internal subset.
1181 */
1182 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1183 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1184 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1185 oldAttributes = ret->attributes;
1186 ret->attributes = NULL;
1187 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1188 xmlFreeElement(ret);
1189 }
Owen Taylor3473f882001-02-23 17:55:21 +00001190 }
Owen Taylor3473f882001-02-23 17:55:21 +00001191
1192 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001193 * The element may already be present if one of its attribute
1194 * was registered first
1195 */
1196 ret = xmlHashLookup2(table, name, ns);
1197 if (ret != NULL) {
1198 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1199 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001200 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001201 */
1202 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1203 if (uqname != NULL)
1204 xmlFree(uqname);
1205 return(NULL);
1206 }
1207 } else {
1208 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1209 if (ret == NULL) {
1210 xmlGenericError(xmlGenericErrorContext,
1211 "xmlAddElementDecl: out of memory\n");
1212 return(NULL);
1213 }
1214 memset(ret, 0, sizeof(xmlElement));
1215 ret->type = XML_ELEMENT_DECL;
1216
1217 /*
1218 * fill the structure.
1219 */
1220 ret->name = xmlStrdup(name);
1221 ret->prefix = ns;
1222
1223 /*
1224 * Validity Check:
1225 * Insertion must not fail
1226 */
1227 if (xmlHashAddEntry2(table, name, ns, ret)) {
1228 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001229 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001230 */
1231 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1232 xmlFreeElement(ret);
1233 if (uqname != NULL)
1234 xmlFree(uqname);
1235 return(NULL);
1236 }
1237 }
1238
1239 /*
1240 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001241 */
1242 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001243 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001244 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001245
1246 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001247 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001248 */
1249 ret->parent = dtd;
1250 ret->doc = dtd->doc;
1251 if (dtd->last == NULL) {
1252 dtd->children = dtd->last = (xmlNodePtr) ret;
1253 } else {
1254 dtd->last->next = (xmlNodePtr) ret;
1255 ret->prev = dtd->last;
1256 dtd->last = (xmlNodePtr) ret;
1257 }
1258 if (uqname != NULL)
1259 xmlFree(uqname);
1260 return(ret);
1261}
1262
1263/**
1264 * xmlFreeElementTable:
1265 * @table: An element table
1266 *
1267 * Deallocate the memory used by an element hash table.
1268 */
1269void
1270xmlFreeElementTable(xmlElementTablePtr table) {
1271 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1272}
1273
1274/**
1275 * xmlCopyElement:
1276 * @elem: An element
1277 *
1278 * Build a copy of an element.
1279 *
1280 * Returns the new xmlElementPtr or NULL in case of error.
1281 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001282static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001283xmlCopyElement(xmlElementPtr elem) {
1284 xmlElementPtr cur;
1285
1286 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1287 if (cur == NULL) {
1288 xmlGenericError(xmlGenericErrorContext,
1289 "xmlCopyElement: out of memory !\n");
1290 return(NULL);
1291 }
1292 memset(cur, 0, sizeof(xmlElement));
1293 cur->type = XML_ELEMENT_DECL;
1294 cur->etype = elem->etype;
1295 if (elem->name != NULL)
1296 cur->name = xmlStrdup(elem->name);
1297 else
1298 cur->name = NULL;
1299 if (elem->prefix != NULL)
1300 cur->prefix = xmlStrdup(elem->prefix);
1301 else
1302 cur->prefix = NULL;
1303 cur->content = xmlCopyElementContent(elem->content);
1304 /* TODO : rebuild the attribute list on the copy */
1305 cur->attributes = NULL;
1306 return(cur);
1307}
1308
1309/**
1310 * xmlCopyElementTable:
1311 * @table: An element table
1312 *
1313 * Build a copy of an element table.
1314 *
1315 * Returns the new xmlElementTablePtr or NULL in case of error.
1316 */
1317xmlElementTablePtr
1318xmlCopyElementTable(xmlElementTablePtr table) {
1319 return((xmlElementTablePtr) xmlHashCopy(table,
1320 (xmlHashCopier) xmlCopyElement));
1321}
1322
1323/**
1324 * xmlDumpElementDecl:
1325 * @buf: the XML buffer output
1326 * @elem: An element table
1327 *
1328 * This will dump the content of the element declaration as an XML
1329 * DTD definition
1330 */
1331void
1332xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1333 switch (elem->etype) {
1334 case XML_ELEMENT_TYPE_EMPTY:
1335 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001336 if (elem->prefix != NULL) {
1337 xmlBufferWriteCHAR(buf, elem->prefix);
1338 xmlBufferWriteChar(buf, ":");
1339 }
Owen Taylor3473f882001-02-23 17:55:21 +00001340 xmlBufferWriteCHAR(buf, elem->name);
1341 xmlBufferWriteChar(buf, " EMPTY>\n");
1342 break;
1343 case XML_ELEMENT_TYPE_ANY:
1344 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001345 if (elem->prefix != NULL) {
1346 xmlBufferWriteCHAR(buf, elem->prefix);
1347 xmlBufferWriteChar(buf, ":");
1348 }
Owen Taylor3473f882001-02-23 17:55:21 +00001349 xmlBufferWriteCHAR(buf, elem->name);
1350 xmlBufferWriteChar(buf, " ANY>\n");
1351 break;
1352 case XML_ELEMENT_TYPE_MIXED:
1353 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001354 if (elem->prefix != NULL) {
1355 xmlBufferWriteCHAR(buf, elem->prefix);
1356 xmlBufferWriteChar(buf, ":");
1357 }
Owen Taylor3473f882001-02-23 17:55:21 +00001358 xmlBufferWriteCHAR(buf, elem->name);
1359 xmlBufferWriteChar(buf, " ");
1360 xmlDumpElementContent(buf, elem->content, 1);
1361 xmlBufferWriteChar(buf, ">\n");
1362 break;
1363 case XML_ELEMENT_TYPE_ELEMENT:
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 default:
1375 xmlGenericError(xmlGenericErrorContext,
1376 "xmlDumpElementDecl: internal: unknown type %d\n",
1377 elem->etype);
1378 }
1379}
1380
1381/**
1382 * xmlDumpElementTable:
1383 * @buf: the XML buffer output
1384 * @table: An element table
1385 *
1386 * This will dump the content of the element table as an XML DTD definition
1387 */
1388void
1389xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1390 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1391}
1392
1393/**
1394 * xmlCreateEnumeration:
1395 * @name: the enumeration name or NULL
1396 *
1397 * create and initialize an enumeration attribute node.
1398 *
1399 * Returns the xmlEnumerationPtr just created or NULL in case
1400 * of error.
1401 */
1402xmlEnumerationPtr
1403xmlCreateEnumeration(xmlChar *name) {
1404 xmlEnumerationPtr ret;
1405
1406 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1407 if (ret == NULL) {
1408 xmlGenericError(xmlGenericErrorContext,
1409 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1410 (long)sizeof(xmlEnumeration));
1411 return(NULL);
1412 }
1413 memset(ret, 0, sizeof(xmlEnumeration));
1414
1415 if (name != NULL)
1416 ret->name = xmlStrdup(name);
1417 return(ret);
1418}
1419
1420/**
1421 * xmlFreeEnumeration:
1422 * @cur: the tree to free.
1423 *
1424 * free an enumeration attribute node (recursive).
1425 */
1426void
1427xmlFreeEnumeration(xmlEnumerationPtr cur) {
1428 if (cur == NULL) return;
1429
1430 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1431
1432 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001433 xmlFree(cur);
1434}
1435
1436/**
1437 * xmlCopyEnumeration:
1438 * @cur: the tree to copy.
1439 *
1440 * Copy an enumeration attribute node (recursive).
1441 *
1442 * Returns the xmlEnumerationPtr just created or NULL in case
1443 * of error.
1444 */
1445xmlEnumerationPtr
1446xmlCopyEnumeration(xmlEnumerationPtr cur) {
1447 xmlEnumerationPtr ret;
1448
1449 if (cur == NULL) return(NULL);
1450 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1451
1452 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1453 else ret->next = NULL;
1454
1455 return(ret);
1456}
1457
1458/**
1459 * xmlDumpEnumeration:
1460 * @buf: the XML buffer output
1461 * @enum: An enumeration
1462 *
1463 * This will dump the content of the enumeration
1464 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001465static void
Owen Taylor3473f882001-02-23 17:55:21 +00001466xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1467 if (cur == NULL) return;
1468
1469 xmlBufferWriteCHAR(buf, cur->name);
1470 if (cur->next == NULL)
1471 xmlBufferWriteChar(buf, ")");
1472 else {
1473 xmlBufferWriteChar(buf, " | ");
1474 xmlDumpEnumeration(buf, cur->next);
1475 }
1476}
1477
1478/**
1479 * xmlCreateAttributeTable:
1480 *
1481 * create and initialize an empty attribute hash table.
1482 *
1483 * Returns the xmlAttributeTablePtr just created or NULL in case
1484 * of error.
1485 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001486static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001487xmlCreateAttributeTable(void) {
1488 return(xmlHashCreate(0));
1489}
1490
1491/**
1492 * xmlScanAttributeDeclCallback:
1493 * @attr: the attribute decl
1494 * @list: the list to update
1495 *
1496 * Callback called by xmlScanAttributeDecl when a new attribute
1497 * has to be entered in the list.
1498 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001499static void
Owen Taylor3473f882001-02-23 17:55:21 +00001500xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001501 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001502 attr->nexth = *list;
1503 *list = attr;
1504}
1505
1506/**
1507 * xmlScanAttributeDecl:
1508 * @dtd: pointer to the DTD
1509 * @elem: the element name
1510 *
1511 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001512 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001513 *
1514 * Returns the pointer to the first attribute decl in the chain,
1515 * possibly NULL.
1516 */
1517xmlAttributePtr
1518xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1519 xmlAttributePtr ret = NULL;
1520 xmlAttributeTablePtr table;
1521
1522 if (dtd == NULL) {
1523 xmlGenericError(xmlGenericErrorContext,
1524 "xmlScanAttributeDecl: dtd == NULL\n");
1525 return(NULL);
1526 }
1527 if (elem == NULL) {
1528 xmlGenericError(xmlGenericErrorContext,
1529 "xmlScanAttributeDecl: elem == NULL\n");
1530 return(NULL);
1531 }
1532 table = (xmlAttributeTablePtr) dtd->attributes;
1533 if (table == NULL)
1534 return(NULL);
1535
1536 /* WRONG !!! */
1537 xmlHashScan3(table, NULL, NULL, elem,
1538 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1539 return(ret);
1540}
1541
1542/**
1543 * xmlScanIDAttributeDecl:
1544 * @ctxt: the validation context
1545 * @elem: the element name
1546 *
1547 * Verify that the element don't have too many ID attributes
1548 * declared.
1549 *
1550 * Returns the number of ID attributes found.
1551 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001552static int
Owen Taylor3473f882001-02-23 17:55:21 +00001553xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1554 xmlAttributePtr cur;
1555 int ret = 0;
1556
1557 if (elem == NULL) return(0);
1558 cur = elem->attributes;
1559 while (cur != NULL) {
1560 if (cur->atype == XML_ATTRIBUTE_ID) {
1561 ret ++;
1562 if (ret > 1)
1563 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001564 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001565 elem->name, cur->name);
1566 }
1567 cur = cur->nexth;
1568 }
1569 return(ret);
1570}
1571
1572/**
1573 * xmlFreeAttribute:
1574 * @elem: An attribute
1575 *
1576 * Deallocate the memory used by an attribute definition
1577 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001578static void
Owen Taylor3473f882001-02-23 17:55:21 +00001579xmlFreeAttribute(xmlAttributePtr attr) {
1580 if (attr == NULL) return;
1581 xmlUnlinkNode((xmlNodePtr) attr);
1582 if (attr->tree != NULL)
1583 xmlFreeEnumeration(attr->tree);
1584 if (attr->elem != NULL)
1585 xmlFree((xmlChar *) attr->elem);
1586 if (attr->name != NULL)
1587 xmlFree((xmlChar *) attr->name);
1588 if (attr->defaultValue != NULL)
1589 xmlFree((xmlChar *) attr->defaultValue);
1590 if (attr->prefix != NULL)
1591 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001592 xmlFree(attr);
1593}
1594
1595
1596/**
1597 * xmlAddAttributeDecl:
1598 * @ctxt: the validation context
1599 * @dtd: pointer to the DTD
1600 * @elem: the element name
1601 * @name: the attribute name
1602 * @ns: the attribute namespace prefix
1603 * @type: the attribute type
1604 * @def: the attribute default type
1605 * @defaultValue: the attribute default value
1606 * @tree: if it's an enumeration, the associated list
1607 *
1608 * Register a new attribute declaration
1609 * Note that @tree becomes the ownership of the DTD
1610 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001611 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001612 */
1613xmlAttributePtr
1614xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1615 const xmlChar *name, const xmlChar *ns,
1616 xmlAttributeType type, xmlAttributeDefault def,
1617 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1618 xmlAttributePtr ret;
1619 xmlAttributeTablePtr table;
1620 xmlElementPtr elemDef;
1621
1622 if (dtd == NULL) {
1623 xmlGenericError(xmlGenericErrorContext,
1624 "xmlAddAttributeDecl: dtd == NULL\n");
1625 xmlFreeEnumeration(tree);
1626 return(NULL);
1627 }
1628 if (name == NULL) {
1629 xmlGenericError(xmlGenericErrorContext,
1630 "xmlAddAttributeDecl: name == NULL\n");
1631 xmlFreeEnumeration(tree);
1632 return(NULL);
1633 }
1634 if (elem == NULL) {
1635 xmlGenericError(xmlGenericErrorContext,
1636 "xmlAddAttributeDecl: elem == NULL\n");
1637 xmlFreeEnumeration(tree);
1638 return(NULL);
1639 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001640
Owen Taylor3473f882001-02-23 17:55:21 +00001641 /*
1642 * Check the type and possibly the default value.
1643 */
1644 switch (type) {
1645 case XML_ATTRIBUTE_CDATA:
1646 break;
1647 case XML_ATTRIBUTE_ID:
1648 break;
1649 case XML_ATTRIBUTE_IDREF:
1650 break;
1651 case XML_ATTRIBUTE_IDREFS:
1652 break;
1653 case XML_ATTRIBUTE_ENTITY:
1654 break;
1655 case XML_ATTRIBUTE_ENTITIES:
1656 break;
1657 case XML_ATTRIBUTE_NMTOKEN:
1658 break;
1659 case XML_ATTRIBUTE_NMTOKENS:
1660 break;
1661 case XML_ATTRIBUTE_ENUMERATION:
1662 break;
1663 case XML_ATTRIBUTE_NOTATION:
1664 break;
1665 default:
1666 xmlGenericError(xmlGenericErrorContext,
1667 "xmlAddAttributeDecl: unknown type %d\n", type);
1668 xmlFreeEnumeration(tree);
1669 return(NULL);
1670 }
1671 if ((defaultValue != NULL) &&
1672 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001673 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001674 elem, name, defaultValue);
1675 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001676 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001677 }
1678
1679 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001680 * Check first that an attribute defined in the external subset wasn't
1681 * already defined in the internal subset
1682 */
1683 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1684 (dtd->doc->intSubset != NULL) &&
1685 (dtd->doc->intSubset->attributes != NULL)) {
1686 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1687 if (ret != NULL)
1688 return(NULL);
1689 }
1690
1691 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001692 * Create the Attribute table if needed.
1693 */
1694 table = (xmlAttributeTablePtr) dtd->attributes;
1695 if (table == NULL) {
1696 table = xmlCreateAttributeTable();
1697 dtd->attributes = (void *) table;
1698 }
1699 if (table == NULL) {
1700 xmlGenericError(xmlGenericErrorContext,
1701 "xmlAddAttributeDecl: Table creation failed!\n");
1702 return(NULL);
1703 }
1704
1705
1706 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1707 if (ret == NULL) {
1708 xmlGenericError(xmlGenericErrorContext,
1709 "xmlAddAttributeDecl: out of memory\n");
1710 return(NULL);
1711 }
1712 memset(ret, 0, sizeof(xmlAttribute));
1713 ret->type = XML_ATTRIBUTE_DECL;
1714
1715 /*
1716 * fill the structure.
1717 */
1718 ret->atype = type;
1719 ret->name = xmlStrdup(name);
1720 ret->prefix = xmlStrdup(ns);
1721 ret->elem = xmlStrdup(elem);
1722 ret->def = def;
1723 ret->tree = tree;
1724 if (defaultValue != NULL)
1725 ret->defaultValue = xmlStrdup(defaultValue);
1726
1727 /*
1728 * Validity Check:
1729 * Search the DTD for previous declarations of the ATTLIST
1730 */
1731 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1732 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001733 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001734 */
1735 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001736 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001737 name, elem);
1738 xmlFreeAttribute(ret);
1739 return(NULL);
1740 }
1741
1742 /*
1743 * Validity Check:
1744 * Multiple ID per element
1745 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001746 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001747 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001748
Owen Taylor3473f882001-02-23 17:55:21 +00001749 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001750 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001751 VERROR(ctxt->userData,
1752 "Element %s has too may ID attributes defined : %s\n",
1753 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001754 ctxt->valid = 0;
1755 }
1756
Daniel Veillard48da9102001-08-07 01:10:10 +00001757 /*
1758 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001759 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001760 */
1761 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1762 ((ret->prefix != NULL &&
1763 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1764 ret->nexth = elemDef->attributes;
1765 elemDef->attributes = ret;
1766 } else {
1767 xmlAttributePtr tmp = elemDef->attributes;
1768
1769 while ((tmp != NULL) &&
1770 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1771 ((ret->prefix != NULL &&
1772 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1773 if (tmp->nexth == NULL)
1774 break;
1775 tmp = tmp->nexth;
1776 }
1777 if (tmp != NULL) {
1778 ret->nexth = tmp->nexth;
1779 tmp->nexth = ret;
1780 } else {
1781 ret->nexth = elemDef->attributes;
1782 elemDef->attributes = ret;
1783 }
1784 }
Owen Taylor3473f882001-02-23 17:55:21 +00001785 }
1786
1787 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001788 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001789 */
1790 ret->parent = dtd;
1791 ret->doc = dtd->doc;
1792 if (dtd->last == NULL) {
1793 dtd->children = dtd->last = (xmlNodePtr) ret;
1794 } else {
1795 dtd->last->next = (xmlNodePtr) ret;
1796 ret->prev = dtd->last;
1797 dtd->last = (xmlNodePtr) ret;
1798 }
1799 return(ret);
1800}
1801
1802/**
1803 * xmlFreeAttributeTable:
1804 * @table: An attribute table
1805 *
1806 * Deallocate the memory used by an entities hash table.
1807 */
1808void
1809xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1810 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1811}
1812
1813/**
1814 * xmlCopyAttribute:
1815 * @attr: An attribute
1816 *
1817 * Build a copy of an attribute.
1818 *
1819 * Returns the new xmlAttributePtr or NULL in case of error.
1820 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001821static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001822xmlCopyAttribute(xmlAttributePtr attr) {
1823 xmlAttributePtr cur;
1824
1825 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1826 if (cur == NULL) {
1827 xmlGenericError(xmlGenericErrorContext,
1828 "xmlCopyAttribute: out of memory !\n");
1829 return(NULL);
1830 }
1831 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001832 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001833 cur->atype = attr->atype;
1834 cur->def = attr->def;
1835 cur->tree = xmlCopyEnumeration(attr->tree);
1836 if (attr->elem != NULL)
1837 cur->elem = xmlStrdup(attr->elem);
1838 if (attr->name != NULL)
1839 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001840 if (attr->prefix != NULL)
1841 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001842 if (attr->defaultValue != NULL)
1843 cur->defaultValue = xmlStrdup(attr->defaultValue);
1844 return(cur);
1845}
1846
1847/**
1848 * xmlCopyAttributeTable:
1849 * @table: An attribute table
1850 *
1851 * Build a copy of an attribute table.
1852 *
1853 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1854 */
1855xmlAttributeTablePtr
1856xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1857 return((xmlAttributeTablePtr) xmlHashCopy(table,
1858 (xmlHashCopier) xmlCopyAttribute));
1859}
1860
1861/**
1862 * xmlDumpAttributeDecl:
1863 * @buf: the XML buffer output
1864 * @attr: An attribute declaration
1865 *
1866 * This will dump the content of the attribute declaration as an XML
1867 * DTD definition
1868 */
1869void
1870xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1871 xmlBufferWriteChar(buf, "<!ATTLIST ");
1872 xmlBufferWriteCHAR(buf, attr->elem);
1873 xmlBufferWriteChar(buf, " ");
1874 if (attr->prefix != NULL) {
1875 xmlBufferWriteCHAR(buf, attr->prefix);
1876 xmlBufferWriteChar(buf, ":");
1877 }
1878 xmlBufferWriteCHAR(buf, attr->name);
1879 switch (attr->atype) {
1880 case XML_ATTRIBUTE_CDATA:
1881 xmlBufferWriteChar(buf, " CDATA");
1882 break;
1883 case XML_ATTRIBUTE_ID:
1884 xmlBufferWriteChar(buf, " ID");
1885 break;
1886 case XML_ATTRIBUTE_IDREF:
1887 xmlBufferWriteChar(buf, " IDREF");
1888 break;
1889 case XML_ATTRIBUTE_IDREFS:
1890 xmlBufferWriteChar(buf, " IDREFS");
1891 break;
1892 case XML_ATTRIBUTE_ENTITY:
1893 xmlBufferWriteChar(buf, " ENTITY");
1894 break;
1895 case XML_ATTRIBUTE_ENTITIES:
1896 xmlBufferWriteChar(buf, " ENTITIES");
1897 break;
1898 case XML_ATTRIBUTE_NMTOKEN:
1899 xmlBufferWriteChar(buf, " NMTOKEN");
1900 break;
1901 case XML_ATTRIBUTE_NMTOKENS:
1902 xmlBufferWriteChar(buf, " NMTOKENS");
1903 break;
1904 case XML_ATTRIBUTE_ENUMERATION:
1905 xmlBufferWriteChar(buf, " (");
1906 xmlDumpEnumeration(buf, attr->tree);
1907 break;
1908 case XML_ATTRIBUTE_NOTATION:
1909 xmlBufferWriteChar(buf, " NOTATION (");
1910 xmlDumpEnumeration(buf, attr->tree);
1911 break;
1912 default:
1913 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001914 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001915 attr->atype);
1916 }
1917 switch (attr->def) {
1918 case XML_ATTRIBUTE_NONE:
1919 break;
1920 case XML_ATTRIBUTE_REQUIRED:
1921 xmlBufferWriteChar(buf, " #REQUIRED");
1922 break;
1923 case XML_ATTRIBUTE_IMPLIED:
1924 xmlBufferWriteChar(buf, " #IMPLIED");
1925 break;
1926 case XML_ATTRIBUTE_FIXED:
1927 xmlBufferWriteChar(buf, " #FIXED");
1928 break;
1929 default:
1930 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001931 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001932 attr->def);
1933 }
1934 if (attr->defaultValue != NULL) {
1935 xmlBufferWriteChar(buf, " ");
1936 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1937 }
1938 xmlBufferWriteChar(buf, ">\n");
1939}
1940
1941/**
1942 * xmlDumpAttributeTable:
1943 * @buf: the XML buffer output
1944 * @table: An attribute table
1945 *
1946 * This will dump the content of the attribute table as an XML DTD definition
1947 */
1948void
1949xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1950 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1951}
1952
1953/************************************************************************
1954 * *
1955 * NOTATIONs *
1956 * *
1957 ************************************************************************/
1958/**
1959 * xmlCreateNotationTable:
1960 *
1961 * create and initialize an empty notation hash table.
1962 *
1963 * Returns the xmlNotationTablePtr just created or NULL in case
1964 * of error.
1965 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001966static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001967xmlCreateNotationTable(void) {
1968 return(xmlHashCreate(0));
1969}
1970
1971/**
1972 * xmlFreeNotation:
1973 * @not: A notation
1974 *
1975 * Deallocate the memory used by an notation definition
1976 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001977static void
Owen Taylor3473f882001-02-23 17:55:21 +00001978xmlFreeNotation(xmlNotationPtr nota) {
1979 if (nota == NULL) return;
1980 if (nota->name != NULL)
1981 xmlFree((xmlChar *) nota->name);
1982 if (nota->PublicID != NULL)
1983 xmlFree((xmlChar *) nota->PublicID);
1984 if (nota->SystemID != NULL)
1985 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001986 xmlFree(nota);
1987}
1988
1989
1990/**
1991 * xmlAddNotationDecl:
1992 * @dtd: pointer to the DTD
1993 * @ctxt: the validation context
1994 * @name: the entity name
1995 * @PublicID: the public identifier or NULL
1996 * @SystemID: the system identifier or NULL
1997 *
1998 * Register a new notation declaration
1999 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002000 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00002001 */
2002xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002003xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002004 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00002005 const xmlChar *PublicID, const xmlChar *SystemID) {
2006 xmlNotationPtr ret;
2007 xmlNotationTablePtr table;
2008
2009 if (dtd == NULL) {
2010 xmlGenericError(xmlGenericErrorContext,
2011 "xmlAddNotationDecl: dtd == NULL\n");
2012 return(NULL);
2013 }
2014 if (name == NULL) {
2015 xmlGenericError(xmlGenericErrorContext,
2016 "xmlAddNotationDecl: name == NULL\n");
2017 return(NULL);
2018 }
2019 if ((PublicID == NULL) && (SystemID == NULL)) {
2020 xmlGenericError(xmlGenericErrorContext,
2021 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00002022 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002023 }
2024
2025 /*
2026 * Create the Notation table if needed.
2027 */
2028 table = (xmlNotationTablePtr) dtd->notations;
2029 if (table == NULL)
2030 dtd->notations = table = xmlCreateNotationTable();
2031 if (table == NULL) {
2032 xmlGenericError(xmlGenericErrorContext,
2033 "xmlAddNotationDecl: Table creation failed!\n");
2034 return(NULL);
2035 }
2036
2037 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2038 if (ret == NULL) {
2039 xmlGenericError(xmlGenericErrorContext,
2040 "xmlAddNotationDecl: out of memory\n");
2041 return(NULL);
2042 }
2043 memset(ret, 0, sizeof(xmlNotation));
2044
2045 /*
2046 * fill the structure.
2047 */
2048 ret->name = xmlStrdup(name);
2049 if (SystemID != NULL)
2050 ret->SystemID = xmlStrdup(SystemID);
2051 if (PublicID != NULL)
2052 ret->PublicID = xmlStrdup(PublicID);
2053
2054 /*
2055 * Validity Check:
2056 * Check the DTD for previous declarations of the ATTLIST
2057 */
2058 if (xmlHashAddEntry(table, name, ret)) {
2059 xmlGenericError(xmlGenericErrorContext,
2060 "xmlAddNotationDecl: %s already defined\n", name);
2061 xmlFreeNotation(ret);
2062 return(NULL);
2063 }
2064 return(ret);
2065}
2066
2067/**
2068 * xmlFreeNotationTable:
2069 * @table: An notation table
2070 *
2071 * Deallocate the memory used by an entities hash table.
2072 */
2073void
2074xmlFreeNotationTable(xmlNotationTablePtr table) {
2075 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2076}
2077
2078/**
2079 * xmlCopyNotation:
2080 * @nota: A notation
2081 *
2082 * Build a copy of a notation.
2083 *
2084 * Returns the new xmlNotationPtr or NULL in case of error.
2085 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002086static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002087xmlCopyNotation(xmlNotationPtr nota) {
2088 xmlNotationPtr cur;
2089
2090 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2091 if (cur == NULL) {
2092 xmlGenericError(xmlGenericErrorContext,
2093 "xmlCopyNotation: out of memory !\n");
2094 return(NULL);
2095 }
2096 if (nota->name != NULL)
2097 cur->name = xmlStrdup(nota->name);
2098 else
2099 cur->name = NULL;
2100 if (nota->PublicID != NULL)
2101 cur->PublicID = xmlStrdup(nota->PublicID);
2102 else
2103 cur->PublicID = NULL;
2104 if (nota->SystemID != NULL)
2105 cur->SystemID = xmlStrdup(nota->SystemID);
2106 else
2107 cur->SystemID = NULL;
2108 return(cur);
2109}
2110
2111/**
2112 * xmlCopyNotationTable:
2113 * @table: A notation table
2114 *
2115 * Build a copy of a notation table.
2116 *
2117 * Returns the new xmlNotationTablePtr or NULL in case of error.
2118 */
2119xmlNotationTablePtr
2120xmlCopyNotationTable(xmlNotationTablePtr table) {
2121 return((xmlNotationTablePtr) xmlHashCopy(table,
2122 (xmlHashCopier) xmlCopyNotation));
2123}
2124
2125/**
2126 * xmlDumpNotationDecl:
2127 * @buf: the XML buffer output
2128 * @nota: A notation declaration
2129 *
2130 * This will dump the content the notation declaration as an XML DTD definition
2131 */
2132void
2133xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2134 xmlBufferWriteChar(buf, "<!NOTATION ");
2135 xmlBufferWriteCHAR(buf, nota->name);
2136 if (nota->PublicID != NULL) {
2137 xmlBufferWriteChar(buf, " PUBLIC ");
2138 xmlBufferWriteQuotedString(buf, nota->PublicID);
2139 if (nota->SystemID != NULL) {
2140 xmlBufferWriteChar(buf, " ");
2141 xmlBufferWriteCHAR(buf, nota->SystemID);
2142 }
2143 } else {
2144 xmlBufferWriteChar(buf, " SYSTEM ");
2145 xmlBufferWriteCHAR(buf, nota->SystemID);
2146 }
2147 xmlBufferWriteChar(buf, " >\n");
2148}
2149
2150/**
2151 * xmlDumpNotationTable:
2152 * @buf: the XML buffer output
2153 * @table: A notation table
2154 *
2155 * This will dump the content of the notation table as an XML DTD definition
2156 */
2157void
2158xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2159 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2160}
2161
2162/************************************************************************
2163 * *
2164 * IDs *
2165 * *
2166 ************************************************************************/
2167/**
2168 * xmlCreateIDTable:
2169 *
2170 * create and initialize an empty id hash table.
2171 *
2172 * Returns the xmlIDTablePtr just created or NULL in case
2173 * of error.
2174 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002175static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002176xmlCreateIDTable(void) {
2177 return(xmlHashCreate(0));
2178}
2179
2180/**
2181 * xmlFreeID:
2182 * @not: A id
2183 *
2184 * Deallocate the memory used by an id definition
2185 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002186static void
Owen Taylor3473f882001-02-23 17:55:21 +00002187xmlFreeID(xmlIDPtr id) {
2188 if (id == NULL) return;
2189 if (id->value != NULL)
2190 xmlFree((xmlChar *) id->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002191 if (id->name != NULL)
2192 xmlFree((xmlChar *) id->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002193 xmlFree(id);
2194}
2195
2196/**
2197 * xmlAddID:
2198 * @ctxt: the validation context
2199 * @doc: pointer to the document
2200 * @value: the value name
2201 * @attr: the attribute holding the ID
2202 *
2203 * Register a new id declaration
2204 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002205 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002206 */
2207xmlIDPtr
2208xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2209 xmlAttrPtr attr) {
2210 xmlIDPtr ret;
2211 xmlIDTablePtr table;
2212
2213 if (doc == NULL) {
2214 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002215 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002216 return(NULL);
2217 }
2218 if (value == NULL) {
2219 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002220 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002221 return(NULL);
2222 }
2223 if (attr == NULL) {
2224 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002225 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002226 return(NULL);
2227 }
2228
2229 /*
2230 * Create the ID table if needed.
2231 */
2232 table = (xmlIDTablePtr) doc->ids;
2233 if (table == NULL)
2234 doc->ids = table = xmlCreateIDTable();
2235 if (table == NULL) {
2236 xmlGenericError(xmlGenericErrorContext,
2237 "xmlAddID: Table creation failed!\n");
2238 return(NULL);
2239 }
2240
2241 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2242 if (ret == NULL) {
2243 xmlGenericError(xmlGenericErrorContext,
2244 "xmlAddID: out of memory\n");
2245 return(NULL);
2246 }
2247
2248 /*
2249 * fill the structure.
2250 */
2251 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002252 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2253 /*
2254 * Operating in streaming mode, attr is gonna disapear
2255 */
2256 ret->name = xmlStrdup(attr->name);
2257 ret->attr = NULL;
2258 } else {
2259 ret->attr = attr;
2260 ret->name = NULL;
2261 }
2262 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002263
2264 if (xmlHashAddEntry(table, value, ret) < 0) {
2265 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002266 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002267 */
Daniel Veillard76575762002-09-05 14:21:15 +00002268 if (ctxt != NULL) {
2269 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002270 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002271 }
Owen Taylor3473f882001-02-23 17:55:21 +00002272 xmlFreeID(ret);
2273 return(NULL);
2274 }
2275 return(ret);
2276}
2277
2278/**
2279 * xmlFreeIDTable:
2280 * @table: An id table
2281 *
2282 * Deallocate the memory used by an ID hash table.
2283 */
2284void
2285xmlFreeIDTable(xmlIDTablePtr table) {
2286 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2287}
2288
2289/**
2290 * xmlIsID:
2291 * @doc: the document
2292 * @elem: the element carrying the attribute
2293 * @attr: the attribute
2294 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002295 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002296 * then this is done if DTD loading has been requested. In the case
2297 * of HTML documents parsed with the HTML parser, then ID detection is
2298 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002299 *
2300 * Returns 0 or 1 depending on the lookup result
2301 */
2302int
2303xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2304 if (doc == NULL) return(0);
2305 if (attr == NULL) return(0);
2306 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2307 return(0);
2308 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2309 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2310 (xmlStrEqual(BAD_CAST "name", attr->name)))
2311 return(1);
2312 return(0);
2313 } else {
2314 xmlAttributePtr attrDecl;
2315
2316 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002317 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2318 /*
2319 * TODO: this sucks ... recomputing this every time is stupid
2320 */
2321 int len = xmlStrlen(elem->name) + xmlStrlen(elem->ns->prefix) + 2;
2322 xmlChar *fullname;
2323
2324 fullname = xmlMalloc(len);
2325 if (fullname == NULL)
2326 return(0);
2327 snprintf((char *) fullname, len, "%s:%s", (char *) elem->ns->prefix,
2328 (char *) elem->name);
2329 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2330 attr->name);
2331 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2332 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2333 attr->name);
2334 xmlFree(fullname);
2335 } else {
2336 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2337 attr->name);
2338 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2339 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2340 attr->name);
2341 }
Owen Taylor3473f882001-02-23 17:55:21 +00002342
2343 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2344 return(1);
2345 }
2346 return(0);
2347}
2348
2349/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002350 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002351 * @doc: the document
2352 * @attr: the attribute
2353 *
2354 * Remove the given attribute from the ID table maintained internally.
2355 *
2356 * Returns -1 if the lookup failed and 0 otherwise
2357 */
2358int
2359xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2360 xmlAttrPtr cur;
2361 xmlIDTablePtr table;
2362 xmlChar *ID;
2363
2364 if (doc == NULL) return(-1);
2365 if (attr == NULL) return(-1);
2366 table = (xmlIDTablePtr) doc->ids;
2367 if (table == NULL)
2368 return(-1);
2369
2370 if (attr == NULL)
2371 return(-1);
2372 ID = xmlNodeListGetString(doc, attr->children, 1);
2373 if (ID == NULL)
2374 return(-1);
2375 cur = xmlHashLookup(table, ID);
2376 if (cur != attr) {
2377 xmlFree(ID);
2378 return(-1);
2379 }
2380 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2381 xmlFree(ID);
2382 return(0);
2383}
2384
2385/**
2386 * xmlGetID:
2387 * @doc: pointer to the document
2388 * @ID: the ID value
2389 *
2390 * Search the attribute declaring the given ID
2391 *
2392 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2393 */
2394xmlAttrPtr
2395xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2396 xmlIDTablePtr table;
2397 xmlIDPtr id;
2398
2399 if (doc == NULL) {
2400 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2401 return(NULL);
2402 }
2403
2404 if (ID == NULL) {
2405 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2406 return(NULL);
2407 }
2408
2409 table = (xmlIDTablePtr) doc->ids;
2410 if (table == NULL)
2411 return(NULL);
2412
2413 id = xmlHashLookup(table, ID);
2414 if (id == NULL)
2415 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002416 if (id->attr == NULL) {
2417 /*
2418 * We are operating on a stream, return a well known reference
2419 * since the attribute node doesn't exist anymore
2420 */
2421 return((xmlAttrPtr) doc);
2422 }
Owen Taylor3473f882001-02-23 17:55:21 +00002423 return(id->attr);
2424}
2425
2426/************************************************************************
2427 * *
2428 * Refs *
2429 * *
2430 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002431typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002432{
2433 xmlListPtr l;
2434 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002435} xmlRemoveMemo;
2436
2437typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2438
2439typedef struct xmlValidateMemo_t
2440{
2441 xmlValidCtxtPtr ctxt;
2442 const xmlChar *name;
2443} xmlValidateMemo;
2444
2445typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002446
2447/**
2448 * xmlCreateRefTable:
2449 *
2450 * create and initialize an empty ref hash table.
2451 *
2452 * Returns the xmlRefTablePtr just created or NULL in case
2453 * of error.
2454 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002455static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002456xmlCreateRefTable(void) {
2457 return(xmlHashCreate(0));
2458}
2459
2460/**
2461 * xmlFreeRef:
2462 * @lk: A list link
2463 *
2464 * Deallocate the memory used by a ref definition
2465 */
2466static void
2467xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002468 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2469 if (ref == NULL) return;
2470 if (ref->value != NULL)
2471 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002472 if (ref->name != NULL)
2473 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002474 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002475}
2476
2477/**
2478 * xmlFreeRefList:
2479 * @list_ref: A list of references.
2480 *
2481 * Deallocate the memory used by a list of references
2482 */
2483static void
2484xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002485 if (list_ref == NULL) return;
2486 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002487}
2488
2489/**
2490 * xmlWalkRemoveRef:
2491 * @data: Contents of current link
2492 * @user: Value supplied by the user
2493 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002494 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002495 */
2496static int
2497xmlWalkRemoveRef(const void *data, const void *user)
2498{
Daniel Veillard37721922001-05-04 15:21:12 +00002499 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2500 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2501 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002502
Daniel Veillard37721922001-05-04 15:21:12 +00002503 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2504 xmlListRemoveFirst(ref_list, (void *)data);
2505 return 0;
2506 }
2507 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002508}
2509
2510/**
2511 * xmlAddRef:
2512 * @ctxt: the validation context
2513 * @doc: pointer to the document
2514 * @value: the value name
2515 * @attr: the attribute holding the Ref
2516 *
2517 * Register a new ref declaration
2518 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002519 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002520 */
2521xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002522xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002523 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002524 xmlRefPtr ret;
2525 xmlRefTablePtr table;
2526 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002527
Daniel Veillard37721922001-05-04 15:21:12 +00002528 if (doc == NULL) {
2529 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002530 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002531 return(NULL);
2532 }
2533 if (value == NULL) {
2534 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002535 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002536 return(NULL);
2537 }
2538 if (attr == NULL) {
2539 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002540 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002541 return(NULL);
2542 }
Owen Taylor3473f882001-02-23 17:55:21 +00002543
Daniel Veillard37721922001-05-04 15:21:12 +00002544 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002545 * Create the Ref table if needed.
2546 */
Daniel Veillard37721922001-05-04 15:21:12 +00002547 table = (xmlRefTablePtr) doc->refs;
2548 if (table == NULL)
2549 doc->refs = table = xmlCreateRefTable();
2550 if (table == NULL) {
2551 xmlGenericError(xmlGenericErrorContext,
2552 "xmlAddRef: Table creation failed!\n");
2553 return(NULL);
2554 }
Owen Taylor3473f882001-02-23 17:55:21 +00002555
Daniel Veillard37721922001-05-04 15:21:12 +00002556 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2557 if (ret == NULL) {
2558 xmlGenericError(xmlGenericErrorContext,
2559 "xmlAddRef: out of memory\n");
2560 return(NULL);
2561 }
Owen Taylor3473f882001-02-23 17:55:21 +00002562
Daniel Veillard37721922001-05-04 15:21:12 +00002563 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002564 * fill the structure.
2565 */
Daniel Veillard37721922001-05-04 15:21:12 +00002566 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002567 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2568 /*
2569 * Operating in streaming mode, attr is gonna disapear
2570 */
2571 ret->name = xmlStrdup(attr->name);
2572 ret->attr = NULL;
2573 } else {
2574 ret->name = NULL;
2575 ret->attr = attr;
2576 }
2577 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002578
Daniel Veillard37721922001-05-04 15:21:12 +00002579 /* To add a reference :-
2580 * References are maintained as a list of references,
2581 * Lookup the entry, if no entry create new nodelist
2582 * Add the owning node to the NodeList
2583 * Return the ref
2584 */
Owen Taylor3473f882001-02-23 17:55:21 +00002585
Daniel Veillard37721922001-05-04 15:21:12 +00002586 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2587 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2588 xmlGenericError(xmlGenericErrorContext,
2589 "xmlAddRef: Reference list creation failed!\n");
2590 return(NULL);
2591 }
2592 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2593 xmlListDelete(ref_list);
2594 xmlGenericError(xmlGenericErrorContext,
2595 "xmlAddRef: Reference list insertion failed!\n");
2596 return(NULL);
2597 }
2598 }
2599 xmlListInsert(ref_list, ret);
2600 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002601}
2602
2603/**
2604 * xmlFreeRefTable:
2605 * @table: An ref table
2606 *
2607 * Deallocate the memory used by an Ref hash table.
2608 */
2609void
2610xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002611 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002612}
2613
2614/**
2615 * xmlIsRef:
2616 * @doc: the document
2617 * @elem: the element carrying the attribute
2618 * @attr: the attribute
2619 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002620 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002621 * then this is simple, otherwise we use an heuristic: name Ref (upper
2622 * or lowercase).
2623 *
2624 * Returns 0 or 1 depending on the lookup result
2625 */
2626int
2627xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002628 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2629 return(0);
2630 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2631 /* TODO @@@ */
2632 return(0);
2633 } else {
2634 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002635
Daniel Veillard37721922001-05-04 15:21:12 +00002636 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2637 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2638 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2639 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002640
Daniel Veillard37721922001-05-04 15:21:12 +00002641 if ((attrDecl != NULL) &&
2642 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2643 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2644 return(1);
2645 }
2646 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002647}
2648
2649/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002650 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002651 * @doc: the document
2652 * @attr: the attribute
2653 *
2654 * Remove the given attribute from the Ref table maintained internally.
2655 *
2656 * Returns -1 if the lookup failed and 0 otherwise
2657 */
2658int
2659xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002660 xmlListPtr ref_list;
2661 xmlRefTablePtr table;
2662 xmlChar *ID;
2663 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002664
Daniel Veillard37721922001-05-04 15:21:12 +00002665 if (doc == NULL) return(-1);
2666 if (attr == NULL) return(-1);
2667 table = (xmlRefTablePtr) doc->refs;
2668 if (table == NULL)
2669 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002670
Daniel Veillard37721922001-05-04 15:21:12 +00002671 if (attr == NULL)
2672 return(-1);
2673 ID = xmlNodeListGetString(doc, attr->children, 1);
2674 if (ID == NULL)
2675 return(-1);
2676 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002677
Daniel Veillard37721922001-05-04 15:21:12 +00002678 if(ref_list == NULL) {
2679 xmlFree(ID);
2680 return (-1);
2681 }
2682 /* At this point, ref_list refers to a list of references which
2683 * have the same key as the supplied attr. Our list of references
2684 * is ordered by reference address and we don't have that information
2685 * here to use when removing. We'll have to walk the list and
2686 * check for a matching attribute, when we find one stop the walk
2687 * and remove the entry.
2688 * The list is ordered by reference, so that means we don't have the
2689 * key. Passing the list and the reference to the walker means we
2690 * will have enough data to be able to remove the entry.
2691 */
2692 target.l = ref_list;
2693 target.ap = attr;
2694
2695 /* Remove the supplied attr from our list */
2696 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002697
Daniel Veillard37721922001-05-04 15:21:12 +00002698 /*If the list is empty then remove the list entry in the hash */
2699 if (xmlListEmpty(ref_list))
2700 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2701 xmlFreeRefList);
2702 xmlFree(ID);
2703 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002704}
2705
2706/**
2707 * xmlGetRefs:
2708 * @doc: pointer to the document
2709 * @ID: the ID value
2710 *
2711 * Find the set of references for the supplied ID.
2712 *
2713 * Returns NULL if not found, otherwise node set for the ID.
2714 */
2715xmlListPtr
2716xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002717 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002718
Daniel Veillard37721922001-05-04 15:21:12 +00002719 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002720 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002721 return(NULL);
2722 }
Owen Taylor3473f882001-02-23 17:55:21 +00002723
Daniel Veillard37721922001-05-04 15:21:12 +00002724 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002725 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002726 return(NULL);
2727 }
Owen Taylor3473f882001-02-23 17:55:21 +00002728
Daniel Veillard37721922001-05-04 15:21:12 +00002729 table = (xmlRefTablePtr) doc->refs;
2730 if (table == NULL)
2731 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002732
Daniel Veillard37721922001-05-04 15:21:12 +00002733 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002734}
2735
2736/************************************************************************
2737 * *
2738 * Routines for validity checking *
2739 * *
2740 ************************************************************************/
2741
2742/**
2743 * xmlGetDtdElementDesc:
2744 * @dtd: a pointer to the DtD to search
2745 * @name: the element name
2746 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002747 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002748 *
2749 * returns the xmlElementPtr if found or NULL
2750 */
2751
2752xmlElementPtr
2753xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2754 xmlElementTablePtr table;
2755 xmlElementPtr cur;
2756 xmlChar *uqname = NULL, *prefix = NULL;
2757
2758 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002759 if (dtd->elements == NULL)
2760 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002761 table = (xmlElementTablePtr) dtd->elements;
2762
2763 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002764 if (uqname != NULL)
2765 name = uqname;
2766 cur = xmlHashLookup2(table, name, prefix);
2767 if (prefix != NULL) xmlFree(prefix);
2768 if (uqname != NULL) xmlFree(uqname);
2769 return(cur);
2770}
2771/**
2772 * xmlGetDtdElementDesc2:
2773 * @dtd: a pointer to the DtD to search
2774 * @name: the element name
2775 * @create: create an empty description if not found
2776 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002777 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002778 *
2779 * returns the xmlElementPtr if found or NULL
2780 */
2781
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002782static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002783xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2784 xmlElementTablePtr table;
2785 xmlElementPtr cur;
2786 xmlChar *uqname = NULL, *prefix = NULL;
2787
2788 if (dtd == NULL) return(NULL);
2789 if (dtd->elements == NULL) {
2790 if (!create)
2791 return(NULL);
2792 /*
2793 * Create the Element table if needed.
2794 */
2795 table = (xmlElementTablePtr) dtd->elements;
2796 if (table == NULL) {
2797 table = xmlCreateElementTable();
2798 dtd->elements = (void *) table;
2799 }
2800 if (table == NULL) {
2801 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002802 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002803 return(NULL);
2804 }
2805 }
2806 table = (xmlElementTablePtr) dtd->elements;
2807
2808 uqname = xmlSplitQName2(name, &prefix);
2809 if (uqname != NULL)
2810 name = uqname;
2811 cur = xmlHashLookup2(table, name, prefix);
2812 if ((cur == NULL) && (create)) {
2813 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2814 if (cur == NULL) {
2815 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002816 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002817 return(NULL);
2818 }
2819 memset(cur, 0, sizeof(xmlElement));
2820 cur->type = XML_ELEMENT_DECL;
2821
2822 /*
2823 * fill the structure.
2824 */
2825 cur->name = xmlStrdup(name);
2826 cur->prefix = xmlStrdup(prefix);
2827 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2828
2829 xmlHashAddEntry2(table, name, prefix, cur);
2830 }
2831 if (prefix != NULL) xmlFree(prefix);
2832 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002833 return(cur);
2834}
2835
2836/**
2837 * xmlGetDtdQElementDesc:
2838 * @dtd: a pointer to the DtD to search
2839 * @name: the element name
2840 * @prefix: the element namespace prefix
2841 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002842 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002843 *
2844 * returns the xmlElementPtr if found or NULL
2845 */
2846
Daniel Veillard48da9102001-08-07 01:10:10 +00002847xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002848xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2849 const xmlChar *prefix) {
2850 xmlElementTablePtr table;
2851
2852 if (dtd == NULL) return(NULL);
2853 if (dtd->elements == NULL) return(NULL);
2854 table = (xmlElementTablePtr) dtd->elements;
2855
2856 return(xmlHashLookup2(table, name, prefix));
2857}
2858
2859/**
2860 * xmlGetDtdAttrDesc:
2861 * @dtd: a pointer to the DtD to search
2862 * @elem: the element name
2863 * @name: the attribute name
2864 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002865 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002866 * this element.
2867 *
2868 * returns the xmlAttributePtr if found or NULL
2869 */
2870
2871xmlAttributePtr
2872xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2873 xmlAttributeTablePtr table;
2874 xmlAttributePtr cur;
2875 xmlChar *uqname = NULL, *prefix = NULL;
2876
2877 if (dtd == NULL) return(NULL);
2878 if (dtd->attributes == NULL) return(NULL);
2879
2880 table = (xmlAttributeTablePtr) dtd->attributes;
2881 if (table == NULL)
2882 return(NULL);
2883
2884 uqname = xmlSplitQName2(name, &prefix);
2885
2886 if (uqname != NULL) {
2887 cur = xmlHashLookup3(table, uqname, prefix, elem);
2888 if (prefix != NULL) xmlFree(prefix);
2889 if (uqname != NULL) xmlFree(uqname);
2890 } else
2891 cur = xmlHashLookup3(table, name, NULL, elem);
2892 return(cur);
2893}
2894
2895/**
2896 * xmlGetDtdQAttrDesc:
2897 * @dtd: a pointer to the DtD to search
2898 * @elem: the element name
2899 * @name: the attribute name
2900 * @prefix: the attribute namespace prefix
2901 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002902 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002903 * this element.
2904 *
2905 * returns the xmlAttributePtr if found or NULL
2906 */
2907
Daniel Veillard48da9102001-08-07 01:10:10 +00002908xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002909xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2910 const xmlChar *prefix) {
2911 xmlAttributeTablePtr table;
2912
2913 if (dtd == NULL) return(NULL);
2914 if (dtd->attributes == NULL) return(NULL);
2915 table = (xmlAttributeTablePtr) dtd->attributes;
2916
2917 return(xmlHashLookup3(table, name, prefix, elem));
2918}
2919
2920/**
2921 * xmlGetDtdNotationDesc:
2922 * @dtd: a pointer to the DtD to search
2923 * @name: the notation name
2924 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002925 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002926 *
2927 * returns the xmlNotationPtr if found or NULL
2928 */
2929
2930xmlNotationPtr
2931xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2932 xmlNotationTablePtr table;
2933
2934 if (dtd == NULL) return(NULL);
2935 if (dtd->notations == NULL) return(NULL);
2936 table = (xmlNotationTablePtr) dtd->notations;
2937
2938 return(xmlHashLookup(table, name));
2939}
2940
2941/**
2942 * xmlValidateNotationUse:
2943 * @ctxt: the validation context
2944 * @doc: the document
2945 * @notationName: the notation name to check
2946 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002947 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002948 * - [ VC: Notation Declared ]
2949 *
2950 * returns 1 if valid or 0 otherwise
2951 */
2952
2953int
2954xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2955 const xmlChar *notationName) {
2956 xmlNotationPtr notaDecl;
2957 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2958
2959 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2960 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2961 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2962
2963 if (notaDecl == NULL) {
2964 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2965 notationName);
2966 return(0);
2967 }
2968 return(1);
2969}
2970
2971/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002972 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00002973 * @doc: the document
2974 * @name: the element name
2975 *
2976 * Search in the DtDs whether an element accept Mixed content (or ANY)
2977 * basically if it is supposed to accept text childs
2978 *
2979 * returns 0 if no, 1 if yes, and -1 if no element description is available
2980 */
2981
2982int
2983xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2984 xmlElementPtr elemDecl;
2985
2986 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2987
2988 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2989 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2990 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2991 if (elemDecl == NULL) return(-1);
2992 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002993 case XML_ELEMENT_TYPE_UNDEFINED:
2994 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002995 case XML_ELEMENT_TYPE_ELEMENT:
2996 return(0);
2997 case XML_ELEMENT_TYPE_EMPTY:
2998 /*
2999 * return 1 for EMPTY since we want VC error to pop up
3000 * on <empty> </empty> for example
3001 */
3002 case XML_ELEMENT_TYPE_ANY:
3003 case XML_ELEMENT_TYPE_MIXED:
3004 return(1);
3005 }
3006 return(1);
3007}
3008
3009/**
3010 * xmlValidateNameValue:
3011 * @value: an Name value
3012 *
3013 * Validate that the given value match Name production
3014 *
3015 * returns 1 if valid or 0 otherwise
3016 */
3017
Daniel Veillard9b731d72002-04-14 12:56:08 +00003018int
Owen Taylor3473f882001-02-23 17:55:21 +00003019xmlValidateNameValue(const xmlChar *value) {
3020 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003021 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003022
3023 if (value == NULL) return(0);
3024 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003025 val = xmlStringCurrentChar(NULL, cur, &len);
3026 cur += len;
3027 if (!IS_LETTER(val) && (val != '_') &&
3028 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003029 return(0);
3030 }
3031
Daniel Veillardd8224e02002-01-13 15:43:22 +00003032 val = xmlStringCurrentChar(NULL, cur, &len);
3033 cur += len;
3034 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3035 (val == '.') || (val == '-') ||
3036 (val == '_') || (val == ':') ||
3037 (IS_COMBINING(val)) ||
3038 (IS_EXTENDER(val))) {
3039 val = xmlStringCurrentChar(NULL, cur, &len);
3040 cur += len;
3041 }
Owen Taylor3473f882001-02-23 17:55:21 +00003042
Daniel Veillardd8224e02002-01-13 15:43:22 +00003043 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003044
3045 return(1);
3046}
3047
3048/**
3049 * xmlValidateNamesValue:
3050 * @value: an Names value
3051 *
3052 * Validate that the given value match Names production
3053 *
3054 * returns 1 if valid or 0 otherwise
3055 */
3056
Daniel Veillard9b731d72002-04-14 12:56:08 +00003057int
Owen Taylor3473f882001-02-23 17:55:21 +00003058xmlValidateNamesValue(const xmlChar *value) {
3059 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003060 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003061
3062 if (value == NULL) return(0);
3063 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003064 val = xmlStringCurrentChar(NULL, cur, &len);
3065 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003066
Daniel Veillardd8224e02002-01-13 15:43:22 +00003067 if (!IS_LETTER(val) && (val != '_') &&
3068 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003069 return(0);
3070 }
3071
Daniel Veillardd8224e02002-01-13 15:43:22 +00003072 val = xmlStringCurrentChar(NULL, cur, &len);
3073 cur += len;
3074 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3075 (val == '.') || (val == '-') ||
3076 (val == '_') || (val == ':') ||
3077 (IS_COMBINING(val)) ||
3078 (IS_EXTENDER(val))) {
3079 val = xmlStringCurrentChar(NULL, cur, &len);
3080 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003081 }
3082
Daniel Veillardd8224e02002-01-13 15:43:22 +00003083 while (IS_BLANK(val)) {
3084 while (IS_BLANK(val)) {
3085 val = xmlStringCurrentChar(NULL, cur, &len);
3086 cur += len;
3087 }
3088
3089 if (!IS_LETTER(val) && (val != '_') &&
3090 (val != ':')) {
3091 return(0);
3092 }
3093 val = xmlStringCurrentChar(NULL, cur, &len);
3094 cur += len;
3095
3096 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3097 (val == '.') || (val == '-') ||
3098 (val == '_') || (val == ':') ||
3099 (IS_COMBINING(val)) ||
3100 (IS_EXTENDER(val))) {
3101 val = xmlStringCurrentChar(NULL, cur, &len);
3102 cur += len;
3103 }
3104 }
3105
3106 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003107
3108 return(1);
3109}
3110
3111/**
3112 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003113 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003114 *
3115 * Validate that the given value match Nmtoken production
3116 *
3117 * [ VC: Name Token ]
3118 *
3119 * returns 1 if valid or 0 otherwise
3120 */
3121
Daniel Veillard9b731d72002-04-14 12:56:08 +00003122int
Owen Taylor3473f882001-02-23 17:55:21 +00003123xmlValidateNmtokenValue(const xmlChar *value) {
3124 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003125 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003126
3127 if (value == NULL) return(0);
3128 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003129 val = xmlStringCurrentChar(NULL, cur, &len);
3130 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003131
Daniel Veillardd8224e02002-01-13 15:43:22 +00003132 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3133 (val != '.') && (val != '-') &&
3134 (val != '_') && (val != ':') &&
3135 (!IS_COMBINING(val)) &&
3136 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003137 return(0);
3138
Daniel Veillardd8224e02002-01-13 15:43:22 +00003139 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3140 (val == '.') || (val == '-') ||
3141 (val == '_') || (val == ':') ||
3142 (IS_COMBINING(val)) ||
3143 (IS_EXTENDER(val))) {
3144 val = xmlStringCurrentChar(NULL, cur, &len);
3145 cur += len;
3146 }
Owen Taylor3473f882001-02-23 17:55:21 +00003147
Daniel Veillardd8224e02002-01-13 15:43:22 +00003148 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003149
3150 return(1);
3151}
3152
3153/**
3154 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003155 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003156 *
3157 * Validate that the given value match Nmtokens production
3158 *
3159 * [ VC: Name Token ]
3160 *
3161 * returns 1 if valid or 0 otherwise
3162 */
3163
Daniel Veillard9b731d72002-04-14 12:56:08 +00003164int
Owen Taylor3473f882001-02-23 17:55:21 +00003165xmlValidateNmtokensValue(const xmlChar *value) {
3166 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003167 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003168
3169 if (value == NULL) return(0);
3170 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003171 val = xmlStringCurrentChar(NULL, cur, &len);
3172 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003173
Daniel Veillardd8224e02002-01-13 15:43:22 +00003174 while (IS_BLANK(val)) {
3175 val = xmlStringCurrentChar(NULL, cur, &len);
3176 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003177 }
3178
Daniel Veillardd8224e02002-01-13 15:43:22 +00003179 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3180 (val != '.') && (val != '-') &&
3181 (val != '_') && (val != ':') &&
3182 (!IS_COMBINING(val)) &&
3183 (!IS_EXTENDER(val)))
3184 return(0);
3185
3186 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3187 (val == '.') || (val == '-') ||
3188 (val == '_') || (val == ':') ||
3189 (IS_COMBINING(val)) ||
3190 (IS_EXTENDER(val))) {
3191 val = xmlStringCurrentChar(NULL, cur, &len);
3192 cur += len;
3193 }
3194
3195 while (IS_BLANK(val)) {
3196 while (IS_BLANK(val)) {
3197 val = xmlStringCurrentChar(NULL, cur, &len);
3198 cur += len;
3199 }
3200 if (val == 0) return(1);
3201
3202 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3203 (val != '.') && (val != '-') &&
3204 (val != '_') && (val != ':') &&
3205 (!IS_COMBINING(val)) &&
3206 (!IS_EXTENDER(val)))
3207 return(0);
3208
3209 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3210 (val == '.') || (val == '-') ||
3211 (val == '_') || (val == ':') ||
3212 (IS_COMBINING(val)) ||
3213 (IS_EXTENDER(val))) {
3214 val = xmlStringCurrentChar(NULL, cur, &len);
3215 cur += len;
3216 }
3217 }
3218
3219 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003220
3221 return(1);
3222}
3223
3224/**
3225 * xmlValidateNotationDecl:
3226 * @ctxt: the validation context
3227 * @doc: a document instance
3228 * @nota: a notation definition
3229 *
3230 * Try to validate a single notation definition
3231 * basically it does the following checks as described by the
3232 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003233 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003234 * But this function get called anyway ...
3235 *
3236 * returns 1 if valid or 0 otherwise
3237 */
3238
3239int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003240xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3241 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003242 int ret = 1;
3243
3244 return(ret);
3245}
3246
3247/**
3248 * xmlValidateAttributeValue:
3249 * @type: an attribute type
3250 * @value: an attribute value
3251 *
3252 * Validate that the given attribute value match the proper production
3253 *
3254 * [ VC: ID ]
3255 * Values of type ID must match the Name production....
3256 *
3257 * [ VC: IDREF ]
3258 * Values of type IDREF must match the Name production, and values
3259 * of type IDREFS must match Names ...
3260 *
3261 * [ VC: Entity Name ]
3262 * Values of type ENTITY must match the Name production, values
3263 * of type ENTITIES must match Names ...
3264 *
3265 * [ VC: Name Token ]
3266 * Values of type NMTOKEN must match the Nmtoken production; values
3267 * of type NMTOKENS must match Nmtokens.
3268 *
3269 * returns 1 if valid or 0 otherwise
3270 */
3271
3272int
3273xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3274 switch (type) {
3275 case XML_ATTRIBUTE_ENTITIES:
3276 case XML_ATTRIBUTE_IDREFS:
3277 return(xmlValidateNamesValue(value));
3278 case XML_ATTRIBUTE_ENTITY:
3279 case XML_ATTRIBUTE_IDREF:
3280 case XML_ATTRIBUTE_ID:
3281 case XML_ATTRIBUTE_NOTATION:
3282 return(xmlValidateNameValue(value));
3283 case XML_ATTRIBUTE_NMTOKENS:
3284 case XML_ATTRIBUTE_ENUMERATION:
3285 return(xmlValidateNmtokensValue(value));
3286 case XML_ATTRIBUTE_NMTOKEN:
3287 return(xmlValidateNmtokenValue(value));
3288 case XML_ATTRIBUTE_CDATA:
3289 break;
3290 }
3291 return(1);
3292}
3293
3294/**
3295 * xmlValidateAttributeValue2:
3296 * @ctxt: the validation context
3297 * @doc: the document
3298 * @name: the attribute name (used for error reporting only)
3299 * @type: the attribute type
3300 * @value: the attribute value
3301 *
3302 * Validate that the given attribute value match a given type.
3303 * This typically cannot be done before having finished parsing
3304 * the subsets.
3305 *
3306 * [ VC: IDREF ]
3307 * Values of type IDREF must match one of the declared IDs
3308 * Values of type IDREFS must match a sequence of the declared IDs
3309 * each Name must match the value of an ID attribute on some element
3310 * in the XML document; i.e. IDREF values must match the value of
3311 * some ID attribute
3312 *
3313 * [ VC: Entity Name ]
3314 * Values of type ENTITY must match one declared entity
3315 * Values of type ENTITIES must match a sequence of declared entities
3316 *
3317 * [ VC: Notation Attributes ]
3318 * all notation names in the declaration must be declared.
3319 *
3320 * returns 1 if valid or 0 otherwise
3321 */
3322
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003323static int
Owen Taylor3473f882001-02-23 17:55:21 +00003324xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3325 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3326 int ret = 1;
3327 switch (type) {
3328 case XML_ATTRIBUTE_IDREFS:
3329 case XML_ATTRIBUTE_IDREF:
3330 case XML_ATTRIBUTE_ID:
3331 case XML_ATTRIBUTE_NMTOKENS:
3332 case XML_ATTRIBUTE_ENUMERATION:
3333 case XML_ATTRIBUTE_NMTOKEN:
3334 case XML_ATTRIBUTE_CDATA:
3335 break;
3336 case XML_ATTRIBUTE_ENTITY: {
3337 xmlEntityPtr ent;
3338
3339 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003340 if ((ent == NULL) && (doc->standalone == 1)) {
3341 doc->standalone = 0;
3342 ent = xmlGetDocEntity(doc, value);
3343 if (ent != NULL) {
3344 VERROR(ctxt->userData,
3345"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3346 name, value);
3347 /* WAIT to get answer from the Core WG on this
3348 ret = 0;
3349 */
3350 }
3351 }
Owen Taylor3473f882001-02-23 17:55:21 +00003352 if (ent == NULL) {
3353 VERROR(ctxt->userData,
3354 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3355 name, value);
3356 ret = 0;
3357 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3358 VERROR(ctxt->userData,
3359 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3360 name, value);
3361 ret = 0;
3362 }
3363 break;
3364 }
3365 case XML_ATTRIBUTE_ENTITIES: {
3366 xmlChar *dup, *nam = NULL, *cur, save;
3367 xmlEntityPtr ent;
3368
3369 dup = xmlStrdup(value);
3370 if (dup == NULL)
3371 return(0);
3372 cur = dup;
3373 while (*cur != 0) {
3374 nam = cur;
3375 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3376 save = *cur;
3377 *cur = 0;
3378 ent = xmlGetDocEntity(doc, nam);
3379 if (ent == NULL) {
3380 VERROR(ctxt->userData,
3381 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3382 name, nam);
3383 ret = 0;
3384 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3385 VERROR(ctxt->userData,
3386 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3387 name, nam);
3388 ret = 0;
3389 }
3390 if (save == 0)
3391 break;
3392 *cur = save;
3393 while (IS_BLANK(*cur)) cur++;
3394 }
3395 xmlFree(dup);
3396 break;
3397 }
3398 case XML_ATTRIBUTE_NOTATION: {
3399 xmlNotationPtr nota;
3400
3401 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3402 if ((nota == NULL) && (doc->extSubset != NULL))
3403 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3404
3405 if (nota == NULL) {
3406 VERROR(ctxt->userData,
3407 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3408 name, value);
3409 ret = 0;
3410 }
3411 break;
3412 }
3413 }
3414 return(ret);
3415}
3416
3417/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003418 * xmlValidCtxtNormalizeAttributeValue:
3419 * @ctxt: the validation context
3420 * @doc: the document
3421 * @elem: the parent
3422 * @name: the attribute name
3423 * @value: the attribute value
3424 * @ctxt: the validation context or NULL
3425 *
3426 * Does the validation related extra step of the normalization of attribute
3427 * values:
3428 *
3429 * If the declared value is not CDATA, then the XML processor must further
3430 * process the normalized attribute value by discarding any leading and
3431 * trailing space (#x20) characters, and by replacing sequences of space
3432 * (#x20) characters by single space (#x20) character.
3433 *
3434 * Also check VC: Standalone Document Declaration in P32, and update
3435 * ctxt->valid accordingly
3436 *
3437 * returns a new normalized string if normalization is needed, NULL otherwise
3438 * the caller must free the returned value.
3439 */
3440
3441xmlChar *
3442xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3443 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3444 xmlChar *ret, *dst;
3445 const xmlChar *src;
3446 xmlAttributePtr attrDecl = NULL;
3447 int extsubset = 0;
3448
3449 if (doc == NULL) return(NULL);
3450 if (elem == NULL) return(NULL);
3451 if (name == NULL) return(NULL);
3452 if (value == NULL) return(NULL);
3453
3454 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3455 xmlChar qname[500];
3456 snprintf((char *) qname, sizeof(qname), "%s:%s",
3457 elem->ns->prefix, elem->name);
3458 qname[sizeof(qname) - 1] = 0;
3459 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3460 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3461 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3462 if (attrDecl != NULL)
3463 extsubset = 1;
3464 }
3465 }
3466 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3467 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3468 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3469 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3470 if (attrDecl != NULL)
3471 extsubset = 1;
3472 }
3473
3474 if (attrDecl == NULL)
3475 return(NULL);
3476 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3477 return(NULL);
3478
3479 ret = xmlStrdup(value);
3480 if (ret == NULL)
3481 return(NULL);
3482 src = value;
3483 dst = ret;
3484 while (*src == 0x20) src++;
3485 while (*src != 0) {
3486 if (*src == 0x20) {
3487 while (*src == 0x20) src++;
3488 if (*src != 0)
3489 *dst++ = 0x20;
3490 } else {
3491 *dst++ = *src++;
3492 }
3493 }
3494 *dst = 0;
3495 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3496 VERROR(ctxt->userData,
3497"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3498 name, elem->name);
3499 ctxt->valid = 0;
3500 }
3501 return(ret);
3502}
3503
3504/**
Owen Taylor3473f882001-02-23 17:55:21 +00003505 * xmlValidNormalizeAttributeValue:
3506 * @doc: the document
3507 * @elem: the parent
3508 * @name: the attribute name
3509 * @value: the attribute value
3510 *
3511 * Does the validation related extra step of the normalization of attribute
3512 * values:
3513 *
3514 * If the declared value is not CDATA, then the XML processor must further
3515 * process the normalized attribute value by discarding any leading and
3516 * trailing space (#x20) characters, and by replacing sequences of space
3517 * (#x20) characters by single space (#x20) character.
3518 *
3519 * returns a new normalized string if normalization is needed, NULL otherwise
3520 * the caller must free the returned value.
3521 */
3522
3523xmlChar *
3524xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3525 const xmlChar *name, const xmlChar *value) {
3526 xmlChar *ret, *dst;
3527 const xmlChar *src;
3528 xmlAttributePtr attrDecl = NULL;
3529
3530 if (doc == NULL) return(NULL);
3531 if (elem == NULL) return(NULL);
3532 if (name == NULL) return(NULL);
3533 if (value == NULL) return(NULL);
3534
3535 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3536 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003537 snprintf((char *) qname, sizeof(qname), "%s:%s",
3538 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003539 qname[sizeof(qname) - 1] = 0;
3540 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3541 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3542 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3543 }
3544 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3545 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3546 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3547
3548 if (attrDecl == NULL)
3549 return(NULL);
3550 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3551 return(NULL);
3552
3553 ret = xmlStrdup(value);
3554 if (ret == NULL)
3555 return(NULL);
3556 src = value;
3557 dst = ret;
3558 while (*src == 0x20) src++;
3559 while (*src != 0) {
3560 if (*src == 0x20) {
3561 while (*src == 0x20) src++;
3562 if (*src != 0)
3563 *dst++ = 0x20;
3564 } else {
3565 *dst++ = *src++;
3566 }
3567 }
3568 *dst = 0;
3569 return(ret);
3570}
3571
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003572static void
Owen Taylor3473f882001-02-23 17:55:21 +00003573xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003574 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003575 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3576}
3577
3578/**
3579 * xmlValidateAttributeDecl:
3580 * @ctxt: the validation context
3581 * @doc: a document instance
3582 * @attr: an attribute definition
3583 *
3584 * Try to validate a single attribute definition
3585 * basically it does the following checks as described by the
3586 * XML-1.0 recommendation:
3587 * - [ VC: Attribute Default Legal ]
3588 * - [ VC: Enumeration ]
3589 * - [ VC: ID Attribute Default ]
3590 *
3591 * The ID/IDREF uniqueness and matching are done separately
3592 *
3593 * returns 1 if valid or 0 otherwise
3594 */
3595
3596int
3597xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3598 xmlAttributePtr attr) {
3599 int ret = 1;
3600 int val;
3601 CHECK_DTD;
3602 if(attr == NULL) return(1);
3603
3604 /* Attribute Default Legal */
3605 /* Enumeration */
3606 if (attr->defaultValue != NULL) {
3607 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3608 if (val == 0) {
3609 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003610 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003611 attr->name, attr->elem);
3612 }
3613 ret &= val;
3614 }
3615
3616 /* ID Attribute Default */
3617 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3618 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3619 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3620 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003621 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003622 attr->name, attr->elem);
3623 ret = 0;
3624 }
3625
3626 /* One ID per Element Type */
3627 if (attr->atype == XML_ATTRIBUTE_ID) {
3628 int nbId;
3629
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003630 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003631 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3632 attr->elem);
3633 if (elem != NULL) {
3634 nbId = xmlScanIDAttributeDecl(NULL, elem);
3635 } else {
3636 xmlAttributeTablePtr table;
3637
3638 /*
3639 * The attribute may be declared in the internal subset and the
3640 * element in the external subset.
3641 */
3642 nbId = 0;
3643 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3644 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3645 xmlValidateAttributeIdCallback, &nbId);
3646 }
3647 if (nbId > 1) {
3648 VERROR(ctxt->userData,
3649 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3650 attr->elem, nbId, attr->name);
3651 } else if (doc->extSubset != NULL) {
3652 int extId = 0;
3653 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3654 if (elem != NULL) {
3655 extId = xmlScanIDAttributeDecl(NULL, elem);
3656 }
3657 if (extId > 1) {
3658 VERROR(ctxt->userData,
3659 "Element %s has %d ID attribute defined in the external subset : %s\n",
3660 attr->elem, extId, attr->name);
3661 } else if (extId + nbId > 1) {
3662 VERROR(ctxt->userData,
3663"Element %s has ID attributes defined in the internal and external subset : %s\n",
3664 attr->elem, attr->name);
3665 }
3666 }
3667 }
3668
3669 /* Validity Constraint: Enumeration */
3670 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3671 xmlEnumerationPtr tree = attr->tree;
3672 while (tree != NULL) {
3673 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3674 tree = tree->next;
3675 }
3676 if (tree == NULL) {
3677 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003678"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003679 attr->defaultValue, attr->name, attr->elem);
3680 ret = 0;
3681 }
3682 }
3683
3684 return(ret);
3685}
3686
3687/**
3688 * xmlValidateElementDecl:
3689 * @ctxt: the validation context
3690 * @doc: a document instance
3691 * @elem: an element definition
3692 *
3693 * Try to validate a single element definition
3694 * basically it does the following checks as described by the
3695 * XML-1.0 recommendation:
3696 * - [ VC: One ID per Element Type ]
3697 * - [ VC: No Duplicate Types ]
3698 * - [ VC: Unique Element Type Declaration ]
3699 *
3700 * returns 1 if valid or 0 otherwise
3701 */
3702
3703int
3704xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3705 xmlElementPtr elem) {
3706 int ret = 1;
3707 xmlElementPtr tst;
3708
3709 CHECK_DTD;
3710
3711 if (elem == NULL) return(1);
3712
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003713#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003714#ifdef LIBXML_REGEXP_ENABLED
3715 /* Build the regexp associated to the content model */
3716 ret = xmlValidBuildContentModel(ctxt, elem);
3717#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003718#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003719
Owen Taylor3473f882001-02-23 17:55:21 +00003720 /* No Duplicate Types */
3721 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3722 xmlElementContentPtr cur, next;
3723 const xmlChar *name;
3724
3725 cur = elem->content;
3726 while (cur != NULL) {
3727 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3728 if (cur->c1 == NULL) break;
3729 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3730 name = cur->c1->name;
3731 next = cur->c2;
3732 while (next != NULL) {
3733 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3734 if (xmlStrEqual(next->name, name)) {
3735 VERROR(ctxt->userData,
3736 "Definition of %s has duplicate references of %s\n",
3737 elem->name, name);
3738 ret = 0;
3739 }
3740 break;
3741 }
3742 if (next->c1 == NULL) break;
3743 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3744 if (xmlStrEqual(next->c1->name, name)) {
3745 VERROR(ctxt->userData,
3746 "Definition of %s has duplicate references of %s\n",
3747 elem->name, name);
3748 ret = 0;
3749 }
3750 next = next->c2;
3751 }
3752 }
3753 cur = cur->c2;
3754 }
3755 }
3756
3757 /* VC: Unique Element Type Declaration */
3758 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003759 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003760 ((tst->prefix == elem->prefix) ||
3761 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003762 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003763 VERROR(ctxt->userData, "Redefinition of element %s\n",
3764 elem->name);
3765 ret = 0;
3766 }
3767 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003768 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003769 ((tst->prefix == elem->prefix) ||
3770 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003771 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003772 VERROR(ctxt->userData, "Redefinition of element %s\n",
3773 elem->name);
3774 ret = 0;
3775 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003776 /* One ID per Element Type
3777 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003778 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3779 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003780 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003781 return(ret);
3782}
3783
3784/**
3785 * xmlValidateOneAttribute:
3786 * @ctxt: the validation context
3787 * @doc: a document instance
3788 * @elem: an element instance
3789 * @attr: an attribute instance
3790 * @value: the attribute value (without entities processing)
3791 *
3792 * Try to validate a single attribute for an element
3793 * basically it does the following checks as described by the
3794 * XML-1.0 recommendation:
3795 * - [ VC: Attribute Value Type ]
3796 * - [ VC: Fixed Attribute Default ]
3797 * - [ VC: Entity Name ]
3798 * - [ VC: Name Token ]
3799 * - [ VC: ID ]
3800 * - [ VC: IDREF ]
3801 * - [ VC: Entity Name ]
3802 * - [ VC: Notation Attributes ]
3803 *
3804 * The ID/IDREF uniqueness and matching are done separately
3805 *
3806 * returns 1 if valid or 0 otherwise
3807 */
3808
3809int
3810xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3811 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3812 /* xmlElementPtr elemDecl; */
3813 xmlAttributePtr attrDecl = NULL;
3814 int val;
3815 int ret = 1;
3816
3817 CHECK_DTD;
3818 if ((elem == NULL) || (elem->name == NULL)) return(0);
3819 if ((attr == NULL) || (attr->name == NULL)) return(0);
3820
3821 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3822 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003823 snprintf((char *) qname, sizeof(qname), "%s:%s",
3824 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003825 qname[sizeof(qname) - 1] = 0;
3826 if (attr->ns != NULL) {
3827 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3828 attr->name, attr->ns->prefix);
3829 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3830 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3831 attr->name, attr->ns->prefix);
3832 } else {
3833 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3834 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3835 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3836 qname, attr->name);
3837 }
3838 }
3839 if (attrDecl == NULL) {
3840 if (attr->ns != NULL) {
3841 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3842 attr->name, attr->ns->prefix);
3843 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3844 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3845 attr->name, attr->ns->prefix);
3846 } else {
3847 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3848 elem->name, attr->name);
3849 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3850 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3851 elem->name, attr->name);
3852 }
3853 }
3854
3855
3856 /* Validity Constraint: Attribute Value Type */
3857 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003858 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003859 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003860 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003861 attr->name, elem->name);
3862 return(0);
3863 }
3864 attr->atype = attrDecl->atype;
3865
3866 val = xmlValidateAttributeValue(attrDecl->atype, value);
3867 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003868 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003869 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003870 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003871 attr->name, elem->name);
3872 ret = 0;
3873 }
3874
3875 /* Validity constraint: Fixed Attribute Default */
3876 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3877 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003878 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003879 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003880 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003881 attr->name, elem->name, attrDecl->defaultValue);
3882 ret = 0;
3883 }
3884 }
3885
3886 /* Validity Constraint: ID uniqueness */
3887 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3888 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3889 ret = 0;
3890 }
3891
3892 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3893 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3894 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3895 ret = 0;
3896 }
3897
3898 /* Validity Constraint: Notation Attributes */
3899 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3900 xmlEnumerationPtr tree = attrDecl->tree;
3901 xmlNotationPtr nota;
3902
3903 /* First check that the given NOTATION was declared */
3904 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3905 if (nota == NULL)
3906 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3907
3908 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003909 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003910 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003911 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003912 value, attr->name, elem->name);
3913 ret = 0;
3914 }
3915
3916 /* Second, verify that it's among the list */
3917 while (tree != NULL) {
3918 if (xmlStrEqual(tree->name, value)) break;
3919 tree = tree->next;
3920 }
3921 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003922 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003923 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003924"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003925 value, attr->name, elem->name);
3926 ret = 0;
3927 }
3928 }
3929
3930 /* Validity Constraint: Enumeration */
3931 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3932 xmlEnumerationPtr tree = attrDecl->tree;
3933 while (tree != NULL) {
3934 if (xmlStrEqual(tree->name, value)) break;
3935 tree = tree->next;
3936 }
3937 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003938 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003939 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003940 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003941 value, attr->name, elem->name);
3942 ret = 0;
3943 }
3944 }
3945
3946 /* Fixed Attribute Default */
3947 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3948 (!xmlStrEqual(attrDecl->defaultValue, value))) {
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 for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003952 attr->name, elem->name, attrDecl->defaultValue);
3953 ret = 0;
3954 }
3955
3956 /* Extra check for the attribute value */
3957 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3958 attrDecl->atype, value);
3959
3960 return(ret);
3961}
3962
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003963/**
3964 * xmlValidateOneNamespace:
3965 * @ctxt: the validation context
3966 * @doc: a document instance
3967 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00003968 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003969 * @ns: an namespace declaration instance
3970 * @value: the attribute value (without entities processing)
3971 *
3972 * Try to validate a single namespace declaration for an element
3973 * basically it does the following checks as described by the
3974 * XML-1.0 recommendation:
3975 * - [ VC: Attribute Value Type ]
3976 * - [ VC: Fixed Attribute Default ]
3977 * - [ VC: Entity Name ]
3978 * - [ VC: Name Token ]
3979 * - [ VC: ID ]
3980 * - [ VC: IDREF ]
3981 * - [ VC: Entity Name ]
3982 * - [ VC: Notation Attributes ]
3983 *
3984 * The ID/IDREF uniqueness and matching are done separately
3985 *
3986 * returns 1 if valid or 0 otherwise
3987 */
3988
3989int
3990xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3991xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
3992 /* xmlElementPtr elemDecl; */
3993 xmlAttributePtr attrDecl = NULL;
3994 int val;
3995 int ret = 1;
3996
3997 CHECK_DTD;
3998 if ((elem == NULL) || (elem->name == NULL)) return(0);
3999 if ((ns == NULL) || (ns->href == NULL)) return(0);
4000
4001 if (prefix != NULL) {
4002 xmlChar qname[500];
4003 snprintf((char *) qname, sizeof(qname), "%s:%s",
4004 prefix, elem->name);
4005 qname[sizeof(qname) - 1] = 0;
4006 if (ns->prefix != NULL) {
4007 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
4008 ns->prefix, BAD_CAST "xmlns");
4009 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4010 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
4011 ns->prefix, BAD_CAST "xmlns");
4012 } else {
4013 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname,
4014 BAD_CAST "xmlns");
4015 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4016 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname,
4017 BAD_CAST "xmlns");
4018 }
4019 }
4020 if (attrDecl == NULL) {
4021 if (ns->prefix != NULL) {
4022 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
4023 ns->prefix, BAD_CAST "xmlns");
4024 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4025 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
4026 ns->prefix, BAD_CAST "xmlns");
4027 } else {
4028 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
4029 elem->name, BAD_CAST "xmlns");
4030 if ((attrDecl == NULL) && (doc->extSubset != NULL))
4031 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
4032 elem->name, BAD_CAST "xmlns");
4033 }
4034 }
4035
4036
4037 /* Validity Constraint: Attribute Value Type */
4038 if (attrDecl == NULL) {
4039 VECTXT(ctxt, elem);
4040 if (ns->prefix != NULL) {
4041 VERROR(ctxt->userData,
4042 "No declaration for attribute xmlns:%s of element %s\n",
4043 ns->prefix, elem->name);
4044 } else {
4045 VERROR(ctxt->userData,
4046 "No declaration for attribute xmlns of element %s\n",
4047 elem->name);
4048 }
4049 return(0);
4050 }
4051
4052 val = xmlValidateAttributeValue(attrDecl->atype, value);
4053 if (val == 0) {
4054 VECTXT(ctxt, elem);
4055 if (ns->prefix != NULL) {
4056 VERROR(ctxt->userData,
4057 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
4058 ns->prefix, elem->name);
4059 } else {
4060 VERROR(ctxt->userData,
4061 "Syntax of value for attribute xmlns of %s is not valid\n",
4062 elem->name);
4063 }
4064 ret = 0;
4065 }
4066
4067 /* Validity constraint: Fixed Attribute Default */
4068 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4069 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4070 VECTXT(ctxt, elem);
4071 if (ns->prefix != NULL) {
4072 VERROR(ctxt->userData,
4073 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4074 ns->prefix, elem->name, attrDecl->defaultValue);
4075 } else {
4076 VERROR(ctxt->userData,
4077 "Value for attribute xmlns of %s is different from default \"%s\"\n",
4078 elem->name, attrDecl->defaultValue);
4079 }
4080 ret = 0;
4081 }
4082 }
4083
4084 /* Validity Constraint: ID uniqueness */
4085 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4086 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4087 ret = 0;
4088 }
4089
4090 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4091 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4092 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4093 ret = 0;
4094 }
4095
4096 /* Validity Constraint: Notation Attributes */
4097 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4098 xmlEnumerationPtr tree = attrDecl->tree;
4099 xmlNotationPtr nota;
4100
4101 /* First check that the given NOTATION was declared */
4102 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4103 if (nota == NULL)
4104 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4105
4106 if (nota == NULL) {
4107 VECTXT(ctxt, elem);
4108 if (ns->prefix != NULL) {
4109 VERROR(ctxt->userData,
4110 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4111 value, ns->prefix, elem->name);
4112 } else {
4113 VERROR(ctxt->userData,
4114 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
4115 value, elem->name);
4116 }
4117 ret = 0;
4118 }
4119
4120 /* Second, verify that it's among the list */
4121 while (tree != NULL) {
4122 if (xmlStrEqual(tree->name, value)) break;
4123 tree = tree->next;
4124 }
4125 if (tree == NULL) {
4126 VECTXT(ctxt, elem);
4127 if (ns->prefix != NULL) {
4128 VERROR(ctxt->userData,
4129"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4130 value, ns->prefix, elem->name);
4131 } else {
4132 VERROR(ctxt->userData,
4133"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
4134 value, elem->name);
4135 }
4136 ret = 0;
4137 }
4138 }
4139
4140 /* Validity Constraint: Enumeration */
4141 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4142 xmlEnumerationPtr tree = attrDecl->tree;
4143 while (tree != NULL) {
4144 if (xmlStrEqual(tree->name, value)) break;
4145 tree = tree->next;
4146 }
4147 if (tree == NULL) {
4148 VECTXT(ctxt, elem);
4149 if (ns->prefix != NULL) {
4150 VERROR(ctxt->userData,
4151"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4152 value, ns->prefix, elem->name);
4153 } else {
4154 VERROR(ctxt->userData,
4155"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4156 value, elem->name);
4157 }
4158 ret = 0;
4159 }
4160 }
4161
4162 /* Fixed Attribute Default */
4163 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4164 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4165 VECTXT(ctxt, elem);
4166 if (ns->prefix != NULL) {
4167 VERROR(ctxt->userData,
4168 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4169 ns->prefix, elem->name, attrDecl->defaultValue);
4170 } else {
4171 VERROR(ctxt->userData,
4172 "Value for attribute xmlns of %s must be \"%s\"\n",
4173 elem->name, attrDecl->defaultValue);
4174 }
4175 ret = 0;
4176 }
4177
4178 /* Extra check for the attribute value */
4179 if (ns->prefix != NULL) {
4180 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4181 attrDecl->atype, value);
4182 } else {
4183 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4184 attrDecl->atype, value);
4185 }
4186
4187 return(ret);
4188}
4189
Daniel Veillard118aed72002-09-24 14:13:13 +00004190#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004191/**
4192 * xmlValidateSkipIgnorable:
4193 * @ctxt: the validation context
4194 * @child: the child list
4195 *
4196 * Skip ignorable elements w.r.t. the validation process
4197 *
4198 * returns the first element to consider for validation of the content model
4199 */
4200
4201static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004202xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004203 while (child != NULL) {
4204 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004205 /* These things are ignored (skipped) during validation. */
4206 case XML_PI_NODE:
4207 case XML_COMMENT_NODE:
4208 case XML_XINCLUDE_START:
4209 case XML_XINCLUDE_END:
4210 child = child->next;
4211 break;
4212 case XML_TEXT_NODE:
4213 if (xmlIsBlankNode(child))
4214 child = child->next;
4215 else
4216 return(child);
4217 break;
4218 /* keep current node */
4219 default:
4220 return(child);
4221 }
4222 }
4223 return(child);
4224}
4225
4226/**
4227 * xmlValidateElementType:
4228 * @ctxt: the validation context
4229 *
4230 * Try to validate the content model of an element internal function
4231 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004232 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4233 * reference is found and -3 if the validation succeeded but
4234 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004235 */
4236
4237static int
4238xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004239 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004240 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004241
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004242 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004243 if ((NODE == NULL) && (CONT == NULL))
4244 return(1);
4245 if ((NODE == NULL) &&
4246 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4247 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4248 return(1);
4249 }
4250 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004251 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004252 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004253
4254 /*
4255 * We arrive here when more states need to be examined
4256 */
4257cont:
4258
4259 /*
4260 * We just recovered from a rollback generated by a possible
4261 * epsilon transition, go directly to the analysis phase
4262 */
4263 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004264 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004265 DEBUG_VALID_STATE(NODE, CONT)
4266 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004267 goto analyze;
4268 }
4269
4270 DEBUG_VALID_STATE(NODE, CONT)
4271 /*
4272 * we may have to save a backup state here. This is the equivalent
4273 * of handling epsilon transition in NFAs.
4274 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004275 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004276 ((CONT->parent == NULL) ||
4277 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004278 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004279 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004280 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004281 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004282 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4283 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004284 }
4285
4286
4287 /*
4288 * Check first if the content matches
4289 */
4290 switch (CONT->type) {
4291 case XML_ELEMENT_CONTENT_PCDATA:
4292 if (NODE == NULL) {
4293 DEBUG_VALID_MSG("pcdata failed no node");
4294 ret = 0;
4295 break;
4296 }
4297 if (NODE->type == XML_TEXT_NODE) {
4298 DEBUG_VALID_MSG("pcdata found, skip to next");
4299 /*
4300 * go to next element in the content model
4301 * skipping ignorable elems
4302 */
4303 do {
4304 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004305 NODE = xmlValidateSkipIgnorable(NODE);
4306 if ((NODE != NULL) &&
4307 (NODE->type == XML_ENTITY_REF_NODE))
4308 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004309 } while ((NODE != NULL) &&
4310 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004311 (NODE->type != XML_TEXT_NODE) &&
4312 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004313 ret = 1;
4314 break;
4315 } else {
4316 DEBUG_VALID_MSG("pcdata failed");
4317 ret = 0;
4318 break;
4319 }
4320 break;
4321 case XML_ELEMENT_CONTENT_ELEMENT:
4322 if (NODE == NULL) {
4323 DEBUG_VALID_MSG("element failed no node");
4324 ret = 0;
4325 break;
4326 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004327 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4328 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004329 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004330 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4331 ret = (CONT->prefix == NULL);
4332 } else if (CONT->prefix == NULL) {
4333 ret = 0;
4334 } else {
4335 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4336 }
4337 }
4338 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004339 DEBUG_VALID_MSG("element found, skip to next");
4340 /*
4341 * go to next element in the content model
4342 * skipping ignorable elems
4343 */
4344 do {
4345 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004346 NODE = xmlValidateSkipIgnorable(NODE);
4347 if ((NODE != NULL) &&
4348 (NODE->type == XML_ENTITY_REF_NODE))
4349 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004350 } while ((NODE != NULL) &&
4351 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004352 (NODE->type != XML_TEXT_NODE) &&
4353 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004354 } else {
4355 DEBUG_VALID_MSG("element failed");
4356 ret = 0;
4357 break;
4358 }
4359 break;
4360 case XML_ELEMENT_CONTENT_OR:
4361 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004362 * Small optimization.
4363 */
4364 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4365 if ((NODE == NULL) ||
4366 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4367 DEPTH++;
4368 CONT = CONT->c2;
4369 goto cont;
4370 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004371 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4372 ret = (CONT->c1->prefix == NULL);
4373 } else if (CONT->c1->prefix == NULL) {
4374 ret = 0;
4375 } else {
4376 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4377 }
4378 if (ret == 0) {
4379 DEPTH++;
4380 CONT = CONT->c2;
4381 goto cont;
4382 }
Daniel Veillard85349052001-04-20 13:48:21 +00004383 }
4384
4385 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004386 * save the second branch 'or' branch
4387 */
4388 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004389 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4390 OCCURS, ROLLBACK_OR) < 0)
4391 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004392 DEPTH++;
4393 CONT = CONT->c1;
4394 goto cont;
4395 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004396 /*
4397 * Small optimization.
4398 */
4399 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4400 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4401 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4402 if ((NODE == NULL) ||
4403 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4404 DEPTH++;
4405 CONT = CONT->c2;
4406 goto cont;
4407 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004408 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4409 ret = (CONT->c1->prefix == NULL);
4410 } else if (CONT->c1->prefix == NULL) {
4411 ret = 0;
4412 } else {
4413 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4414 }
4415 if (ret == 0) {
4416 DEPTH++;
4417 CONT = CONT->c2;
4418 goto cont;
4419 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004420 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004421 DEPTH++;
4422 CONT = CONT->c1;
4423 goto cont;
4424 }
4425
4426 /*
4427 * At this point handle going up in the tree
4428 */
4429 if (ret == -1) {
4430 DEBUG_VALID_MSG("error found returning");
4431 return(ret);
4432 }
4433analyze:
4434 while (CONT != NULL) {
4435 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004436 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004437 * this level.
4438 */
4439 if (ret == 0) {
4440 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004441 xmlNodePtr cur;
4442
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004443 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004444 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004445 DEBUG_VALID_MSG("Once branch failed, rollback");
4446 if (vstateVPop(ctxt) < 0 ) {
4447 DEBUG_VALID_MSG("exhaustion, failed");
4448 return(0);
4449 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004450 if (cur != ctxt->vstate->node)
4451 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004452 goto cont;
4453 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004454 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004455 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004456 DEBUG_VALID_MSG("Plus 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 }
4465 DEBUG_VALID_MSG("Plus branch found");
4466 ret = 1;
4467 break;
4468 case XML_ELEMENT_CONTENT_MULT:
4469#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004470 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004471 DEBUG_VALID_MSG("Mult branch failed");
4472 } else {
4473 DEBUG_VALID_MSG("Mult branch found");
4474 }
4475#endif
4476 ret = 1;
4477 break;
4478 case XML_ELEMENT_CONTENT_OPT:
4479 DEBUG_VALID_MSG("Option branch failed");
4480 ret = 1;
4481 break;
4482 }
4483 } else {
4484 switch (CONT->ocur) {
4485 case XML_ELEMENT_CONTENT_OPT:
4486 DEBUG_VALID_MSG("Option branch succeeded");
4487 ret = 1;
4488 break;
4489 case XML_ELEMENT_CONTENT_ONCE:
4490 DEBUG_VALID_MSG("Once branch succeeded");
4491 ret = 1;
4492 break;
4493 case XML_ELEMENT_CONTENT_PLUS:
4494 if (STATE == ROLLBACK_PARENT) {
4495 DEBUG_VALID_MSG("Plus branch rollback");
4496 ret = 1;
4497 break;
4498 }
4499 if (NODE == NULL) {
4500 DEBUG_VALID_MSG("Plus branch exhausted");
4501 ret = 1;
4502 break;
4503 }
4504 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004505 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004506 goto cont;
4507 case XML_ELEMENT_CONTENT_MULT:
4508 if (STATE == ROLLBACK_PARENT) {
4509 DEBUG_VALID_MSG("Mult branch rollback");
4510 ret = 1;
4511 break;
4512 }
4513 if (NODE == NULL) {
4514 DEBUG_VALID_MSG("Mult branch exhausted");
4515 ret = 1;
4516 break;
4517 }
4518 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004519 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004520 goto cont;
4521 }
4522 }
4523 STATE = 0;
4524
4525 /*
4526 * Then act accordingly at the parent level
4527 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004528 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004529 if (CONT->parent == NULL)
4530 break;
4531
4532 switch (CONT->parent->type) {
4533 case XML_ELEMENT_CONTENT_PCDATA:
4534 DEBUG_VALID_MSG("Error: parent pcdata");
4535 return(-1);
4536 case XML_ELEMENT_CONTENT_ELEMENT:
4537 DEBUG_VALID_MSG("Error: parent element");
4538 return(-1);
4539 case XML_ELEMENT_CONTENT_OR:
4540 if (ret == 1) {
4541 DEBUG_VALID_MSG("Or succeeded");
4542 CONT = CONT->parent;
4543 DEPTH--;
4544 } else {
4545 DEBUG_VALID_MSG("Or failed");
4546 CONT = CONT->parent;
4547 DEPTH--;
4548 }
4549 break;
4550 case XML_ELEMENT_CONTENT_SEQ:
4551 if (ret == 0) {
4552 DEBUG_VALID_MSG("Sequence failed");
4553 CONT = CONT->parent;
4554 DEPTH--;
4555 } else if (CONT == CONT->parent->c1) {
4556 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4557 CONT = CONT->parent->c2;
4558 goto cont;
4559 } else {
4560 DEBUG_VALID_MSG("Sequence succeeded");
4561 CONT = CONT->parent;
4562 DEPTH--;
4563 }
4564 }
4565 }
4566 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004567 xmlNodePtr cur;
4568
4569 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004570 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4571 if (vstateVPop(ctxt) < 0 ) {
4572 DEBUG_VALID_MSG("exhaustion, failed");
4573 return(0);
4574 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004575 if (cur != ctxt->vstate->node)
4576 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004577 goto cont;
4578 }
4579 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004580 xmlNodePtr cur;
4581
4582 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004583 DEBUG_VALID_MSG("Failure, rollback");
4584 if (vstateVPop(ctxt) < 0 ) {
4585 DEBUG_VALID_MSG("exhaustion, failed");
4586 return(0);
4587 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004588 if (cur != ctxt->vstate->node)
4589 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004590 goto cont;
4591 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004592 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004593}
Daniel Veillard23e73572002-09-19 19:56:43 +00004594#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004595
4596/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004597 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004598 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004599 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004600 * @content: An element
4601 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4602 *
4603 * This will dump the list of elements to the buffer
4604 * Intended just for the debug routine
4605 */
4606static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004607xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004608 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004609 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004610
4611 if (node == NULL) return;
4612 if (glob) strcat(buf, "(");
4613 cur = node;
4614 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004615 len = strlen(buf);
4616 if (size - len < 50) {
4617 if ((size - len > 4) && (buf[len - 1] != '.'))
4618 strcat(buf, " ...");
4619 return;
4620 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004621 switch (cur->type) {
4622 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004623 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004624 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004625 if ((size - len > 4) && (buf[len - 1] != '.'))
4626 strcat(buf, " ...");
4627 return;
4628 }
4629 strcat(buf, (char *) cur->ns->prefix);
4630 strcat(buf, ":");
4631 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004632 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004633 if ((size - len > 4) && (buf[len - 1] != '.'))
4634 strcat(buf, " ...");
4635 return;
4636 }
4637 strcat(buf, (char *) cur->name);
4638 if (cur->next != NULL)
4639 strcat(buf, " ");
4640 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004641 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004642 if (xmlIsBlankNode(cur))
4643 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004644 case XML_CDATA_SECTION_NODE:
4645 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004646 strcat(buf, "CDATA");
4647 if (cur->next != NULL)
4648 strcat(buf, " ");
4649 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004650 case XML_ATTRIBUTE_NODE:
4651 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004652#ifdef LIBXML_DOCB_ENABLED
4653 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004654#endif
4655 case XML_HTML_DOCUMENT_NODE:
4656 case XML_DOCUMENT_TYPE_NODE:
4657 case XML_DOCUMENT_FRAG_NODE:
4658 case XML_NOTATION_NODE:
4659 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004660 strcat(buf, "???");
4661 if (cur->next != NULL)
4662 strcat(buf, " ");
4663 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004664 case XML_ENTITY_NODE:
4665 case XML_PI_NODE:
4666 case XML_DTD_NODE:
4667 case XML_COMMENT_NODE:
4668 case XML_ELEMENT_DECL:
4669 case XML_ATTRIBUTE_DECL:
4670 case XML_ENTITY_DECL:
4671 case XML_XINCLUDE_START:
4672 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004673 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004674 }
4675 cur = cur->next;
4676 }
4677 if (glob) strcat(buf, ")");
4678}
4679
4680/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004681 * xmlValidateElementContent:
4682 * @ctxt: the validation context
4683 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004684 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004685 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004686 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004687 *
4688 * Try to validate the content model of an element
4689 *
4690 * returns 1 if valid or 0 if not and -1 in case of error
4691 */
4692
4693static int
4694xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004695 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004696 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004697#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004698 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004699#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004700 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004701 xmlElementContentPtr cont;
4702 const xmlChar *name;
4703
4704 if (elemDecl == NULL)
4705 return(-1);
4706 cont = elemDecl->content;
4707 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004708
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004709#ifdef LIBXML_REGEXP_ENABLED
4710 /* Build the regexp associated to the content model */
4711 if (elemDecl->contModel == NULL)
4712 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4713 if (elemDecl->contModel == NULL) {
4714 ret = -1;
4715 } else {
4716 xmlRegExecCtxtPtr exec;
4717
Daniel Veillard01992e02002-10-09 10:20:30 +00004718 ctxt->nodeMax = 0;
4719 ctxt->nodeNr = 0;
4720 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004721 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4722 if (exec != NULL) {
4723 cur = child;
4724 while (cur != NULL) {
4725 switch (cur->type) {
4726 case XML_ENTITY_REF_NODE:
4727 /*
4728 * Push the current node to be able to roll back
4729 * and process within the entity
4730 */
4731 if ((cur->children != NULL) &&
4732 (cur->children->children != NULL)) {
4733 nodeVPush(ctxt, cur);
4734 cur = cur->children->children;
4735 continue;
4736 }
4737 break;
4738 case XML_TEXT_NODE:
4739 if (xmlIsBlankNode(cur))
4740 break;
4741 ret = 0;
4742 goto fail;
4743 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004744 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004745 ret = 0;
4746 goto fail;
4747 case XML_ELEMENT_NODE:
4748 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
4749 xmlChar *QName;
4750 int len;
4751
4752 len = xmlStrlen(cur->name) +
4753 xmlStrlen(cur->ns->prefix) + 2;
4754 QName = xmlMalloc(len);
4755 if (QName == NULL) {
4756 ret = -1;
4757 goto fail;
4758 }
4759 snprintf((char *) QName, len, "%s:%s",
4760 (char *)cur->ns->prefix,
4761 (char *)cur->name);
4762 ret = xmlRegExecPushString(exec, QName, NULL);
4763 xmlFree(QName);
4764 } else {
4765 ret = xmlRegExecPushString(exec, cur->name, NULL);
4766 }
4767 break;
4768 default:
4769 break;
4770 }
4771 /*
4772 * Switch to next element
4773 */
4774 cur = cur->next;
4775 while (cur == NULL) {
4776 cur = nodeVPop(ctxt);
4777 if (cur == NULL)
4778 break;
4779 cur = cur->next;
4780 }
4781 }
4782 ret = xmlRegExecPushString(exec, NULL, NULL);
4783fail:
4784 xmlRegFreeExecCtxt(exec);
4785 }
4786 }
4787#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004788 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004789 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004790 */
4791 ctxt->vstateMax = 8;
4792 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4793 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4794 if (ctxt->vstateTab == NULL) {
4795 xmlGenericError(xmlGenericErrorContext,
4796 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004797 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004798 }
4799 /*
4800 * The first entry in the stack is reserved to the current state
4801 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004802 ctxt->nodeMax = 0;
4803 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004804 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004805 ctxt->vstate = &ctxt->vstateTab[0];
4806 ctxt->vstateNr = 1;
4807 CONT = cont;
4808 NODE = child;
4809 DEPTH = 0;
4810 OCCURS = 0;
4811 STATE = 0;
4812 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004813 if ((ret == -3) && (warn)) {
4814 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004815 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004816 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004817 /*
4818 * An entities reference appeared at this level.
4819 * Buid a minimal representation of this node content
4820 * sufficient to run the validation process on it
4821 */
4822 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004823 cur = child;
4824 while (cur != NULL) {
4825 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004826 case XML_ENTITY_REF_NODE:
4827 /*
4828 * Push the current node to be able to roll back
4829 * and process within the entity
4830 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004831 if ((cur->children != NULL) &&
4832 (cur->children->children != NULL)) {
4833 nodeVPush(ctxt, cur);
4834 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004835 continue;
4836 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004837 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004838 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004839 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004840 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004841 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004842 case XML_CDATA_SECTION_NODE:
4843 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004844 case XML_ELEMENT_NODE:
4845 /*
4846 * Allocate a new node and minimally fills in
4847 * what's required
4848 */
4849 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4850 if (tmp == NULL) {
4851 xmlGenericError(xmlGenericErrorContext,
4852 "xmlValidateElementContent : malloc failed\n");
4853 xmlFreeNodeList(repl);
4854 ret = -1;
4855 goto done;
4856 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004857 tmp->type = cur->type;
4858 tmp->name = cur->name;
4859 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004860 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004861 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004862 if (repl == NULL)
4863 repl = last = tmp;
4864 else {
4865 last->next = tmp;
4866 last = tmp;
4867 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004868 if (cur->type == XML_CDATA_SECTION_NODE) {
4869 /*
4870 * E59 spaces in CDATA does not match the
4871 * nonterminal S
4872 */
4873 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4874 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004875 break;
4876 default:
4877 break;
4878 }
4879 /*
4880 * Switch to next element
4881 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004882 cur = cur->next;
4883 while (cur == NULL) {
4884 cur = nodeVPop(ctxt);
4885 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004886 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004887 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004888 }
4889 }
4890
4891 /*
4892 * Relaunch the validation
4893 */
4894 ctxt->vstate = &ctxt->vstateTab[0];
4895 ctxt->vstateNr = 1;
4896 CONT = cont;
4897 NODE = repl;
4898 DEPTH = 0;
4899 OCCURS = 0;
4900 STATE = 0;
4901 ret = xmlValidateElementType(ctxt);
4902 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004903#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004904 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004905 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4906 char expr[5000];
4907 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004908
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004909 expr[0] = 0;
4910 xmlSnprintfElementContent(expr, 5000, cont, 1);
4911 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004912#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004913 if (repl != NULL)
4914 xmlSnprintfElements(list, 5000, repl, 1);
4915 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004916#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004917 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004918
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004919 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004920 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004921 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004922 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004923 name, expr, list);
4924 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004925 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004926 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004927 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004928 expr, list);
4929 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004930 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004931 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004932 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004933 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004934 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004935 name);
4936 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004937 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004938 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004939 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004940 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004941 }
4942 ret = 0;
4943 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004944 if (ret == -3)
4945 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004946
Daniel Veillard23e73572002-09-19 19:56:43 +00004947#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004948done:
4949 /*
4950 * Deallocate the copy if done, and free up the validation stack
4951 */
4952 while (repl != NULL) {
4953 tmp = repl->next;
4954 xmlFree(repl);
4955 repl = tmp;
4956 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004957 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004958 if (ctxt->vstateTab != NULL) {
4959 xmlFree(ctxt->vstateTab);
4960 ctxt->vstateTab = NULL;
4961 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004962#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004963 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004964 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004965 if (ctxt->nodeTab != NULL) {
4966 xmlFree(ctxt->nodeTab);
4967 ctxt->nodeTab = NULL;
4968 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004969 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004970
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004971}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004972
Owen Taylor3473f882001-02-23 17:55:21 +00004973/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004974 * xmlValidateCdataElement:
4975 * @ctxt: the validation context
4976 * @doc: a document instance
4977 * @elem: an element instance
4978 *
4979 * Check that an element follows #CDATA
4980 *
4981 * returns 1 if valid or 0 otherwise
4982 */
4983static int
4984xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4985 xmlNodePtr elem) {
4986 int ret = 1;
4987 xmlNodePtr cur, child;
4988
Daniel Veillardceb09b92002-10-04 11:46:37 +00004989 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004990 return(0);
4991
4992 child = elem->children;
4993
4994 cur = child;
4995 while (cur != NULL) {
4996 switch (cur->type) {
4997 case XML_ENTITY_REF_NODE:
4998 /*
4999 * Push the current node to be able to roll back
5000 * and process within the entity
5001 */
5002 if ((cur->children != NULL) &&
5003 (cur->children->children != NULL)) {
5004 nodeVPush(ctxt, cur);
5005 cur = cur->children->children;
5006 continue;
5007 }
5008 break;
5009 case XML_COMMENT_NODE:
5010 case XML_PI_NODE:
5011 case XML_TEXT_NODE:
5012 case XML_CDATA_SECTION_NODE:
5013 break;
5014 default:
5015 ret = 0;
5016 goto done;
5017 }
5018 /*
5019 * Switch to next element
5020 */
5021 cur = cur->next;
5022 while (cur == NULL) {
5023 cur = nodeVPop(ctxt);
5024 if (cur == NULL)
5025 break;
5026 cur = cur->next;
5027 }
5028 }
5029done:
5030 ctxt->nodeMax = 0;
5031 ctxt->nodeNr = 0;
5032 if (ctxt->nodeTab != NULL) {
5033 xmlFree(ctxt->nodeTab);
5034 ctxt->nodeTab = NULL;
5035 }
5036 return(ret);
5037}
5038
5039/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005040 * xmlValidateCheckMixed:
5041 * @ctxt: the validation context
5042 * @cont: the mixed content model
5043 * @qname: the qualified name as appearing in the serialization
5044 *
5045 * Check if the given node is part of the content model.
5046 *
5047 * Returns 1 if yes, 0 if no, -1 in case of error
5048 */
5049static int
5050xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5051 xmlElementContentPtr cont, const xmlChar *qname) {
5052 while (cont != NULL) {
5053 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5054 if (xmlStrEqual(cont->name, qname))
5055 return(1);
5056 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5057 (cont->c1 != NULL) &&
5058 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5059 if (xmlStrEqual(cont->c1->name, qname))
5060 return(1);
5061 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5062 (cont->c1 == NULL) ||
5063 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5064 /* Internal error !!! */
5065 xmlGenericError(xmlGenericErrorContext,
5066 "Internal: MIXED struct bad\n");
5067 break;
5068 }
5069 cont = cont->c2;
5070 }
5071 return(0);
5072}
5073
5074/**
5075 * xmlValidGetElemDecl:
5076 * @ctxt: the validation context
5077 * @doc: a document instance
5078 * @elem: an element instance
5079 * @extsubset: pointer, (out) indicate if the declaration was found
5080 * in the external subset.
5081 *
5082 * Finds a declaration associated to an element in the document.
5083 *
5084 * returns the pointer to the declaration or NULL if not found.
5085 */
5086static xmlElementPtr
5087xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5088 xmlNodePtr elem, int *extsubset) {
5089 xmlElementPtr elemDecl = NULL;
5090 const xmlChar *prefix = NULL;
5091
5092 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5093 if (extsubset != NULL)
5094 *extsubset = 0;
5095
5096 /*
5097 * Fetch the declaration for the qualified name
5098 */
5099 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5100 prefix = elem->ns->prefix;
5101
5102 if (prefix != NULL) {
5103 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5104 elem->name, prefix);
5105 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5106 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5107 elem->name, prefix);
5108 if ((elemDecl != NULL) && (extsubset != NULL))
5109 *extsubset = 1;
5110 }
5111 }
5112
5113 /*
5114 * Fetch the declaration for the non qualified name
5115 * This is "non-strict" validation should be done on the
5116 * full QName but in that case being flexible makes sense.
5117 */
5118 if (elemDecl == NULL) {
5119 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5120 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5121 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5122 if ((elemDecl != NULL) && (extsubset != NULL))
5123 *extsubset = 1;
5124 }
5125 }
5126 if (elemDecl == NULL) {
5127 VECTXT(ctxt, elem);
5128 VERROR(ctxt->userData, "No declaration for element %s\n",
5129 elem->name);
5130 }
5131 return(elemDecl);
5132}
5133
5134/**
5135 * xmlValidatePushElement:
5136 * @ctxt: the validation context
5137 * @doc: a document instance
5138 * @elem: an element instance
5139 * @qname: the qualified name as appearing in the serialization
5140 *
5141 * Push a new element start on the validation stack.
5142 *
5143 * returns 1 if no validation problem was found or 0 otherwise
5144 */
5145int
5146xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5147 xmlNodePtr elem, const xmlChar *qname) {
5148 int ret = 1;
5149 xmlElementPtr eDecl;
5150 int extsubset = 0;
5151
5152 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5153 xmlValidStatePtr state = ctxt->vstate;
5154 xmlElementPtr elemDecl;
5155
5156 /*
5157 * Check the new element agaisnt the content model of the new elem.
5158 */
5159 if (state->elemDecl != NULL) {
5160 elemDecl = state->elemDecl;
5161
5162 switch(elemDecl->etype) {
5163 case XML_ELEMENT_TYPE_UNDEFINED:
5164 ret = 0;
5165 break;
5166 case XML_ELEMENT_TYPE_EMPTY:
5167 VECTXT(ctxt, state->node);
5168 VERROR(ctxt->userData,
5169 "Element %s was declared EMPTY this one has content\n",
5170 state->node->name);
5171 ret = 0;
5172 break;
5173 case XML_ELEMENT_TYPE_ANY:
5174 /* I don't think anything is required then */
5175 break;
5176 case XML_ELEMENT_TYPE_MIXED:
5177 /* simple case of declared as #PCDATA */
5178 if ((elemDecl->content != NULL) &&
5179 (elemDecl->content->type ==
5180 XML_ELEMENT_CONTENT_PCDATA)) {
5181 VECTXT(ctxt, state->node);
5182 VERROR(ctxt->userData,
5183 "Element %s was declared #PCDATA but contains non text nodes\n",
5184 state->node->name);
5185 ret = 0;
5186 } else {
5187 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5188 qname);
5189 if (ret != 1) {
5190 VECTXT(ctxt, state->node);
5191 VERROR(ctxt->userData,
5192 "Element %s is not declared in %s list of possible children\n",
5193 qname, state->node->name);
5194 }
5195 }
5196 break;
5197 case XML_ELEMENT_TYPE_ELEMENT:
5198 /*
5199 * TODO:
5200 * VC: Standalone Document Declaration
5201 * - element types with element content, if white space
5202 * occurs directly within any instance of those types.
5203 */
5204 if (state->exec != NULL) {
5205 ret = xmlRegExecPushString(state->exec, qname, NULL);
5206 if (ret < 0) {
5207 VECTXT(ctxt, state->node);
5208 VERROR(ctxt->userData,
5209 "Element %s content does not follow the DTD\nMisplaced %s\n",
5210 state->node->name, qname);
5211 ret = 0;
5212 } else {
5213 ret = 1;
5214 }
5215 }
5216 break;
5217 }
5218 }
5219 }
5220 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5221 vstateVPush(ctxt, eDecl, elem);
5222 return(ret);
5223}
5224
5225/**
5226 * xmlValidatePushCData:
5227 * @ctxt: the validation context
5228 * @data: some character data read
5229 * @len: the lenght of the data
5230 *
5231 * check the CData parsed for validation in the current stack
5232 *
5233 * returns 1 if no validation problem was found or 0 otherwise
5234 */
5235int
5236xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5237 int ret = 1;
5238
5239 if (len <= 0)
5240 return(ret);
5241 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5242 xmlValidStatePtr state = ctxt->vstate;
5243 xmlElementPtr elemDecl;
5244
5245 /*
5246 * Check the new element agaisnt the content model of the new elem.
5247 */
5248 if (state->elemDecl != NULL) {
5249 elemDecl = state->elemDecl;
5250
5251 switch(elemDecl->etype) {
5252 case XML_ELEMENT_TYPE_UNDEFINED:
5253 ret = 0;
5254 break;
5255 case XML_ELEMENT_TYPE_EMPTY:
5256 VECTXT(ctxt, state->node);
5257 VERROR(ctxt->userData,
5258 "Element %s was declared EMPTY this one has content\n",
5259 state->node->name);
5260 ret = 0;
5261 break;
5262 case XML_ELEMENT_TYPE_ANY:
5263 break;
5264 case XML_ELEMENT_TYPE_MIXED:
5265 break;
5266 case XML_ELEMENT_TYPE_ELEMENT:
5267 if (len > 0) {
5268 int i;
5269
5270 for (i = 0;i < len;i++) {
5271 if (!IS_BLANK(data[i])) {
5272 VECTXT(ctxt, state->node);
5273 VERROR(ctxt->userData,
5274 "Element %s content does not follow the DTD\nText not allowed\n",
5275 state->node->name);
5276 ret = 0;
5277 goto done;
5278 }
5279 }
5280 /*
5281 * TODO:
5282 * VC: Standalone Document Declaration
5283 * element types with element content, if white space
5284 * occurs directly within any instance of those types.
5285 */
5286 }
5287 break;
5288 }
5289 }
5290 }
5291done:
5292 return(ret);
5293}
5294
5295/**
5296 * xmlValidatePopElement:
5297 * @ctxt: the validation context
5298 * @doc: a document instance
5299 * @elem: an element instance
5300 * @qname: the qualified name as appearing in the serialization
5301 *
5302 * Pop the element end from the validation stack.
5303 *
5304 * returns 1 if no validation problem was found or 0 otherwise
5305 */
5306int
5307xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
5308 xmlNodePtr elem, const xmlChar *qname ATTRIBUTE_UNUSED) {
5309 int ret = 1;
5310
5311 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5312 xmlValidStatePtr state = ctxt->vstate;
5313 xmlElementPtr elemDecl;
5314
5315 /*
5316 * Check the new element agaisnt the content model of the new elem.
5317 */
5318 if (state->elemDecl != NULL) {
5319 elemDecl = state->elemDecl;
5320
5321 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5322 if (state->exec != NULL) {
5323 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5324 if (ret == 0) {
5325 VECTXT(ctxt, state->node);
5326 VERROR(ctxt->userData,
5327 "Element %s content does not follow the DTD\nExpecting more child\n",
5328 state->node->name);
5329 } else {
5330 /*
5331 * previous validation errors should not generate
5332 * a new one here
5333 */
5334 ret = 1;
5335 }
5336 }
5337 }
5338 }
5339 vstateVPop(ctxt);
5340 }
5341 return(ret);
5342}
5343
5344/**
Owen Taylor3473f882001-02-23 17:55:21 +00005345 * xmlValidateOneElement:
5346 * @ctxt: the validation context
5347 * @doc: a document instance
5348 * @elem: an element instance
5349 *
5350 * Try to validate a single element and it's attributes,
5351 * basically it does the following checks as described by the
5352 * XML-1.0 recommendation:
5353 * - [ VC: Element Valid ]
5354 * - [ VC: Required Attribute ]
5355 * Then call xmlValidateOneAttribute() for each attribute present.
5356 *
5357 * The ID/IDREF checkings are done separately
5358 *
5359 * returns 1 if valid or 0 otherwise
5360 */
5361
5362int
5363xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5364 xmlNodePtr elem) {
5365 xmlElementPtr elemDecl = NULL;
5366 xmlElementContentPtr cont;
5367 xmlAttributePtr attr;
5368 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005369 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005370 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005371 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005372
5373 CHECK_DTD;
5374
5375 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005376 switch (elem->type) {
5377 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005378 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005379 VERROR(ctxt->userData,
5380 "Attribute element not expected here\n");
5381 return(0);
5382 case XML_TEXT_NODE:
5383 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005384 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005385 VERROR(ctxt->userData, "Text element has childs !\n");
5386 return(0);
5387 }
5388 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005389 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005390 VERROR(ctxt->userData, "Text element has attributes !\n");
5391 return(0);
5392 }
5393 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005394 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005395 VERROR(ctxt->userData, "Text element has namespace !\n");
5396 return(0);
5397 }
5398 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005399 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005400 VERROR(ctxt->userData,
5401 "Text element carries namespace definitions !\n");
5402 return(0);
5403 }
5404 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005405 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005406 VERROR(ctxt->userData,
5407 "Text element has no content !\n");
5408 return(0);
5409 }
5410 return(1);
5411 case XML_XINCLUDE_START:
5412 case XML_XINCLUDE_END:
5413 return(1);
5414 case XML_CDATA_SECTION_NODE:
5415 case XML_ENTITY_REF_NODE:
5416 case XML_PI_NODE:
5417 case XML_COMMENT_NODE:
5418 return(1);
5419 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005420 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005421 VERROR(ctxt->userData,
5422 "Entity element not expected here\n");
5423 return(0);
5424 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005425 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005426 VERROR(ctxt->userData,
5427 "Notation element not expected here\n");
5428 return(0);
5429 case XML_DOCUMENT_NODE:
5430 case XML_DOCUMENT_TYPE_NODE:
5431 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005432 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005433 VERROR(ctxt->userData,
5434 "Document element not expected here\n");
5435 return(0);
5436 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005437 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005438 VERROR(ctxt->userData,
5439 "\n");
5440 return(0);
5441 case XML_ELEMENT_NODE:
5442 break;
5443 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005444 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005445 VERROR(ctxt->userData,
5446 "unknown element type %d\n", elem->type);
5447 return(0);
5448 }
Owen Taylor3473f882001-02-23 17:55:21 +00005449
5450 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005451 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005452 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005453 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5454 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005455 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005456
Daniel Veillardea7751d2002-12-20 00:16:24 +00005457 /*
5458 * If vstateNr is not zero that means continuous validation is
5459 * activated, do not try to check the content model at that level.
5460 */
5461 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005462 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005463 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005464 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005465 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005466 VERROR(ctxt->userData, "No declaration for element %s\n",
5467 elem->name);
5468 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005469 case XML_ELEMENT_TYPE_EMPTY:
5470 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005471 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005472 VERROR(ctxt->userData,
5473 "Element %s was declared EMPTY this one has content\n",
5474 elem->name);
5475 ret = 0;
5476 }
5477 break;
5478 case XML_ELEMENT_TYPE_ANY:
5479 /* I don't think anything is required then */
5480 break;
5481 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005482
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005483 /* simple case of declared as #PCDATA */
5484 if ((elemDecl->content != NULL) &&
5485 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5486 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5487 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005488 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005489 VERROR(ctxt->userData,
5490 "Element %s was declared #PCDATA but contains non text nodes\n",
5491 elem->name);
5492 }
5493 break;
5494 }
Owen Taylor3473f882001-02-23 17:55:21 +00005495 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005496 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005497 while (child != NULL) {
5498 if (child->type == XML_ELEMENT_NODE) {
5499 name = child->name;
5500 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
5501 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005502 snprintf((char *) qname, sizeof(qname), "%s:%s",
5503 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005504 qname[sizeof(qname) - 1] = 0;
5505 cont = elemDecl->content;
5506 while (cont != NULL) {
5507 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5508 if (xmlStrEqual(cont->name, qname)) break;
5509 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5510 (cont->c1 != NULL) &&
5511 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5512 if (xmlStrEqual(cont->c1->name, qname)) break;
5513 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5514 (cont->c1 == NULL) ||
5515 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5516 /* Internal error !!! */
5517 xmlGenericError(xmlGenericErrorContext,
5518 "Internal: MIXED struct bad\n");
5519 break;
5520 }
5521 cont = cont->c2;
5522 }
5523 if (cont != NULL)
5524 goto child_ok;
5525 }
5526 cont = elemDecl->content;
5527 while (cont != NULL) {
5528 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5529 if (xmlStrEqual(cont->name, name)) break;
5530 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5531 (cont->c1 != NULL) &&
5532 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5533 if (xmlStrEqual(cont->c1->name, name)) break;
5534 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5535 (cont->c1 == NULL) ||
5536 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5537 /* Internal error !!! */
5538 xmlGenericError(xmlGenericErrorContext,
5539 "Internal: MIXED struct bad\n");
5540 break;
5541 }
5542 cont = cont->c2;
5543 }
5544 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005545 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005546 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005547 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005548 name, elem->name);
5549 ret = 0;
5550 }
5551 }
5552child_ok:
5553 child = child->next;
5554 }
5555 break;
5556 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005557 if ((doc->standalone == 1) && (extsubset == 1)) {
5558 /*
5559 * VC: Standalone Document Declaration
5560 * - element types with element content, if white space
5561 * occurs directly within any instance of those types.
5562 */
5563 child = elem->children;
5564 while (child != NULL) {
5565 if (child->type == XML_TEXT_NODE) {
5566 const xmlChar *content = child->content;
5567
5568 while (IS_BLANK(*content))
5569 content++;
5570 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005571 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005572 VERROR(ctxt->userData,
5573"standalone: %s declared in the external subset contains white spaces nodes\n",
5574 elem->name);
5575 ret = 0;
5576 break;
5577 }
5578 }
5579 child =child->next;
5580 }
5581 }
Owen Taylor3473f882001-02-23 17:55:21 +00005582 child = elem->children;
5583 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005584 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005585 if (tmp <= 0)
5586 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005587 break;
5588 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005589 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005590
5591 /* [ VC: Required Attribute ] */
5592 attr = elemDecl->attributes;
5593 while (attr != NULL) {
5594 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005595 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005596
Daniel Veillarde4301c82002-02-13 13:32:35 +00005597 if ((attr->prefix == NULL) &&
5598 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5599 xmlNsPtr ns;
5600
5601 ns = elem->nsDef;
5602 while (ns != NULL) {
5603 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005604 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005605 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005606 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005607 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5608 xmlNsPtr ns;
5609
5610 ns = elem->nsDef;
5611 while (ns != NULL) {
5612 if (xmlStrEqual(attr->name, ns->prefix))
5613 goto found;
5614 ns = ns->next;
5615 }
5616 } else {
5617 xmlAttrPtr attrib;
5618
5619 attrib = elem->properties;
5620 while (attrib != NULL) {
5621 if (xmlStrEqual(attrib->name, attr->name)) {
5622 if (attr->prefix != NULL) {
5623 xmlNsPtr nameSpace = attrib->ns;
5624
5625 if (nameSpace == NULL)
5626 nameSpace = elem->ns;
5627 /*
5628 * qualified names handling is problematic, having a
5629 * different prefix should be possible but DTDs don't
5630 * allow to define the URI instead of the prefix :-(
5631 */
5632 if (nameSpace == NULL) {
5633 if (qualified < 0)
5634 qualified = 0;
5635 } else if (!xmlStrEqual(nameSpace->prefix,
5636 attr->prefix)) {
5637 if (qualified < 1)
5638 qualified = 1;
5639 } else
5640 goto found;
5641 } else {
5642 /*
5643 * We should allow applications to define namespaces
5644 * for their application even if the DTD doesn't
5645 * carry one, otherwise, basically we would always
5646 * break.
5647 */
5648 goto found;
5649 }
5650 }
5651 attrib = attrib->next;
5652 }
Owen Taylor3473f882001-02-23 17:55:21 +00005653 }
5654 if (qualified == -1) {
5655 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005656 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005657 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005658 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005659 elem->name, attr->name);
5660 ret = 0;
5661 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005662 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005663 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005664 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005665 elem->name, attr->prefix,attr->name);
5666 ret = 0;
5667 }
5668 } else if (qualified == 0) {
5669 VWARNING(ctxt->userData,
5670 "Element %s required attribute %s:%s has no prefix\n",
5671 elem->name, attr->prefix,attr->name);
5672 } else if (qualified == 1) {
5673 VWARNING(ctxt->userData,
5674 "Element %s required attribute %s:%s has different prefix\n",
5675 elem->name, attr->prefix,attr->name);
5676 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005677 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5678 /*
5679 * Special tests checking #FIXED namespace declarations
5680 * have the right value since this is not done as an
5681 * attribute checking
5682 */
5683 if ((attr->prefix == NULL) &&
5684 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5685 xmlNsPtr ns;
5686
5687 ns = elem->nsDef;
5688 while (ns != NULL) {
5689 if (ns->prefix == NULL) {
5690 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005691 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005692 VERROR(ctxt->userData,
5693 "Element %s namespace name for default namespace does not match the DTD\n",
5694 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005695 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005696 }
5697 goto found;
5698 }
5699 ns = ns->next;
5700 }
5701 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5702 xmlNsPtr ns;
5703
5704 ns = elem->nsDef;
5705 while (ns != NULL) {
5706 if (xmlStrEqual(attr->name, ns->prefix)) {
5707 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005708 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005709 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005710 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005711 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005712 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005713 }
5714 goto found;
5715 }
5716 ns = ns->next;
5717 }
5718 }
Owen Taylor3473f882001-02-23 17:55:21 +00005719 }
5720found:
5721 attr = attr->nexth;
5722 }
5723 return(ret);
5724}
5725
5726/**
5727 * xmlValidateRoot:
5728 * @ctxt: the validation context
5729 * @doc: a document instance
5730 *
5731 * Try to validate a the root element
5732 * basically it does the following check as described by the
5733 * XML-1.0 recommendation:
5734 * - [ VC: Root Element Type ]
5735 * it doesn't try to recurse or apply other check to the element
5736 *
5737 * returns 1 if valid or 0 otherwise
5738 */
5739
5740int
5741xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5742 xmlNodePtr root;
5743 if (doc == NULL) return(0);
5744
5745 root = xmlDocGetRootElement(doc);
5746 if ((root == NULL) || (root->name == NULL)) {
5747 VERROR(ctxt->userData, "Not valid: no root element\n");
5748 return(0);
5749 }
5750
5751 /*
5752 * When doing post validation against a separate DTD, those may
5753 * no internal subset has been generated
5754 */
5755 if ((doc->intSubset != NULL) &&
5756 (doc->intSubset->name != NULL)) {
5757 /*
5758 * Check first the document root against the NQName
5759 */
5760 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5761 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
5762 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005763 snprintf((char *) qname, sizeof(qname), "%s:%s",
5764 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005765 qname[sizeof(qname) - 1] = 0;
5766 if (xmlStrEqual(doc->intSubset->name, qname))
5767 goto name_ok;
5768 }
5769 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5770 (xmlStrEqual(root->name, BAD_CAST "html")))
5771 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005772 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005773 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005774 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005775 root->name, doc->intSubset->name);
5776 return(0);
5777
5778 }
5779 }
5780name_ok:
5781 return(1);
5782}
5783
5784
5785/**
5786 * xmlValidateElement:
5787 * @ctxt: the validation context
5788 * @doc: a document instance
5789 * @elem: an element instance
5790 *
5791 * Try to validate the subtree under an element
5792 *
5793 * returns 1 if valid or 0 otherwise
5794 */
5795
5796int
5797xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5798 xmlNodePtr child;
5799 xmlAttrPtr attr;
5800 xmlChar *value;
5801 int ret = 1;
5802
5803 if (elem == NULL) return(0);
5804
5805 /*
5806 * XInclude elements were added after parsing in the infoset,
5807 * they don't really mean anything validation wise.
5808 */
5809 if ((elem->type == XML_XINCLUDE_START) ||
5810 (elem->type == XML_XINCLUDE_END))
5811 return(1);
5812
5813 CHECK_DTD;
5814
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005815 /*
5816 * Entities references have to be handled separately
5817 */
5818 if (elem->type == XML_ENTITY_REF_NODE) {
5819 return(1);
5820 }
5821
Owen Taylor3473f882001-02-23 17:55:21 +00005822 ret &= xmlValidateOneElement(ctxt, doc, elem);
5823 attr = elem->properties;
5824 while(attr != NULL) {
5825 value = xmlNodeListGetString(doc, attr->children, 0);
5826 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5827 if (value != NULL)
5828 xmlFree(value);
5829 attr= attr->next;
5830 }
5831 child = elem->children;
5832 while (child != NULL) {
5833 ret &= xmlValidateElement(ctxt, doc, child);
5834 child = child->next;
5835 }
5836
5837 return(ret);
5838}
5839
Daniel Veillard8730c562001-02-26 10:49:57 +00005840/**
5841 * xmlValidateRef:
5842 * @ref: A reference to be validated
5843 * @ctxt: Validation context
5844 * @name: Name of ID we are searching for
5845 *
5846 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005847static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005848xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005849 const xmlChar *name) {
5850 xmlAttrPtr id;
5851 xmlAttrPtr attr;
5852
5853 if (ref == NULL)
5854 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005855 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00005856 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005857 attr = ref->attr;
5858 if (attr == NULL) {
5859 xmlChar *dup, *str = NULL, *cur, save;
5860
5861 dup = xmlStrdup(name);
5862 if (dup == NULL) {
5863 ctxt->valid = 0;
5864 return;
5865 }
5866 cur = dup;
5867 while (*cur != 0) {
5868 str = cur;
5869 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5870 save = *cur;
5871 *cur = 0;
5872 id = xmlGetID(ctxt->doc, str);
5873 if (id == NULL) {
5874 VERROR(ctxt->userData,
5875 "attribute %s line %d references an unknown ID \"%s\"\n",
5876 ref->name, ref->lineno, str);
5877 ctxt->valid = 0;
5878 }
5879 if (save == 0)
5880 break;
5881 *cur = save;
5882 while (IS_BLANK(*cur)) cur++;
5883 }
5884 xmlFree(dup);
5885 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00005886 id = xmlGetID(ctxt->doc, name);
5887 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005888 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005889 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005890 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005891 attr->name, name);
5892 ctxt->valid = 0;
5893 }
5894 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5895 xmlChar *dup, *str = NULL, *cur, save;
5896
5897 dup = xmlStrdup(name);
5898 if (dup == NULL) {
5899 ctxt->valid = 0;
5900 return;
5901 }
5902 cur = dup;
5903 while (*cur != 0) {
5904 str = cur;
5905 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5906 save = *cur;
5907 *cur = 0;
5908 id = xmlGetID(ctxt->doc, str);
5909 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005910 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005911 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005912 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005913 attr->name, str);
5914 ctxt->valid = 0;
5915 }
5916 if (save == 0)
5917 break;
5918 *cur = save;
5919 while (IS_BLANK(*cur)) cur++;
5920 }
5921 xmlFree(dup);
5922 }
5923}
5924
5925/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005926 * xmlWalkValidateList:
5927 * @data: Contents of current link
5928 * @user: Value supplied by the user
5929 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005930 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005931 */
5932static int
5933xmlWalkValidateList(const void *data, const void *user)
5934{
5935 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5936 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5937 return 1;
5938}
5939
5940/**
5941 * xmlValidateCheckRefCallback:
5942 * @ref_list: List of references
5943 * @ctxt: Validation context
5944 * @name: Name of ID we are searching for
5945 *
5946 */
5947static void
5948xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5949 const xmlChar *name) {
5950 xmlValidateMemo memo;
5951
5952 if (ref_list == NULL)
5953 return;
5954 memo.ctxt = ctxt;
5955 memo.name = name;
5956
5957 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5958
5959}
5960
5961/**
Owen Taylor3473f882001-02-23 17:55:21 +00005962 * xmlValidateDocumentFinal:
5963 * @ctxt: the validation context
5964 * @doc: a document instance
5965 *
5966 * Does the final step for the document validation once all the
5967 * incremental validation steps have been completed
5968 *
5969 * basically it does the following checks described by the XML Rec
5970 *
5971 *
5972 * returns 1 if valid or 0 otherwise
5973 */
5974
5975int
5976xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5977 xmlRefTablePtr table;
5978
5979 if (doc == NULL) {
5980 xmlGenericError(xmlGenericErrorContext,
5981 "xmlValidateDocumentFinal: doc == NULL\n");
5982 return(0);
5983 }
5984
5985 /*
5986 * Check all the NOTATION/NOTATIONS attributes
5987 */
5988 /*
5989 * Check all the ENTITY/ENTITIES attributes definition for validity
5990 */
5991 /*
5992 * Check all the IDREF/IDREFS attributes definition for validity
5993 */
5994 table = (xmlRefTablePtr) doc->refs;
5995 ctxt->doc = doc;
5996 ctxt->valid = 1;
5997 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
5998 return(ctxt->valid);
5999}
6000
6001/**
6002 * xmlValidateDtd:
6003 * @ctxt: the validation context
6004 * @doc: a document instance
6005 * @dtd: a dtd instance
6006 *
6007 * Try to validate the document against the dtd instance
6008 *
6009 * basically it does check all the definitions in the DtD.
6010 *
6011 * returns 1 if valid or 0 otherwise
6012 */
6013
6014int
6015xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6016 int ret;
6017 xmlDtdPtr oldExt;
6018 xmlNodePtr root;
6019
6020 if (dtd == NULL) return(0);
6021 if (doc == NULL) return(0);
6022 oldExt = doc->extSubset;
6023 doc->extSubset = dtd;
6024 ret = xmlValidateRoot(ctxt, doc);
6025 if (ret == 0) {
6026 doc->extSubset = oldExt;
6027 return(ret);
6028 }
6029 if (doc->ids != NULL) {
6030 xmlFreeIDTable(doc->ids);
6031 doc->ids = NULL;
6032 }
6033 if (doc->refs != NULL) {
6034 xmlFreeRefTable(doc->refs);
6035 doc->refs = NULL;
6036 }
6037 root = xmlDocGetRootElement(doc);
6038 ret = xmlValidateElement(ctxt, doc, root);
6039 ret &= xmlValidateDocumentFinal(ctxt, doc);
6040 doc->extSubset = oldExt;
6041 return(ret);
6042}
6043
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006044static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006045xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6046 const xmlChar *name ATTRIBUTE_UNUSED) {
6047 if (cur == NULL)
6048 return;
6049 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6050 xmlChar *notation = cur->content;
6051
Daniel Veillard878eab02002-02-19 13:46:09 +00006052 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006053 int ret;
6054
6055 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6056 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006057 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006058 }
6059 }
6060 }
6061}
6062
6063static void
Owen Taylor3473f882001-02-23 17:55:21 +00006064xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006065 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006066 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006067 xmlDocPtr doc;
6068 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006069
Owen Taylor3473f882001-02-23 17:55:21 +00006070 if (cur == NULL)
6071 return;
6072 switch (cur->atype) {
6073 case XML_ATTRIBUTE_CDATA:
6074 case XML_ATTRIBUTE_ID:
6075 case XML_ATTRIBUTE_IDREF :
6076 case XML_ATTRIBUTE_IDREFS:
6077 case XML_ATTRIBUTE_NMTOKEN:
6078 case XML_ATTRIBUTE_NMTOKENS:
6079 case XML_ATTRIBUTE_ENUMERATION:
6080 break;
6081 case XML_ATTRIBUTE_ENTITY:
6082 case XML_ATTRIBUTE_ENTITIES:
6083 case XML_ATTRIBUTE_NOTATION:
6084 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006085
6086 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6087 cur->atype, cur->defaultValue);
6088 if ((ret == 0) && (ctxt->valid == 1))
6089 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006090 }
6091 if (cur->tree != NULL) {
6092 xmlEnumerationPtr tree = cur->tree;
6093 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006094 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006095 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006096 if ((ret == 0) && (ctxt->valid == 1))
6097 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006098 tree = tree->next;
6099 }
6100 }
6101 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006102 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6103 doc = cur->doc;
6104 if ((doc == NULL) || (cur->elem == NULL)) {
6105 VERROR(ctxt->userData,
6106 "xmlValidateAttributeCallback(%s): internal error\n",
6107 cur->name);
6108 return;
6109 }
6110 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6111 if (elem == NULL)
6112 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6113 if (elem == NULL) {
6114 VERROR(ctxt->userData,
6115 "attribute %s: could not find decl for element %s\n",
6116 cur->name, cur->elem);
6117 return;
6118 }
6119 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6120 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006121 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00006122 cur->name, cur->elem);
6123 ctxt->valid = 0;
6124 }
6125 }
Owen Taylor3473f882001-02-23 17:55:21 +00006126}
6127
6128/**
6129 * xmlValidateDtdFinal:
6130 * @ctxt: the validation context
6131 * @doc: a document instance
6132 *
6133 * Does the final step for the dtds validation once all the
6134 * subsets have been parsed
6135 *
6136 * basically it does the following checks described by the XML Rec
6137 * - check that ENTITY and ENTITIES type attributes default or
6138 * possible values matches one of the defined entities.
6139 * - check that NOTATION type attributes default or
6140 * possible values matches one of the defined notations.
6141 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006142 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006143 */
6144
6145int
6146xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006147 xmlDtdPtr dtd;
6148 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006149 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006150
6151 if (doc == NULL) return(0);
6152 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6153 return(0);
6154 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006155 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006156 dtd = doc->intSubset;
6157 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6158 table = (xmlAttributeTablePtr) dtd->attributes;
6159 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006160 }
6161 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006162 entities = (xmlEntitiesTablePtr) dtd->entities;
6163 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6164 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006165 }
6166 dtd = doc->extSubset;
6167 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6168 table = (xmlAttributeTablePtr) dtd->attributes;
6169 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006170 }
6171 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006172 entities = (xmlEntitiesTablePtr) dtd->entities;
6173 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6174 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006175 }
6176 return(ctxt->valid);
6177}
6178
6179/**
6180 * xmlValidateDocument:
6181 * @ctxt: the validation context
6182 * @doc: a document instance
6183 *
6184 * Try to validate the document instance
6185 *
6186 * basically it does the all the checks described by the XML Rec
6187 * i.e. validates the internal and external subset (if present)
6188 * and validate the document tree.
6189 *
6190 * returns 1 if valid or 0 otherwise
6191 */
6192
6193int
6194xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6195 int ret;
6196 xmlNodePtr root;
6197
Daniel Veillard2fd85422002-10-16 14:32:41 +00006198 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6199 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00006200 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006201 }
Owen Taylor3473f882001-02-23 17:55:21 +00006202 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6203 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6204 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6205 doc->intSubset->SystemID);
6206 if (doc->extSubset == NULL) {
6207 if (doc->intSubset->SystemID != NULL) {
6208 VERROR(ctxt->userData,
6209 "Could not load the external subset \"%s\"\n",
6210 doc->intSubset->SystemID);
6211 } else {
6212 VERROR(ctxt->userData,
6213 "Could not load the external subset \"%s\"\n",
6214 doc->intSubset->ExternalID);
6215 }
6216 return(0);
6217 }
6218 }
6219
6220 if (doc->ids != NULL) {
6221 xmlFreeIDTable(doc->ids);
6222 doc->ids = NULL;
6223 }
6224 if (doc->refs != NULL) {
6225 xmlFreeRefTable(doc->refs);
6226 doc->refs = NULL;
6227 }
6228 ret = xmlValidateDtdFinal(ctxt, doc);
6229 if (!xmlValidateRoot(ctxt, doc)) return(0);
6230
6231 root = xmlDocGetRootElement(doc);
6232 ret &= xmlValidateElement(ctxt, doc, root);
6233 ret &= xmlValidateDocumentFinal(ctxt, doc);
6234 return(ret);
6235}
6236
6237
6238/************************************************************************
6239 * *
6240 * Routines for dynamic validation editing *
6241 * *
6242 ************************************************************************/
6243
6244/**
6245 * xmlValidGetPotentialChildren:
6246 * @ctree: an element content tree
6247 * @list: an array to store the list of child names
6248 * @len: a pointer to the number of element in the list
6249 * @max: the size of the array
6250 *
6251 * Build/extend a list of potential children allowed by the content tree
6252 *
6253 * returns the number of element in the list, or -1 in case of error.
6254 */
6255
6256int
6257xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6258 int *len, int max) {
6259 int i;
6260
6261 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6262 return(-1);
6263 if (*len >= max) return(*len);
6264
6265 switch (ctree->type) {
6266 case XML_ELEMENT_CONTENT_PCDATA:
6267 for (i = 0; i < *len;i++)
6268 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6269 list[(*len)++] = BAD_CAST "#PCDATA";
6270 break;
6271 case XML_ELEMENT_CONTENT_ELEMENT:
6272 for (i = 0; i < *len;i++)
6273 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6274 list[(*len)++] = ctree->name;
6275 break;
6276 case XML_ELEMENT_CONTENT_SEQ:
6277 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6278 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6279 break;
6280 case XML_ELEMENT_CONTENT_OR:
6281 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6282 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6283 break;
6284 }
6285
6286 return(*len);
6287}
6288
6289/**
6290 * xmlValidGetValidElements:
6291 * @prev: an element to insert after
6292 * @next: an element to insert next
6293 * @list: an array to store the list of child names
6294 * @max: the size of the array
6295 *
6296 * This function returns the list of authorized children to insert
6297 * within an existing tree while respecting the validity constraints
6298 * forced by the Dtd. The insertion point is defined using @prev and
6299 * @next in the following ways:
6300 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6301 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6302 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6303 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6304 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6305 *
6306 * pointers to the element names are inserted at the beginning of the array
6307 * and do not need to be freed.
6308 *
6309 * returns the number of element in the list, or -1 in case of error. If
6310 * the function returns the value @max the caller is invited to grow the
6311 * receiving array and retry.
6312 */
6313
6314int
6315xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6316 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006317 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006318 int nb_valid_elements = 0;
6319 const xmlChar *elements[256];
6320 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006321 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006322
6323 xmlNode *ref_node;
6324 xmlNode *parent;
6325 xmlNode *test_node;
6326
6327 xmlNode *prev_next;
6328 xmlNode *next_prev;
6329 xmlNode *parent_childs;
6330 xmlNode *parent_last;
6331
6332 xmlElement *element_desc;
6333
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006334 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006335
Owen Taylor3473f882001-02-23 17:55:21 +00006336 if (prev == NULL && next == NULL)
6337 return(-1);
6338
6339 if (list == NULL) return(-1);
6340 if (max <= 0) return(-1);
6341
6342 nb_valid_elements = 0;
6343 ref_node = prev ? prev : next;
6344 parent = ref_node->parent;
6345
6346 /*
6347 * Retrieves the parent element declaration
6348 */
6349 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6350 parent->name);
6351 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6352 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6353 parent->name);
6354 if (element_desc == NULL) return(-1);
6355
6356 /*
6357 * Do a backup of the current tree structure
6358 */
6359 prev_next = prev ? prev->next : NULL;
6360 next_prev = next ? next->prev : NULL;
6361 parent_childs = parent->children;
6362 parent_last = parent->last;
6363
6364 /*
6365 * Creates a dummy node and insert it into the tree
6366 */
6367 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6368 test_node->doc = ref_node->doc;
6369 test_node->parent = parent;
6370 test_node->prev = prev;
6371 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006372 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006373
6374 if (prev) prev->next = test_node;
6375 else parent->children = test_node;
6376
6377 if (next) next->prev = test_node;
6378 else parent->last = test_node;
6379
6380 /*
6381 * Insert each potential child node and check if the parent is
6382 * still valid
6383 */
6384 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6385 elements, &nb_elements, 256);
6386
6387 for (i = 0;i < nb_elements;i++) {
6388 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006389 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006390 int j;
6391
6392 for (j = 0; j < nb_valid_elements;j++)
6393 if (xmlStrEqual(elements[i], list[j])) break;
6394 list[nb_valid_elements++] = elements[i];
6395 if (nb_valid_elements >= max) break;
6396 }
6397 }
6398
6399 /*
6400 * Restore the tree structure
6401 */
6402 if (prev) prev->next = prev_next;
6403 if (next) next->prev = next_prev;
6404 parent->children = parent_childs;
6405 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006406
6407 /*
6408 * Free up the dummy node
6409 */
6410 test_node->name = name;
6411 xmlFreeNode(test_node);
6412
Owen Taylor3473f882001-02-23 17:55:21 +00006413 return(nb_valid_elements);
6414}