blob: 4dbe11715016914dca5dfaf548af4135a537a849 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * valid.c : part of the code use to do the DTD handling and the validity
3 * checking
4 *
5 * See Copyright for the status of this software.
6 *
Daniel Veillardc5d64342001-06-24 12:13:24 +00007 * daniel@veillard.com
Owen Taylor3473f882001-02-23 17:55:21 +00008 */
9
Daniel Veillard34ce8be2002-03-18 19:37:11 +000010#define IN_LIBXML
Bjorn Reese70a9da52001-04-21 16:57:29 +000011#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000012
Owen Taylor3473f882001-02-23 17:55:21 +000013#include <string.h>
14
15#ifdef HAVE_STDLIB_H
16#include <stdlib.h>
17#endif
18
19#include <libxml/xmlmemory.h>
20#include <libxml/hash.h>
21#include <libxml/valid.h>
22#include <libxml/parser.h>
23#include <libxml/parserInternals.h>
24#include <libxml/xmlerror.h>
25#include <libxml/list.h>
Daniel Veillard3c01b1d2001-10-17 15:58:35 +000026#include <libxml/globals.h>
Owen Taylor3473f882001-02-23 17:55:21 +000027
Daniel Veillarde62d36c2001-05-15 08:53:16 +000028/* #define DEBUG_VALID_ALGO */
Daniel Veillard5acfd6b2002-09-18 16:29:02 +000029/* #define DEBUG_REGEXP_ALGO */
Daniel Veillarde62d36c2001-05-15 08:53:16 +000030
Daniel Veillarda646cfd2002-09-17 21:50:03 +000031#define TODO \
32 xmlGenericError(xmlGenericErrorContext, \
33 "Unimplemented block at %s:%d\n", \
34 __FILE__, __LINE__);
Owen Taylor3473f882001-02-23 17:55:21 +000035
Daniel Veillardea7751d2002-12-20 00:16:24 +000036#define VERROR \
37 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
Owen Taylor3473f882001-02-23 17:55:21 +000038
Daniel Veillardea7751d2002-12-20 00:16:24 +000039#define VWARNING \
40 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
41
42
43#ifdef LIBXML_REGEXP_ENABLED
44/*
45 * If regexp are enabled we can do continuous validation without the
46 * need of a tree to validate the content model. this is done in each
47 * callbacks.
48 * Each xmlValidState represent the validation state associated to the
49 * set of nodes currently open from the document root to the current element.
50 */
51
52
53typedef struct _xmlValidState {
54 xmlElementPtr elemDecl; /* pointer to the content model */
55 xmlNodePtr node; /* pointer to the current node */
56 xmlRegExecCtxtPtr exec; /* regexp runtime */
57} _xmlValidState;
58
59
60static int
61vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) {
62 if (ctxt->vstateMax == 0) {
63 ctxt->vstateMax = 10;
64 ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax *
65 sizeof(ctxt->vstateTab[0]));
66 if (ctxt->vstateTab == NULL) {
67 VERROR(ctxt->userData, "realloc failed !n");
68 return(-1);
69 }
70 }
71
72 if (ctxt->vstateNr >= ctxt->vstateMax) {
73 ctxt->vstateMax *= 2;
74 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
75 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
76 if (ctxt->vstateTab == NULL) {
77 VERROR(ctxt->userData, "realloc failed !n");
78 return(-1);
79 }
80 }
81 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr];
82 ctxt->vstateTab[ctxt->vstateNr].elemDecl = elemDecl;
83 ctxt->vstateTab[ctxt->vstateNr].node = node;
84 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
85 if (elemDecl->contModel == NULL)
86 xmlValidBuildContentModel(ctxt, elemDecl);
87 if (elemDecl->contModel != NULL) {
88 ctxt->vstateTab[ctxt->vstateNr].exec =
89 xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
90 } else {
91 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
92 VERROR(ctxt->userData,
93 "Failed to build content model regexp for %s", node->name);
94 }
95 }
96 return(ctxt->vstateNr++);
97}
98
99static int
100vstateVPop(xmlValidCtxtPtr ctxt) {
101 xmlElementPtr elemDecl;
102
Daniel Veillard336fc7d2002-12-27 19:37:04 +0000103 if (ctxt->vstateNr < 1) return(-1);
Daniel Veillardea7751d2002-12-20 00:16:24 +0000104 ctxt->vstateNr--;
105 elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
106 ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
107 ctxt->vstateTab[ctxt->vstateNr].node = NULL;
108 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
109 xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
110 }
111 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
112 if (ctxt->vstateNr >= 1)
113 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
114 else
115 ctxt->vstate = NULL;
116 return(ctxt->vstateNr);
117}
118
119#else /* not LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000120/*
Daniel Veillard1c732d22002-11-30 11:22:59 +0000121 * If regexp are not enabled, it uses a home made algorithm less
122 * complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000123 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000124 * only restriction is on the deepness of the tree limited by the
125 * size of the occurs bitfield
126 *
127 * this is the content of a saved state for rollbacks
128 */
129
130#define ROLLBACK_OR 0
131#define ROLLBACK_PARENT 1
132
Daniel Veillardb44025c2001-10-11 22:55:55 +0000133typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000134 xmlElementContentPtr cont; /* pointer to the content model subtree */
135 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000136 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000137 unsigned char depth; /* current depth in the overall tree */
138 unsigned char state; /* ROLLBACK_XXX */
139} _xmlValidState;
140
Daniel Veillardfc57b412002-04-29 15:50:14 +0000141#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000142#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
143#define CONT ctxt->vstate->cont
144#define NODE ctxt->vstate->node
145#define DEPTH ctxt->vstate->depth
146#define OCCURS ctxt->vstate->occurs
147#define STATE ctxt->vstate->state
148
Daniel Veillard5344c602001-12-31 16:37:34 +0000149#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
150#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000151
Daniel Veillard5344c602001-12-31 16:37:34 +0000152#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
153#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000154
155static int
156vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
157 xmlNodePtr node, unsigned char depth, long occurs,
158 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000159 int i = ctxt->vstateNr - 1;
160
Daniel Veillard940492d2002-04-15 10:15:25 +0000161 if (ctxt->vstateNr > MAX_RECURSE) {
162 return(-1);
163 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000164 if (ctxt->vstateNr >= ctxt->vstateMax) {
165 ctxt->vstateMax *= 2;
166 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
167 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
168 if (ctxt->vstateTab == NULL) {
169 xmlGenericError(xmlGenericErrorContext,
170 "realloc failed !n");
Daniel Veillard940492d2002-04-15 10:15:25 +0000171 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000172 }
Daniel Veillard06803992001-04-22 10:35:56 +0000173 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000174 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000175 /*
176 * Don't push on the stack a state already here
177 */
178 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
179 (ctxt->vstateTab[i].node == node) &&
180 (ctxt->vstateTab[i].depth == depth) &&
181 (ctxt->vstateTab[i].occurs == occurs) &&
182 (ctxt->vstateTab[i].state == state))
183 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000184 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
185 ctxt->vstateTab[ctxt->vstateNr].node = node;
186 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
187 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
188 ctxt->vstateTab[ctxt->vstateNr].state = state;
189 return(ctxt->vstateNr++);
190}
191
192static int
193vstateVPop(xmlValidCtxtPtr ctxt) {
194 if (ctxt->vstateNr <= 1) return(-1);
195 ctxt->vstateNr--;
196 ctxt->vstate = &ctxt->vstateTab[0];
197 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
198 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
199 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
200 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
201 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
202 return(ctxt->vstateNr);
203}
204
Daniel Veillard118aed72002-09-24 14:13:13 +0000205#endif /* LIBXML_REGEXP_ENABLED */
206
Daniel Veillard1c732d22002-11-30 11:22:59 +0000207static int
208nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value)
209{
210 if (ctxt->nodeMax <= 0) {
211 ctxt->nodeMax = 4;
212 ctxt->nodeTab =
213 (xmlNodePtr *) xmlMalloc(ctxt->nodeMax *
214 sizeof(ctxt->nodeTab[0]));
215 if (ctxt->nodeTab == NULL) {
216 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
217 ctxt->nodeMax = 0;
218 return (0);
219 }
220 }
221 if (ctxt->nodeNr >= ctxt->nodeMax) {
222 ctxt->nodeMax *= 2;
223 ctxt->nodeTab =
224 (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
225 ctxt->nodeMax *
226 sizeof(ctxt->nodeTab[0]));
227 if (ctxt->nodeTab == NULL) {
228 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
229 return (0);
230 }
231 }
232 ctxt->nodeTab[ctxt->nodeNr] = value;
233 ctxt->node = value;
234 return (ctxt->nodeNr++);
235}
236static xmlNodePtr
237nodeVPop(xmlValidCtxtPtr ctxt)
238{
239 xmlNodePtr ret;
240
241 if (ctxt->nodeNr <= 0)
242 return (0);
243 ctxt->nodeNr--;
244 if (ctxt->nodeNr > 0)
245 ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1];
246 else
247 ctxt->node = NULL;
248 ret = ctxt->nodeTab[ctxt->nodeNr];
249 ctxt->nodeTab[ctxt->nodeNr] = 0;
250 return (ret);
251}
Owen Taylor3473f882001-02-23 17:55:21 +0000252
Daniel Veillard336fc7d2002-12-27 19:37:04 +0000253#if 0
254/**
255 * xmlFreeValidCtxt:
256 * @ctxt: a validation context
257 *
258 * Free the memory allocated for a validation context
259 */
260void
261xmlFreeValidCtxt(xmlValidCtxtPtr ctxt) {
262 if (ctxt == NULL)
263 return;
264#ifdef LIBXML_REGEXP_ENABLED
265 while (ctxt->vstateNr >= 0)
266 vstateVPop(ctxt);
267 if (ctxt->vstateNr <= 1) return(-1);
268 ctxt->vstateNr--;
269 elemDecl = ctxt->vstateTab[ctxt->vstateNr].elemDecl;
270 ctxt->vstateTab[ctxt->vstateNr].elemDecl = NULL;
271 ctxt->vstateTab[ctxt->vstateNr].node = NULL;
272 if ((elemDecl != NULL) && (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT)) {
273 xmlRegFreeExecCtxt(ctxt->vstateTab[ctxt->vstateNr].exec);
274 }
275 ctxt->vstateTab[ctxt->vstateNr].exec = NULL;
276 if (ctxt->vstateNr >= 1)
277 ctxt->vstate = &ctxt->vstateTab[ctxt->vstateNr - 1];
278 else
279 ctxt->vstate = NULL;
280 return(ctxt->vstateNr);
281#else /* ! LIBXML_REGEXP_ENABLED */
282#endif /* LIBXML_REGEXP_ENABLED */
283}
284#endif
285
Owen Taylor3473f882001-02-23 17:55:21 +0000286#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000287static void
288xmlValidPrintNode(xmlNodePtr cur) {
289 if (cur == NULL) {
290 xmlGenericError(xmlGenericErrorContext, "null");
291 return;
292 }
293 switch (cur->type) {
294 case XML_ELEMENT_NODE:
295 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
296 break;
297 case XML_TEXT_NODE:
298 xmlGenericError(xmlGenericErrorContext, "text ");
299 break;
300 case XML_CDATA_SECTION_NODE:
301 xmlGenericError(xmlGenericErrorContext, "cdata ");
302 break;
303 case XML_ENTITY_REF_NODE:
304 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
305 break;
306 case XML_PI_NODE:
307 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
308 break;
309 case XML_COMMENT_NODE:
310 xmlGenericError(xmlGenericErrorContext, "comment ");
311 break;
312 case XML_ATTRIBUTE_NODE:
313 xmlGenericError(xmlGenericErrorContext, "?attr? ");
314 break;
315 case XML_ENTITY_NODE:
316 xmlGenericError(xmlGenericErrorContext, "?ent? ");
317 break;
318 case XML_DOCUMENT_NODE:
319 xmlGenericError(xmlGenericErrorContext, "?doc? ");
320 break;
321 case XML_DOCUMENT_TYPE_NODE:
322 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
323 break;
324 case XML_DOCUMENT_FRAG_NODE:
325 xmlGenericError(xmlGenericErrorContext, "?frag? ");
326 break;
327 case XML_NOTATION_NODE:
328 xmlGenericError(xmlGenericErrorContext, "?nota? ");
329 break;
330 case XML_HTML_DOCUMENT_NODE:
331 xmlGenericError(xmlGenericErrorContext, "?html? ");
332 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000333#ifdef LIBXML_DOCB_ENABLED
334 case XML_DOCB_DOCUMENT_NODE:
335 xmlGenericError(xmlGenericErrorContext, "?docb? ");
336 break;
337#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000338 case XML_DTD_NODE:
339 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
340 break;
341 case XML_ELEMENT_DECL:
342 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
343 break;
344 case XML_ATTRIBUTE_DECL:
345 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
346 break;
347 case XML_ENTITY_DECL:
348 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
349 break;
350 case XML_NAMESPACE_DECL:
351 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
352 break;
353 case XML_XINCLUDE_START:
354 xmlGenericError(xmlGenericErrorContext, "incstart ");
355 break;
356 case XML_XINCLUDE_END:
357 xmlGenericError(xmlGenericErrorContext, "incend ");
358 break;
359 }
360}
361
362static void
363xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000364 if (cur == NULL)
365 xmlGenericError(xmlGenericErrorContext, "null ");
366 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000367 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000368 cur = cur->next;
369 }
370}
371
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000372static void
373xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Daniel Veillard83391282003-03-06 21:37:30 +0000374 char expr[5000];
Owen Taylor3473f882001-02-23 17:55:21 +0000375
376 expr[0] = 0;
377 xmlGenericError(xmlGenericErrorContext, "valid: ");
378 xmlValidPrintNodeList(cur);
379 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000380 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000381 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
382}
383
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000384static void
385xmlValidDebugState(xmlValidStatePtr state) {
386 xmlGenericError(xmlGenericErrorContext, "(");
387 if (state->cont == NULL)
388 xmlGenericError(xmlGenericErrorContext, "null,");
389 else
390 switch (state->cont->type) {
391 case XML_ELEMENT_CONTENT_PCDATA:
392 xmlGenericError(xmlGenericErrorContext, "pcdata,");
393 break;
394 case XML_ELEMENT_CONTENT_ELEMENT:
395 xmlGenericError(xmlGenericErrorContext, "%s,",
396 state->cont->name);
397 break;
398 case XML_ELEMENT_CONTENT_SEQ:
399 xmlGenericError(xmlGenericErrorContext, "seq,");
400 break;
401 case XML_ELEMENT_CONTENT_OR:
402 xmlGenericError(xmlGenericErrorContext, "or,");
403 break;
404 }
405 xmlValidPrintNode(state->node);
406 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
407 state->depth, state->occurs, state->state);
408}
409
410static void
411xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
412 int i, j;
413
414 xmlGenericError(xmlGenericErrorContext, "state: ");
415 xmlValidDebugState(ctxt->vstate);
416 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
417 ctxt->vstateNr - 1);
418 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
419 xmlValidDebugState(&ctxt->vstateTab[j]);
420 xmlGenericError(xmlGenericErrorContext, "\n");
421}
422
423/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000424#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000425 *****/
426
427#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000428#define DEBUG_VALID_MSG(m) \
429 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
430
Owen Taylor3473f882001-02-23 17:55:21 +0000431#else
432#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000433#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000434#endif
435
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000436/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000437
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000438#define VECTXT(ctxt, node) \
439 if ((ctxt != NULL) && (ctxt->error != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000440 (node != NULL)) { \
441 xmlChar *base = xmlNodeGetBase(NULL,node); \
442 if (base != NULL) { \
443 ctxt->error(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000444 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000445 xmlFree(base); \
446 } else \
447 ctxt->error(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000448 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000449 }
450
451#define VWCTXT(ctxt, node) \
452 if ((ctxt != NULL) && (ctxt->warning != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000453 (node != NULL)) { \
454 xmlChar *base = xmlNodeGetBase(NULL,node); \
455 if (base != NULL) { \
456 ctxt->warning(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000457 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000458 xmlFree(base); \
459 } else \
460 ctxt->warning(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000461 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000462 }
463
Owen Taylor3473f882001-02-23 17:55:21 +0000464#define CHECK_DTD \
465 if (doc == NULL) return(0); \
466 else if ((doc->intSubset == NULL) && \
467 (doc->extSubset == NULL)) return(0)
468
Daniel Veillarda10efa82001-04-18 13:09:01 +0000469static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
470 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000471xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
472
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000473#ifdef LIBXML_REGEXP_ENABLED
474
475/************************************************************************
476 * *
477 * Content model validation based on the regexps *
478 * *
479 ************************************************************************/
480
481/**
482 * xmlValidBuildAContentModel:
483 * @content: the content model
484 * @ctxt: the schema parser context
485 * @name: the element name whose content is being built
486 *
487 * Generate the automata sequence needed for that type
488 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000489 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000490 */
491static int
492xmlValidBuildAContentModel(xmlElementContentPtr content,
493 xmlValidCtxtPtr ctxt,
494 const xmlChar *name) {
495 if (content == NULL) {
496 VERROR(ctxt->userData,
497 "Found unexpected type = NULL in %s content model\n", name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000498 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000499 }
500 switch (content->type) {
501 case XML_ELEMENT_CONTENT_PCDATA:
502 VERROR(ctxt->userData, "ContentModel found PCDATA for element %s\n",
503 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000504 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000505 break;
506 case XML_ELEMENT_CONTENT_ELEMENT: {
507 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillardc00cda82003-04-07 10:22:39 +0000508 xmlChar fn[50];
509 xmlChar *fullname;
510
511 fullname = xmlBuildQName(content->name, content->prefix, fn, 50);
512 if (fullname == NULL) {
513 VERROR(ctxt->userData, "Out of memory\n");
514 return(0);
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000515 }
516
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000517 switch (content->ocur) {
518 case XML_ELEMENT_CONTENT_ONCE:
519 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000520 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000521 break;
522 case XML_ELEMENT_CONTENT_OPT:
523 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000524 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000525 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
526 break;
527 case XML_ELEMENT_CONTENT_PLUS:
528 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000529 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000530 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000531 ctxt->state, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000532 break;
533 case XML_ELEMENT_CONTENT_MULT:
534 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000535 ctxt->state, fullname, NULL);
Daniel Veillard57e79b32003-02-04 15:33:12 +0000536 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, ctxt->state,
537 NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000538 break;
539 }
Daniel Veillardc00cda82003-04-07 10:22:39 +0000540 if ((fullname != fn) && (fullname != content->name))
541 xmlFree(fullname);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000542 break;
543 }
544 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000545 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000546 xmlElementContentOccur ocur;
547
548 /*
549 * Simply iterate over the content
550 */
551 oldstate = ctxt->state;
552 ocur = content->ocur;
Daniel Veillardf4be0182003-02-24 19:54:33 +0000553 if (ocur != XML_ELEMENT_CONTENT_ONCE) {
554 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
555 oldstate = ctxt->state;
556 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000557 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000558 xmlValidBuildAContentModel(content->c1, ctxt, name);
559 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000560 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
561 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
562 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000563 oldend = ctxt->state;
564 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000565 switch (ocur) {
566 case XML_ELEMENT_CONTENT_ONCE:
567 break;
568 case XML_ELEMENT_CONTENT_OPT:
569 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
570 break;
571 case XML_ELEMENT_CONTENT_MULT:
572 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000573 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000574 break;
575 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000576 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000577 break;
578 }
579 break;
580 }
581 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000582 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000583 xmlElementContentOccur ocur;
584
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000585 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000586 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
587 (ocur == XML_ELEMENT_CONTENT_MULT)) {
588 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
589 ctxt->state, NULL);
590 }
591 oldstate = ctxt->state;
592 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000593
594 /*
595 * iterate over the subtypes and remerge the end with an
596 * epsilon transition
597 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000598 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000599 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000600 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000601 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000602 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000603 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
604 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000605 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000606 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000607 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
608 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000609 switch (ocur) {
610 case XML_ELEMENT_CONTENT_ONCE:
611 break;
612 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000613 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000614 break;
615 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000616 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
617 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000618 break;
619 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000620 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000621 break;
622 }
623 break;
624 }
625 default:
626 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
627 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000628 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000629 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000630 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000631}
632/**
633 * xmlValidBuildContentModel:
634 * @ctxt: a validation context
635 * @elem: an element declaration node
636 *
637 * (Re)Build the automata associated to the content model of this
638 * element
639 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000640 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000641 */
642int
643xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
644 xmlAutomataStatePtr start;
645
646 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000647 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000648 if (elem->type != XML_ELEMENT_DECL)
649 return(0);
650 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
651 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000652 /* TODO: should we rebuild in this case ? */
Daniel Veillardec498e12003-02-05 11:01:50 +0000653 if (elem->contModel != NULL) {
654 if (!xmlRegexpIsDeterminist(elem->contModel)) {
655 ctxt->valid = 0;
656 return(0);
657 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000658 return(1);
Daniel Veillardec498e12003-02-05 11:01:50 +0000659 }
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;
Daniel Veillardec498e12003-02-05 11:01:50 +0000681 ctxt->state = NULL;
682 xmlFreeAutomata(ctxt->am);
683 ctxt->am = NULL;
684 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000685 }
686 ctxt->state = NULL;
687 xmlFreeAutomata(ctxt->am);
688 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000689 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000690}
691
692#endif /* LIBXML_REGEXP_ENABLED */
693
Owen Taylor3473f882001-02-23 17:55:21 +0000694/****************************************************************
695 * *
696 * Util functions for data allocation/deallocation *
697 * *
698 ****************************************************************/
699
700/**
701 * xmlNewElementContent:
702 * @name: the subelement name or NULL
703 * @type: the type of element content decl
704 *
705 * Allocate an element content structure.
706 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000707 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000708 */
709xmlElementContentPtr
710xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
711 xmlElementContentPtr ret;
712
713 switch(type) {
714 case XML_ELEMENT_CONTENT_ELEMENT:
715 if (name == NULL) {
716 xmlGenericError(xmlGenericErrorContext,
717 "xmlNewElementContent : name == NULL !\n");
718 }
719 break;
720 case XML_ELEMENT_CONTENT_PCDATA:
721 case XML_ELEMENT_CONTENT_SEQ:
722 case XML_ELEMENT_CONTENT_OR:
723 if (name != NULL) {
724 xmlGenericError(xmlGenericErrorContext,
725 "xmlNewElementContent : name != NULL !\n");
726 }
727 break;
728 default:
729 xmlGenericError(xmlGenericErrorContext,
730 "xmlNewElementContent: unknown type %d\n", type);
731 return(NULL);
732 }
733 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
734 if (ret == NULL) {
735 xmlGenericError(xmlGenericErrorContext,
736 "xmlNewElementContent : out of memory!\n");
737 return(NULL);
738 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000739 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000740 ret->type = type;
741 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000742 if (name != NULL) {
743 xmlChar *prefix = NULL;
744 ret->name = xmlSplitQName2(name, &prefix);
745 if (ret->name == NULL)
746 ret->name = xmlStrdup(name);
747 ret->prefix = prefix;
748 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000749 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000750 ret->prefix = NULL;
751 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000752 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000753 return(ret);
754}
755
756/**
757 * xmlCopyElementContent:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000758 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +0000759 *
760 * Build a copy of an element content description.
761 *
762 * Returns the new xmlElementContentPtr or NULL in case of error.
763 */
764xmlElementContentPtr
765xmlCopyElementContent(xmlElementContentPtr cur) {
766 xmlElementContentPtr ret;
767
768 if (cur == NULL) return(NULL);
769 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
770 if (ret == NULL) {
771 xmlGenericError(xmlGenericErrorContext,
772 "xmlCopyElementContent : out of memory\n");
773 return(NULL);
774 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000775 if (cur->prefix != NULL)
776 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000777 ret->ocur = cur->ocur;
778 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000779 if (ret->c1 != NULL)
780 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000781 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000782 if (ret->c2 != NULL)
783 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000784 return(ret);
785}
786
787/**
788 * xmlFreeElementContent:
789 * @cur: the element content tree to free
790 *
791 * Free an element content structure. This is a recursive call !
792 */
793void
794xmlFreeElementContent(xmlElementContentPtr cur) {
795 if (cur == NULL) return;
796 switch (cur->type) {
797 case XML_ELEMENT_CONTENT_PCDATA:
798 case XML_ELEMENT_CONTENT_ELEMENT:
799 case XML_ELEMENT_CONTENT_SEQ:
800 case XML_ELEMENT_CONTENT_OR:
801 break;
802 default:
803 xmlGenericError(xmlGenericErrorContext,
804 "xmlFreeElementContent : type %d\n", cur->type);
805 return;
806 }
807 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
808 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
809 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000810 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000811 xmlFree(cur);
812}
813
814/**
815 * xmlDumpElementContent:
816 * @buf: An XML buffer
817 * @content: An element table
818 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
819 *
820 * This will dump the content of the element table as an XML DTD definition
821 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000822static void
Owen Taylor3473f882001-02-23 17:55:21 +0000823xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
824 if (content == NULL) return;
825
826 if (glob) xmlBufferWriteChar(buf, "(");
827 switch (content->type) {
828 case XML_ELEMENT_CONTENT_PCDATA:
829 xmlBufferWriteChar(buf, "#PCDATA");
830 break;
831 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000832 if (content->prefix != NULL) {
833 xmlBufferWriteCHAR(buf, content->prefix);
834 xmlBufferWriteChar(buf, ":");
835 }
Owen Taylor3473f882001-02-23 17:55:21 +0000836 xmlBufferWriteCHAR(buf, content->name);
837 break;
838 case XML_ELEMENT_CONTENT_SEQ:
839 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
840 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
841 xmlDumpElementContent(buf, content->c1, 1);
842 else
843 xmlDumpElementContent(buf, content->c1, 0);
844 xmlBufferWriteChar(buf, " , ");
845 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
846 xmlDumpElementContent(buf, content->c2, 1);
847 else
848 xmlDumpElementContent(buf, content->c2, 0);
849 break;
850 case XML_ELEMENT_CONTENT_OR:
851 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
852 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
853 xmlDumpElementContent(buf, content->c1, 1);
854 else
855 xmlDumpElementContent(buf, content->c1, 0);
856 xmlBufferWriteChar(buf, " | ");
857 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
858 xmlDumpElementContent(buf, content->c2, 1);
859 else
860 xmlDumpElementContent(buf, content->c2, 0);
861 break;
862 default:
863 xmlGenericError(xmlGenericErrorContext,
864 "xmlDumpElementContent: unknown type %d\n",
865 content->type);
866 }
867 if (glob)
868 xmlBufferWriteChar(buf, ")");
869 switch (content->ocur) {
870 case XML_ELEMENT_CONTENT_ONCE:
871 break;
872 case XML_ELEMENT_CONTENT_OPT:
873 xmlBufferWriteChar(buf, "?");
874 break;
875 case XML_ELEMENT_CONTENT_MULT:
876 xmlBufferWriteChar(buf, "*");
877 break;
878 case XML_ELEMENT_CONTENT_PLUS:
879 xmlBufferWriteChar(buf, "+");
880 break;
881 }
882}
883
884/**
885 * xmlSprintfElementContent:
886 * @buf: an output buffer
887 * @content: An element table
888 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
889 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000890 * Deprecated, unsafe, use xmlSnprintfElementContent
891 */
892void
893xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
894 xmlElementContentPtr content ATTRIBUTE_UNUSED,
895 int glob ATTRIBUTE_UNUSED) {
896}
897
898/**
899 * xmlSnprintfElementContent:
900 * @buf: an output buffer
901 * @size: the buffer size
902 * @content: An element table
903 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
904 *
Owen Taylor3473f882001-02-23 17:55:21 +0000905 * This will dump the content of the element content definition
906 * Intended just for the debug routine
907 */
908void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000909xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
910 int len;
911
Owen Taylor3473f882001-02-23 17:55:21 +0000912 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000913 len = strlen(buf);
914 if (size - len < 50) {
915 if ((size - len > 4) && (buf[len - 1] != '.'))
916 strcat(buf, " ...");
917 return;
918 }
Owen Taylor3473f882001-02-23 17:55:21 +0000919 if (glob) strcat(buf, "(");
920 switch (content->type) {
921 case XML_ELEMENT_CONTENT_PCDATA:
922 strcat(buf, "#PCDATA");
923 break;
924 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000925 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000926 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000927 strcat(buf, " ...");
928 return;
929 }
930 strcat(buf, (char *) content->prefix);
931 strcat(buf, ":");
932 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000933 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000934 strcat(buf, " ...");
935 return;
936 }
Owen Taylor3473f882001-02-23 17:55:21 +0000937 strcat(buf, (char *) content->name);
938 break;
939 case XML_ELEMENT_CONTENT_SEQ:
940 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
941 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000942 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000943 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000944 xmlSnprintfElementContent(buf, size, content->c1, 0);
945 len = strlen(buf);
946 if (size - len < 50) {
947 if ((size - len > 4) && (buf[len - 1] != '.'))
948 strcat(buf, " ...");
949 return;
950 }
Owen Taylor3473f882001-02-23 17:55:21 +0000951 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000952 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
953 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
954 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000955 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000956 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000957 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000958 break;
959 case XML_ELEMENT_CONTENT_OR:
960 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
961 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000962 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000963 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000964 xmlSnprintfElementContent(buf, size, content->c1, 0);
965 len = strlen(buf);
966 if (size - len < 50) {
967 if ((size - len > 4) && (buf[len - 1] != '.'))
968 strcat(buf, " ...");
969 return;
970 }
Owen Taylor3473f882001-02-23 17:55:21 +0000971 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000972 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
973 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
974 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000975 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000976 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000977 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000978 break;
979 }
980 if (glob)
981 strcat(buf, ")");
982 switch (content->ocur) {
983 case XML_ELEMENT_CONTENT_ONCE:
984 break;
985 case XML_ELEMENT_CONTENT_OPT:
986 strcat(buf, "?");
987 break;
988 case XML_ELEMENT_CONTENT_MULT:
989 strcat(buf, "*");
990 break;
991 case XML_ELEMENT_CONTENT_PLUS:
992 strcat(buf, "+");
993 break;
994 }
995}
996
997/****************************************************************
998 * *
999 * Registration of DTD declarations *
1000 * *
1001 ****************************************************************/
1002
1003/**
1004 * xmlCreateElementTable:
1005 *
1006 * create and initialize an empty element hash table.
1007 *
1008 * Returns the xmlElementTablePtr just created or NULL in case of error.
1009 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001010static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001011xmlCreateElementTable(void) {
1012 return(xmlHashCreate(0));
1013}
1014
1015/**
1016 * xmlFreeElement:
1017 * @elem: An element
1018 *
1019 * Deallocate the memory used by an element definition
1020 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001021static void
Owen Taylor3473f882001-02-23 17:55:21 +00001022xmlFreeElement(xmlElementPtr elem) {
1023 if (elem == NULL) return;
1024 xmlUnlinkNode((xmlNodePtr) elem);
1025 xmlFreeElementContent(elem->content);
1026 if (elem->name != NULL)
1027 xmlFree((xmlChar *) elem->name);
1028 if (elem->prefix != NULL)
1029 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +00001030#ifdef LIBXML_REGEXP_ENABLED
1031 if (elem->contModel != NULL)
1032 xmlRegFreeRegexp(elem->contModel);
1033#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001034 xmlFree(elem);
1035}
1036
1037
1038/**
1039 * xmlAddElementDecl:
1040 * @ctxt: the validation context
1041 * @dtd: pointer to the DTD
1042 * @name: the entity name
1043 * @type: the element type
1044 * @content: the element content tree or NULL
1045 *
1046 * Register a new element declaration
1047 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001048 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001049 */
1050xmlElementPtr
1051xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
1052 xmlElementTypeVal type,
1053 xmlElementContentPtr content) {
1054 xmlElementPtr ret;
1055 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001056 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001057 xmlChar *ns, *uqname;
1058
1059 if (dtd == NULL) {
1060 xmlGenericError(xmlGenericErrorContext,
1061 "xmlAddElementDecl: dtd == NULL\n");
1062 return(NULL);
1063 }
1064 if (name == NULL) {
1065 xmlGenericError(xmlGenericErrorContext,
1066 "xmlAddElementDecl: name == NULL\n");
1067 return(NULL);
1068 }
1069 switch (type) {
1070 case XML_ELEMENT_TYPE_EMPTY:
1071 if (content != NULL) {
1072 xmlGenericError(xmlGenericErrorContext,
1073 "xmlAddElementDecl: content != NULL for EMPTY\n");
1074 return(NULL);
1075 }
1076 break;
1077 case XML_ELEMENT_TYPE_ANY:
1078 if (content != NULL) {
1079 xmlGenericError(xmlGenericErrorContext,
1080 "xmlAddElementDecl: content != NULL for ANY\n");
1081 return(NULL);
1082 }
1083 break;
1084 case XML_ELEMENT_TYPE_MIXED:
1085 if (content == NULL) {
1086 xmlGenericError(xmlGenericErrorContext,
1087 "xmlAddElementDecl: content == NULL for MIXED\n");
1088 return(NULL);
1089 }
1090 break;
1091 case XML_ELEMENT_TYPE_ELEMENT:
1092 if (content == NULL) {
1093 xmlGenericError(xmlGenericErrorContext,
1094 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1095 return(NULL);
1096 }
1097 break;
1098 default:
1099 xmlGenericError(xmlGenericErrorContext,
1100 "xmlAddElementDecl: unknown type %d\n", type);
1101 return(NULL);
1102 }
1103
1104 /*
1105 * check if name is a QName
1106 */
1107 uqname = xmlSplitQName2(name, &ns);
1108 if (uqname != NULL)
1109 name = uqname;
1110
1111 /*
1112 * Create the Element table if needed.
1113 */
1114 table = (xmlElementTablePtr) dtd->elements;
1115 if (table == NULL) {
1116 table = xmlCreateElementTable();
1117 dtd->elements = (void *) table;
1118 }
1119 if (table == NULL) {
1120 xmlGenericError(xmlGenericErrorContext,
1121 "xmlAddElementDecl: Table creation failed!\n");
1122 return(NULL);
1123 }
1124
Daniel Veillarda10efa82001-04-18 13:09:01 +00001125 /*
1126 * lookup old attributes inserted on an undefined element in the
1127 * internal subset.
1128 */
1129 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1130 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1131 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1132 oldAttributes = ret->attributes;
1133 ret->attributes = NULL;
1134 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1135 xmlFreeElement(ret);
1136 }
Owen Taylor3473f882001-02-23 17:55:21 +00001137 }
Owen Taylor3473f882001-02-23 17:55:21 +00001138
1139 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001140 * The element may already be present if one of its attribute
1141 * was registered first
1142 */
1143 ret = xmlHashLookup2(table, name, ns);
1144 if (ret != NULL) {
1145 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1146 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001147 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001148 */
1149 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1150 if (uqname != NULL)
1151 xmlFree(uqname);
1152 return(NULL);
1153 }
1154 } else {
1155 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1156 if (ret == NULL) {
1157 xmlGenericError(xmlGenericErrorContext,
1158 "xmlAddElementDecl: out of memory\n");
1159 return(NULL);
1160 }
1161 memset(ret, 0, sizeof(xmlElement));
1162 ret->type = XML_ELEMENT_DECL;
1163
1164 /*
1165 * fill the structure.
1166 */
1167 ret->name = xmlStrdup(name);
1168 ret->prefix = ns;
1169
1170 /*
1171 * Validity Check:
1172 * Insertion must not fail
1173 */
1174 if (xmlHashAddEntry2(table, name, ns, ret)) {
1175 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001176 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001177 */
1178 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1179 xmlFreeElement(ret);
1180 if (uqname != NULL)
1181 xmlFree(uqname);
1182 return(NULL);
1183 }
1184 }
1185
1186 /*
1187 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001188 */
1189 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001190 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001191 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001192
1193 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001194 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001195 */
1196 ret->parent = dtd;
1197 ret->doc = dtd->doc;
1198 if (dtd->last == NULL) {
1199 dtd->children = dtd->last = (xmlNodePtr) ret;
1200 } else {
1201 dtd->last->next = (xmlNodePtr) ret;
1202 ret->prev = dtd->last;
1203 dtd->last = (xmlNodePtr) ret;
1204 }
1205 if (uqname != NULL)
1206 xmlFree(uqname);
1207 return(ret);
1208}
1209
1210/**
1211 * xmlFreeElementTable:
1212 * @table: An element table
1213 *
1214 * Deallocate the memory used by an element hash table.
1215 */
1216void
1217xmlFreeElementTable(xmlElementTablePtr table) {
1218 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1219}
1220
1221/**
1222 * xmlCopyElement:
1223 * @elem: An element
1224 *
1225 * Build a copy of an element.
1226 *
1227 * Returns the new xmlElementPtr or NULL in case of error.
1228 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001229static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001230xmlCopyElement(xmlElementPtr elem) {
1231 xmlElementPtr cur;
1232
1233 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1234 if (cur == NULL) {
1235 xmlGenericError(xmlGenericErrorContext,
1236 "xmlCopyElement: out of memory !\n");
1237 return(NULL);
1238 }
1239 memset(cur, 0, sizeof(xmlElement));
1240 cur->type = XML_ELEMENT_DECL;
1241 cur->etype = elem->etype;
1242 if (elem->name != NULL)
1243 cur->name = xmlStrdup(elem->name);
1244 else
1245 cur->name = NULL;
1246 if (elem->prefix != NULL)
1247 cur->prefix = xmlStrdup(elem->prefix);
1248 else
1249 cur->prefix = NULL;
1250 cur->content = xmlCopyElementContent(elem->content);
1251 /* TODO : rebuild the attribute list on the copy */
1252 cur->attributes = NULL;
1253 return(cur);
1254}
1255
1256/**
1257 * xmlCopyElementTable:
1258 * @table: An element table
1259 *
1260 * Build a copy of an element table.
1261 *
1262 * Returns the new xmlElementTablePtr or NULL in case of error.
1263 */
1264xmlElementTablePtr
1265xmlCopyElementTable(xmlElementTablePtr table) {
1266 return((xmlElementTablePtr) xmlHashCopy(table,
1267 (xmlHashCopier) xmlCopyElement));
1268}
1269
1270/**
1271 * xmlDumpElementDecl:
1272 * @buf: the XML buffer output
1273 * @elem: An element table
1274 *
1275 * This will dump the content of the element declaration as an XML
1276 * DTD definition
1277 */
1278void
1279xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1280 switch (elem->etype) {
1281 case XML_ELEMENT_TYPE_EMPTY:
1282 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001283 if (elem->prefix != NULL) {
1284 xmlBufferWriteCHAR(buf, elem->prefix);
1285 xmlBufferWriteChar(buf, ":");
1286 }
Owen Taylor3473f882001-02-23 17:55:21 +00001287 xmlBufferWriteCHAR(buf, elem->name);
1288 xmlBufferWriteChar(buf, " EMPTY>\n");
1289 break;
1290 case XML_ELEMENT_TYPE_ANY:
1291 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001292 if (elem->prefix != NULL) {
1293 xmlBufferWriteCHAR(buf, elem->prefix);
1294 xmlBufferWriteChar(buf, ":");
1295 }
Owen Taylor3473f882001-02-23 17:55:21 +00001296 xmlBufferWriteCHAR(buf, elem->name);
1297 xmlBufferWriteChar(buf, " ANY>\n");
1298 break;
1299 case XML_ELEMENT_TYPE_MIXED:
1300 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001301 if (elem->prefix != NULL) {
1302 xmlBufferWriteCHAR(buf, elem->prefix);
1303 xmlBufferWriteChar(buf, ":");
1304 }
Owen Taylor3473f882001-02-23 17:55:21 +00001305 xmlBufferWriteCHAR(buf, elem->name);
1306 xmlBufferWriteChar(buf, " ");
1307 xmlDumpElementContent(buf, elem->content, 1);
1308 xmlBufferWriteChar(buf, ">\n");
1309 break;
1310 case XML_ELEMENT_TYPE_ELEMENT:
1311 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001312 if (elem->prefix != NULL) {
1313 xmlBufferWriteCHAR(buf, elem->prefix);
1314 xmlBufferWriteChar(buf, ":");
1315 }
Owen Taylor3473f882001-02-23 17:55:21 +00001316 xmlBufferWriteCHAR(buf, elem->name);
1317 xmlBufferWriteChar(buf, " ");
1318 xmlDumpElementContent(buf, elem->content, 1);
1319 xmlBufferWriteChar(buf, ">\n");
1320 break;
1321 default:
1322 xmlGenericError(xmlGenericErrorContext,
1323 "xmlDumpElementDecl: internal: unknown type %d\n",
1324 elem->etype);
1325 }
1326}
1327
1328/**
1329 * xmlDumpElementTable:
1330 * @buf: the XML buffer output
1331 * @table: An element table
1332 *
1333 * This will dump the content of the element table as an XML DTD definition
1334 */
1335void
1336xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1337 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1338}
1339
1340/**
1341 * xmlCreateEnumeration:
1342 * @name: the enumeration name or NULL
1343 *
1344 * create and initialize an enumeration attribute node.
1345 *
1346 * Returns the xmlEnumerationPtr just created or NULL in case
1347 * of error.
1348 */
1349xmlEnumerationPtr
1350xmlCreateEnumeration(xmlChar *name) {
1351 xmlEnumerationPtr ret;
1352
1353 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1354 if (ret == NULL) {
1355 xmlGenericError(xmlGenericErrorContext,
1356 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1357 (long)sizeof(xmlEnumeration));
1358 return(NULL);
1359 }
1360 memset(ret, 0, sizeof(xmlEnumeration));
1361
1362 if (name != NULL)
1363 ret->name = xmlStrdup(name);
1364 return(ret);
1365}
1366
1367/**
1368 * xmlFreeEnumeration:
1369 * @cur: the tree to free.
1370 *
1371 * free an enumeration attribute node (recursive).
1372 */
1373void
1374xmlFreeEnumeration(xmlEnumerationPtr cur) {
1375 if (cur == NULL) return;
1376
1377 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1378
1379 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001380 xmlFree(cur);
1381}
1382
1383/**
1384 * xmlCopyEnumeration:
1385 * @cur: the tree to copy.
1386 *
1387 * Copy an enumeration attribute node (recursive).
1388 *
1389 * Returns the xmlEnumerationPtr just created or NULL in case
1390 * of error.
1391 */
1392xmlEnumerationPtr
1393xmlCopyEnumeration(xmlEnumerationPtr cur) {
1394 xmlEnumerationPtr ret;
1395
1396 if (cur == NULL) return(NULL);
1397 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1398
1399 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1400 else ret->next = NULL;
1401
1402 return(ret);
1403}
1404
1405/**
1406 * xmlDumpEnumeration:
1407 * @buf: the XML buffer output
1408 * @enum: An enumeration
1409 *
1410 * This will dump the content of the enumeration
1411 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001412static void
Owen Taylor3473f882001-02-23 17:55:21 +00001413xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1414 if (cur == NULL) return;
1415
1416 xmlBufferWriteCHAR(buf, cur->name);
1417 if (cur->next == NULL)
1418 xmlBufferWriteChar(buf, ")");
1419 else {
1420 xmlBufferWriteChar(buf, " | ");
1421 xmlDumpEnumeration(buf, cur->next);
1422 }
1423}
1424
1425/**
1426 * xmlCreateAttributeTable:
1427 *
1428 * create and initialize an empty attribute hash table.
1429 *
1430 * Returns the xmlAttributeTablePtr just created or NULL in case
1431 * of error.
1432 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001433static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001434xmlCreateAttributeTable(void) {
1435 return(xmlHashCreate(0));
1436}
1437
1438/**
1439 * xmlScanAttributeDeclCallback:
1440 * @attr: the attribute decl
1441 * @list: the list to update
1442 *
1443 * Callback called by xmlScanAttributeDecl when a new attribute
1444 * has to be entered in the list.
1445 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001446static void
Owen Taylor3473f882001-02-23 17:55:21 +00001447xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001448 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001449 attr->nexth = *list;
1450 *list = attr;
1451}
1452
1453/**
1454 * xmlScanAttributeDecl:
1455 * @dtd: pointer to the DTD
1456 * @elem: the element name
1457 *
1458 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001459 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001460 *
1461 * Returns the pointer to the first attribute decl in the chain,
1462 * possibly NULL.
1463 */
1464xmlAttributePtr
1465xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1466 xmlAttributePtr ret = NULL;
1467 xmlAttributeTablePtr table;
1468
1469 if (dtd == NULL) {
1470 xmlGenericError(xmlGenericErrorContext,
1471 "xmlScanAttributeDecl: dtd == NULL\n");
1472 return(NULL);
1473 }
1474 if (elem == NULL) {
1475 xmlGenericError(xmlGenericErrorContext,
1476 "xmlScanAttributeDecl: elem == NULL\n");
1477 return(NULL);
1478 }
1479 table = (xmlAttributeTablePtr) dtd->attributes;
1480 if (table == NULL)
1481 return(NULL);
1482
1483 /* WRONG !!! */
1484 xmlHashScan3(table, NULL, NULL, elem,
1485 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1486 return(ret);
1487}
1488
1489/**
1490 * xmlScanIDAttributeDecl:
1491 * @ctxt: the validation context
1492 * @elem: the element name
1493 *
1494 * Verify that the element don't have too many ID attributes
1495 * declared.
1496 *
1497 * Returns the number of ID attributes found.
1498 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001499static int
Owen Taylor3473f882001-02-23 17:55:21 +00001500xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1501 xmlAttributePtr cur;
1502 int ret = 0;
1503
1504 if (elem == NULL) return(0);
1505 cur = elem->attributes;
1506 while (cur != NULL) {
1507 if (cur->atype == XML_ATTRIBUTE_ID) {
1508 ret ++;
1509 if (ret > 1)
1510 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001511 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001512 elem->name, cur->name);
1513 }
1514 cur = cur->nexth;
1515 }
1516 return(ret);
1517}
1518
1519/**
1520 * xmlFreeAttribute:
1521 * @elem: An attribute
1522 *
1523 * Deallocate the memory used by an attribute definition
1524 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001525static void
Owen Taylor3473f882001-02-23 17:55:21 +00001526xmlFreeAttribute(xmlAttributePtr attr) {
1527 if (attr == NULL) return;
1528 xmlUnlinkNode((xmlNodePtr) attr);
1529 if (attr->tree != NULL)
1530 xmlFreeEnumeration(attr->tree);
1531 if (attr->elem != NULL)
1532 xmlFree((xmlChar *) attr->elem);
1533 if (attr->name != NULL)
1534 xmlFree((xmlChar *) attr->name);
1535 if (attr->defaultValue != NULL)
1536 xmlFree((xmlChar *) attr->defaultValue);
1537 if (attr->prefix != NULL)
1538 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001539 xmlFree(attr);
1540}
1541
1542
1543/**
1544 * xmlAddAttributeDecl:
1545 * @ctxt: the validation context
1546 * @dtd: pointer to the DTD
1547 * @elem: the element name
1548 * @name: the attribute name
1549 * @ns: the attribute namespace prefix
1550 * @type: the attribute type
1551 * @def: the attribute default type
1552 * @defaultValue: the attribute default value
1553 * @tree: if it's an enumeration, the associated list
1554 *
1555 * Register a new attribute declaration
1556 * Note that @tree becomes the ownership of the DTD
1557 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001558 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001559 */
1560xmlAttributePtr
1561xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1562 const xmlChar *name, const xmlChar *ns,
1563 xmlAttributeType type, xmlAttributeDefault def,
1564 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1565 xmlAttributePtr ret;
1566 xmlAttributeTablePtr table;
1567 xmlElementPtr elemDef;
1568
1569 if (dtd == NULL) {
1570 xmlGenericError(xmlGenericErrorContext,
1571 "xmlAddAttributeDecl: dtd == NULL\n");
1572 xmlFreeEnumeration(tree);
1573 return(NULL);
1574 }
1575 if (name == NULL) {
1576 xmlGenericError(xmlGenericErrorContext,
1577 "xmlAddAttributeDecl: name == NULL\n");
1578 xmlFreeEnumeration(tree);
1579 return(NULL);
1580 }
1581 if (elem == NULL) {
1582 xmlGenericError(xmlGenericErrorContext,
1583 "xmlAddAttributeDecl: elem == NULL\n");
1584 xmlFreeEnumeration(tree);
1585 return(NULL);
1586 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001587
Owen Taylor3473f882001-02-23 17:55:21 +00001588 /*
1589 * Check the type and possibly the default value.
1590 */
1591 switch (type) {
1592 case XML_ATTRIBUTE_CDATA:
1593 break;
1594 case XML_ATTRIBUTE_ID:
1595 break;
1596 case XML_ATTRIBUTE_IDREF:
1597 break;
1598 case XML_ATTRIBUTE_IDREFS:
1599 break;
1600 case XML_ATTRIBUTE_ENTITY:
1601 break;
1602 case XML_ATTRIBUTE_ENTITIES:
1603 break;
1604 case XML_ATTRIBUTE_NMTOKEN:
1605 break;
1606 case XML_ATTRIBUTE_NMTOKENS:
1607 break;
1608 case XML_ATTRIBUTE_ENUMERATION:
1609 break;
1610 case XML_ATTRIBUTE_NOTATION:
1611 break;
1612 default:
1613 xmlGenericError(xmlGenericErrorContext,
1614 "xmlAddAttributeDecl: unknown type %d\n", type);
1615 xmlFreeEnumeration(tree);
1616 return(NULL);
1617 }
1618 if ((defaultValue != NULL) &&
1619 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001620 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001621 elem, name, defaultValue);
1622 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001623 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001624 }
1625
1626 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001627 * Check first that an attribute defined in the external subset wasn't
1628 * already defined in the internal subset
1629 */
1630 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1631 (dtd->doc->intSubset != NULL) &&
1632 (dtd->doc->intSubset->attributes != NULL)) {
1633 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1634 if (ret != NULL)
1635 return(NULL);
1636 }
1637
1638 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001639 * Create the Attribute table if needed.
1640 */
1641 table = (xmlAttributeTablePtr) dtd->attributes;
1642 if (table == NULL) {
1643 table = xmlCreateAttributeTable();
1644 dtd->attributes = (void *) table;
1645 }
1646 if (table == NULL) {
1647 xmlGenericError(xmlGenericErrorContext,
1648 "xmlAddAttributeDecl: Table creation failed!\n");
1649 return(NULL);
1650 }
1651
1652
1653 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1654 if (ret == NULL) {
1655 xmlGenericError(xmlGenericErrorContext,
1656 "xmlAddAttributeDecl: out of memory\n");
1657 return(NULL);
1658 }
1659 memset(ret, 0, sizeof(xmlAttribute));
1660 ret->type = XML_ATTRIBUTE_DECL;
1661
1662 /*
1663 * fill the structure.
1664 */
1665 ret->atype = type;
1666 ret->name = xmlStrdup(name);
1667 ret->prefix = xmlStrdup(ns);
1668 ret->elem = xmlStrdup(elem);
1669 ret->def = def;
1670 ret->tree = tree;
1671 if (defaultValue != NULL)
1672 ret->defaultValue = xmlStrdup(defaultValue);
1673
1674 /*
1675 * Validity Check:
1676 * Search the DTD for previous declarations of the ATTLIST
1677 */
1678 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1679 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001680 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001681 */
1682 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001683 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001684 name, elem);
1685 xmlFreeAttribute(ret);
1686 return(NULL);
1687 }
1688
1689 /*
1690 * Validity Check:
1691 * Multiple ID per element
1692 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001693 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001694 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001695
Owen Taylor3473f882001-02-23 17:55:21 +00001696 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001697 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001698 VERROR(ctxt->userData,
1699 "Element %s has too may ID attributes defined : %s\n",
1700 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001701 ctxt->valid = 0;
1702 }
1703
Daniel Veillard48da9102001-08-07 01:10:10 +00001704 /*
1705 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001706 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001707 */
1708 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1709 ((ret->prefix != NULL &&
1710 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1711 ret->nexth = elemDef->attributes;
1712 elemDef->attributes = ret;
1713 } else {
1714 xmlAttributePtr tmp = elemDef->attributes;
1715
1716 while ((tmp != NULL) &&
1717 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1718 ((ret->prefix != NULL &&
1719 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1720 if (tmp->nexth == NULL)
1721 break;
1722 tmp = tmp->nexth;
1723 }
1724 if (tmp != NULL) {
1725 ret->nexth = tmp->nexth;
1726 tmp->nexth = ret;
1727 } else {
1728 ret->nexth = elemDef->attributes;
1729 elemDef->attributes = ret;
1730 }
1731 }
Owen Taylor3473f882001-02-23 17:55:21 +00001732 }
1733
1734 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001735 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001736 */
1737 ret->parent = dtd;
1738 ret->doc = dtd->doc;
1739 if (dtd->last == NULL) {
1740 dtd->children = dtd->last = (xmlNodePtr) ret;
1741 } else {
1742 dtd->last->next = (xmlNodePtr) ret;
1743 ret->prev = dtd->last;
1744 dtd->last = (xmlNodePtr) ret;
1745 }
1746 return(ret);
1747}
1748
1749/**
1750 * xmlFreeAttributeTable:
1751 * @table: An attribute table
1752 *
1753 * Deallocate the memory used by an entities hash table.
1754 */
1755void
1756xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1757 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1758}
1759
1760/**
1761 * xmlCopyAttribute:
1762 * @attr: An attribute
1763 *
1764 * Build a copy of an attribute.
1765 *
1766 * Returns the new xmlAttributePtr or NULL in case of error.
1767 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001768static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001769xmlCopyAttribute(xmlAttributePtr attr) {
1770 xmlAttributePtr cur;
1771
1772 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1773 if (cur == NULL) {
1774 xmlGenericError(xmlGenericErrorContext,
1775 "xmlCopyAttribute: out of memory !\n");
1776 return(NULL);
1777 }
1778 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001779 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001780 cur->atype = attr->atype;
1781 cur->def = attr->def;
1782 cur->tree = xmlCopyEnumeration(attr->tree);
1783 if (attr->elem != NULL)
1784 cur->elem = xmlStrdup(attr->elem);
1785 if (attr->name != NULL)
1786 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001787 if (attr->prefix != NULL)
1788 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001789 if (attr->defaultValue != NULL)
1790 cur->defaultValue = xmlStrdup(attr->defaultValue);
1791 return(cur);
1792}
1793
1794/**
1795 * xmlCopyAttributeTable:
1796 * @table: An attribute table
1797 *
1798 * Build a copy of an attribute table.
1799 *
1800 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1801 */
1802xmlAttributeTablePtr
1803xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1804 return((xmlAttributeTablePtr) xmlHashCopy(table,
1805 (xmlHashCopier) xmlCopyAttribute));
1806}
1807
1808/**
1809 * xmlDumpAttributeDecl:
1810 * @buf: the XML buffer output
1811 * @attr: An attribute declaration
1812 *
1813 * This will dump the content of the attribute declaration as an XML
1814 * DTD definition
1815 */
1816void
1817xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1818 xmlBufferWriteChar(buf, "<!ATTLIST ");
1819 xmlBufferWriteCHAR(buf, attr->elem);
1820 xmlBufferWriteChar(buf, " ");
1821 if (attr->prefix != NULL) {
1822 xmlBufferWriteCHAR(buf, attr->prefix);
1823 xmlBufferWriteChar(buf, ":");
1824 }
1825 xmlBufferWriteCHAR(buf, attr->name);
1826 switch (attr->atype) {
1827 case XML_ATTRIBUTE_CDATA:
1828 xmlBufferWriteChar(buf, " CDATA");
1829 break;
1830 case XML_ATTRIBUTE_ID:
1831 xmlBufferWriteChar(buf, " ID");
1832 break;
1833 case XML_ATTRIBUTE_IDREF:
1834 xmlBufferWriteChar(buf, " IDREF");
1835 break;
1836 case XML_ATTRIBUTE_IDREFS:
1837 xmlBufferWriteChar(buf, " IDREFS");
1838 break;
1839 case XML_ATTRIBUTE_ENTITY:
1840 xmlBufferWriteChar(buf, " ENTITY");
1841 break;
1842 case XML_ATTRIBUTE_ENTITIES:
1843 xmlBufferWriteChar(buf, " ENTITIES");
1844 break;
1845 case XML_ATTRIBUTE_NMTOKEN:
1846 xmlBufferWriteChar(buf, " NMTOKEN");
1847 break;
1848 case XML_ATTRIBUTE_NMTOKENS:
1849 xmlBufferWriteChar(buf, " NMTOKENS");
1850 break;
1851 case XML_ATTRIBUTE_ENUMERATION:
1852 xmlBufferWriteChar(buf, " (");
1853 xmlDumpEnumeration(buf, attr->tree);
1854 break;
1855 case XML_ATTRIBUTE_NOTATION:
1856 xmlBufferWriteChar(buf, " NOTATION (");
1857 xmlDumpEnumeration(buf, attr->tree);
1858 break;
1859 default:
1860 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001861 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001862 attr->atype);
1863 }
1864 switch (attr->def) {
1865 case XML_ATTRIBUTE_NONE:
1866 break;
1867 case XML_ATTRIBUTE_REQUIRED:
1868 xmlBufferWriteChar(buf, " #REQUIRED");
1869 break;
1870 case XML_ATTRIBUTE_IMPLIED:
1871 xmlBufferWriteChar(buf, " #IMPLIED");
1872 break;
1873 case XML_ATTRIBUTE_FIXED:
1874 xmlBufferWriteChar(buf, " #FIXED");
1875 break;
1876 default:
1877 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001878 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001879 attr->def);
1880 }
1881 if (attr->defaultValue != NULL) {
1882 xmlBufferWriteChar(buf, " ");
1883 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1884 }
1885 xmlBufferWriteChar(buf, ">\n");
1886}
1887
1888/**
1889 * xmlDumpAttributeTable:
1890 * @buf: the XML buffer output
1891 * @table: An attribute table
1892 *
1893 * This will dump the content of the attribute table as an XML DTD definition
1894 */
1895void
1896xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1897 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1898}
1899
1900/************************************************************************
1901 * *
1902 * NOTATIONs *
1903 * *
1904 ************************************************************************/
1905/**
1906 * xmlCreateNotationTable:
1907 *
1908 * create and initialize an empty notation hash table.
1909 *
1910 * Returns the xmlNotationTablePtr just created or NULL in case
1911 * of error.
1912 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001913static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001914xmlCreateNotationTable(void) {
1915 return(xmlHashCreate(0));
1916}
1917
1918/**
1919 * xmlFreeNotation:
1920 * @not: A notation
1921 *
1922 * Deallocate the memory used by an notation definition
1923 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001924static void
Owen Taylor3473f882001-02-23 17:55:21 +00001925xmlFreeNotation(xmlNotationPtr nota) {
1926 if (nota == NULL) return;
1927 if (nota->name != NULL)
1928 xmlFree((xmlChar *) nota->name);
1929 if (nota->PublicID != NULL)
1930 xmlFree((xmlChar *) nota->PublicID);
1931 if (nota->SystemID != NULL)
1932 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001933 xmlFree(nota);
1934}
1935
1936
1937/**
1938 * xmlAddNotationDecl:
1939 * @dtd: pointer to the DTD
1940 * @ctxt: the validation context
1941 * @name: the entity name
1942 * @PublicID: the public identifier or NULL
1943 * @SystemID: the system identifier or NULL
1944 *
1945 * Register a new notation declaration
1946 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001947 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001948 */
1949xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001950xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001951 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001952 const xmlChar *PublicID, const xmlChar *SystemID) {
1953 xmlNotationPtr ret;
1954 xmlNotationTablePtr table;
1955
1956 if (dtd == NULL) {
1957 xmlGenericError(xmlGenericErrorContext,
1958 "xmlAddNotationDecl: dtd == NULL\n");
1959 return(NULL);
1960 }
1961 if (name == NULL) {
1962 xmlGenericError(xmlGenericErrorContext,
1963 "xmlAddNotationDecl: name == NULL\n");
1964 return(NULL);
1965 }
1966 if ((PublicID == NULL) && (SystemID == NULL)) {
1967 xmlGenericError(xmlGenericErrorContext,
1968 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001969 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001970 }
1971
1972 /*
1973 * Create the Notation table if needed.
1974 */
1975 table = (xmlNotationTablePtr) dtd->notations;
1976 if (table == NULL)
1977 dtd->notations = table = xmlCreateNotationTable();
1978 if (table == NULL) {
1979 xmlGenericError(xmlGenericErrorContext,
1980 "xmlAddNotationDecl: Table creation failed!\n");
1981 return(NULL);
1982 }
1983
1984 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1985 if (ret == NULL) {
1986 xmlGenericError(xmlGenericErrorContext,
1987 "xmlAddNotationDecl: out of memory\n");
1988 return(NULL);
1989 }
1990 memset(ret, 0, sizeof(xmlNotation));
1991
1992 /*
1993 * fill the structure.
1994 */
1995 ret->name = xmlStrdup(name);
1996 if (SystemID != NULL)
1997 ret->SystemID = xmlStrdup(SystemID);
1998 if (PublicID != NULL)
1999 ret->PublicID = xmlStrdup(PublicID);
2000
2001 /*
2002 * Validity Check:
2003 * Check the DTD for previous declarations of the ATTLIST
2004 */
2005 if (xmlHashAddEntry(table, name, ret)) {
2006 xmlGenericError(xmlGenericErrorContext,
2007 "xmlAddNotationDecl: %s already defined\n", name);
2008 xmlFreeNotation(ret);
2009 return(NULL);
2010 }
2011 return(ret);
2012}
2013
2014/**
2015 * xmlFreeNotationTable:
2016 * @table: An notation table
2017 *
2018 * Deallocate the memory used by an entities hash table.
2019 */
2020void
2021xmlFreeNotationTable(xmlNotationTablePtr table) {
2022 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
2023}
2024
2025/**
2026 * xmlCopyNotation:
2027 * @nota: A notation
2028 *
2029 * Build a copy of a notation.
2030 *
2031 * Returns the new xmlNotationPtr or NULL in case of error.
2032 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002033static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002034xmlCopyNotation(xmlNotationPtr nota) {
2035 xmlNotationPtr cur;
2036
2037 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2038 if (cur == NULL) {
2039 xmlGenericError(xmlGenericErrorContext,
2040 "xmlCopyNotation: out of memory !\n");
2041 return(NULL);
2042 }
2043 if (nota->name != NULL)
2044 cur->name = xmlStrdup(nota->name);
2045 else
2046 cur->name = NULL;
2047 if (nota->PublicID != NULL)
2048 cur->PublicID = xmlStrdup(nota->PublicID);
2049 else
2050 cur->PublicID = NULL;
2051 if (nota->SystemID != NULL)
2052 cur->SystemID = xmlStrdup(nota->SystemID);
2053 else
2054 cur->SystemID = NULL;
2055 return(cur);
2056}
2057
2058/**
2059 * xmlCopyNotationTable:
2060 * @table: A notation table
2061 *
2062 * Build a copy of a notation table.
2063 *
2064 * Returns the new xmlNotationTablePtr or NULL in case of error.
2065 */
2066xmlNotationTablePtr
2067xmlCopyNotationTable(xmlNotationTablePtr table) {
2068 return((xmlNotationTablePtr) xmlHashCopy(table,
2069 (xmlHashCopier) xmlCopyNotation));
2070}
2071
2072/**
2073 * xmlDumpNotationDecl:
2074 * @buf: the XML buffer output
2075 * @nota: A notation declaration
2076 *
2077 * This will dump the content the notation declaration as an XML DTD definition
2078 */
2079void
2080xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2081 xmlBufferWriteChar(buf, "<!NOTATION ");
2082 xmlBufferWriteCHAR(buf, nota->name);
2083 if (nota->PublicID != NULL) {
2084 xmlBufferWriteChar(buf, " PUBLIC ");
2085 xmlBufferWriteQuotedString(buf, nota->PublicID);
2086 if (nota->SystemID != NULL) {
2087 xmlBufferWriteChar(buf, " ");
2088 xmlBufferWriteCHAR(buf, nota->SystemID);
2089 }
2090 } else {
2091 xmlBufferWriteChar(buf, " SYSTEM ");
2092 xmlBufferWriteCHAR(buf, nota->SystemID);
2093 }
2094 xmlBufferWriteChar(buf, " >\n");
2095}
2096
2097/**
2098 * xmlDumpNotationTable:
2099 * @buf: the XML buffer output
2100 * @table: A notation table
2101 *
2102 * This will dump the content of the notation table as an XML DTD definition
2103 */
2104void
2105xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2106 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2107}
2108
2109/************************************************************************
2110 * *
2111 * IDs *
2112 * *
2113 ************************************************************************/
2114/**
2115 * xmlCreateIDTable:
2116 *
2117 * create and initialize an empty id hash table.
2118 *
2119 * Returns the xmlIDTablePtr just created or NULL in case
2120 * of error.
2121 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002122static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002123xmlCreateIDTable(void) {
2124 return(xmlHashCreate(0));
2125}
2126
2127/**
2128 * xmlFreeID:
2129 * @not: A id
2130 *
2131 * Deallocate the memory used by an id definition
2132 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002133static void
Owen Taylor3473f882001-02-23 17:55:21 +00002134xmlFreeID(xmlIDPtr id) {
2135 if (id == NULL) return;
2136 if (id->value != NULL)
2137 xmlFree((xmlChar *) id->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002138 if (id->name != NULL)
2139 xmlFree((xmlChar *) id->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002140 xmlFree(id);
2141}
2142
2143/**
2144 * xmlAddID:
2145 * @ctxt: the validation context
2146 * @doc: pointer to the document
2147 * @value: the value name
2148 * @attr: the attribute holding the ID
2149 *
2150 * Register a new id declaration
2151 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002152 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002153 */
2154xmlIDPtr
2155xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2156 xmlAttrPtr attr) {
2157 xmlIDPtr ret;
2158 xmlIDTablePtr table;
2159
2160 if (doc == NULL) {
2161 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002162 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002163 return(NULL);
2164 }
2165 if (value == NULL) {
2166 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002167 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002168 return(NULL);
2169 }
2170 if (attr == NULL) {
2171 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002172 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002173 return(NULL);
2174 }
2175
2176 /*
2177 * Create the ID table if needed.
2178 */
2179 table = (xmlIDTablePtr) doc->ids;
2180 if (table == NULL)
2181 doc->ids = table = xmlCreateIDTable();
2182 if (table == NULL) {
2183 xmlGenericError(xmlGenericErrorContext,
2184 "xmlAddID: Table creation failed!\n");
2185 return(NULL);
2186 }
2187
2188 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2189 if (ret == NULL) {
2190 xmlGenericError(xmlGenericErrorContext,
2191 "xmlAddID: out of memory\n");
2192 return(NULL);
2193 }
2194
2195 /*
2196 * fill the structure.
2197 */
2198 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002199 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2200 /*
2201 * Operating in streaming mode, attr is gonna disapear
2202 */
2203 ret->name = xmlStrdup(attr->name);
2204 ret->attr = NULL;
2205 } else {
2206 ret->attr = attr;
2207 ret->name = NULL;
2208 }
2209 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002210
2211 if (xmlHashAddEntry(table, value, ret) < 0) {
2212 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002213 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002214 */
Daniel Veillard76575762002-09-05 14:21:15 +00002215 if (ctxt != NULL) {
2216 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002217 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002218 }
Owen Taylor3473f882001-02-23 17:55:21 +00002219 xmlFreeID(ret);
2220 return(NULL);
2221 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002222 if (attr != NULL)
2223 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002224 return(ret);
2225}
2226
2227/**
2228 * xmlFreeIDTable:
2229 * @table: An id table
2230 *
2231 * Deallocate the memory used by an ID hash table.
2232 */
2233void
2234xmlFreeIDTable(xmlIDTablePtr table) {
2235 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2236}
2237
2238/**
2239 * xmlIsID:
2240 * @doc: the document
2241 * @elem: the element carrying the attribute
2242 * @attr: the attribute
2243 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002244 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002245 * then this is done if DTD loading has been requested. In the case
2246 * of HTML documents parsed with the HTML parser, then ID detection is
2247 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002248 *
2249 * Returns 0 or 1 depending on the lookup result
2250 */
2251int
2252xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2253 if (doc == NULL) return(0);
2254 if (attr == NULL) return(0);
2255 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2256 return(0);
2257 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2258 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2259 (xmlStrEqual(BAD_CAST "name", attr->name)))
2260 return(1);
2261 return(0);
2262 } else {
2263 xmlAttributePtr attrDecl;
2264
2265 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002266 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00002267 xmlChar fn[50];
Daniel Veillard37f961d2002-07-06 17:53:56 +00002268 xmlChar *fullname;
Daniel Veillardc00cda82003-04-07 10:22:39 +00002269
2270 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002271 if (fullname == NULL)
2272 return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002273 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2274 attr->name);
2275 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2276 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2277 attr->name);
Daniel Veillardc00cda82003-04-07 10:22:39 +00002278 if ((fullname != fn) && (fullname != elem->name))
2279 xmlFree(fullname);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002280 } else {
2281 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2282 attr->name);
2283 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2284 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2285 attr->name);
2286 }
Owen Taylor3473f882001-02-23 17:55:21 +00002287
2288 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2289 return(1);
2290 }
2291 return(0);
2292}
2293
2294/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002295 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002296 * @doc: the document
2297 * @attr: the attribute
2298 *
2299 * Remove the given attribute from the ID table maintained internally.
2300 *
2301 * Returns -1 if the lookup failed and 0 otherwise
2302 */
2303int
2304xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2305 xmlAttrPtr cur;
2306 xmlIDTablePtr table;
2307 xmlChar *ID;
2308
2309 if (doc == NULL) return(-1);
2310 if (attr == NULL) return(-1);
2311 table = (xmlIDTablePtr) doc->ids;
2312 if (table == NULL)
2313 return(-1);
2314
2315 if (attr == NULL)
2316 return(-1);
2317 ID = xmlNodeListGetString(doc, attr->children, 1);
2318 if (ID == NULL)
2319 return(-1);
2320 cur = xmlHashLookup(table, ID);
2321 if (cur != attr) {
2322 xmlFree(ID);
2323 return(-1);
2324 }
2325 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2326 xmlFree(ID);
2327 return(0);
2328}
2329
2330/**
2331 * xmlGetID:
2332 * @doc: pointer to the document
2333 * @ID: the ID value
2334 *
2335 * Search the attribute declaring the given ID
2336 *
2337 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2338 */
2339xmlAttrPtr
2340xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2341 xmlIDTablePtr table;
2342 xmlIDPtr id;
2343
2344 if (doc == NULL) {
2345 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2346 return(NULL);
2347 }
2348
2349 if (ID == NULL) {
2350 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2351 return(NULL);
2352 }
2353
2354 table = (xmlIDTablePtr) doc->ids;
2355 if (table == NULL)
2356 return(NULL);
2357
2358 id = xmlHashLookup(table, ID);
2359 if (id == NULL)
2360 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002361 if (id->attr == NULL) {
2362 /*
2363 * We are operating on a stream, return a well known reference
2364 * since the attribute node doesn't exist anymore
2365 */
2366 return((xmlAttrPtr) doc);
2367 }
Owen Taylor3473f882001-02-23 17:55:21 +00002368 return(id->attr);
2369}
2370
2371/************************************************************************
2372 * *
2373 * Refs *
2374 * *
2375 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002376typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002377{
2378 xmlListPtr l;
2379 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002380} xmlRemoveMemo;
2381
2382typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2383
2384typedef struct xmlValidateMemo_t
2385{
2386 xmlValidCtxtPtr ctxt;
2387 const xmlChar *name;
2388} xmlValidateMemo;
2389
2390typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002391
2392/**
2393 * xmlCreateRefTable:
2394 *
2395 * create and initialize an empty ref hash table.
2396 *
2397 * Returns the xmlRefTablePtr just created or NULL in case
2398 * of error.
2399 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002400static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002401xmlCreateRefTable(void) {
2402 return(xmlHashCreate(0));
2403}
2404
2405/**
2406 * xmlFreeRef:
2407 * @lk: A list link
2408 *
2409 * Deallocate the memory used by a ref definition
2410 */
2411static void
2412xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002413 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2414 if (ref == NULL) return;
2415 if (ref->value != NULL)
2416 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002417 if (ref->name != NULL)
2418 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002419 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002420}
2421
2422/**
2423 * xmlFreeRefList:
2424 * @list_ref: A list of references.
2425 *
2426 * Deallocate the memory used by a list of references
2427 */
2428static void
2429xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002430 if (list_ref == NULL) return;
2431 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002432}
2433
2434/**
2435 * xmlWalkRemoveRef:
2436 * @data: Contents of current link
2437 * @user: Value supplied by the user
2438 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002439 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002440 */
2441static int
2442xmlWalkRemoveRef(const void *data, const void *user)
2443{
Daniel Veillard37721922001-05-04 15:21:12 +00002444 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2445 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2446 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002447
Daniel Veillard37721922001-05-04 15:21:12 +00002448 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2449 xmlListRemoveFirst(ref_list, (void *)data);
2450 return 0;
2451 }
2452 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002453}
2454
2455/**
2456 * xmlAddRef:
2457 * @ctxt: the validation context
2458 * @doc: pointer to the document
2459 * @value: the value name
2460 * @attr: the attribute holding the Ref
2461 *
2462 * Register a new ref declaration
2463 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002464 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002465 */
2466xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002467xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002468 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002469 xmlRefPtr ret;
2470 xmlRefTablePtr table;
2471 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002472
Daniel Veillard37721922001-05-04 15:21:12 +00002473 if (doc == NULL) {
2474 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002475 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002476 return(NULL);
2477 }
2478 if (value == NULL) {
2479 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002480 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002481 return(NULL);
2482 }
2483 if (attr == NULL) {
2484 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002485 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002486 return(NULL);
2487 }
Owen Taylor3473f882001-02-23 17:55:21 +00002488
Daniel Veillard37721922001-05-04 15:21:12 +00002489 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002490 * Create the Ref table if needed.
2491 */
Daniel Veillard37721922001-05-04 15:21:12 +00002492 table = (xmlRefTablePtr) doc->refs;
2493 if (table == NULL)
2494 doc->refs = table = xmlCreateRefTable();
2495 if (table == NULL) {
2496 xmlGenericError(xmlGenericErrorContext,
2497 "xmlAddRef: Table creation failed!\n");
2498 return(NULL);
2499 }
Owen Taylor3473f882001-02-23 17:55:21 +00002500
Daniel Veillard37721922001-05-04 15:21:12 +00002501 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2502 if (ret == NULL) {
2503 xmlGenericError(xmlGenericErrorContext,
2504 "xmlAddRef: out of memory\n");
2505 return(NULL);
2506 }
Owen Taylor3473f882001-02-23 17:55:21 +00002507
Daniel Veillard37721922001-05-04 15:21:12 +00002508 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002509 * fill the structure.
2510 */
Daniel Veillard37721922001-05-04 15:21:12 +00002511 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002512 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2513 /*
2514 * Operating in streaming mode, attr is gonna disapear
2515 */
2516 ret->name = xmlStrdup(attr->name);
2517 ret->attr = NULL;
2518 } else {
2519 ret->name = NULL;
2520 ret->attr = attr;
2521 }
2522 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002523
Daniel Veillard37721922001-05-04 15:21:12 +00002524 /* To add a reference :-
2525 * References are maintained as a list of references,
2526 * Lookup the entry, if no entry create new nodelist
2527 * Add the owning node to the NodeList
2528 * Return the ref
2529 */
Owen Taylor3473f882001-02-23 17:55:21 +00002530
Daniel Veillard37721922001-05-04 15:21:12 +00002531 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2532 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2533 xmlGenericError(xmlGenericErrorContext,
2534 "xmlAddRef: Reference list creation failed!\n");
2535 return(NULL);
2536 }
2537 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2538 xmlListDelete(ref_list);
2539 xmlGenericError(xmlGenericErrorContext,
2540 "xmlAddRef: Reference list insertion failed!\n");
2541 return(NULL);
2542 }
2543 }
2544 xmlListInsert(ref_list, ret);
2545 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002546}
2547
2548/**
2549 * xmlFreeRefTable:
2550 * @table: An ref table
2551 *
2552 * Deallocate the memory used by an Ref hash table.
2553 */
2554void
2555xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002556 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002557}
2558
2559/**
2560 * xmlIsRef:
2561 * @doc: the document
2562 * @elem: the element carrying the attribute
2563 * @attr: the attribute
2564 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002565 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002566 * then this is simple, otherwise we use an heuristic: name Ref (upper
2567 * or lowercase).
2568 *
2569 * Returns 0 or 1 depending on the lookup result
2570 */
2571int
2572xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002573 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2574 return(0);
2575 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2576 /* TODO @@@ */
2577 return(0);
2578 } else {
2579 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002580
Daniel Veillard37721922001-05-04 15:21:12 +00002581 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2582 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2583 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2584 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002585
Daniel Veillard37721922001-05-04 15:21:12 +00002586 if ((attrDecl != NULL) &&
2587 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2588 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2589 return(1);
2590 }
2591 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002592}
2593
2594/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002595 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002596 * @doc: the document
2597 * @attr: the attribute
2598 *
2599 * Remove the given attribute from the Ref table maintained internally.
2600 *
2601 * Returns -1 if the lookup failed and 0 otherwise
2602 */
2603int
2604xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002605 xmlListPtr ref_list;
2606 xmlRefTablePtr table;
2607 xmlChar *ID;
2608 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002609
Daniel Veillard37721922001-05-04 15:21:12 +00002610 if (doc == NULL) return(-1);
2611 if (attr == NULL) return(-1);
2612 table = (xmlRefTablePtr) doc->refs;
2613 if (table == NULL)
2614 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002615
Daniel Veillard37721922001-05-04 15:21:12 +00002616 if (attr == NULL)
2617 return(-1);
2618 ID = xmlNodeListGetString(doc, attr->children, 1);
2619 if (ID == NULL)
2620 return(-1);
2621 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002622
Daniel Veillard37721922001-05-04 15:21:12 +00002623 if(ref_list == NULL) {
2624 xmlFree(ID);
2625 return (-1);
2626 }
2627 /* At this point, ref_list refers to a list of references which
2628 * have the same key as the supplied attr. Our list of references
2629 * is ordered by reference address and we don't have that information
2630 * here to use when removing. We'll have to walk the list and
2631 * check for a matching attribute, when we find one stop the walk
2632 * and remove the entry.
2633 * The list is ordered by reference, so that means we don't have the
2634 * key. Passing the list and the reference to the walker means we
2635 * will have enough data to be able to remove the entry.
2636 */
2637 target.l = ref_list;
2638 target.ap = attr;
2639
2640 /* Remove the supplied attr from our list */
2641 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002642
Daniel Veillard37721922001-05-04 15:21:12 +00002643 /*If the list is empty then remove the list entry in the hash */
2644 if (xmlListEmpty(ref_list))
2645 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2646 xmlFreeRefList);
2647 xmlFree(ID);
2648 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002649}
2650
2651/**
2652 * xmlGetRefs:
2653 * @doc: pointer to the document
2654 * @ID: the ID value
2655 *
2656 * Find the set of references for the supplied ID.
2657 *
2658 * Returns NULL if not found, otherwise node set for the ID.
2659 */
2660xmlListPtr
2661xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002662 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002663
Daniel Veillard37721922001-05-04 15:21:12 +00002664 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002665 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002666 return(NULL);
2667 }
Owen Taylor3473f882001-02-23 17:55:21 +00002668
Daniel Veillard37721922001-05-04 15:21:12 +00002669 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002670 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002671 return(NULL);
2672 }
Owen Taylor3473f882001-02-23 17:55:21 +00002673
Daniel Veillard37721922001-05-04 15:21:12 +00002674 table = (xmlRefTablePtr) doc->refs;
2675 if (table == NULL)
2676 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002677
Daniel Veillard37721922001-05-04 15:21:12 +00002678 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002679}
2680
2681/************************************************************************
2682 * *
2683 * Routines for validity checking *
2684 * *
2685 ************************************************************************/
2686
2687/**
2688 * xmlGetDtdElementDesc:
2689 * @dtd: a pointer to the DtD to search
2690 * @name: the element name
2691 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002692 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002693 *
2694 * returns the xmlElementPtr if found or NULL
2695 */
2696
2697xmlElementPtr
2698xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2699 xmlElementTablePtr table;
2700 xmlElementPtr cur;
2701 xmlChar *uqname = NULL, *prefix = NULL;
2702
2703 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002704 if (dtd->elements == NULL)
2705 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002706 table = (xmlElementTablePtr) dtd->elements;
2707
2708 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002709 if (uqname != NULL)
2710 name = uqname;
2711 cur = xmlHashLookup2(table, name, prefix);
2712 if (prefix != NULL) xmlFree(prefix);
2713 if (uqname != NULL) xmlFree(uqname);
2714 return(cur);
2715}
2716/**
2717 * xmlGetDtdElementDesc2:
2718 * @dtd: a pointer to the DtD to search
2719 * @name: the element name
2720 * @create: create an empty description if not found
2721 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002722 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002723 *
2724 * returns the xmlElementPtr if found or NULL
2725 */
2726
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002727static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002728xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2729 xmlElementTablePtr table;
2730 xmlElementPtr cur;
2731 xmlChar *uqname = NULL, *prefix = NULL;
2732
2733 if (dtd == NULL) return(NULL);
2734 if (dtd->elements == NULL) {
2735 if (!create)
2736 return(NULL);
2737 /*
2738 * Create the Element table if needed.
2739 */
2740 table = (xmlElementTablePtr) dtd->elements;
2741 if (table == NULL) {
2742 table = xmlCreateElementTable();
2743 dtd->elements = (void *) table;
2744 }
2745 if (table == NULL) {
2746 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002747 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002748 return(NULL);
2749 }
2750 }
2751 table = (xmlElementTablePtr) dtd->elements;
2752
2753 uqname = xmlSplitQName2(name, &prefix);
2754 if (uqname != NULL)
2755 name = uqname;
2756 cur = xmlHashLookup2(table, name, prefix);
2757 if ((cur == NULL) && (create)) {
2758 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2759 if (cur == NULL) {
2760 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002761 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002762 return(NULL);
2763 }
2764 memset(cur, 0, sizeof(xmlElement));
2765 cur->type = XML_ELEMENT_DECL;
2766
2767 /*
2768 * fill the structure.
2769 */
2770 cur->name = xmlStrdup(name);
2771 cur->prefix = xmlStrdup(prefix);
2772 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2773
2774 xmlHashAddEntry2(table, name, prefix, cur);
2775 }
2776 if (prefix != NULL) xmlFree(prefix);
2777 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002778 return(cur);
2779}
2780
2781/**
2782 * xmlGetDtdQElementDesc:
2783 * @dtd: a pointer to the DtD to search
2784 * @name: the element name
2785 * @prefix: the element namespace prefix
2786 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002787 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002788 *
2789 * returns the xmlElementPtr if found or NULL
2790 */
2791
Daniel Veillard48da9102001-08-07 01:10:10 +00002792xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002793xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2794 const xmlChar *prefix) {
2795 xmlElementTablePtr table;
2796
2797 if (dtd == NULL) return(NULL);
2798 if (dtd->elements == NULL) return(NULL);
2799 table = (xmlElementTablePtr) dtd->elements;
2800
2801 return(xmlHashLookup2(table, name, prefix));
2802}
2803
2804/**
2805 * xmlGetDtdAttrDesc:
2806 * @dtd: a pointer to the DtD to search
2807 * @elem: the element name
2808 * @name: the attribute name
2809 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002810 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002811 * this element.
2812 *
2813 * returns the xmlAttributePtr if found or NULL
2814 */
2815
2816xmlAttributePtr
2817xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2818 xmlAttributeTablePtr table;
2819 xmlAttributePtr cur;
2820 xmlChar *uqname = NULL, *prefix = NULL;
2821
2822 if (dtd == NULL) return(NULL);
2823 if (dtd->attributes == NULL) return(NULL);
2824
2825 table = (xmlAttributeTablePtr) dtd->attributes;
2826 if (table == NULL)
2827 return(NULL);
2828
2829 uqname = xmlSplitQName2(name, &prefix);
2830
2831 if (uqname != NULL) {
2832 cur = xmlHashLookup3(table, uqname, prefix, elem);
2833 if (prefix != NULL) xmlFree(prefix);
2834 if (uqname != NULL) xmlFree(uqname);
2835 } else
2836 cur = xmlHashLookup3(table, name, NULL, elem);
2837 return(cur);
2838}
2839
2840/**
2841 * xmlGetDtdQAttrDesc:
2842 * @dtd: a pointer to the DtD to search
2843 * @elem: the element name
2844 * @name: the attribute name
2845 * @prefix: the attribute namespace prefix
2846 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002847 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002848 * this element.
2849 *
2850 * returns the xmlAttributePtr if found or NULL
2851 */
2852
Daniel Veillard48da9102001-08-07 01:10:10 +00002853xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002854xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2855 const xmlChar *prefix) {
2856 xmlAttributeTablePtr table;
2857
2858 if (dtd == NULL) return(NULL);
2859 if (dtd->attributes == NULL) return(NULL);
2860 table = (xmlAttributeTablePtr) dtd->attributes;
2861
2862 return(xmlHashLookup3(table, name, prefix, elem));
2863}
2864
2865/**
2866 * xmlGetDtdNotationDesc:
2867 * @dtd: a pointer to the DtD to search
2868 * @name: the notation name
2869 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002870 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002871 *
2872 * returns the xmlNotationPtr if found or NULL
2873 */
2874
2875xmlNotationPtr
2876xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2877 xmlNotationTablePtr table;
2878
2879 if (dtd == NULL) return(NULL);
2880 if (dtd->notations == NULL) return(NULL);
2881 table = (xmlNotationTablePtr) dtd->notations;
2882
2883 return(xmlHashLookup(table, name));
2884}
2885
2886/**
2887 * xmlValidateNotationUse:
2888 * @ctxt: the validation context
2889 * @doc: the document
2890 * @notationName: the notation name to check
2891 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002892 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002893 * - [ VC: Notation Declared ]
2894 *
2895 * returns 1 if valid or 0 otherwise
2896 */
2897
2898int
2899xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2900 const xmlChar *notationName) {
2901 xmlNotationPtr notaDecl;
2902 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2903
2904 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2905 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2906 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2907
Daniel Veillarde637c4a2003-03-30 21:10:09 +00002908 if ((notaDecl == NULL) && (ctxt != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00002909 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2910 notationName);
2911 return(0);
2912 }
2913 return(1);
2914}
2915
2916/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002917 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00002918 * @doc: the document
2919 * @name: the element name
2920 *
2921 * Search in the DtDs whether an element accept Mixed content (or ANY)
2922 * basically if it is supposed to accept text childs
2923 *
2924 * returns 0 if no, 1 if yes, and -1 if no element description is available
2925 */
2926
2927int
2928xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2929 xmlElementPtr elemDecl;
2930
2931 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2932
2933 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2934 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2935 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2936 if (elemDecl == NULL) return(-1);
2937 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002938 case XML_ELEMENT_TYPE_UNDEFINED:
2939 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002940 case XML_ELEMENT_TYPE_ELEMENT:
2941 return(0);
2942 case XML_ELEMENT_TYPE_EMPTY:
2943 /*
2944 * return 1 for EMPTY since we want VC error to pop up
2945 * on <empty> </empty> for example
2946 */
2947 case XML_ELEMENT_TYPE_ANY:
2948 case XML_ELEMENT_TYPE_MIXED:
2949 return(1);
2950 }
2951 return(1);
2952}
2953
2954/**
2955 * xmlValidateNameValue:
2956 * @value: an Name value
2957 *
2958 * Validate that the given value match Name production
2959 *
2960 * returns 1 if valid or 0 otherwise
2961 */
2962
Daniel Veillard9b731d72002-04-14 12:56:08 +00002963int
Owen Taylor3473f882001-02-23 17:55:21 +00002964xmlValidateNameValue(const xmlChar *value) {
2965 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002966 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002967
2968 if (value == NULL) return(0);
2969 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002970 val = xmlStringCurrentChar(NULL, cur, &len);
2971 cur += len;
2972 if (!IS_LETTER(val) && (val != '_') &&
2973 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002974 return(0);
2975 }
2976
Daniel Veillardd8224e02002-01-13 15:43:22 +00002977 val = xmlStringCurrentChar(NULL, cur, &len);
2978 cur += len;
2979 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2980 (val == '.') || (val == '-') ||
2981 (val == '_') || (val == ':') ||
2982 (IS_COMBINING(val)) ||
2983 (IS_EXTENDER(val))) {
2984 val = xmlStringCurrentChar(NULL, cur, &len);
2985 cur += len;
2986 }
Owen Taylor3473f882001-02-23 17:55:21 +00002987
Daniel Veillardd8224e02002-01-13 15:43:22 +00002988 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002989
2990 return(1);
2991}
2992
2993/**
2994 * xmlValidateNamesValue:
2995 * @value: an Names value
2996 *
2997 * Validate that the given value match Names production
2998 *
2999 * returns 1 if valid or 0 otherwise
3000 */
3001
Daniel Veillard9b731d72002-04-14 12:56:08 +00003002int
Owen Taylor3473f882001-02-23 17:55:21 +00003003xmlValidateNamesValue(const xmlChar *value) {
3004 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003005 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003006
3007 if (value == NULL) return(0);
3008 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003009 val = xmlStringCurrentChar(NULL, cur, &len);
3010 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003011
Daniel Veillardd8224e02002-01-13 15:43:22 +00003012 if (!IS_LETTER(val) && (val != '_') &&
3013 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00003014 return(0);
3015 }
3016
Daniel Veillardd8224e02002-01-13 15:43:22 +00003017 val = xmlStringCurrentChar(NULL, cur, &len);
3018 cur += len;
3019 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3020 (val == '.') || (val == '-') ||
3021 (val == '_') || (val == ':') ||
3022 (IS_COMBINING(val)) ||
3023 (IS_EXTENDER(val))) {
3024 val = xmlStringCurrentChar(NULL, cur, &len);
3025 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003026 }
3027
Daniel Veillardd8224e02002-01-13 15:43:22 +00003028 while (IS_BLANK(val)) {
3029 while (IS_BLANK(val)) {
3030 val = xmlStringCurrentChar(NULL, cur, &len);
3031 cur += len;
3032 }
3033
3034 if (!IS_LETTER(val) && (val != '_') &&
3035 (val != ':')) {
3036 return(0);
3037 }
3038 val = xmlStringCurrentChar(NULL, cur, &len);
3039 cur += len;
3040
3041 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3042 (val == '.') || (val == '-') ||
3043 (val == '_') || (val == ':') ||
3044 (IS_COMBINING(val)) ||
3045 (IS_EXTENDER(val))) {
3046 val = xmlStringCurrentChar(NULL, cur, &len);
3047 cur += len;
3048 }
3049 }
3050
3051 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003052
3053 return(1);
3054}
3055
3056/**
3057 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003058 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003059 *
3060 * Validate that the given value match Nmtoken production
3061 *
3062 * [ VC: Name Token ]
3063 *
3064 * returns 1 if valid or 0 otherwise
3065 */
3066
Daniel Veillard9b731d72002-04-14 12:56:08 +00003067int
Owen Taylor3473f882001-02-23 17:55:21 +00003068xmlValidateNmtokenValue(const xmlChar *value) {
3069 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003070 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003071
3072 if (value == NULL) return(0);
3073 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003074 val = xmlStringCurrentChar(NULL, cur, &len);
3075 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003076
Daniel Veillardd8224e02002-01-13 15:43:22 +00003077 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3078 (val != '.') && (val != '-') &&
3079 (val != '_') && (val != ':') &&
3080 (!IS_COMBINING(val)) &&
3081 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003082 return(0);
3083
Daniel Veillardd8224e02002-01-13 15:43:22 +00003084 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3085 (val == '.') || (val == '-') ||
3086 (val == '_') || (val == ':') ||
3087 (IS_COMBINING(val)) ||
3088 (IS_EXTENDER(val))) {
3089 val = xmlStringCurrentChar(NULL, cur, &len);
3090 cur += len;
3091 }
Owen Taylor3473f882001-02-23 17:55:21 +00003092
Daniel Veillardd8224e02002-01-13 15:43:22 +00003093 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003094
3095 return(1);
3096}
3097
3098/**
3099 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003100 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003101 *
3102 * Validate that the given value match Nmtokens production
3103 *
3104 * [ VC: Name Token ]
3105 *
3106 * returns 1 if valid or 0 otherwise
3107 */
3108
Daniel Veillard9b731d72002-04-14 12:56:08 +00003109int
Owen Taylor3473f882001-02-23 17:55:21 +00003110xmlValidateNmtokensValue(const xmlChar *value) {
3111 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003112 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003113
3114 if (value == NULL) return(0);
3115 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003116 val = xmlStringCurrentChar(NULL, cur, &len);
3117 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003118
Daniel Veillardd8224e02002-01-13 15:43:22 +00003119 while (IS_BLANK(val)) {
3120 val = xmlStringCurrentChar(NULL, cur, &len);
3121 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003122 }
3123
Daniel Veillardd8224e02002-01-13 15:43:22 +00003124 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3125 (val != '.') && (val != '-') &&
3126 (val != '_') && (val != ':') &&
3127 (!IS_COMBINING(val)) &&
3128 (!IS_EXTENDER(val)))
3129 return(0);
3130
3131 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3132 (val == '.') || (val == '-') ||
3133 (val == '_') || (val == ':') ||
3134 (IS_COMBINING(val)) ||
3135 (IS_EXTENDER(val))) {
3136 val = xmlStringCurrentChar(NULL, cur, &len);
3137 cur += len;
3138 }
3139
3140 while (IS_BLANK(val)) {
3141 while (IS_BLANK(val)) {
3142 val = xmlStringCurrentChar(NULL, cur, &len);
3143 cur += len;
3144 }
3145 if (val == 0) return(1);
3146
3147 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3148 (val != '.') && (val != '-') &&
3149 (val != '_') && (val != ':') &&
3150 (!IS_COMBINING(val)) &&
3151 (!IS_EXTENDER(val)))
3152 return(0);
3153
3154 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3155 (val == '.') || (val == '-') ||
3156 (val == '_') || (val == ':') ||
3157 (IS_COMBINING(val)) ||
3158 (IS_EXTENDER(val))) {
3159 val = xmlStringCurrentChar(NULL, cur, &len);
3160 cur += len;
3161 }
3162 }
3163
3164 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003165
3166 return(1);
3167}
3168
3169/**
3170 * xmlValidateNotationDecl:
3171 * @ctxt: the validation context
3172 * @doc: a document instance
3173 * @nota: a notation definition
3174 *
3175 * Try to validate a single notation definition
3176 * basically it does the following checks as described by the
3177 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003178 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003179 * But this function get called anyway ...
3180 *
3181 * returns 1 if valid or 0 otherwise
3182 */
3183
3184int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003185xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3186 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003187 int ret = 1;
3188
3189 return(ret);
3190}
3191
3192/**
3193 * xmlValidateAttributeValue:
3194 * @type: an attribute type
3195 * @value: an attribute value
3196 *
3197 * Validate that the given attribute value match the proper production
3198 *
3199 * [ VC: ID ]
3200 * Values of type ID must match the Name production....
3201 *
3202 * [ VC: IDREF ]
3203 * Values of type IDREF must match the Name production, and values
3204 * of type IDREFS must match Names ...
3205 *
3206 * [ VC: Entity Name ]
3207 * Values of type ENTITY must match the Name production, values
3208 * of type ENTITIES must match Names ...
3209 *
3210 * [ VC: Name Token ]
3211 * Values of type NMTOKEN must match the Nmtoken production; values
3212 * of type NMTOKENS must match Nmtokens.
3213 *
3214 * returns 1 if valid or 0 otherwise
3215 */
3216
3217int
3218xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3219 switch (type) {
3220 case XML_ATTRIBUTE_ENTITIES:
3221 case XML_ATTRIBUTE_IDREFS:
3222 return(xmlValidateNamesValue(value));
3223 case XML_ATTRIBUTE_ENTITY:
3224 case XML_ATTRIBUTE_IDREF:
3225 case XML_ATTRIBUTE_ID:
3226 case XML_ATTRIBUTE_NOTATION:
3227 return(xmlValidateNameValue(value));
3228 case XML_ATTRIBUTE_NMTOKENS:
3229 case XML_ATTRIBUTE_ENUMERATION:
3230 return(xmlValidateNmtokensValue(value));
3231 case XML_ATTRIBUTE_NMTOKEN:
3232 return(xmlValidateNmtokenValue(value));
3233 case XML_ATTRIBUTE_CDATA:
3234 break;
3235 }
3236 return(1);
3237}
3238
3239/**
3240 * xmlValidateAttributeValue2:
3241 * @ctxt: the validation context
3242 * @doc: the document
3243 * @name: the attribute name (used for error reporting only)
3244 * @type: the attribute type
3245 * @value: the attribute value
3246 *
3247 * Validate that the given attribute value match a given type.
3248 * This typically cannot be done before having finished parsing
3249 * the subsets.
3250 *
3251 * [ VC: IDREF ]
3252 * Values of type IDREF must match one of the declared IDs
3253 * Values of type IDREFS must match a sequence of the declared IDs
3254 * each Name must match the value of an ID attribute on some element
3255 * in the XML document; i.e. IDREF values must match the value of
3256 * some ID attribute
3257 *
3258 * [ VC: Entity Name ]
3259 * Values of type ENTITY must match one declared entity
3260 * Values of type ENTITIES must match a sequence of declared entities
3261 *
3262 * [ VC: Notation Attributes ]
3263 * all notation names in the declaration must be declared.
3264 *
3265 * returns 1 if valid or 0 otherwise
3266 */
3267
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003268static int
Owen Taylor3473f882001-02-23 17:55:21 +00003269xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3270 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3271 int ret = 1;
3272 switch (type) {
3273 case XML_ATTRIBUTE_IDREFS:
3274 case XML_ATTRIBUTE_IDREF:
3275 case XML_ATTRIBUTE_ID:
3276 case XML_ATTRIBUTE_NMTOKENS:
3277 case XML_ATTRIBUTE_ENUMERATION:
3278 case XML_ATTRIBUTE_NMTOKEN:
3279 case XML_ATTRIBUTE_CDATA:
3280 break;
3281 case XML_ATTRIBUTE_ENTITY: {
3282 xmlEntityPtr ent;
3283
3284 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003285 if ((ent == NULL) && (doc->standalone == 1)) {
3286 doc->standalone = 0;
3287 ent = xmlGetDocEntity(doc, value);
3288 if (ent != NULL) {
3289 VERROR(ctxt->userData,
3290"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3291 name, value);
3292 /* WAIT to get answer from the Core WG on this
3293 ret = 0;
3294 */
3295 }
3296 }
Owen Taylor3473f882001-02-23 17:55:21 +00003297 if (ent == NULL) {
3298 VERROR(ctxt->userData,
3299 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3300 name, value);
3301 ret = 0;
3302 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3303 VERROR(ctxt->userData,
3304 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3305 name, value);
3306 ret = 0;
3307 }
3308 break;
3309 }
3310 case XML_ATTRIBUTE_ENTITIES: {
3311 xmlChar *dup, *nam = NULL, *cur, save;
3312 xmlEntityPtr ent;
3313
3314 dup = xmlStrdup(value);
3315 if (dup == NULL)
3316 return(0);
3317 cur = dup;
3318 while (*cur != 0) {
3319 nam = cur;
3320 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3321 save = *cur;
3322 *cur = 0;
3323 ent = xmlGetDocEntity(doc, nam);
3324 if (ent == NULL) {
3325 VERROR(ctxt->userData,
3326 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3327 name, nam);
3328 ret = 0;
3329 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3330 VERROR(ctxt->userData,
3331 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3332 name, nam);
3333 ret = 0;
3334 }
3335 if (save == 0)
3336 break;
3337 *cur = save;
3338 while (IS_BLANK(*cur)) cur++;
3339 }
3340 xmlFree(dup);
3341 break;
3342 }
3343 case XML_ATTRIBUTE_NOTATION: {
3344 xmlNotationPtr nota;
3345
3346 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3347 if ((nota == NULL) && (doc->extSubset != NULL))
3348 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3349
3350 if (nota == NULL) {
3351 VERROR(ctxt->userData,
3352 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3353 name, value);
3354 ret = 0;
3355 }
3356 break;
3357 }
3358 }
3359 return(ret);
3360}
3361
3362/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003363 * xmlValidCtxtNormalizeAttributeValue:
3364 * @ctxt: the validation context
3365 * @doc: the document
3366 * @elem: the parent
3367 * @name: the attribute name
3368 * @value: the attribute value
3369 * @ctxt: the validation context or NULL
3370 *
3371 * Does the validation related extra step of the normalization of attribute
3372 * values:
3373 *
3374 * If the declared value is not CDATA, then the XML processor must further
3375 * process the normalized attribute value by discarding any leading and
3376 * trailing space (#x20) characters, and by replacing sequences of space
3377 * (#x20) characters by single space (#x20) character.
3378 *
3379 * Also check VC: Standalone Document Declaration in P32, and update
3380 * ctxt->valid accordingly
3381 *
3382 * returns a new normalized string if normalization is needed, NULL otherwise
3383 * the caller must free the returned value.
3384 */
3385
3386xmlChar *
3387xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3388 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3389 xmlChar *ret, *dst;
3390 const xmlChar *src;
3391 xmlAttributePtr attrDecl = NULL;
3392 int extsubset = 0;
3393
3394 if (doc == NULL) return(NULL);
3395 if (elem == NULL) return(NULL);
3396 if (name == NULL) return(NULL);
3397 if (value == NULL) return(NULL);
3398
3399 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003400 xmlChar fn[50];
3401 xmlChar *fullname;
3402
3403 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3404 if (fullname == NULL)
3405 return(0);
3406 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003407 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003408 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003409 if (attrDecl != NULL)
3410 extsubset = 1;
3411 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003412 if ((fullname != fn) && (fullname != elem->name))
3413 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003414 }
3415 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3416 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3417 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3418 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3419 if (attrDecl != NULL)
3420 extsubset = 1;
3421 }
3422
3423 if (attrDecl == NULL)
3424 return(NULL);
3425 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3426 return(NULL);
3427
3428 ret = xmlStrdup(value);
3429 if (ret == NULL)
3430 return(NULL);
3431 src = value;
3432 dst = ret;
3433 while (*src == 0x20) src++;
3434 while (*src != 0) {
3435 if (*src == 0x20) {
3436 while (*src == 0x20) src++;
3437 if (*src != 0)
3438 *dst++ = 0x20;
3439 } else {
3440 *dst++ = *src++;
3441 }
3442 }
3443 *dst = 0;
3444 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3445 VERROR(ctxt->userData,
3446"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3447 name, elem->name);
3448 ctxt->valid = 0;
3449 }
3450 return(ret);
3451}
3452
3453/**
Owen Taylor3473f882001-02-23 17:55:21 +00003454 * xmlValidNormalizeAttributeValue:
3455 * @doc: the document
3456 * @elem: the parent
3457 * @name: the attribute name
3458 * @value: the attribute value
3459 *
3460 * Does the validation related extra step of the normalization of attribute
3461 * values:
3462 *
3463 * If the declared value is not CDATA, then the XML processor must further
3464 * process the normalized attribute value by discarding any leading and
3465 * trailing space (#x20) characters, and by replacing sequences of space
3466 * (#x20) characters by single space (#x20) character.
3467 *
3468 * returns a new normalized string if normalization is needed, NULL otherwise
3469 * the caller must free the returned value.
3470 */
3471
3472xmlChar *
3473xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3474 const xmlChar *name, const xmlChar *value) {
3475 xmlChar *ret, *dst;
3476 const xmlChar *src;
3477 xmlAttributePtr attrDecl = NULL;
3478
3479 if (doc == NULL) return(NULL);
3480 if (elem == NULL) return(NULL);
3481 if (name == NULL) return(NULL);
3482 if (value == NULL) return(NULL);
3483
3484 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003485 xmlChar fn[50];
3486 xmlChar *fullname;
3487
3488 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3489 if (fullname == NULL)
3490 return(0);
3491 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003492 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003493 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3494 if ((fullname != fn) && (fullname != elem->name))
3495 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003496 }
3497 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3498 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3499 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3500
3501 if (attrDecl == NULL)
3502 return(NULL);
3503 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3504 return(NULL);
3505
3506 ret = xmlStrdup(value);
3507 if (ret == NULL)
3508 return(NULL);
3509 src = value;
3510 dst = ret;
3511 while (*src == 0x20) src++;
3512 while (*src != 0) {
3513 if (*src == 0x20) {
3514 while (*src == 0x20) src++;
3515 if (*src != 0)
3516 *dst++ = 0x20;
3517 } else {
3518 *dst++ = *src++;
3519 }
3520 }
3521 *dst = 0;
3522 return(ret);
3523}
3524
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003525static void
Owen Taylor3473f882001-02-23 17:55:21 +00003526xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003527 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003528 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3529}
3530
3531/**
3532 * xmlValidateAttributeDecl:
3533 * @ctxt: the validation context
3534 * @doc: a document instance
3535 * @attr: an attribute definition
3536 *
3537 * Try to validate a single attribute definition
3538 * basically it does the following checks as described by the
3539 * XML-1.0 recommendation:
3540 * - [ VC: Attribute Default Legal ]
3541 * - [ VC: Enumeration ]
3542 * - [ VC: ID Attribute Default ]
3543 *
3544 * The ID/IDREF uniqueness and matching are done separately
3545 *
3546 * returns 1 if valid or 0 otherwise
3547 */
3548
3549int
3550xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3551 xmlAttributePtr attr) {
3552 int ret = 1;
3553 int val;
3554 CHECK_DTD;
3555 if(attr == NULL) return(1);
3556
3557 /* Attribute Default Legal */
3558 /* Enumeration */
3559 if (attr->defaultValue != NULL) {
3560 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3561 if (val == 0) {
3562 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003563 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003564 attr->name, attr->elem);
3565 }
3566 ret &= val;
3567 }
3568
3569 /* ID Attribute Default */
3570 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3571 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3572 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3573 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003574 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003575 attr->name, attr->elem);
3576 ret = 0;
3577 }
3578
3579 /* One ID per Element Type */
3580 if (attr->atype == XML_ATTRIBUTE_ID) {
3581 int nbId;
3582
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003583 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003584 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3585 attr->elem);
3586 if (elem != NULL) {
3587 nbId = xmlScanIDAttributeDecl(NULL, elem);
3588 } else {
3589 xmlAttributeTablePtr table;
3590
3591 /*
3592 * The attribute may be declared in the internal subset and the
3593 * element in the external subset.
3594 */
3595 nbId = 0;
3596 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3597 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3598 xmlValidateAttributeIdCallback, &nbId);
3599 }
3600 if (nbId > 1) {
3601 VERROR(ctxt->userData,
3602 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3603 attr->elem, nbId, attr->name);
3604 } else if (doc->extSubset != NULL) {
3605 int extId = 0;
3606 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3607 if (elem != NULL) {
3608 extId = xmlScanIDAttributeDecl(NULL, elem);
3609 }
3610 if (extId > 1) {
3611 VERROR(ctxt->userData,
3612 "Element %s has %d ID attribute defined in the external subset : %s\n",
3613 attr->elem, extId, attr->name);
3614 } else if (extId + nbId > 1) {
3615 VERROR(ctxt->userData,
3616"Element %s has ID attributes defined in the internal and external subset : %s\n",
3617 attr->elem, attr->name);
3618 }
3619 }
3620 }
3621
3622 /* Validity Constraint: Enumeration */
3623 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3624 xmlEnumerationPtr tree = attr->tree;
3625 while (tree != NULL) {
3626 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3627 tree = tree->next;
3628 }
3629 if (tree == NULL) {
3630 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003631"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003632 attr->defaultValue, attr->name, attr->elem);
3633 ret = 0;
3634 }
3635 }
3636
3637 return(ret);
3638}
3639
3640/**
3641 * xmlValidateElementDecl:
3642 * @ctxt: the validation context
3643 * @doc: a document instance
3644 * @elem: an element definition
3645 *
3646 * Try to validate a single element definition
3647 * basically it does the following checks as described by the
3648 * XML-1.0 recommendation:
3649 * - [ VC: One ID per Element Type ]
3650 * - [ VC: No Duplicate Types ]
3651 * - [ VC: Unique Element Type Declaration ]
3652 *
3653 * returns 1 if valid or 0 otherwise
3654 */
3655
3656int
3657xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3658 xmlElementPtr elem) {
3659 int ret = 1;
3660 xmlElementPtr tst;
3661
3662 CHECK_DTD;
3663
3664 if (elem == NULL) return(1);
3665
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003666#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003667#ifdef LIBXML_REGEXP_ENABLED
3668 /* Build the regexp associated to the content model */
3669 ret = xmlValidBuildContentModel(ctxt, elem);
3670#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003671#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003672
Owen Taylor3473f882001-02-23 17:55:21 +00003673 /* No Duplicate Types */
3674 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3675 xmlElementContentPtr cur, next;
3676 const xmlChar *name;
3677
3678 cur = elem->content;
3679 while (cur != NULL) {
3680 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3681 if (cur->c1 == NULL) break;
3682 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3683 name = cur->c1->name;
3684 next = cur->c2;
3685 while (next != NULL) {
3686 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3687 if (xmlStrEqual(next->name, name)) {
3688 VERROR(ctxt->userData,
3689 "Definition of %s has duplicate references of %s\n",
3690 elem->name, name);
3691 ret = 0;
3692 }
3693 break;
3694 }
3695 if (next->c1 == NULL) break;
3696 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3697 if (xmlStrEqual(next->c1->name, name)) {
3698 VERROR(ctxt->userData,
3699 "Definition of %s has duplicate references of %s\n",
3700 elem->name, name);
3701 ret = 0;
3702 }
3703 next = next->c2;
3704 }
3705 }
3706 cur = cur->c2;
3707 }
3708 }
3709
3710 /* VC: Unique Element Type Declaration */
3711 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003712 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003713 ((tst->prefix == elem->prefix) ||
3714 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003715 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003716 VERROR(ctxt->userData, "Redefinition of element %s\n",
3717 elem->name);
3718 ret = 0;
3719 }
3720 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003721 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003722 ((tst->prefix == elem->prefix) ||
3723 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003724 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003725 VERROR(ctxt->userData, "Redefinition of element %s\n",
3726 elem->name);
3727 ret = 0;
3728 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003729 /* One ID per Element Type
3730 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003731 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3732 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003733 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003734 return(ret);
3735}
3736
3737/**
3738 * xmlValidateOneAttribute:
3739 * @ctxt: the validation context
3740 * @doc: a document instance
3741 * @elem: an element instance
3742 * @attr: an attribute instance
3743 * @value: the attribute value (without entities processing)
3744 *
3745 * Try to validate a single attribute for an element
3746 * basically it does the following checks as described by the
3747 * XML-1.0 recommendation:
3748 * - [ VC: Attribute Value Type ]
3749 * - [ VC: Fixed Attribute Default ]
3750 * - [ VC: Entity Name ]
3751 * - [ VC: Name Token ]
3752 * - [ VC: ID ]
3753 * - [ VC: IDREF ]
3754 * - [ VC: Entity Name ]
3755 * - [ VC: Notation Attributes ]
3756 *
3757 * The ID/IDREF uniqueness and matching are done separately
3758 *
3759 * returns 1 if valid or 0 otherwise
3760 */
3761
3762int
3763xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3764 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3765 /* xmlElementPtr elemDecl; */
3766 xmlAttributePtr attrDecl = NULL;
3767 int val;
3768 int ret = 1;
3769
3770 CHECK_DTD;
3771 if ((elem == NULL) || (elem->name == NULL)) return(0);
3772 if ((attr == NULL) || (attr->name == NULL)) return(0);
3773
3774 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003775 xmlChar fn[50];
3776 xmlChar *fullname;
3777
3778 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3779 if (fullname == NULL)
3780 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003781 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003782 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003783 attr->name, attr->ns->prefix);
3784 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003785 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003786 attr->name, attr->ns->prefix);
3787 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003788 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003789 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3790 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00003791 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003792 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003793 if ((fullname != fn) && (fullname != elem->name))
3794 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003795 }
3796 if (attrDecl == NULL) {
3797 if (attr->ns != NULL) {
3798 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3799 attr->name, attr->ns->prefix);
3800 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3801 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3802 attr->name, attr->ns->prefix);
3803 } else {
3804 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3805 elem->name, attr->name);
3806 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3807 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3808 elem->name, attr->name);
3809 }
3810 }
3811
3812
3813 /* Validity Constraint: Attribute Value Type */
3814 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003815 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003816 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003817 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003818 attr->name, elem->name);
3819 return(0);
3820 }
3821 attr->atype = attrDecl->atype;
3822
3823 val = xmlValidateAttributeValue(attrDecl->atype, value);
3824 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003825 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003826 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003827 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003828 attr->name, elem->name);
3829 ret = 0;
3830 }
3831
3832 /* Validity constraint: Fixed Attribute Default */
3833 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3834 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003835 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003836 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003837 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003838 attr->name, elem->name, attrDecl->defaultValue);
3839 ret = 0;
3840 }
3841 }
3842
3843 /* Validity Constraint: ID uniqueness */
3844 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3845 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3846 ret = 0;
3847 }
3848
3849 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3850 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3851 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3852 ret = 0;
3853 }
3854
3855 /* Validity Constraint: Notation Attributes */
3856 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3857 xmlEnumerationPtr tree = attrDecl->tree;
3858 xmlNotationPtr nota;
3859
3860 /* First check that the given NOTATION was declared */
3861 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3862 if (nota == NULL)
3863 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3864
3865 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003866 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003867 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003868 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003869 value, attr->name, elem->name);
3870 ret = 0;
3871 }
3872
3873 /* Second, verify that it's among the list */
3874 while (tree != NULL) {
3875 if (xmlStrEqual(tree->name, value)) break;
3876 tree = tree->next;
3877 }
3878 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003879 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003880 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003881"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003882 value, attr->name, elem->name);
3883 ret = 0;
3884 }
3885 }
3886
3887 /* Validity Constraint: Enumeration */
3888 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3889 xmlEnumerationPtr tree = attrDecl->tree;
3890 while (tree != NULL) {
3891 if (xmlStrEqual(tree->name, value)) break;
3892 tree = tree->next;
3893 }
3894 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003895 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003896 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003897 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003898 value, attr->name, elem->name);
3899 ret = 0;
3900 }
3901 }
3902
3903 /* Fixed Attribute Default */
3904 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3905 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003906 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003907 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003908 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003909 attr->name, elem->name, attrDecl->defaultValue);
3910 ret = 0;
3911 }
3912
3913 /* Extra check for the attribute value */
3914 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3915 attrDecl->atype, value);
3916
3917 return(ret);
3918}
3919
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003920/**
3921 * xmlValidateOneNamespace:
3922 * @ctxt: the validation context
3923 * @doc: a document instance
3924 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00003925 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003926 * @ns: an namespace declaration instance
3927 * @value: the attribute value (without entities processing)
3928 *
3929 * Try to validate a single namespace declaration for an element
3930 * basically it does the following checks as described by the
3931 * XML-1.0 recommendation:
3932 * - [ VC: Attribute Value Type ]
3933 * - [ VC: Fixed Attribute Default ]
3934 * - [ VC: Entity Name ]
3935 * - [ VC: Name Token ]
3936 * - [ VC: ID ]
3937 * - [ VC: IDREF ]
3938 * - [ VC: Entity Name ]
3939 * - [ VC: Notation Attributes ]
3940 *
3941 * The ID/IDREF uniqueness and matching are done separately
3942 *
3943 * returns 1 if valid or 0 otherwise
3944 */
3945
3946int
3947xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3948xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
3949 /* xmlElementPtr elemDecl; */
3950 xmlAttributePtr attrDecl = NULL;
3951 int val;
3952 int ret = 1;
3953
3954 CHECK_DTD;
3955 if ((elem == NULL) || (elem->name == NULL)) return(0);
3956 if ((ns == NULL) || (ns->href == NULL)) return(0);
3957
3958 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003959 xmlChar fn[50];
3960 xmlChar *fullname;
3961
3962 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
3963 if (fullname == NULL) {
3964 VERROR(ctxt->userData, "Out of memory\n");
3965 return(0);
3966 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003967 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003968 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003969 ns->prefix, BAD_CAST "xmlns");
3970 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003971 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003972 ns->prefix, BAD_CAST "xmlns");
3973 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003974 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003975 BAD_CAST "xmlns");
3976 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003977 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003978 BAD_CAST "xmlns");
3979 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003980 if ((fullname != fn) && (fullname != elem->name))
3981 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003982 }
3983 if (attrDecl == NULL) {
3984 if (ns->prefix != NULL) {
3985 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3986 ns->prefix, BAD_CAST "xmlns");
3987 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3988 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3989 ns->prefix, BAD_CAST "xmlns");
3990 } else {
3991 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3992 elem->name, BAD_CAST "xmlns");
3993 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3994 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3995 elem->name, BAD_CAST "xmlns");
3996 }
3997 }
3998
3999
4000 /* Validity Constraint: Attribute Value Type */
4001 if (attrDecl == NULL) {
4002 VECTXT(ctxt, elem);
4003 if (ns->prefix != NULL) {
4004 VERROR(ctxt->userData,
4005 "No declaration for attribute xmlns:%s of element %s\n",
4006 ns->prefix, elem->name);
4007 } else {
4008 VERROR(ctxt->userData,
4009 "No declaration for attribute xmlns of element %s\n",
4010 elem->name);
4011 }
4012 return(0);
4013 }
4014
4015 val = xmlValidateAttributeValue(attrDecl->atype, value);
4016 if (val == 0) {
4017 VECTXT(ctxt, elem);
4018 if (ns->prefix != NULL) {
4019 VERROR(ctxt->userData,
4020 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
4021 ns->prefix, elem->name);
4022 } else {
4023 VERROR(ctxt->userData,
4024 "Syntax of value for attribute xmlns of %s is not valid\n",
4025 elem->name);
4026 }
4027 ret = 0;
4028 }
4029
4030 /* Validity constraint: Fixed Attribute Default */
4031 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
4032 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4033 VECTXT(ctxt, elem);
4034 if (ns->prefix != NULL) {
4035 VERROR(ctxt->userData,
4036 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4037 ns->prefix, elem->name, attrDecl->defaultValue);
4038 } else {
4039 VERROR(ctxt->userData,
4040 "Value for attribute xmlns of %s is different from default \"%s\"\n",
4041 elem->name, attrDecl->defaultValue);
4042 }
4043 ret = 0;
4044 }
4045 }
4046
4047 /* Validity Constraint: ID uniqueness */
4048 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4049 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4050 ret = 0;
4051 }
4052
4053 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4054 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4055 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4056 ret = 0;
4057 }
4058
4059 /* Validity Constraint: Notation Attributes */
4060 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4061 xmlEnumerationPtr tree = attrDecl->tree;
4062 xmlNotationPtr nota;
4063
4064 /* First check that the given NOTATION was declared */
4065 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4066 if (nota == NULL)
4067 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4068
4069 if (nota == NULL) {
4070 VECTXT(ctxt, elem);
4071 if (ns->prefix != NULL) {
4072 VERROR(ctxt->userData,
4073 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4074 value, ns->prefix, elem->name);
4075 } else {
4076 VERROR(ctxt->userData,
4077 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
4078 value, elem->name);
4079 }
4080 ret = 0;
4081 }
4082
4083 /* Second, verify that it's among the list */
4084 while (tree != NULL) {
4085 if (xmlStrEqual(tree->name, value)) break;
4086 tree = tree->next;
4087 }
4088 if (tree == NULL) {
4089 VECTXT(ctxt, elem);
4090 if (ns->prefix != NULL) {
4091 VERROR(ctxt->userData,
4092"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4093 value, ns->prefix, elem->name);
4094 } else {
4095 VERROR(ctxt->userData,
4096"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
4097 value, elem->name);
4098 }
4099 ret = 0;
4100 }
4101 }
4102
4103 /* Validity Constraint: Enumeration */
4104 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4105 xmlEnumerationPtr tree = attrDecl->tree;
4106 while (tree != NULL) {
4107 if (xmlStrEqual(tree->name, value)) break;
4108 tree = tree->next;
4109 }
4110 if (tree == NULL) {
4111 VECTXT(ctxt, elem);
4112 if (ns->prefix != NULL) {
4113 VERROR(ctxt->userData,
4114"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4115 value, ns->prefix, elem->name);
4116 } else {
4117 VERROR(ctxt->userData,
4118"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4119 value, elem->name);
4120 }
4121 ret = 0;
4122 }
4123 }
4124
4125 /* Fixed Attribute Default */
4126 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4127 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4128 VECTXT(ctxt, elem);
4129 if (ns->prefix != NULL) {
4130 VERROR(ctxt->userData,
4131 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4132 ns->prefix, elem->name, attrDecl->defaultValue);
4133 } else {
4134 VERROR(ctxt->userData,
4135 "Value for attribute xmlns of %s must be \"%s\"\n",
4136 elem->name, attrDecl->defaultValue);
4137 }
4138 ret = 0;
4139 }
4140
4141 /* Extra check for the attribute value */
4142 if (ns->prefix != NULL) {
4143 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4144 attrDecl->atype, value);
4145 } else {
4146 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4147 attrDecl->atype, value);
4148 }
4149
4150 return(ret);
4151}
4152
Daniel Veillard118aed72002-09-24 14:13:13 +00004153#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004154/**
4155 * xmlValidateSkipIgnorable:
4156 * @ctxt: the validation context
4157 * @child: the child list
4158 *
4159 * Skip ignorable elements w.r.t. the validation process
4160 *
4161 * returns the first element to consider for validation of the content model
4162 */
4163
4164static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004165xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004166 while (child != NULL) {
4167 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004168 /* These things are ignored (skipped) during validation. */
4169 case XML_PI_NODE:
4170 case XML_COMMENT_NODE:
4171 case XML_XINCLUDE_START:
4172 case XML_XINCLUDE_END:
4173 child = child->next;
4174 break;
4175 case XML_TEXT_NODE:
4176 if (xmlIsBlankNode(child))
4177 child = child->next;
4178 else
4179 return(child);
4180 break;
4181 /* keep current node */
4182 default:
4183 return(child);
4184 }
4185 }
4186 return(child);
4187}
4188
4189/**
4190 * xmlValidateElementType:
4191 * @ctxt: the validation context
4192 *
4193 * Try to validate the content model of an element internal function
4194 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004195 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4196 * reference is found and -3 if the validation succeeded but
4197 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004198 */
4199
4200static int
4201xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004202 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004203 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004204
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004205 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004206 if ((NODE == NULL) && (CONT == NULL))
4207 return(1);
4208 if ((NODE == NULL) &&
4209 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4210 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4211 return(1);
4212 }
4213 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004214 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004215 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004216
4217 /*
4218 * We arrive here when more states need to be examined
4219 */
4220cont:
4221
4222 /*
4223 * We just recovered from a rollback generated by a possible
4224 * epsilon transition, go directly to the analysis phase
4225 */
4226 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004227 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004228 DEBUG_VALID_STATE(NODE, CONT)
4229 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004230 goto analyze;
4231 }
4232
4233 DEBUG_VALID_STATE(NODE, CONT)
4234 /*
4235 * we may have to save a backup state here. This is the equivalent
4236 * of handling epsilon transition in NFAs.
4237 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004238 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004239 ((CONT->parent == NULL) ||
4240 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004241 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004242 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004243 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004244 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004245 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4246 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004247 }
4248
4249
4250 /*
4251 * Check first if the content matches
4252 */
4253 switch (CONT->type) {
4254 case XML_ELEMENT_CONTENT_PCDATA:
4255 if (NODE == NULL) {
4256 DEBUG_VALID_MSG("pcdata failed no node");
4257 ret = 0;
4258 break;
4259 }
4260 if (NODE->type == XML_TEXT_NODE) {
4261 DEBUG_VALID_MSG("pcdata found, skip to next");
4262 /*
4263 * go to next element in the content model
4264 * skipping ignorable elems
4265 */
4266 do {
4267 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004268 NODE = xmlValidateSkipIgnorable(NODE);
4269 if ((NODE != NULL) &&
4270 (NODE->type == XML_ENTITY_REF_NODE))
4271 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004272 } while ((NODE != NULL) &&
4273 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004274 (NODE->type != XML_TEXT_NODE) &&
4275 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004276 ret = 1;
4277 break;
4278 } else {
4279 DEBUG_VALID_MSG("pcdata failed");
4280 ret = 0;
4281 break;
4282 }
4283 break;
4284 case XML_ELEMENT_CONTENT_ELEMENT:
4285 if (NODE == NULL) {
4286 DEBUG_VALID_MSG("element failed no node");
4287 ret = 0;
4288 break;
4289 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004290 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4291 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004292 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004293 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4294 ret = (CONT->prefix == NULL);
4295 } else if (CONT->prefix == NULL) {
4296 ret = 0;
4297 } else {
4298 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4299 }
4300 }
4301 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004302 DEBUG_VALID_MSG("element found, skip to next");
4303 /*
4304 * go to next element in the content model
4305 * skipping ignorable elems
4306 */
4307 do {
4308 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004309 NODE = xmlValidateSkipIgnorable(NODE);
4310 if ((NODE != NULL) &&
4311 (NODE->type == XML_ENTITY_REF_NODE))
4312 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004313 } while ((NODE != NULL) &&
4314 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004315 (NODE->type != XML_TEXT_NODE) &&
4316 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004317 } else {
4318 DEBUG_VALID_MSG("element failed");
4319 ret = 0;
4320 break;
4321 }
4322 break;
4323 case XML_ELEMENT_CONTENT_OR:
4324 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004325 * Small optimization.
4326 */
4327 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4328 if ((NODE == NULL) ||
4329 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4330 DEPTH++;
4331 CONT = CONT->c2;
4332 goto cont;
4333 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004334 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4335 ret = (CONT->c1->prefix == NULL);
4336 } else if (CONT->c1->prefix == NULL) {
4337 ret = 0;
4338 } else {
4339 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4340 }
4341 if (ret == 0) {
4342 DEPTH++;
4343 CONT = CONT->c2;
4344 goto cont;
4345 }
Daniel Veillard85349052001-04-20 13:48:21 +00004346 }
4347
4348 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004349 * save the second branch 'or' branch
4350 */
4351 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004352 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4353 OCCURS, ROLLBACK_OR) < 0)
4354 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004355 DEPTH++;
4356 CONT = CONT->c1;
4357 goto cont;
4358 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004359 /*
4360 * Small optimization.
4361 */
4362 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4363 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4364 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
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 Veillard1d047672001-06-09 16:41:01 +00004383 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004384 DEPTH++;
4385 CONT = CONT->c1;
4386 goto cont;
4387 }
4388
4389 /*
4390 * At this point handle going up in the tree
4391 */
4392 if (ret == -1) {
4393 DEBUG_VALID_MSG("error found returning");
4394 return(ret);
4395 }
4396analyze:
4397 while (CONT != NULL) {
4398 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004399 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004400 * this level.
4401 */
4402 if (ret == 0) {
4403 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004404 xmlNodePtr cur;
4405
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004406 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004407 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004408 DEBUG_VALID_MSG("Once branch failed, rollback");
4409 if (vstateVPop(ctxt) < 0 ) {
4410 DEBUG_VALID_MSG("exhaustion, failed");
4411 return(0);
4412 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004413 if (cur != ctxt->vstate->node)
4414 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004415 goto cont;
4416 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004417 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004418 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004419 DEBUG_VALID_MSG("Plus branch failed, rollback");
4420 if (vstateVPop(ctxt) < 0 ) {
4421 DEBUG_VALID_MSG("exhaustion, failed");
4422 return(0);
4423 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004424 if (cur != ctxt->vstate->node)
4425 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004426 goto cont;
4427 }
4428 DEBUG_VALID_MSG("Plus branch found");
4429 ret = 1;
4430 break;
4431 case XML_ELEMENT_CONTENT_MULT:
4432#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004433 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004434 DEBUG_VALID_MSG("Mult branch failed");
4435 } else {
4436 DEBUG_VALID_MSG("Mult branch found");
4437 }
4438#endif
4439 ret = 1;
4440 break;
4441 case XML_ELEMENT_CONTENT_OPT:
4442 DEBUG_VALID_MSG("Option branch failed");
4443 ret = 1;
4444 break;
4445 }
4446 } else {
4447 switch (CONT->ocur) {
4448 case XML_ELEMENT_CONTENT_OPT:
4449 DEBUG_VALID_MSG("Option branch succeeded");
4450 ret = 1;
4451 break;
4452 case XML_ELEMENT_CONTENT_ONCE:
4453 DEBUG_VALID_MSG("Once branch succeeded");
4454 ret = 1;
4455 break;
4456 case XML_ELEMENT_CONTENT_PLUS:
4457 if (STATE == ROLLBACK_PARENT) {
4458 DEBUG_VALID_MSG("Plus branch rollback");
4459 ret = 1;
4460 break;
4461 }
4462 if (NODE == NULL) {
4463 DEBUG_VALID_MSG("Plus branch exhausted");
4464 ret = 1;
4465 break;
4466 }
4467 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004468 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004469 goto cont;
4470 case XML_ELEMENT_CONTENT_MULT:
4471 if (STATE == ROLLBACK_PARENT) {
4472 DEBUG_VALID_MSG("Mult branch rollback");
4473 ret = 1;
4474 break;
4475 }
4476 if (NODE == NULL) {
4477 DEBUG_VALID_MSG("Mult branch exhausted");
4478 ret = 1;
4479 break;
4480 }
4481 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004482 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004483 goto cont;
4484 }
4485 }
4486 STATE = 0;
4487
4488 /*
4489 * Then act accordingly at the parent level
4490 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004491 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004492 if (CONT->parent == NULL)
4493 break;
4494
4495 switch (CONT->parent->type) {
4496 case XML_ELEMENT_CONTENT_PCDATA:
4497 DEBUG_VALID_MSG("Error: parent pcdata");
4498 return(-1);
4499 case XML_ELEMENT_CONTENT_ELEMENT:
4500 DEBUG_VALID_MSG("Error: parent element");
4501 return(-1);
4502 case XML_ELEMENT_CONTENT_OR:
4503 if (ret == 1) {
4504 DEBUG_VALID_MSG("Or succeeded");
4505 CONT = CONT->parent;
4506 DEPTH--;
4507 } else {
4508 DEBUG_VALID_MSG("Or failed");
4509 CONT = CONT->parent;
4510 DEPTH--;
4511 }
4512 break;
4513 case XML_ELEMENT_CONTENT_SEQ:
4514 if (ret == 0) {
4515 DEBUG_VALID_MSG("Sequence failed");
4516 CONT = CONT->parent;
4517 DEPTH--;
4518 } else if (CONT == CONT->parent->c1) {
4519 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4520 CONT = CONT->parent->c2;
4521 goto cont;
4522 } else {
4523 DEBUG_VALID_MSG("Sequence succeeded");
4524 CONT = CONT->parent;
4525 DEPTH--;
4526 }
4527 }
4528 }
4529 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004530 xmlNodePtr cur;
4531
4532 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004533 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4534 if (vstateVPop(ctxt) < 0 ) {
4535 DEBUG_VALID_MSG("exhaustion, failed");
4536 return(0);
4537 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004538 if (cur != ctxt->vstate->node)
4539 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004540 goto cont;
4541 }
4542 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004543 xmlNodePtr cur;
4544
4545 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004546 DEBUG_VALID_MSG("Failure, rollback");
4547 if (vstateVPop(ctxt) < 0 ) {
4548 DEBUG_VALID_MSG("exhaustion, failed");
4549 return(0);
4550 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004551 if (cur != ctxt->vstate->node)
4552 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004553 goto cont;
4554 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004555 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004556}
Daniel Veillard23e73572002-09-19 19:56:43 +00004557#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004558
4559/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004560 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004561 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004562 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004563 * @content: An element
4564 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4565 *
4566 * This will dump the list of elements to the buffer
4567 * Intended just for the debug routine
4568 */
4569static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004570xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004571 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004572 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004573
4574 if (node == NULL) return;
4575 if (glob) strcat(buf, "(");
4576 cur = node;
4577 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004578 len = strlen(buf);
4579 if (size - len < 50) {
4580 if ((size - len > 4) && (buf[len - 1] != '.'))
4581 strcat(buf, " ...");
4582 return;
4583 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004584 switch (cur->type) {
4585 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004586 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004587 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004588 if ((size - len > 4) && (buf[len - 1] != '.'))
4589 strcat(buf, " ...");
4590 return;
4591 }
4592 strcat(buf, (char *) cur->ns->prefix);
4593 strcat(buf, ":");
4594 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004595 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004596 if ((size - len > 4) && (buf[len - 1] != '.'))
4597 strcat(buf, " ...");
4598 return;
4599 }
4600 strcat(buf, (char *) cur->name);
4601 if (cur->next != NULL)
4602 strcat(buf, " ");
4603 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004604 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004605 if (xmlIsBlankNode(cur))
4606 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004607 case XML_CDATA_SECTION_NODE:
4608 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004609 strcat(buf, "CDATA");
4610 if (cur->next != NULL)
4611 strcat(buf, " ");
4612 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004613 case XML_ATTRIBUTE_NODE:
4614 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004615#ifdef LIBXML_DOCB_ENABLED
4616 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004617#endif
4618 case XML_HTML_DOCUMENT_NODE:
4619 case XML_DOCUMENT_TYPE_NODE:
4620 case XML_DOCUMENT_FRAG_NODE:
4621 case XML_NOTATION_NODE:
4622 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004623 strcat(buf, "???");
4624 if (cur->next != NULL)
4625 strcat(buf, " ");
4626 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004627 case XML_ENTITY_NODE:
4628 case XML_PI_NODE:
4629 case XML_DTD_NODE:
4630 case XML_COMMENT_NODE:
4631 case XML_ELEMENT_DECL:
4632 case XML_ATTRIBUTE_DECL:
4633 case XML_ENTITY_DECL:
4634 case XML_XINCLUDE_START:
4635 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004636 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004637 }
4638 cur = cur->next;
4639 }
4640 if (glob) strcat(buf, ")");
4641}
4642
4643/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004644 * xmlValidateElementContent:
4645 * @ctxt: the validation context
4646 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004647 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004648 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004649 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004650 *
4651 * Try to validate the content model of an element
4652 *
4653 * returns 1 if valid or 0 if not and -1 in case of error
4654 */
4655
4656static int
4657xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004658 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004659 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004660#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004661 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004662#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004663 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004664 xmlElementContentPtr cont;
4665 const xmlChar *name;
4666
4667 if (elemDecl == NULL)
4668 return(-1);
4669 cont = elemDecl->content;
4670 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004671
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004672#ifdef LIBXML_REGEXP_ENABLED
4673 /* Build the regexp associated to the content model */
4674 if (elemDecl->contModel == NULL)
4675 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4676 if (elemDecl->contModel == NULL) {
4677 ret = -1;
4678 } else {
4679 xmlRegExecCtxtPtr exec;
4680
Daniel Veillardec498e12003-02-05 11:01:50 +00004681 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
4682 return(-1);
4683 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004684 ctxt->nodeMax = 0;
4685 ctxt->nodeNr = 0;
4686 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004687 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4688 if (exec != NULL) {
4689 cur = child;
4690 while (cur != NULL) {
4691 switch (cur->type) {
4692 case XML_ENTITY_REF_NODE:
4693 /*
4694 * Push the current node to be able to roll back
4695 * and process within the entity
4696 */
4697 if ((cur->children != NULL) &&
4698 (cur->children->children != NULL)) {
4699 nodeVPush(ctxt, cur);
4700 cur = cur->children->children;
4701 continue;
4702 }
4703 break;
4704 case XML_TEXT_NODE:
4705 if (xmlIsBlankNode(cur))
4706 break;
4707 ret = 0;
4708 goto fail;
4709 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004710 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004711 ret = 0;
4712 goto fail;
4713 case XML_ELEMENT_NODE:
4714 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004715 xmlChar fn[50];
4716 xmlChar *fullname;
4717
4718 fullname = xmlBuildQName(cur->name,
4719 cur->ns->prefix, fn, 50);
4720 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004721 ret = -1;
4722 goto fail;
4723 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004724 ret = xmlRegExecPushString(exec, fullname, NULL);
4725 if ((fullname != fn) && (fullname != cur->name))
4726 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004727 } else {
4728 ret = xmlRegExecPushString(exec, cur->name, NULL);
4729 }
4730 break;
4731 default:
4732 break;
4733 }
4734 /*
4735 * Switch to next element
4736 */
4737 cur = cur->next;
4738 while (cur == NULL) {
4739 cur = nodeVPop(ctxt);
4740 if (cur == NULL)
4741 break;
4742 cur = cur->next;
4743 }
4744 }
4745 ret = xmlRegExecPushString(exec, NULL, NULL);
4746fail:
4747 xmlRegFreeExecCtxt(exec);
4748 }
4749 }
4750#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004751 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004752 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004753 */
4754 ctxt->vstateMax = 8;
4755 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4756 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4757 if (ctxt->vstateTab == NULL) {
4758 xmlGenericError(xmlGenericErrorContext,
4759 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004760 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004761 }
4762 /*
4763 * The first entry in the stack is reserved to the current state
4764 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004765 ctxt->nodeMax = 0;
4766 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004767 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004768 ctxt->vstate = &ctxt->vstateTab[0];
4769 ctxt->vstateNr = 1;
4770 CONT = cont;
4771 NODE = child;
4772 DEPTH = 0;
4773 OCCURS = 0;
4774 STATE = 0;
4775 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004776 if ((ret == -3) && (warn)) {
4777 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004778 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004779 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004780 /*
4781 * An entities reference appeared at this level.
4782 * Buid a minimal representation of this node content
4783 * sufficient to run the validation process on it
4784 */
4785 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004786 cur = child;
4787 while (cur != NULL) {
4788 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004789 case XML_ENTITY_REF_NODE:
4790 /*
4791 * Push the current node to be able to roll back
4792 * and process within the entity
4793 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004794 if ((cur->children != NULL) &&
4795 (cur->children->children != NULL)) {
4796 nodeVPush(ctxt, cur);
4797 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004798 continue;
4799 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004800 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004801 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004802 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004803 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004804 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004805 case XML_CDATA_SECTION_NODE:
4806 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004807 case XML_ELEMENT_NODE:
4808 /*
4809 * Allocate a new node and minimally fills in
4810 * what's required
4811 */
4812 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4813 if (tmp == NULL) {
4814 xmlGenericError(xmlGenericErrorContext,
4815 "xmlValidateElementContent : malloc failed\n");
4816 xmlFreeNodeList(repl);
4817 ret = -1;
4818 goto done;
4819 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004820 tmp->type = cur->type;
4821 tmp->name = cur->name;
4822 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004823 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004824 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004825 if (repl == NULL)
4826 repl = last = tmp;
4827 else {
4828 last->next = tmp;
4829 last = tmp;
4830 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004831 if (cur->type == XML_CDATA_SECTION_NODE) {
4832 /*
4833 * E59 spaces in CDATA does not match the
4834 * nonterminal S
4835 */
4836 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4837 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004838 break;
4839 default:
4840 break;
4841 }
4842 /*
4843 * Switch to next element
4844 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004845 cur = cur->next;
4846 while (cur == NULL) {
4847 cur = nodeVPop(ctxt);
4848 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004849 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004850 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004851 }
4852 }
4853
4854 /*
4855 * Relaunch the validation
4856 */
4857 ctxt->vstate = &ctxt->vstateTab[0];
4858 ctxt->vstateNr = 1;
4859 CONT = cont;
4860 NODE = repl;
4861 DEPTH = 0;
4862 OCCURS = 0;
4863 STATE = 0;
4864 ret = xmlValidateElementType(ctxt);
4865 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004866#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004867 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004868 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4869 char expr[5000];
4870 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004871
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004872 expr[0] = 0;
4873 xmlSnprintfElementContent(expr, 5000, cont, 1);
4874 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004875#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004876 if (repl != NULL)
4877 xmlSnprintfElements(list, 5000, repl, 1);
4878 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004879#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004880 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004881
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004882 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004883 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004884 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004885 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004886 name, expr, list);
4887 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004888 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004889 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004890 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004891 expr, list);
4892 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004893 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004894 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004895 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004896 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004897 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004898 name);
4899 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004900 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004901 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004902 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004903 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004904 }
4905 ret = 0;
4906 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004907 if (ret == -3)
4908 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004909
Daniel Veillard23e73572002-09-19 19:56:43 +00004910#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004911done:
4912 /*
4913 * Deallocate the copy if done, and free up the validation stack
4914 */
4915 while (repl != NULL) {
4916 tmp = repl->next;
4917 xmlFree(repl);
4918 repl = tmp;
4919 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004920 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004921 if (ctxt->vstateTab != NULL) {
4922 xmlFree(ctxt->vstateTab);
4923 ctxt->vstateTab = NULL;
4924 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004925#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004926 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004927 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004928 if (ctxt->nodeTab != NULL) {
4929 xmlFree(ctxt->nodeTab);
4930 ctxt->nodeTab = NULL;
4931 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004932 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004933
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004934}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004935
Owen Taylor3473f882001-02-23 17:55:21 +00004936/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004937 * xmlValidateCdataElement:
4938 * @ctxt: the validation context
4939 * @doc: a document instance
4940 * @elem: an element instance
4941 *
4942 * Check that an element follows #CDATA
4943 *
4944 * returns 1 if valid or 0 otherwise
4945 */
4946static int
4947xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4948 xmlNodePtr elem) {
4949 int ret = 1;
4950 xmlNodePtr cur, child;
4951
Daniel Veillardceb09b92002-10-04 11:46:37 +00004952 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004953 return(0);
4954
4955 child = elem->children;
4956
4957 cur = child;
4958 while (cur != NULL) {
4959 switch (cur->type) {
4960 case XML_ENTITY_REF_NODE:
4961 /*
4962 * Push the current node to be able to roll back
4963 * and process within the entity
4964 */
4965 if ((cur->children != NULL) &&
4966 (cur->children->children != NULL)) {
4967 nodeVPush(ctxt, cur);
4968 cur = cur->children->children;
4969 continue;
4970 }
4971 break;
4972 case XML_COMMENT_NODE:
4973 case XML_PI_NODE:
4974 case XML_TEXT_NODE:
4975 case XML_CDATA_SECTION_NODE:
4976 break;
4977 default:
4978 ret = 0;
4979 goto done;
4980 }
4981 /*
4982 * Switch to next element
4983 */
4984 cur = cur->next;
4985 while (cur == NULL) {
4986 cur = nodeVPop(ctxt);
4987 if (cur == NULL)
4988 break;
4989 cur = cur->next;
4990 }
4991 }
4992done:
4993 ctxt->nodeMax = 0;
4994 ctxt->nodeNr = 0;
4995 if (ctxt->nodeTab != NULL) {
4996 xmlFree(ctxt->nodeTab);
4997 ctxt->nodeTab = NULL;
4998 }
4999 return(ret);
5000}
5001
5002/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00005003 * xmlValidateCheckMixed:
5004 * @ctxt: the validation context
5005 * @cont: the mixed content model
5006 * @qname: the qualified name as appearing in the serialization
5007 *
5008 * Check if the given node is part of the content model.
5009 *
5010 * Returns 1 if yes, 0 if no, -1 in case of error
5011 */
5012static int
5013xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
5014 xmlElementContentPtr cont, const xmlChar *qname) {
5015 while (cont != NULL) {
5016 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5017 if (xmlStrEqual(cont->name, qname))
5018 return(1);
5019 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5020 (cont->c1 != NULL) &&
5021 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5022 if (xmlStrEqual(cont->c1->name, qname))
5023 return(1);
5024 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5025 (cont->c1 == NULL) ||
5026 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5027 /* Internal error !!! */
5028 xmlGenericError(xmlGenericErrorContext,
5029 "Internal: MIXED struct bad\n");
5030 break;
5031 }
5032 cont = cont->c2;
5033 }
5034 return(0);
5035}
5036
5037/**
5038 * xmlValidGetElemDecl:
5039 * @ctxt: the validation context
5040 * @doc: a document instance
5041 * @elem: an element instance
5042 * @extsubset: pointer, (out) indicate if the declaration was found
5043 * in the external subset.
5044 *
5045 * Finds a declaration associated to an element in the document.
5046 *
5047 * returns the pointer to the declaration or NULL if not found.
5048 */
5049static xmlElementPtr
5050xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5051 xmlNodePtr elem, int *extsubset) {
5052 xmlElementPtr elemDecl = NULL;
5053 const xmlChar *prefix = NULL;
5054
5055 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5056 if (extsubset != NULL)
5057 *extsubset = 0;
5058
5059 /*
5060 * Fetch the declaration for the qualified name
5061 */
5062 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5063 prefix = elem->ns->prefix;
5064
5065 if (prefix != NULL) {
5066 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5067 elem->name, prefix);
5068 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5069 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5070 elem->name, prefix);
5071 if ((elemDecl != NULL) && (extsubset != NULL))
5072 *extsubset = 1;
5073 }
5074 }
5075
5076 /*
5077 * Fetch the declaration for the non qualified name
5078 * This is "non-strict" validation should be done on the
5079 * full QName but in that case being flexible makes sense.
5080 */
5081 if (elemDecl == NULL) {
5082 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5083 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5084 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5085 if ((elemDecl != NULL) && (extsubset != NULL))
5086 *extsubset = 1;
5087 }
5088 }
5089 if (elemDecl == NULL) {
5090 VECTXT(ctxt, elem);
5091 VERROR(ctxt->userData, "No declaration for element %s\n",
5092 elem->name);
5093 }
5094 return(elemDecl);
5095}
5096
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005097#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005098/**
5099 * xmlValidatePushElement:
5100 * @ctxt: the validation context
5101 * @doc: a document instance
5102 * @elem: an element instance
5103 * @qname: the qualified name as appearing in the serialization
5104 *
5105 * Push a new element start on the validation stack.
5106 *
5107 * returns 1 if no validation problem was found or 0 otherwise
5108 */
5109int
5110xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5111 xmlNodePtr elem, const xmlChar *qname) {
5112 int ret = 1;
5113 xmlElementPtr eDecl;
5114 int extsubset = 0;
5115
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005116/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005117 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5118 xmlValidStatePtr state = ctxt->vstate;
5119 xmlElementPtr elemDecl;
5120
5121 /*
5122 * Check the new element agaisnt the content model of the new elem.
5123 */
5124 if (state->elemDecl != NULL) {
5125 elemDecl = state->elemDecl;
5126
5127 switch(elemDecl->etype) {
5128 case XML_ELEMENT_TYPE_UNDEFINED:
5129 ret = 0;
5130 break;
5131 case XML_ELEMENT_TYPE_EMPTY:
5132 VECTXT(ctxt, state->node);
5133 VERROR(ctxt->userData,
5134 "Element %s was declared EMPTY this one has content\n",
5135 state->node->name);
5136 ret = 0;
5137 break;
5138 case XML_ELEMENT_TYPE_ANY:
5139 /* I don't think anything is required then */
5140 break;
5141 case XML_ELEMENT_TYPE_MIXED:
5142 /* simple case of declared as #PCDATA */
5143 if ((elemDecl->content != NULL) &&
5144 (elemDecl->content->type ==
5145 XML_ELEMENT_CONTENT_PCDATA)) {
5146 VECTXT(ctxt, state->node);
5147 VERROR(ctxt->userData,
5148 "Element %s was declared #PCDATA but contains non text nodes\n",
5149 state->node->name);
5150 ret = 0;
5151 } else {
5152 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5153 qname);
5154 if (ret != 1) {
5155 VECTXT(ctxt, state->node);
5156 VERROR(ctxt->userData,
5157 "Element %s is not declared in %s list of possible children\n",
5158 qname, state->node->name);
5159 }
5160 }
5161 break;
5162 case XML_ELEMENT_TYPE_ELEMENT:
5163 /*
5164 * TODO:
5165 * VC: Standalone Document Declaration
5166 * - element types with element content, if white space
5167 * occurs directly within any instance of those types.
5168 */
5169 if (state->exec != NULL) {
5170 ret = xmlRegExecPushString(state->exec, qname, NULL);
5171 if (ret < 0) {
5172 VECTXT(ctxt, state->node);
5173 VERROR(ctxt->userData,
5174 "Element %s content does not follow the DTD\nMisplaced %s\n",
5175 state->node->name, qname);
5176 ret = 0;
5177 } else {
5178 ret = 1;
5179 }
5180 }
5181 break;
5182 }
5183 }
5184 }
5185 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5186 vstateVPush(ctxt, eDecl, elem);
5187 return(ret);
5188}
5189
5190/**
5191 * xmlValidatePushCData:
5192 * @ctxt: the validation context
5193 * @data: some character data read
5194 * @len: the lenght of the data
5195 *
5196 * check the CData parsed for validation in the current stack
5197 *
5198 * returns 1 if no validation problem was found or 0 otherwise
5199 */
5200int
5201xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5202 int ret = 1;
5203
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005204/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005205 if (len <= 0)
5206 return(ret);
5207 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5208 xmlValidStatePtr state = ctxt->vstate;
5209 xmlElementPtr elemDecl;
5210
5211 /*
5212 * Check the new element agaisnt the content model of the new elem.
5213 */
5214 if (state->elemDecl != NULL) {
5215 elemDecl = state->elemDecl;
5216
5217 switch(elemDecl->etype) {
5218 case XML_ELEMENT_TYPE_UNDEFINED:
5219 ret = 0;
5220 break;
5221 case XML_ELEMENT_TYPE_EMPTY:
5222 VECTXT(ctxt, state->node);
5223 VERROR(ctxt->userData,
5224 "Element %s was declared EMPTY this one has content\n",
5225 state->node->name);
5226 ret = 0;
5227 break;
5228 case XML_ELEMENT_TYPE_ANY:
5229 break;
5230 case XML_ELEMENT_TYPE_MIXED:
5231 break;
5232 case XML_ELEMENT_TYPE_ELEMENT:
5233 if (len > 0) {
5234 int i;
5235
5236 for (i = 0;i < len;i++) {
5237 if (!IS_BLANK(data[i])) {
5238 VECTXT(ctxt, state->node);
5239 VERROR(ctxt->userData,
5240 "Element %s content does not follow the DTD\nText not allowed\n",
5241 state->node->name);
5242 ret = 0;
5243 goto done;
5244 }
5245 }
5246 /*
5247 * TODO:
5248 * VC: Standalone Document Declaration
5249 * element types with element content, if white space
5250 * occurs directly within any instance of those types.
5251 */
5252 }
5253 break;
5254 }
5255 }
5256 }
5257done:
5258 return(ret);
5259}
5260
5261/**
5262 * xmlValidatePopElement:
5263 * @ctxt: the validation context
5264 * @doc: a document instance
5265 * @elem: an element instance
5266 * @qname: the qualified name as appearing in the serialization
5267 *
5268 * Pop the element end from the validation stack.
5269 *
5270 * returns 1 if no validation problem was found or 0 otherwise
5271 */
5272int
5273xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005274 xmlNodePtr elem ATTRIBUTE_UNUSED,
5275 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005276 int ret = 1;
5277
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005278/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005279 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5280 xmlValidStatePtr state = ctxt->vstate;
5281 xmlElementPtr elemDecl;
5282
5283 /*
5284 * Check the new element agaisnt the content model of the new elem.
5285 */
5286 if (state->elemDecl != NULL) {
5287 elemDecl = state->elemDecl;
5288
5289 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5290 if (state->exec != NULL) {
5291 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5292 if (ret == 0) {
5293 VECTXT(ctxt, state->node);
5294 VERROR(ctxt->userData,
5295 "Element %s content does not follow the DTD\nExpecting more child\n",
5296 state->node->name);
5297 } else {
5298 /*
5299 * previous validation errors should not generate
5300 * a new one here
5301 */
5302 ret = 1;
5303 }
5304 }
5305 }
5306 }
5307 vstateVPop(ctxt);
5308 }
5309 return(ret);
5310}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005311#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005312
5313/**
Owen Taylor3473f882001-02-23 17:55:21 +00005314 * xmlValidateOneElement:
5315 * @ctxt: the validation context
5316 * @doc: a document instance
5317 * @elem: an element instance
5318 *
5319 * Try to validate a single element and it's attributes,
5320 * basically it does the following checks as described by the
5321 * XML-1.0 recommendation:
5322 * - [ VC: Element Valid ]
5323 * - [ VC: Required Attribute ]
5324 * Then call xmlValidateOneAttribute() for each attribute present.
5325 *
5326 * The ID/IDREF checkings are done separately
5327 *
5328 * returns 1 if valid or 0 otherwise
5329 */
5330
5331int
5332xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5333 xmlNodePtr elem) {
5334 xmlElementPtr elemDecl = NULL;
5335 xmlElementContentPtr cont;
5336 xmlAttributePtr attr;
5337 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005338 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005339 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005340 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005341
5342 CHECK_DTD;
5343
5344 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005345 switch (elem->type) {
5346 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005347 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005348 VERROR(ctxt->userData,
5349 "Attribute element not expected here\n");
5350 return(0);
5351 case XML_TEXT_NODE:
5352 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005353 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005354 VERROR(ctxt->userData, "Text element has childs !\n");
5355 return(0);
5356 }
5357 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005358 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005359 VERROR(ctxt->userData, "Text element has attributes !\n");
5360 return(0);
5361 }
5362 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005363 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005364 VERROR(ctxt->userData, "Text element has namespace !\n");
5365 return(0);
5366 }
5367 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005368 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005369 VERROR(ctxt->userData,
5370 "Text element carries namespace definitions !\n");
5371 return(0);
5372 }
5373 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005374 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005375 VERROR(ctxt->userData,
5376 "Text element has no content !\n");
5377 return(0);
5378 }
5379 return(1);
5380 case XML_XINCLUDE_START:
5381 case XML_XINCLUDE_END:
5382 return(1);
5383 case XML_CDATA_SECTION_NODE:
5384 case XML_ENTITY_REF_NODE:
5385 case XML_PI_NODE:
5386 case XML_COMMENT_NODE:
5387 return(1);
5388 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005389 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005390 VERROR(ctxt->userData,
5391 "Entity element not expected here\n");
5392 return(0);
5393 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005394 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005395 VERROR(ctxt->userData,
5396 "Notation element not expected here\n");
5397 return(0);
5398 case XML_DOCUMENT_NODE:
5399 case XML_DOCUMENT_TYPE_NODE:
5400 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005401 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005402 VERROR(ctxt->userData,
5403 "Document element not expected here\n");
5404 return(0);
5405 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005406 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005407 VERROR(ctxt->userData,
5408 "\n");
5409 return(0);
5410 case XML_ELEMENT_NODE:
5411 break;
5412 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005413 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005414 VERROR(ctxt->userData,
5415 "unknown element type %d\n", elem->type);
5416 return(0);
5417 }
Owen Taylor3473f882001-02-23 17:55:21 +00005418
5419 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005420 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005421 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005422 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5423 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005424 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005425
Daniel Veillardea7751d2002-12-20 00:16:24 +00005426 /*
5427 * If vstateNr is not zero that means continuous validation is
5428 * activated, do not try to check the content model at that level.
5429 */
5430 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005431 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005432 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005433 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005434 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005435 VERROR(ctxt->userData, "No declaration for element %s\n",
5436 elem->name);
5437 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005438 case XML_ELEMENT_TYPE_EMPTY:
5439 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005440 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005441 VERROR(ctxt->userData,
5442 "Element %s was declared EMPTY this one has content\n",
5443 elem->name);
5444 ret = 0;
5445 }
5446 break;
5447 case XML_ELEMENT_TYPE_ANY:
5448 /* I don't think anything is required then */
5449 break;
5450 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005451
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005452 /* simple case of declared as #PCDATA */
5453 if ((elemDecl->content != NULL) &&
5454 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5455 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5456 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005457 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005458 VERROR(ctxt->userData,
5459 "Element %s was declared #PCDATA but contains non text nodes\n",
5460 elem->name);
5461 }
5462 break;
5463 }
Owen Taylor3473f882001-02-23 17:55:21 +00005464 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005465 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005466 while (child != NULL) {
5467 if (child->type == XML_ELEMENT_NODE) {
5468 name = child->name;
5469 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005470 xmlChar fn[50];
5471 xmlChar *fullname;
5472
5473 fullname = xmlBuildQName(child->name, child->ns->prefix,
5474 fn, 50);
5475 if (fullname == NULL)
5476 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005477 cont = elemDecl->content;
5478 while (cont != NULL) {
5479 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005480 if (xmlStrEqual(cont->name, fullname))
5481 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005482 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5483 (cont->c1 != NULL) &&
5484 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005485 if (xmlStrEqual(cont->c1->name, fullname))
5486 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005487 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5488 (cont->c1 == NULL) ||
5489 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5490 /* Internal error !!! */
5491 xmlGenericError(xmlGenericErrorContext,
5492 "Internal: MIXED struct bad\n");
5493 break;
5494 }
5495 cont = cont->c2;
5496 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005497 if ((fullname != fn) && (fullname != child->name))
5498 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005499 if (cont != NULL)
5500 goto child_ok;
5501 }
5502 cont = elemDecl->content;
5503 while (cont != NULL) {
5504 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5505 if (xmlStrEqual(cont->name, name)) break;
5506 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5507 (cont->c1 != NULL) &&
5508 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5509 if (xmlStrEqual(cont->c1->name, name)) break;
5510 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5511 (cont->c1 == NULL) ||
5512 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5513 /* Internal error !!! */
5514 xmlGenericError(xmlGenericErrorContext,
5515 "Internal: MIXED struct bad\n");
5516 break;
5517 }
5518 cont = cont->c2;
5519 }
5520 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005521 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005522 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005523 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005524 name, elem->name);
5525 ret = 0;
5526 }
5527 }
5528child_ok:
5529 child = child->next;
5530 }
5531 break;
5532 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005533 if ((doc->standalone == 1) && (extsubset == 1)) {
5534 /*
5535 * VC: Standalone Document Declaration
5536 * - element types with element content, if white space
5537 * occurs directly within any instance of those types.
5538 */
5539 child = elem->children;
5540 while (child != NULL) {
5541 if (child->type == XML_TEXT_NODE) {
5542 const xmlChar *content = child->content;
5543
5544 while (IS_BLANK(*content))
5545 content++;
5546 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005547 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005548 VERROR(ctxt->userData,
5549"standalone: %s declared in the external subset contains white spaces nodes\n",
5550 elem->name);
5551 ret = 0;
5552 break;
5553 }
5554 }
5555 child =child->next;
5556 }
5557 }
Owen Taylor3473f882001-02-23 17:55:21 +00005558 child = elem->children;
5559 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005560 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005561 if (tmp <= 0)
5562 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005563 break;
5564 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005565 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005566
5567 /* [ VC: Required Attribute ] */
5568 attr = elemDecl->attributes;
5569 while (attr != NULL) {
5570 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005571 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005572
Daniel Veillarde4301c82002-02-13 13:32:35 +00005573 if ((attr->prefix == NULL) &&
5574 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5575 xmlNsPtr ns;
5576
5577 ns = elem->nsDef;
5578 while (ns != NULL) {
5579 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005580 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005581 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005582 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005583 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5584 xmlNsPtr ns;
5585
5586 ns = elem->nsDef;
5587 while (ns != NULL) {
5588 if (xmlStrEqual(attr->name, ns->prefix))
5589 goto found;
5590 ns = ns->next;
5591 }
5592 } else {
5593 xmlAttrPtr attrib;
5594
5595 attrib = elem->properties;
5596 while (attrib != NULL) {
5597 if (xmlStrEqual(attrib->name, attr->name)) {
5598 if (attr->prefix != NULL) {
5599 xmlNsPtr nameSpace = attrib->ns;
5600
5601 if (nameSpace == NULL)
5602 nameSpace = elem->ns;
5603 /*
5604 * qualified names handling is problematic, having a
5605 * different prefix should be possible but DTDs don't
5606 * allow to define the URI instead of the prefix :-(
5607 */
5608 if (nameSpace == NULL) {
5609 if (qualified < 0)
5610 qualified = 0;
5611 } else if (!xmlStrEqual(nameSpace->prefix,
5612 attr->prefix)) {
5613 if (qualified < 1)
5614 qualified = 1;
5615 } else
5616 goto found;
5617 } else {
5618 /*
5619 * We should allow applications to define namespaces
5620 * for their application even if the DTD doesn't
5621 * carry one, otherwise, basically we would always
5622 * break.
5623 */
5624 goto found;
5625 }
5626 }
5627 attrib = attrib->next;
5628 }
Owen Taylor3473f882001-02-23 17:55:21 +00005629 }
5630 if (qualified == -1) {
5631 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005632 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005633 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005634 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005635 elem->name, attr->name);
5636 ret = 0;
5637 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005638 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005639 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005640 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005641 elem->name, attr->prefix,attr->name);
5642 ret = 0;
5643 }
5644 } else if (qualified == 0) {
5645 VWARNING(ctxt->userData,
5646 "Element %s required attribute %s:%s has no prefix\n",
5647 elem->name, attr->prefix,attr->name);
5648 } else if (qualified == 1) {
5649 VWARNING(ctxt->userData,
5650 "Element %s required attribute %s:%s has different prefix\n",
5651 elem->name, attr->prefix,attr->name);
5652 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005653 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5654 /*
5655 * Special tests checking #FIXED namespace declarations
5656 * have the right value since this is not done as an
5657 * attribute checking
5658 */
5659 if ((attr->prefix == NULL) &&
5660 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5661 xmlNsPtr ns;
5662
5663 ns = elem->nsDef;
5664 while (ns != NULL) {
5665 if (ns->prefix == NULL) {
5666 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005667 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005668 VERROR(ctxt->userData,
5669 "Element %s namespace name for default namespace does not match the DTD\n",
5670 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005671 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005672 }
5673 goto found;
5674 }
5675 ns = ns->next;
5676 }
5677 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5678 xmlNsPtr ns;
5679
5680 ns = elem->nsDef;
5681 while (ns != NULL) {
5682 if (xmlStrEqual(attr->name, ns->prefix)) {
5683 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005684 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005685 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005686 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005687 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005688 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005689 }
5690 goto found;
5691 }
5692 ns = ns->next;
5693 }
5694 }
Owen Taylor3473f882001-02-23 17:55:21 +00005695 }
5696found:
5697 attr = attr->nexth;
5698 }
5699 return(ret);
5700}
5701
5702/**
5703 * xmlValidateRoot:
5704 * @ctxt: the validation context
5705 * @doc: a document instance
5706 *
5707 * Try to validate a the root element
5708 * basically it does the following check as described by the
5709 * XML-1.0 recommendation:
5710 * - [ VC: Root Element Type ]
5711 * it doesn't try to recurse or apply other check to the element
5712 *
5713 * returns 1 if valid or 0 otherwise
5714 */
5715
5716int
5717xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5718 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00005719 int ret;
5720
Owen Taylor3473f882001-02-23 17:55:21 +00005721 if (doc == NULL) return(0);
5722
5723 root = xmlDocGetRootElement(doc);
5724 if ((root == NULL) || (root->name == NULL)) {
5725 VERROR(ctxt->userData, "Not valid: no root element\n");
5726 return(0);
5727 }
5728
5729 /*
5730 * When doing post validation against a separate DTD, those may
5731 * no internal subset has been generated
5732 */
5733 if ((doc->intSubset != NULL) &&
5734 (doc->intSubset->name != NULL)) {
5735 /*
5736 * Check first the document root against the NQName
5737 */
5738 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5739 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005740 xmlChar fn[50];
5741 xmlChar *fullname;
5742
5743 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
5744 if (fullname == NULL) {
5745 VERROR(ctxt->userData, "Out of memory\n");
5746 return(0);
5747 }
5748 ret = xmlStrEqual(doc->intSubset->name, fullname);
5749 if ((fullname != fn) && (fullname != root->name))
5750 xmlFree(fullname);
5751 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00005752 goto name_ok;
5753 }
5754 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5755 (xmlStrEqual(root->name, BAD_CAST "html")))
5756 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005757 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005758 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005759 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005760 root->name, doc->intSubset->name);
5761 return(0);
5762
5763 }
5764 }
5765name_ok:
5766 return(1);
5767}
5768
5769
5770/**
5771 * xmlValidateElement:
5772 * @ctxt: the validation context
5773 * @doc: a document instance
5774 * @elem: an element instance
5775 *
5776 * Try to validate the subtree under an element
5777 *
5778 * returns 1 if valid or 0 otherwise
5779 */
5780
5781int
5782xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5783 xmlNodePtr child;
5784 xmlAttrPtr attr;
5785 xmlChar *value;
5786 int ret = 1;
5787
5788 if (elem == NULL) return(0);
5789
5790 /*
5791 * XInclude elements were added after parsing in the infoset,
5792 * they don't really mean anything validation wise.
5793 */
5794 if ((elem->type == XML_XINCLUDE_START) ||
5795 (elem->type == XML_XINCLUDE_END))
5796 return(1);
5797
5798 CHECK_DTD;
5799
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005800 /*
5801 * Entities references have to be handled separately
5802 */
5803 if (elem->type == XML_ENTITY_REF_NODE) {
5804 return(1);
5805 }
5806
Owen Taylor3473f882001-02-23 17:55:21 +00005807 ret &= xmlValidateOneElement(ctxt, doc, elem);
5808 attr = elem->properties;
5809 while(attr != NULL) {
5810 value = xmlNodeListGetString(doc, attr->children, 0);
5811 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5812 if (value != NULL)
5813 xmlFree(value);
5814 attr= attr->next;
5815 }
5816 child = elem->children;
5817 while (child != NULL) {
5818 ret &= xmlValidateElement(ctxt, doc, child);
5819 child = child->next;
5820 }
5821
5822 return(ret);
5823}
5824
Daniel Veillard8730c562001-02-26 10:49:57 +00005825/**
5826 * xmlValidateRef:
5827 * @ref: A reference to be validated
5828 * @ctxt: Validation context
5829 * @name: Name of ID we are searching for
5830 *
5831 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005832static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005833xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005834 const xmlChar *name) {
5835 xmlAttrPtr id;
5836 xmlAttrPtr attr;
5837
5838 if (ref == NULL)
5839 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005840 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00005841 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005842 attr = ref->attr;
5843 if (attr == NULL) {
5844 xmlChar *dup, *str = NULL, *cur, save;
5845
5846 dup = xmlStrdup(name);
5847 if (dup == NULL) {
5848 ctxt->valid = 0;
5849 return;
5850 }
5851 cur = dup;
5852 while (*cur != 0) {
5853 str = cur;
5854 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5855 save = *cur;
5856 *cur = 0;
5857 id = xmlGetID(ctxt->doc, str);
5858 if (id == NULL) {
5859 VERROR(ctxt->userData,
5860 "attribute %s line %d references an unknown ID \"%s\"\n",
5861 ref->name, ref->lineno, str);
5862 ctxt->valid = 0;
5863 }
5864 if (save == 0)
5865 break;
5866 *cur = save;
5867 while (IS_BLANK(*cur)) cur++;
5868 }
5869 xmlFree(dup);
5870 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00005871 id = xmlGetID(ctxt->doc, name);
5872 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005873 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005874 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005875 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005876 attr->name, name);
5877 ctxt->valid = 0;
5878 }
5879 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5880 xmlChar *dup, *str = NULL, *cur, save;
5881
5882 dup = xmlStrdup(name);
5883 if (dup == NULL) {
5884 ctxt->valid = 0;
5885 return;
5886 }
5887 cur = dup;
5888 while (*cur != 0) {
5889 str = cur;
5890 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5891 save = *cur;
5892 *cur = 0;
5893 id = xmlGetID(ctxt->doc, str);
5894 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005895 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005896 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005897 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005898 attr->name, str);
5899 ctxt->valid = 0;
5900 }
5901 if (save == 0)
5902 break;
5903 *cur = save;
5904 while (IS_BLANK(*cur)) cur++;
5905 }
5906 xmlFree(dup);
5907 }
5908}
5909
5910/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005911 * xmlWalkValidateList:
5912 * @data: Contents of current link
5913 * @user: Value supplied by the user
5914 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005915 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005916 */
5917static int
5918xmlWalkValidateList(const void *data, const void *user)
5919{
5920 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5921 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5922 return 1;
5923}
5924
5925/**
5926 * xmlValidateCheckRefCallback:
5927 * @ref_list: List of references
5928 * @ctxt: Validation context
5929 * @name: Name of ID we are searching for
5930 *
5931 */
5932static void
5933xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5934 const xmlChar *name) {
5935 xmlValidateMemo memo;
5936
5937 if (ref_list == NULL)
5938 return;
5939 memo.ctxt = ctxt;
5940 memo.name = name;
5941
5942 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5943
5944}
5945
5946/**
Owen Taylor3473f882001-02-23 17:55:21 +00005947 * xmlValidateDocumentFinal:
5948 * @ctxt: the validation context
5949 * @doc: a document instance
5950 *
5951 * Does the final step for the document validation once all the
5952 * incremental validation steps have been completed
5953 *
5954 * basically it does the following checks described by the XML Rec
5955 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00005956 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00005957 *
5958 * returns 1 if valid or 0 otherwise
5959 */
5960
5961int
5962xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5963 xmlRefTablePtr table;
5964
5965 if (doc == NULL) {
5966 xmlGenericError(xmlGenericErrorContext,
5967 "xmlValidateDocumentFinal: doc == NULL\n");
5968 return(0);
5969 }
5970
5971 /*
5972 * Check all the NOTATION/NOTATIONS attributes
5973 */
5974 /*
5975 * Check all the ENTITY/ENTITIES attributes definition for validity
5976 */
5977 /*
5978 * Check all the IDREF/IDREFS attributes definition for validity
5979 */
5980 table = (xmlRefTablePtr) doc->refs;
5981 ctxt->doc = doc;
5982 ctxt->valid = 1;
5983 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
5984 return(ctxt->valid);
5985}
5986
5987/**
5988 * xmlValidateDtd:
5989 * @ctxt: the validation context
5990 * @doc: a document instance
5991 * @dtd: a dtd instance
5992 *
5993 * Try to validate the document against the dtd instance
5994 *
5995 * basically it does check all the definitions in the DtD.
5996 *
5997 * returns 1 if valid or 0 otherwise
5998 */
5999
6000int
6001xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
6002 int ret;
6003 xmlDtdPtr oldExt;
6004 xmlNodePtr root;
6005
6006 if (dtd == NULL) return(0);
6007 if (doc == NULL) return(0);
6008 oldExt = doc->extSubset;
6009 doc->extSubset = dtd;
6010 ret = xmlValidateRoot(ctxt, doc);
6011 if (ret == 0) {
6012 doc->extSubset = oldExt;
6013 return(ret);
6014 }
6015 if (doc->ids != NULL) {
6016 xmlFreeIDTable(doc->ids);
6017 doc->ids = NULL;
6018 }
6019 if (doc->refs != NULL) {
6020 xmlFreeRefTable(doc->refs);
6021 doc->refs = NULL;
6022 }
6023 root = xmlDocGetRootElement(doc);
6024 ret = xmlValidateElement(ctxt, doc, root);
6025 ret &= xmlValidateDocumentFinal(ctxt, doc);
6026 doc->extSubset = oldExt;
6027 return(ret);
6028}
6029
Daniel Veillard56a4cb82001-03-24 17:00:36 +00006030static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006031xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
6032 const xmlChar *name ATTRIBUTE_UNUSED) {
6033 if (cur == NULL)
6034 return;
6035 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6036 xmlChar *notation = cur->content;
6037
Daniel Veillard878eab02002-02-19 13:46:09 +00006038 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006039 int ret;
6040
6041 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6042 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006043 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006044 }
6045 }
6046 }
6047}
6048
6049static void
Owen Taylor3473f882001-02-23 17:55:21 +00006050xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006051 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006052 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006053 xmlDocPtr doc;
6054 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006055
Owen Taylor3473f882001-02-23 17:55:21 +00006056 if (cur == NULL)
6057 return;
6058 switch (cur->atype) {
6059 case XML_ATTRIBUTE_CDATA:
6060 case XML_ATTRIBUTE_ID:
6061 case XML_ATTRIBUTE_IDREF :
6062 case XML_ATTRIBUTE_IDREFS:
6063 case XML_ATTRIBUTE_NMTOKEN:
6064 case XML_ATTRIBUTE_NMTOKENS:
6065 case XML_ATTRIBUTE_ENUMERATION:
6066 break;
6067 case XML_ATTRIBUTE_ENTITY:
6068 case XML_ATTRIBUTE_ENTITIES:
6069 case XML_ATTRIBUTE_NOTATION:
6070 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006071
6072 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6073 cur->atype, cur->defaultValue);
6074 if ((ret == 0) && (ctxt->valid == 1))
6075 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006076 }
6077 if (cur->tree != NULL) {
6078 xmlEnumerationPtr tree = cur->tree;
6079 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006080 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006081 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006082 if ((ret == 0) && (ctxt->valid == 1))
6083 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006084 tree = tree->next;
6085 }
6086 }
6087 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006088 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6089 doc = cur->doc;
6090 if ((doc == NULL) || (cur->elem == NULL)) {
6091 VERROR(ctxt->userData,
6092 "xmlValidateAttributeCallback(%s): internal error\n",
6093 cur->name);
6094 return;
6095 }
6096 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6097 if (elem == NULL)
6098 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6099 if (elem == NULL) {
6100 VERROR(ctxt->userData,
6101 "attribute %s: could not find decl for element %s\n",
6102 cur->name, cur->elem);
6103 return;
6104 }
6105 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6106 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006107 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00006108 cur->name, cur->elem);
6109 ctxt->valid = 0;
6110 }
6111 }
Owen Taylor3473f882001-02-23 17:55:21 +00006112}
6113
6114/**
6115 * xmlValidateDtdFinal:
6116 * @ctxt: the validation context
6117 * @doc: a document instance
6118 *
6119 * Does the final step for the dtds validation once all the
6120 * subsets have been parsed
6121 *
6122 * basically it does the following checks described by the XML Rec
6123 * - check that ENTITY and ENTITIES type attributes default or
6124 * possible values matches one of the defined entities.
6125 * - check that NOTATION type attributes default or
6126 * possible values matches one of the defined notations.
6127 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006128 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006129 */
6130
6131int
6132xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006133 xmlDtdPtr dtd;
6134 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006135 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006136
6137 if (doc == NULL) return(0);
6138 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6139 return(0);
6140 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006141 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006142 dtd = doc->intSubset;
6143 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6144 table = (xmlAttributeTablePtr) dtd->attributes;
6145 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006146 }
6147 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006148 entities = (xmlEntitiesTablePtr) dtd->entities;
6149 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6150 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006151 }
6152 dtd = doc->extSubset;
6153 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6154 table = (xmlAttributeTablePtr) dtd->attributes;
6155 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006156 }
6157 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006158 entities = (xmlEntitiesTablePtr) dtd->entities;
6159 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6160 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006161 }
6162 return(ctxt->valid);
6163}
6164
6165/**
6166 * xmlValidateDocument:
6167 * @ctxt: the validation context
6168 * @doc: a document instance
6169 *
6170 * Try to validate the document instance
6171 *
6172 * basically it does the all the checks described by the XML Rec
6173 * i.e. validates the internal and external subset (if present)
6174 * and validate the document tree.
6175 *
6176 * returns 1 if valid or 0 otherwise
6177 */
6178
6179int
6180xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6181 int ret;
6182 xmlNodePtr root;
6183
Daniel Veillard2fd85422002-10-16 14:32:41 +00006184 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6185 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00006186 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006187 }
Owen Taylor3473f882001-02-23 17:55:21 +00006188 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6189 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6190 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6191 doc->intSubset->SystemID);
6192 if (doc->extSubset == NULL) {
6193 if (doc->intSubset->SystemID != NULL) {
6194 VERROR(ctxt->userData,
6195 "Could not load the external subset \"%s\"\n",
6196 doc->intSubset->SystemID);
6197 } else {
6198 VERROR(ctxt->userData,
6199 "Could not load the external subset \"%s\"\n",
6200 doc->intSubset->ExternalID);
6201 }
6202 return(0);
6203 }
6204 }
6205
6206 if (doc->ids != NULL) {
6207 xmlFreeIDTable(doc->ids);
6208 doc->ids = NULL;
6209 }
6210 if (doc->refs != NULL) {
6211 xmlFreeRefTable(doc->refs);
6212 doc->refs = NULL;
6213 }
6214 ret = xmlValidateDtdFinal(ctxt, doc);
6215 if (!xmlValidateRoot(ctxt, doc)) return(0);
6216
6217 root = xmlDocGetRootElement(doc);
6218 ret &= xmlValidateElement(ctxt, doc, root);
6219 ret &= xmlValidateDocumentFinal(ctxt, doc);
6220 return(ret);
6221}
6222
6223
6224/************************************************************************
6225 * *
6226 * Routines for dynamic validation editing *
6227 * *
6228 ************************************************************************/
6229
6230/**
6231 * xmlValidGetPotentialChildren:
6232 * @ctree: an element content tree
6233 * @list: an array to store the list of child names
6234 * @len: a pointer to the number of element in the list
6235 * @max: the size of the array
6236 *
6237 * Build/extend a list of potential children allowed by the content tree
6238 *
6239 * returns the number of element in the list, or -1 in case of error.
6240 */
6241
6242int
6243xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6244 int *len, int max) {
6245 int i;
6246
6247 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6248 return(-1);
6249 if (*len >= max) return(*len);
6250
6251 switch (ctree->type) {
6252 case XML_ELEMENT_CONTENT_PCDATA:
6253 for (i = 0; i < *len;i++)
6254 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6255 list[(*len)++] = BAD_CAST "#PCDATA";
6256 break;
6257 case XML_ELEMENT_CONTENT_ELEMENT:
6258 for (i = 0; i < *len;i++)
6259 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6260 list[(*len)++] = ctree->name;
6261 break;
6262 case XML_ELEMENT_CONTENT_SEQ:
6263 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6264 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6265 break;
6266 case XML_ELEMENT_CONTENT_OR:
6267 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6268 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6269 break;
6270 }
6271
6272 return(*len);
6273}
6274
6275/**
6276 * xmlValidGetValidElements:
6277 * @prev: an element to insert after
6278 * @next: an element to insert next
6279 * @list: an array to store the list of child names
6280 * @max: the size of the array
6281 *
6282 * This function returns the list of authorized children to insert
6283 * within an existing tree while respecting the validity constraints
6284 * forced by the Dtd. The insertion point is defined using @prev and
6285 * @next in the following ways:
6286 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6287 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6288 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6289 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6290 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6291 *
6292 * pointers to the element names are inserted at the beginning of the array
6293 * and do not need to be freed.
6294 *
6295 * returns the number of element in the list, or -1 in case of error. If
6296 * the function returns the value @max the caller is invited to grow the
6297 * receiving array and retry.
6298 */
6299
6300int
6301xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6302 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006303 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006304 int nb_valid_elements = 0;
6305 const xmlChar *elements[256];
6306 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006307 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006308
6309 xmlNode *ref_node;
6310 xmlNode *parent;
6311 xmlNode *test_node;
6312
6313 xmlNode *prev_next;
6314 xmlNode *next_prev;
6315 xmlNode *parent_childs;
6316 xmlNode *parent_last;
6317
6318 xmlElement *element_desc;
6319
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006320 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006321
Owen Taylor3473f882001-02-23 17:55:21 +00006322 if (prev == NULL && next == NULL)
6323 return(-1);
6324
6325 if (list == NULL) return(-1);
6326 if (max <= 0) return(-1);
6327
6328 nb_valid_elements = 0;
6329 ref_node = prev ? prev : next;
6330 parent = ref_node->parent;
6331
6332 /*
6333 * Retrieves the parent element declaration
6334 */
6335 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6336 parent->name);
6337 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6338 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6339 parent->name);
6340 if (element_desc == NULL) return(-1);
6341
6342 /*
6343 * Do a backup of the current tree structure
6344 */
6345 prev_next = prev ? prev->next : NULL;
6346 next_prev = next ? next->prev : NULL;
6347 parent_childs = parent->children;
6348 parent_last = parent->last;
6349
6350 /*
6351 * Creates a dummy node and insert it into the tree
6352 */
6353 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6354 test_node->doc = ref_node->doc;
6355 test_node->parent = parent;
6356 test_node->prev = prev;
6357 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006358 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006359
6360 if (prev) prev->next = test_node;
6361 else parent->children = test_node;
6362
6363 if (next) next->prev = test_node;
6364 else parent->last = test_node;
6365
6366 /*
6367 * Insert each potential child node and check if the parent is
6368 * still valid
6369 */
6370 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6371 elements, &nb_elements, 256);
6372
6373 for (i = 0;i < nb_elements;i++) {
6374 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006375 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006376 int j;
6377
6378 for (j = 0; j < nb_valid_elements;j++)
6379 if (xmlStrEqual(elements[i], list[j])) break;
6380 list[nb_valid_elements++] = elements[i];
6381 if (nb_valid_elements >= max) break;
6382 }
6383 }
6384
6385 /*
6386 * Restore the tree structure
6387 */
6388 if (prev) prev->next = prev_next;
6389 if (next) next->prev = next_prev;
6390 parent->children = parent_childs;
6391 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006392
6393 /*
6394 * Free up the dummy node
6395 */
6396 test_node->name = name;
6397 xmlFreeNode(test_node);
6398
Owen Taylor3473f882001-02-23 17:55:21 +00006399 return(nb_valid_elements);
6400}