blob: 2b5e05179f8fb8eeec89a2ea53dce039e524ab0a [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
Owen Taylor3473f882001-02-23 17:55:21 +0000253#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000254static void
255xmlValidPrintNode(xmlNodePtr cur) {
256 if (cur == NULL) {
257 xmlGenericError(xmlGenericErrorContext, "null");
258 return;
259 }
260 switch (cur->type) {
261 case XML_ELEMENT_NODE:
262 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
263 break;
264 case XML_TEXT_NODE:
265 xmlGenericError(xmlGenericErrorContext, "text ");
266 break;
267 case XML_CDATA_SECTION_NODE:
268 xmlGenericError(xmlGenericErrorContext, "cdata ");
269 break;
270 case XML_ENTITY_REF_NODE:
271 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
272 break;
273 case XML_PI_NODE:
274 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
275 break;
276 case XML_COMMENT_NODE:
277 xmlGenericError(xmlGenericErrorContext, "comment ");
278 break;
279 case XML_ATTRIBUTE_NODE:
280 xmlGenericError(xmlGenericErrorContext, "?attr? ");
281 break;
282 case XML_ENTITY_NODE:
283 xmlGenericError(xmlGenericErrorContext, "?ent? ");
284 break;
285 case XML_DOCUMENT_NODE:
286 xmlGenericError(xmlGenericErrorContext, "?doc? ");
287 break;
288 case XML_DOCUMENT_TYPE_NODE:
289 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
290 break;
291 case XML_DOCUMENT_FRAG_NODE:
292 xmlGenericError(xmlGenericErrorContext, "?frag? ");
293 break;
294 case XML_NOTATION_NODE:
295 xmlGenericError(xmlGenericErrorContext, "?nota? ");
296 break;
297 case XML_HTML_DOCUMENT_NODE:
298 xmlGenericError(xmlGenericErrorContext, "?html? ");
299 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000300#ifdef LIBXML_DOCB_ENABLED
301 case XML_DOCB_DOCUMENT_NODE:
302 xmlGenericError(xmlGenericErrorContext, "?docb? ");
303 break;
304#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000305 case XML_DTD_NODE:
306 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
307 break;
308 case XML_ELEMENT_DECL:
309 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
310 break;
311 case XML_ATTRIBUTE_DECL:
312 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
313 break;
314 case XML_ENTITY_DECL:
315 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
316 break;
317 case XML_NAMESPACE_DECL:
318 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
319 break;
320 case XML_XINCLUDE_START:
321 xmlGenericError(xmlGenericErrorContext, "incstart ");
322 break;
323 case XML_XINCLUDE_END:
324 xmlGenericError(xmlGenericErrorContext, "incend ");
325 break;
326 }
327}
328
329static void
330xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000331 if (cur == NULL)
332 xmlGenericError(xmlGenericErrorContext, "null ");
333 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000334 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000335 cur = cur->next;
336 }
337}
338
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000339static void
340xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Daniel Veillard83391282003-03-06 21:37:30 +0000341 char expr[5000];
Owen Taylor3473f882001-02-23 17:55:21 +0000342
343 expr[0] = 0;
344 xmlGenericError(xmlGenericErrorContext, "valid: ");
345 xmlValidPrintNodeList(cur);
346 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000347 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000348 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
349}
350
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000351static void
352xmlValidDebugState(xmlValidStatePtr state) {
353 xmlGenericError(xmlGenericErrorContext, "(");
354 if (state->cont == NULL)
355 xmlGenericError(xmlGenericErrorContext, "null,");
356 else
357 switch (state->cont->type) {
358 case XML_ELEMENT_CONTENT_PCDATA:
359 xmlGenericError(xmlGenericErrorContext, "pcdata,");
360 break;
361 case XML_ELEMENT_CONTENT_ELEMENT:
362 xmlGenericError(xmlGenericErrorContext, "%s,",
363 state->cont->name);
364 break;
365 case XML_ELEMENT_CONTENT_SEQ:
366 xmlGenericError(xmlGenericErrorContext, "seq,");
367 break;
368 case XML_ELEMENT_CONTENT_OR:
369 xmlGenericError(xmlGenericErrorContext, "or,");
370 break;
371 }
372 xmlValidPrintNode(state->node);
373 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
374 state->depth, state->occurs, state->state);
375}
376
377static void
378xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
379 int i, j;
380
381 xmlGenericError(xmlGenericErrorContext, "state: ");
382 xmlValidDebugState(ctxt->vstate);
383 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
384 ctxt->vstateNr - 1);
385 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
386 xmlValidDebugState(&ctxt->vstateTab[j]);
387 xmlGenericError(xmlGenericErrorContext, "\n");
388}
389
390/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000391#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000392 *****/
393
394#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000395#define DEBUG_VALID_MSG(m) \
396 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
397
Owen Taylor3473f882001-02-23 17:55:21 +0000398#else
399#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000400#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000401#endif
402
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000403/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000404
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000405#define VECTXT(ctxt, node) \
406 if ((ctxt != NULL) && (ctxt->error != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000407 (node != NULL)) { \
408 xmlChar *base = xmlNodeGetBase(NULL,node); \
409 if (base != NULL) { \
410 ctxt->error(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000411 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000412 xmlFree(base); \
413 } else \
414 ctxt->error(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000415 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000416 }
417
418#define VWCTXT(ctxt, node) \
419 if ((ctxt != NULL) && (ctxt->warning != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000420 (node != NULL)) { \
421 xmlChar *base = xmlNodeGetBase(NULL,node); \
422 if (base != NULL) { \
423 ctxt->warning(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000424 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000425 xmlFree(base); \
426 } else \
427 ctxt->warning(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000428 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000429 }
430
Owen Taylor3473f882001-02-23 17:55:21 +0000431#define CHECK_DTD \
432 if (doc == NULL) return(0); \
433 else if ((doc->intSubset == NULL) && \
434 (doc->extSubset == NULL)) return(0)
435
Daniel Veillarda10efa82001-04-18 13:09:01 +0000436static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
437 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000438xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
439
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000440#ifdef LIBXML_REGEXP_ENABLED
441
442/************************************************************************
443 * *
444 * Content model validation based on the regexps *
445 * *
446 ************************************************************************/
447
448/**
449 * xmlValidBuildAContentModel:
450 * @content: the content model
451 * @ctxt: the schema parser context
452 * @name: the element name whose content is being built
453 *
454 * Generate the automata sequence needed for that type
455 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000456 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000457 */
458static int
459xmlValidBuildAContentModel(xmlElementContentPtr content,
460 xmlValidCtxtPtr ctxt,
461 const xmlChar *name) {
462 if (content == NULL) {
463 VERROR(ctxt->userData,
464 "Found unexpected type = NULL in %s content model\n", name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000465 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000466 }
467 switch (content->type) {
468 case XML_ELEMENT_CONTENT_PCDATA:
469 VERROR(ctxt->userData, "ContentModel found PCDATA for element %s\n",
470 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000471 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000472 break;
473 case XML_ELEMENT_CONTENT_ELEMENT: {
474 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillardc00cda82003-04-07 10:22:39 +0000475 xmlChar fn[50];
476 xmlChar *fullname;
477
478 fullname = xmlBuildQName(content->name, content->prefix, fn, 50);
479 if (fullname == NULL) {
480 VERROR(ctxt->userData, "Out of memory\n");
481 return(0);
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000482 }
483
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000484 switch (content->ocur) {
485 case XML_ELEMENT_CONTENT_ONCE:
486 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000487 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000488 break;
489 case XML_ELEMENT_CONTENT_OPT:
490 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000491 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000492 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
493 break;
494 case XML_ELEMENT_CONTENT_PLUS:
495 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000496 ctxt->state, NULL, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000497 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000498 ctxt->state, fullname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000499 break;
500 case XML_ELEMENT_CONTENT_MULT:
501 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillardc00cda82003-04-07 10:22:39 +0000502 ctxt->state, fullname, NULL);
Daniel Veillard57e79b32003-02-04 15:33:12 +0000503 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, ctxt->state,
504 NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000505 break;
506 }
Daniel Veillardc00cda82003-04-07 10:22:39 +0000507 if ((fullname != fn) && (fullname != content->name))
508 xmlFree(fullname);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000509 break;
510 }
511 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000512 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000513 xmlElementContentOccur ocur;
514
515 /*
516 * Simply iterate over the content
517 */
518 oldstate = ctxt->state;
519 ocur = content->ocur;
Daniel Veillardf4be0182003-02-24 19:54:33 +0000520 if (ocur != XML_ELEMENT_CONTENT_ONCE) {
521 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldstate, NULL);
522 oldstate = ctxt->state;
523 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000524 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000525 xmlValidBuildAContentModel(content->c1, ctxt, name);
526 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000527 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
528 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
529 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000530 oldend = ctxt->state;
531 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000532 switch (ocur) {
533 case XML_ELEMENT_CONTENT_ONCE:
534 break;
535 case XML_ELEMENT_CONTENT_OPT:
536 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
537 break;
538 case XML_ELEMENT_CONTENT_MULT:
539 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000540 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000541 break;
542 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000543 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000544 break;
545 }
546 break;
547 }
548 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000549 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000550 xmlElementContentOccur ocur;
551
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000552 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000553 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
554 (ocur == XML_ELEMENT_CONTENT_MULT)) {
555 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
556 ctxt->state, NULL);
557 }
558 oldstate = ctxt->state;
559 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000560
561 /*
562 * iterate over the subtypes and remerge the end with an
563 * epsilon transition
564 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000565 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000566 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000567 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000568 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000569 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000570 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
571 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000572 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000573 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000574 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
575 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000576 switch (ocur) {
577 case XML_ELEMENT_CONTENT_ONCE:
578 break;
579 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000580 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000581 break;
582 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000583 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
584 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000585 break;
586 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000587 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000588 break;
589 }
590 break;
591 }
592 default:
593 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
594 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000595 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000596 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000597 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000598}
599/**
600 * xmlValidBuildContentModel:
601 * @ctxt: a validation context
602 * @elem: an element declaration node
603 *
604 * (Re)Build the automata associated to the content model of this
605 * element
606 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000607 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000608 */
609int
610xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
611 xmlAutomataStatePtr start;
612
613 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000614 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000615 if (elem->type != XML_ELEMENT_DECL)
616 return(0);
617 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
618 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000619 /* TODO: should we rebuild in this case ? */
Daniel Veillardec498e12003-02-05 11:01:50 +0000620 if (elem->contModel != NULL) {
621 if (!xmlRegexpIsDeterminist(elem->contModel)) {
622 ctxt->valid = 0;
623 return(0);
624 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000625 return(1);
Daniel Veillardec498e12003-02-05 11:01:50 +0000626 }
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000627
628 ctxt->am = xmlNewAutomata();
629 if (ctxt->am == NULL) {
630 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
631 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000632 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000633 }
634 start = ctxt->state = xmlAutomataGetInitState(ctxt->am);
635 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
636 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000637 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillard23e73572002-09-19 19:56:43 +0000638 if (!xmlRegexpIsDeterminist(elem->contModel)) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000639 char expr[5000];
640 expr[0] = 0;
641 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
642 VERROR(ctxt->userData, "Content model of %s is not determinist: %s\n",
643 elem->name, expr);
644#ifdef DEBUG_REGEXP_ALGO
645 xmlRegexpPrint(stderr, elem->contModel);
646#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000647 ctxt->valid = 0;
Daniel Veillardec498e12003-02-05 11:01:50 +0000648 ctxt->state = NULL;
649 xmlFreeAutomata(ctxt->am);
650 ctxt->am = NULL;
651 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000652 }
653 ctxt->state = NULL;
654 xmlFreeAutomata(ctxt->am);
655 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000656 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000657}
658
659#endif /* LIBXML_REGEXP_ENABLED */
660
Owen Taylor3473f882001-02-23 17:55:21 +0000661/****************************************************************
662 * *
663 * Util functions for data allocation/deallocation *
664 * *
665 ****************************************************************/
666
667/**
668 * xmlNewElementContent:
669 * @name: the subelement name or NULL
670 * @type: the type of element content decl
671 *
672 * Allocate an element content structure.
673 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000674 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000675 */
676xmlElementContentPtr
677xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
678 xmlElementContentPtr ret;
679
680 switch(type) {
681 case XML_ELEMENT_CONTENT_ELEMENT:
682 if (name == NULL) {
683 xmlGenericError(xmlGenericErrorContext,
684 "xmlNewElementContent : name == NULL !\n");
685 }
686 break;
687 case XML_ELEMENT_CONTENT_PCDATA:
688 case XML_ELEMENT_CONTENT_SEQ:
689 case XML_ELEMENT_CONTENT_OR:
690 if (name != NULL) {
691 xmlGenericError(xmlGenericErrorContext,
692 "xmlNewElementContent : name != NULL !\n");
693 }
694 break;
695 default:
696 xmlGenericError(xmlGenericErrorContext,
697 "xmlNewElementContent: unknown type %d\n", type);
698 return(NULL);
699 }
700 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
701 if (ret == NULL) {
702 xmlGenericError(xmlGenericErrorContext,
703 "xmlNewElementContent : out of memory!\n");
704 return(NULL);
705 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000706 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000707 ret->type = type;
708 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000709 if (name != NULL) {
710 xmlChar *prefix = NULL;
711 ret->name = xmlSplitQName2(name, &prefix);
712 if (ret->name == NULL)
713 ret->name = xmlStrdup(name);
714 ret->prefix = prefix;
715 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000716 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000717 ret->prefix = NULL;
718 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000719 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000720 return(ret);
721}
722
723/**
724 * xmlCopyElementContent:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000725 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +0000726 *
727 * Build a copy of an element content description.
728 *
729 * Returns the new xmlElementContentPtr or NULL in case of error.
730 */
731xmlElementContentPtr
732xmlCopyElementContent(xmlElementContentPtr cur) {
733 xmlElementContentPtr ret;
734
735 if (cur == NULL) return(NULL);
736 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
737 if (ret == NULL) {
738 xmlGenericError(xmlGenericErrorContext,
739 "xmlCopyElementContent : out of memory\n");
740 return(NULL);
741 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000742 if (cur->prefix != NULL)
743 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000744 ret->ocur = cur->ocur;
745 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000746 if (ret->c1 != NULL)
747 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000748 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000749 if (ret->c2 != NULL)
750 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000751 return(ret);
752}
753
754/**
755 * xmlFreeElementContent:
756 * @cur: the element content tree to free
757 *
758 * Free an element content structure. This is a recursive call !
759 */
760void
761xmlFreeElementContent(xmlElementContentPtr cur) {
762 if (cur == NULL) return;
763 switch (cur->type) {
764 case XML_ELEMENT_CONTENT_PCDATA:
765 case XML_ELEMENT_CONTENT_ELEMENT:
766 case XML_ELEMENT_CONTENT_SEQ:
767 case XML_ELEMENT_CONTENT_OR:
768 break;
769 default:
770 xmlGenericError(xmlGenericErrorContext,
771 "xmlFreeElementContent : type %d\n", cur->type);
772 return;
773 }
774 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
775 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
776 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000777 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000778 xmlFree(cur);
779}
780
781/**
782 * xmlDumpElementContent:
783 * @buf: An XML buffer
784 * @content: An element table
785 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
786 *
787 * This will dump the content of the element table as an XML DTD definition
788 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000789static void
Owen Taylor3473f882001-02-23 17:55:21 +0000790xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
791 if (content == NULL) return;
792
793 if (glob) xmlBufferWriteChar(buf, "(");
794 switch (content->type) {
795 case XML_ELEMENT_CONTENT_PCDATA:
796 xmlBufferWriteChar(buf, "#PCDATA");
797 break;
798 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000799 if (content->prefix != NULL) {
800 xmlBufferWriteCHAR(buf, content->prefix);
801 xmlBufferWriteChar(buf, ":");
802 }
Owen Taylor3473f882001-02-23 17:55:21 +0000803 xmlBufferWriteCHAR(buf, content->name);
804 break;
805 case XML_ELEMENT_CONTENT_SEQ:
806 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
807 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
808 xmlDumpElementContent(buf, content->c1, 1);
809 else
810 xmlDumpElementContent(buf, content->c1, 0);
811 xmlBufferWriteChar(buf, " , ");
812 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
813 xmlDumpElementContent(buf, content->c2, 1);
814 else
815 xmlDumpElementContent(buf, content->c2, 0);
816 break;
817 case XML_ELEMENT_CONTENT_OR:
818 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
819 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
820 xmlDumpElementContent(buf, content->c1, 1);
821 else
822 xmlDumpElementContent(buf, content->c1, 0);
823 xmlBufferWriteChar(buf, " | ");
824 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
825 xmlDumpElementContent(buf, content->c2, 1);
826 else
827 xmlDumpElementContent(buf, content->c2, 0);
828 break;
829 default:
830 xmlGenericError(xmlGenericErrorContext,
831 "xmlDumpElementContent: unknown type %d\n",
832 content->type);
833 }
834 if (glob)
835 xmlBufferWriteChar(buf, ")");
836 switch (content->ocur) {
837 case XML_ELEMENT_CONTENT_ONCE:
838 break;
839 case XML_ELEMENT_CONTENT_OPT:
840 xmlBufferWriteChar(buf, "?");
841 break;
842 case XML_ELEMENT_CONTENT_MULT:
843 xmlBufferWriteChar(buf, "*");
844 break;
845 case XML_ELEMENT_CONTENT_PLUS:
846 xmlBufferWriteChar(buf, "+");
847 break;
848 }
849}
850
851/**
852 * xmlSprintfElementContent:
853 * @buf: an output buffer
854 * @content: An element table
855 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
856 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000857 * Deprecated, unsafe, use xmlSnprintfElementContent
858 */
859void
860xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
861 xmlElementContentPtr content ATTRIBUTE_UNUSED,
862 int glob ATTRIBUTE_UNUSED) {
863}
864
865/**
866 * xmlSnprintfElementContent:
867 * @buf: an output buffer
868 * @size: the buffer size
869 * @content: An element table
870 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
871 *
Owen Taylor3473f882001-02-23 17:55:21 +0000872 * This will dump the content of the element content definition
873 * Intended just for the debug routine
874 */
875void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000876xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
877 int len;
878
Owen Taylor3473f882001-02-23 17:55:21 +0000879 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000880 len = strlen(buf);
881 if (size - len < 50) {
882 if ((size - len > 4) && (buf[len - 1] != '.'))
883 strcat(buf, " ...");
884 return;
885 }
Owen Taylor3473f882001-02-23 17:55:21 +0000886 if (glob) strcat(buf, "(");
887 switch (content->type) {
888 case XML_ELEMENT_CONTENT_PCDATA:
889 strcat(buf, "#PCDATA");
890 break;
891 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000892 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000893 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000894 strcat(buf, " ...");
895 return;
896 }
897 strcat(buf, (char *) content->prefix);
898 strcat(buf, ":");
899 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000900 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000901 strcat(buf, " ...");
902 return;
903 }
Owen Taylor3473f882001-02-23 17:55:21 +0000904 strcat(buf, (char *) content->name);
905 break;
906 case XML_ELEMENT_CONTENT_SEQ:
907 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
908 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000909 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000910 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000911 xmlSnprintfElementContent(buf, size, content->c1, 0);
912 len = strlen(buf);
913 if (size - len < 50) {
914 if ((size - len > 4) && (buf[len - 1] != '.'))
915 strcat(buf, " ...");
916 return;
917 }
Owen Taylor3473f882001-02-23 17:55:21 +0000918 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000919 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
920 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
921 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000922 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000923 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000924 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000925 break;
926 case XML_ELEMENT_CONTENT_OR:
927 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
928 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000929 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000930 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000931 xmlSnprintfElementContent(buf, size, content->c1, 0);
932 len = strlen(buf);
933 if (size - len < 50) {
934 if ((size - len > 4) && (buf[len - 1] != '.'))
935 strcat(buf, " ...");
936 return;
937 }
Owen Taylor3473f882001-02-23 17:55:21 +0000938 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000939 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
940 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
941 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000942 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000943 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000944 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000945 break;
946 }
947 if (glob)
948 strcat(buf, ")");
949 switch (content->ocur) {
950 case XML_ELEMENT_CONTENT_ONCE:
951 break;
952 case XML_ELEMENT_CONTENT_OPT:
953 strcat(buf, "?");
954 break;
955 case XML_ELEMENT_CONTENT_MULT:
956 strcat(buf, "*");
957 break;
958 case XML_ELEMENT_CONTENT_PLUS:
959 strcat(buf, "+");
960 break;
961 }
962}
963
964/****************************************************************
965 * *
966 * Registration of DTD declarations *
967 * *
968 ****************************************************************/
969
970/**
971 * xmlCreateElementTable:
972 *
973 * create and initialize an empty element hash table.
974 *
975 * Returns the xmlElementTablePtr just created or NULL in case of error.
976 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000977static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000978xmlCreateElementTable(void) {
979 return(xmlHashCreate(0));
980}
981
982/**
983 * xmlFreeElement:
984 * @elem: An element
985 *
986 * Deallocate the memory used by an element definition
987 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000988static void
Owen Taylor3473f882001-02-23 17:55:21 +0000989xmlFreeElement(xmlElementPtr elem) {
990 if (elem == NULL) return;
991 xmlUnlinkNode((xmlNodePtr) elem);
992 xmlFreeElementContent(elem->content);
993 if (elem->name != NULL)
994 xmlFree((xmlChar *) elem->name);
995 if (elem->prefix != NULL)
996 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000997#ifdef LIBXML_REGEXP_ENABLED
998 if (elem->contModel != NULL)
999 xmlRegFreeRegexp(elem->contModel);
1000#endif
Owen Taylor3473f882001-02-23 17:55:21 +00001001 xmlFree(elem);
1002}
1003
1004
1005/**
1006 * xmlAddElementDecl:
1007 * @ctxt: the validation context
1008 * @dtd: pointer to the DTD
1009 * @name: the entity name
1010 * @type: the element type
1011 * @content: the element content tree or NULL
1012 *
1013 * Register a new element declaration
1014 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001015 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001016 */
1017xmlElementPtr
1018xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
1019 xmlElementTypeVal type,
1020 xmlElementContentPtr content) {
1021 xmlElementPtr ret;
1022 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001023 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001024 xmlChar *ns, *uqname;
1025
1026 if (dtd == NULL) {
1027 xmlGenericError(xmlGenericErrorContext,
1028 "xmlAddElementDecl: dtd == NULL\n");
1029 return(NULL);
1030 }
1031 if (name == NULL) {
1032 xmlGenericError(xmlGenericErrorContext,
1033 "xmlAddElementDecl: name == NULL\n");
1034 return(NULL);
1035 }
1036 switch (type) {
1037 case XML_ELEMENT_TYPE_EMPTY:
1038 if (content != NULL) {
1039 xmlGenericError(xmlGenericErrorContext,
1040 "xmlAddElementDecl: content != NULL for EMPTY\n");
1041 return(NULL);
1042 }
1043 break;
1044 case XML_ELEMENT_TYPE_ANY:
1045 if (content != NULL) {
1046 xmlGenericError(xmlGenericErrorContext,
1047 "xmlAddElementDecl: content != NULL for ANY\n");
1048 return(NULL);
1049 }
1050 break;
1051 case XML_ELEMENT_TYPE_MIXED:
1052 if (content == NULL) {
1053 xmlGenericError(xmlGenericErrorContext,
1054 "xmlAddElementDecl: content == NULL for MIXED\n");
1055 return(NULL);
1056 }
1057 break;
1058 case XML_ELEMENT_TYPE_ELEMENT:
1059 if (content == NULL) {
1060 xmlGenericError(xmlGenericErrorContext,
1061 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1062 return(NULL);
1063 }
1064 break;
1065 default:
1066 xmlGenericError(xmlGenericErrorContext,
1067 "xmlAddElementDecl: unknown type %d\n", type);
1068 return(NULL);
1069 }
1070
1071 /*
1072 * check if name is a QName
1073 */
1074 uqname = xmlSplitQName2(name, &ns);
1075 if (uqname != NULL)
1076 name = uqname;
1077
1078 /*
1079 * Create the Element table if needed.
1080 */
1081 table = (xmlElementTablePtr) dtd->elements;
1082 if (table == NULL) {
1083 table = xmlCreateElementTable();
1084 dtd->elements = (void *) table;
1085 }
1086 if (table == NULL) {
1087 xmlGenericError(xmlGenericErrorContext,
1088 "xmlAddElementDecl: Table creation failed!\n");
1089 return(NULL);
1090 }
1091
Daniel Veillarda10efa82001-04-18 13:09:01 +00001092 /*
1093 * lookup old attributes inserted on an undefined element in the
1094 * internal subset.
1095 */
1096 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1097 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1098 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1099 oldAttributes = ret->attributes;
1100 ret->attributes = NULL;
1101 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1102 xmlFreeElement(ret);
1103 }
Owen Taylor3473f882001-02-23 17:55:21 +00001104 }
Owen Taylor3473f882001-02-23 17:55:21 +00001105
1106 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001107 * The element may already be present if one of its attribute
1108 * was registered first
1109 */
1110 ret = xmlHashLookup2(table, name, ns);
1111 if (ret != NULL) {
1112 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1113 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001114 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001115 */
1116 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1117 if (uqname != NULL)
1118 xmlFree(uqname);
1119 return(NULL);
1120 }
1121 } else {
1122 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1123 if (ret == NULL) {
1124 xmlGenericError(xmlGenericErrorContext,
1125 "xmlAddElementDecl: out of memory\n");
1126 return(NULL);
1127 }
1128 memset(ret, 0, sizeof(xmlElement));
1129 ret->type = XML_ELEMENT_DECL;
1130
1131 /*
1132 * fill the structure.
1133 */
1134 ret->name = xmlStrdup(name);
1135 ret->prefix = ns;
1136
1137 /*
1138 * Validity Check:
1139 * Insertion must not fail
1140 */
1141 if (xmlHashAddEntry2(table, name, ns, ret)) {
1142 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001143 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001144 */
1145 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1146 xmlFreeElement(ret);
1147 if (uqname != NULL)
1148 xmlFree(uqname);
1149 return(NULL);
1150 }
1151 }
1152
1153 /*
1154 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001155 */
1156 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001157 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001158 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001159
1160 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001161 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001162 */
1163 ret->parent = dtd;
1164 ret->doc = dtd->doc;
1165 if (dtd->last == NULL) {
1166 dtd->children = dtd->last = (xmlNodePtr) ret;
1167 } else {
1168 dtd->last->next = (xmlNodePtr) ret;
1169 ret->prev = dtd->last;
1170 dtd->last = (xmlNodePtr) ret;
1171 }
1172 if (uqname != NULL)
1173 xmlFree(uqname);
1174 return(ret);
1175}
1176
1177/**
1178 * xmlFreeElementTable:
1179 * @table: An element table
1180 *
1181 * Deallocate the memory used by an element hash table.
1182 */
1183void
1184xmlFreeElementTable(xmlElementTablePtr table) {
1185 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1186}
1187
1188/**
1189 * xmlCopyElement:
1190 * @elem: An element
1191 *
1192 * Build a copy of an element.
1193 *
1194 * Returns the new xmlElementPtr or NULL in case of error.
1195 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001196static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001197xmlCopyElement(xmlElementPtr elem) {
1198 xmlElementPtr cur;
1199
1200 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1201 if (cur == NULL) {
1202 xmlGenericError(xmlGenericErrorContext,
1203 "xmlCopyElement: out of memory !\n");
1204 return(NULL);
1205 }
1206 memset(cur, 0, sizeof(xmlElement));
1207 cur->type = XML_ELEMENT_DECL;
1208 cur->etype = elem->etype;
1209 if (elem->name != NULL)
1210 cur->name = xmlStrdup(elem->name);
1211 else
1212 cur->name = NULL;
1213 if (elem->prefix != NULL)
1214 cur->prefix = xmlStrdup(elem->prefix);
1215 else
1216 cur->prefix = NULL;
1217 cur->content = xmlCopyElementContent(elem->content);
1218 /* TODO : rebuild the attribute list on the copy */
1219 cur->attributes = NULL;
1220 return(cur);
1221}
1222
1223/**
1224 * xmlCopyElementTable:
1225 * @table: An element table
1226 *
1227 * Build a copy of an element table.
1228 *
1229 * Returns the new xmlElementTablePtr or NULL in case of error.
1230 */
1231xmlElementTablePtr
1232xmlCopyElementTable(xmlElementTablePtr table) {
1233 return((xmlElementTablePtr) xmlHashCopy(table,
1234 (xmlHashCopier) xmlCopyElement));
1235}
1236
1237/**
1238 * xmlDumpElementDecl:
1239 * @buf: the XML buffer output
1240 * @elem: An element table
1241 *
1242 * This will dump the content of the element declaration as an XML
1243 * DTD definition
1244 */
1245void
1246xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1247 switch (elem->etype) {
1248 case XML_ELEMENT_TYPE_EMPTY:
1249 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001250 if (elem->prefix != NULL) {
1251 xmlBufferWriteCHAR(buf, elem->prefix);
1252 xmlBufferWriteChar(buf, ":");
1253 }
Owen Taylor3473f882001-02-23 17:55:21 +00001254 xmlBufferWriteCHAR(buf, elem->name);
1255 xmlBufferWriteChar(buf, " EMPTY>\n");
1256 break;
1257 case XML_ELEMENT_TYPE_ANY:
1258 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001259 if (elem->prefix != NULL) {
1260 xmlBufferWriteCHAR(buf, elem->prefix);
1261 xmlBufferWriteChar(buf, ":");
1262 }
Owen Taylor3473f882001-02-23 17:55:21 +00001263 xmlBufferWriteCHAR(buf, elem->name);
1264 xmlBufferWriteChar(buf, " ANY>\n");
1265 break;
1266 case XML_ELEMENT_TYPE_MIXED:
1267 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001268 if (elem->prefix != NULL) {
1269 xmlBufferWriteCHAR(buf, elem->prefix);
1270 xmlBufferWriteChar(buf, ":");
1271 }
Owen Taylor3473f882001-02-23 17:55:21 +00001272 xmlBufferWriteCHAR(buf, elem->name);
1273 xmlBufferWriteChar(buf, " ");
1274 xmlDumpElementContent(buf, elem->content, 1);
1275 xmlBufferWriteChar(buf, ">\n");
1276 break;
1277 case XML_ELEMENT_TYPE_ELEMENT:
1278 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001279 if (elem->prefix != NULL) {
1280 xmlBufferWriteCHAR(buf, elem->prefix);
1281 xmlBufferWriteChar(buf, ":");
1282 }
Owen Taylor3473f882001-02-23 17:55:21 +00001283 xmlBufferWriteCHAR(buf, elem->name);
1284 xmlBufferWriteChar(buf, " ");
1285 xmlDumpElementContent(buf, elem->content, 1);
1286 xmlBufferWriteChar(buf, ">\n");
1287 break;
1288 default:
1289 xmlGenericError(xmlGenericErrorContext,
1290 "xmlDumpElementDecl: internal: unknown type %d\n",
1291 elem->etype);
1292 }
1293}
1294
1295/**
1296 * xmlDumpElementTable:
1297 * @buf: the XML buffer output
1298 * @table: An element table
1299 *
1300 * This will dump the content of the element table as an XML DTD definition
1301 */
1302void
1303xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1304 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1305}
1306
1307/**
1308 * xmlCreateEnumeration:
1309 * @name: the enumeration name or NULL
1310 *
1311 * create and initialize an enumeration attribute node.
1312 *
1313 * Returns the xmlEnumerationPtr just created or NULL in case
1314 * of error.
1315 */
1316xmlEnumerationPtr
1317xmlCreateEnumeration(xmlChar *name) {
1318 xmlEnumerationPtr ret;
1319
1320 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1321 if (ret == NULL) {
1322 xmlGenericError(xmlGenericErrorContext,
1323 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1324 (long)sizeof(xmlEnumeration));
1325 return(NULL);
1326 }
1327 memset(ret, 0, sizeof(xmlEnumeration));
1328
1329 if (name != NULL)
1330 ret->name = xmlStrdup(name);
1331 return(ret);
1332}
1333
1334/**
1335 * xmlFreeEnumeration:
1336 * @cur: the tree to free.
1337 *
1338 * free an enumeration attribute node (recursive).
1339 */
1340void
1341xmlFreeEnumeration(xmlEnumerationPtr cur) {
1342 if (cur == NULL) return;
1343
1344 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1345
1346 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001347 xmlFree(cur);
1348}
1349
1350/**
1351 * xmlCopyEnumeration:
1352 * @cur: the tree to copy.
1353 *
1354 * Copy an enumeration attribute node (recursive).
1355 *
1356 * Returns the xmlEnumerationPtr just created or NULL in case
1357 * of error.
1358 */
1359xmlEnumerationPtr
1360xmlCopyEnumeration(xmlEnumerationPtr cur) {
1361 xmlEnumerationPtr ret;
1362
1363 if (cur == NULL) return(NULL);
1364 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1365
1366 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1367 else ret->next = NULL;
1368
1369 return(ret);
1370}
1371
1372/**
1373 * xmlDumpEnumeration:
1374 * @buf: the XML buffer output
1375 * @enum: An enumeration
1376 *
1377 * This will dump the content of the enumeration
1378 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001379static void
Owen Taylor3473f882001-02-23 17:55:21 +00001380xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1381 if (cur == NULL) return;
1382
1383 xmlBufferWriteCHAR(buf, cur->name);
1384 if (cur->next == NULL)
1385 xmlBufferWriteChar(buf, ")");
1386 else {
1387 xmlBufferWriteChar(buf, " | ");
1388 xmlDumpEnumeration(buf, cur->next);
1389 }
1390}
1391
1392/**
1393 * xmlCreateAttributeTable:
1394 *
1395 * create and initialize an empty attribute hash table.
1396 *
1397 * Returns the xmlAttributeTablePtr just created or NULL in case
1398 * of error.
1399 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001400static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001401xmlCreateAttributeTable(void) {
1402 return(xmlHashCreate(0));
1403}
1404
1405/**
1406 * xmlScanAttributeDeclCallback:
1407 * @attr: the attribute decl
1408 * @list: the list to update
1409 *
1410 * Callback called by xmlScanAttributeDecl when a new attribute
1411 * has to be entered in the list.
1412 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001413static void
Owen Taylor3473f882001-02-23 17:55:21 +00001414xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001415 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001416 attr->nexth = *list;
1417 *list = attr;
1418}
1419
1420/**
1421 * xmlScanAttributeDecl:
1422 * @dtd: pointer to the DTD
1423 * @elem: the element name
1424 *
1425 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001426 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001427 *
1428 * Returns the pointer to the first attribute decl in the chain,
1429 * possibly NULL.
1430 */
1431xmlAttributePtr
1432xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1433 xmlAttributePtr ret = NULL;
1434 xmlAttributeTablePtr table;
1435
1436 if (dtd == NULL) {
1437 xmlGenericError(xmlGenericErrorContext,
1438 "xmlScanAttributeDecl: dtd == NULL\n");
1439 return(NULL);
1440 }
1441 if (elem == NULL) {
1442 xmlGenericError(xmlGenericErrorContext,
1443 "xmlScanAttributeDecl: elem == NULL\n");
1444 return(NULL);
1445 }
1446 table = (xmlAttributeTablePtr) dtd->attributes;
1447 if (table == NULL)
1448 return(NULL);
1449
1450 /* WRONG !!! */
1451 xmlHashScan3(table, NULL, NULL, elem,
1452 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1453 return(ret);
1454}
1455
1456/**
1457 * xmlScanIDAttributeDecl:
1458 * @ctxt: the validation context
1459 * @elem: the element name
1460 *
1461 * Verify that the element don't have too many ID attributes
1462 * declared.
1463 *
1464 * Returns the number of ID attributes found.
1465 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001466static int
Owen Taylor3473f882001-02-23 17:55:21 +00001467xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1468 xmlAttributePtr cur;
1469 int ret = 0;
1470
1471 if (elem == NULL) return(0);
1472 cur = elem->attributes;
1473 while (cur != NULL) {
1474 if (cur->atype == XML_ATTRIBUTE_ID) {
1475 ret ++;
1476 if (ret > 1)
1477 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001478 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001479 elem->name, cur->name);
1480 }
1481 cur = cur->nexth;
1482 }
1483 return(ret);
1484}
1485
1486/**
1487 * xmlFreeAttribute:
1488 * @elem: An attribute
1489 *
1490 * Deallocate the memory used by an attribute definition
1491 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001492static void
Owen Taylor3473f882001-02-23 17:55:21 +00001493xmlFreeAttribute(xmlAttributePtr attr) {
1494 if (attr == NULL) return;
1495 xmlUnlinkNode((xmlNodePtr) attr);
1496 if (attr->tree != NULL)
1497 xmlFreeEnumeration(attr->tree);
1498 if (attr->elem != NULL)
1499 xmlFree((xmlChar *) attr->elem);
1500 if (attr->name != NULL)
1501 xmlFree((xmlChar *) attr->name);
1502 if (attr->defaultValue != NULL)
1503 xmlFree((xmlChar *) attr->defaultValue);
1504 if (attr->prefix != NULL)
1505 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001506 xmlFree(attr);
1507}
1508
1509
1510/**
1511 * xmlAddAttributeDecl:
1512 * @ctxt: the validation context
1513 * @dtd: pointer to the DTD
1514 * @elem: the element name
1515 * @name: the attribute name
1516 * @ns: the attribute namespace prefix
1517 * @type: the attribute type
1518 * @def: the attribute default type
1519 * @defaultValue: the attribute default value
1520 * @tree: if it's an enumeration, the associated list
1521 *
1522 * Register a new attribute declaration
1523 * Note that @tree becomes the ownership of the DTD
1524 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001525 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001526 */
1527xmlAttributePtr
1528xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1529 const xmlChar *name, const xmlChar *ns,
1530 xmlAttributeType type, xmlAttributeDefault def,
1531 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1532 xmlAttributePtr ret;
1533 xmlAttributeTablePtr table;
1534 xmlElementPtr elemDef;
1535
1536 if (dtd == NULL) {
1537 xmlGenericError(xmlGenericErrorContext,
1538 "xmlAddAttributeDecl: dtd == NULL\n");
1539 xmlFreeEnumeration(tree);
1540 return(NULL);
1541 }
1542 if (name == NULL) {
1543 xmlGenericError(xmlGenericErrorContext,
1544 "xmlAddAttributeDecl: name == NULL\n");
1545 xmlFreeEnumeration(tree);
1546 return(NULL);
1547 }
1548 if (elem == NULL) {
1549 xmlGenericError(xmlGenericErrorContext,
1550 "xmlAddAttributeDecl: elem == NULL\n");
1551 xmlFreeEnumeration(tree);
1552 return(NULL);
1553 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001554
Owen Taylor3473f882001-02-23 17:55:21 +00001555 /*
1556 * Check the type and possibly the default value.
1557 */
1558 switch (type) {
1559 case XML_ATTRIBUTE_CDATA:
1560 break;
1561 case XML_ATTRIBUTE_ID:
1562 break;
1563 case XML_ATTRIBUTE_IDREF:
1564 break;
1565 case XML_ATTRIBUTE_IDREFS:
1566 break;
1567 case XML_ATTRIBUTE_ENTITY:
1568 break;
1569 case XML_ATTRIBUTE_ENTITIES:
1570 break;
1571 case XML_ATTRIBUTE_NMTOKEN:
1572 break;
1573 case XML_ATTRIBUTE_NMTOKENS:
1574 break;
1575 case XML_ATTRIBUTE_ENUMERATION:
1576 break;
1577 case XML_ATTRIBUTE_NOTATION:
1578 break;
1579 default:
1580 xmlGenericError(xmlGenericErrorContext,
1581 "xmlAddAttributeDecl: unknown type %d\n", type);
1582 xmlFreeEnumeration(tree);
1583 return(NULL);
1584 }
1585 if ((defaultValue != NULL) &&
1586 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001587 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001588 elem, name, defaultValue);
1589 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001590 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001591 }
1592
1593 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001594 * Check first that an attribute defined in the external subset wasn't
1595 * already defined in the internal subset
1596 */
1597 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1598 (dtd->doc->intSubset != NULL) &&
1599 (dtd->doc->intSubset->attributes != NULL)) {
1600 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1601 if (ret != NULL)
1602 return(NULL);
1603 }
1604
1605 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001606 * Create the Attribute table if needed.
1607 */
1608 table = (xmlAttributeTablePtr) dtd->attributes;
1609 if (table == NULL) {
1610 table = xmlCreateAttributeTable();
1611 dtd->attributes = (void *) table;
1612 }
1613 if (table == NULL) {
1614 xmlGenericError(xmlGenericErrorContext,
1615 "xmlAddAttributeDecl: Table creation failed!\n");
1616 return(NULL);
1617 }
1618
1619
1620 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1621 if (ret == NULL) {
1622 xmlGenericError(xmlGenericErrorContext,
1623 "xmlAddAttributeDecl: out of memory\n");
1624 return(NULL);
1625 }
1626 memset(ret, 0, sizeof(xmlAttribute));
1627 ret->type = XML_ATTRIBUTE_DECL;
1628
1629 /*
1630 * fill the structure.
1631 */
1632 ret->atype = type;
1633 ret->name = xmlStrdup(name);
1634 ret->prefix = xmlStrdup(ns);
1635 ret->elem = xmlStrdup(elem);
1636 ret->def = def;
1637 ret->tree = tree;
1638 if (defaultValue != NULL)
1639 ret->defaultValue = xmlStrdup(defaultValue);
1640
1641 /*
1642 * Validity Check:
1643 * Search the DTD for previous declarations of the ATTLIST
1644 */
1645 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1646 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001647 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001648 */
1649 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001650 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001651 name, elem);
1652 xmlFreeAttribute(ret);
1653 return(NULL);
1654 }
1655
1656 /*
1657 * Validity Check:
1658 * Multiple ID per element
1659 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001660 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001661 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001662
Owen Taylor3473f882001-02-23 17:55:21 +00001663 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001664 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001665 VERROR(ctxt->userData,
1666 "Element %s has too may ID attributes defined : %s\n",
1667 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001668 ctxt->valid = 0;
1669 }
1670
Daniel Veillard48da9102001-08-07 01:10:10 +00001671 /*
1672 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001673 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001674 */
1675 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1676 ((ret->prefix != NULL &&
1677 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1678 ret->nexth = elemDef->attributes;
1679 elemDef->attributes = ret;
1680 } else {
1681 xmlAttributePtr tmp = elemDef->attributes;
1682
1683 while ((tmp != NULL) &&
1684 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1685 ((ret->prefix != NULL &&
1686 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1687 if (tmp->nexth == NULL)
1688 break;
1689 tmp = tmp->nexth;
1690 }
1691 if (tmp != NULL) {
1692 ret->nexth = tmp->nexth;
1693 tmp->nexth = ret;
1694 } else {
1695 ret->nexth = elemDef->attributes;
1696 elemDef->attributes = ret;
1697 }
1698 }
Owen Taylor3473f882001-02-23 17:55:21 +00001699 }
1700
1701 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001702 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001703 */
1704 ret->parent = dtd;
1705 ret->doc = dtd->doc;
1706 if (dtd->last == NULL) {
1707 dtd->children = dtd->last = (xmlNodePtr) ret;
1708 } else {
1709 dtd->last->next = (xmlNodePtr) ret;
1710 ret->prev = dtd->last;
1711 dtd->last = (xmlNodePtr) ret;
1712 }
1713 return(ret);
1714}
1715
1716/**
1717 * xmlFreeAttributeTable:
1718 * @table: An attribute table
1719 *
1720 * Deallocate the memory used by an entities hash table.
1721 */
1722void
1723xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1724 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1725}
1726
1727/**
1728 * xmlCopyAttribute:
1729 * @attr: An attribute
1730 *
1731 * Build a copy of an attribute.
1732 *
1733 * Returns the new xmlAttributePtr or NULL in case of error.
1734 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001735static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001736xmlCopyAttribute(xmlAttributePtr attr) {
1737 xmlAttributePtr cur;
1738
1739 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1740 if (cur == NULL) {
1741 xmlGenericError(xmlGenericErrorContext,
1742 "xmlCopyAttribute: out of memory !\n");
1743 return(NULL);
1744 }
1745 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001746 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001747 cur->atype = attr->atype;
1748 cur->def = attr->def;
1749 cur->tree = xmlCopyEnumeration(attr->tree);
1750 if (attr->elem != NULL)
1751 cur->elem = xmlStrdup(attr->elem);
1752 if (attr->name != NULL)
1753 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001754 if (attr->prefix != NULL)
1755 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001756 if (attr->defaultValue != NULL)
1757 cur->defaultValue = xmlStrdup(attr->defaultValue);
1758 return(cur);
1759}
1760
1761/**
1762 * xmlCopyAttributeTable:
1763 * @table: An attribute table
1764 *
1765 * Build a copy of an attribute table.
1766 *
1767 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1768 */
1769xmlAttributeTablePtr
1770xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1771 return((xmlAttributeTablePtr) xmlHashCopy(table,
1772 (xmlHashCopier) xmlCopyAttribute));
1773}
1774
1775/**
1776 * xmlDumpAttributeDecl:
1777 * @buf: the XML buffer output
1778 * @attr: An attribute declaration
1779 *
1780 * This will dump the content of the attribute declaration as an XML
1781 * DTD definition
1782 */
1783void
1784xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1785 xmlBufferWriteChar(buf, "<!ATTLIST ");
1786 xmlBufferWriteCHAR(buf, attr->elem);
1787 xmlBufferWriteChar(buf, " ");
1788 if (attr->prefix != NULL) {
1789 xmlBufferWriteCHAR(buf, attr->prefix);
1790 xmlBufferWriteChar(buf, ":");
1791 }
1792 xmlBufferWriteCHAR(buf, attr->name);
1793 switch (attr->atype) {
1794 case XML_ATTRIBUTE_CDATA:
1795 xmlBufferWriteChar(buf, " CDATA");
1796 break;
1797 case XML_ATTRIBUTE_ID:
1798 xmlBufferWriteChar(buf, " ID");
1799 break;
1800 case XML_ATTRIBUTE_IDREF:
1801 xmlBufferWriteChar(buf, " IDREF");
1802 break;
1803 case XML_ATTRIBUTE_IDREFS:
1804 xmlBufferWriteChar(buf, " IDREFS");
1805 break;
1806 case XML_ATTRIBUTE_ENTITY:
1807 xmlBufferWriteChar(buf, " ENTITY");
1808 break;
1809 case XML_ATTRIBUTE_ENTITIES:
1810 xmlBufferWriteChar(buf, " ENTITIES");
1811 break;
1812 case XML_ATTRIBUTE_NMTOKEN:
1813 xmlBufferWriteChar(buf, " NMTOKEN");
1814 break;
1815 case XML_ATTRIBUTE_NMTOKENS:
1816 xmlBufferWriteChar(buf, " NMTOKENS");
1817 break;
1818 case XML_ATTRIBUTE_ENUMERATION:
1819 xmlBufferWriteChar(buf, " (");
1820 xmlDumpEnumeration(buf, attr->tree);
1821 break;
1822 case XML_ATTRIBUTE_NOTATION:
1823 xmlBufferWriteChar(buf, " NOTATION (");
1824 xmlDumpEnumeration(buf, attr->tree);
1825 break;
1826 default:
1827 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001828 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001829 attr->atype);
1830 }
1831 switch (attr->def) {
1832 case XML_ATTRIBUTE_NONE:
1833 break;
1834 case XML_ATTRIBUTE_REQUIRED:
1835 xmlBufferWriteChar(buf, " #REQUIRED");
1836 break;
1837 case XML_ATTRIBUTE_IMPLIED:
1838 xmlBufferWriteChar(buf, " #IMPLIED");
1839 break;
1840 case XML_ATTRIBUTE_FIXED:
1841 xmlBufferWriteChar(buf, " #FIXED");
1842 break;
1843 default:
1844 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001845 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001846 attr->def);
1847 }
1848 if (attr->defaultValue != NULL) {
1849 xmlBufferWriteChar(buf, " ");
1850 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1851 }
1852 xmlBufferWriteChar(buf, ">\n");
1853}
1854
1855/**
1856 * xmlDumpAttributeTable:
1857 * @buf: the XML buffer output
1858 * @table: An attribute table
1859 *
1860 * This will dump the content of the attribute table as an XML DTD definition
1861 */
1862void
1863xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1864 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1865}
1866
1867/************************************************************************
1868 * *
1869 * NOTATIONs *
1870 * *
1871 ************************************************************************/
1872/**
1873 * xmlCreateNotationTable:
1874 *
1875 * create and initialize an empty notation hash table.
1876 *
1877 * Returns the xmlNotationTablePtr just created or NULL in case
1878 * of error.
1879 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001880static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001881xmlCreateNotationTable(void) {
1882 return(xmlHashCreate(0));
1883}
1884
1885/**
1886 * xmlFreeNotation:
1887 * @not: A notation
1888 *
1889 * Deallocate the memory used by an notation definition
1890 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001891static void
Owen Taylor3473f882001-02-23 17:55:21 +00001892xmlFreeNotation(xmlNotationPtr nota) {
1893 if (nota == NULL) return;
1894 if (nota->name != NULL)
1895 xmlFree((xmlChar *) nota->name);
1896 if (nota->PublicID != NULL)
1897 xmlFree((xmlChar *) nota->PublicID);
1898 if (nota->SystemID != NULL)
1899 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001900 xmlFree(nota);
1901}
1902
1903
1904/**
1905 * xmlAddNotationDecl:
1906 * @dtd: pointer to the DTD
1907 * @ctxt: the validation context
1908 * @name: the entity name
1909 * @PublicID: the public identifier or NULL
1910 * @SystemID: the system identifier or NULL
1911 *
1912 * Register a new notation declaration
1913 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001914 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001915 */
1916xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001917xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001918 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001919 const xmlChar *PublicID, const xmlChar *SystemID) {
1920 xmlNotationPtr ret;
1921 xmlNotationTablePtr table;
1922
1923 if (dtd == NULL) {
1924 xmlGenericError(xmlGenericErrorContext,
1925 "xmlAddNotationDecl: dtd == NULL\n");
1926 return(NULL);
1927 }
1928 if (name == NULL) {
1929 xmlGenericError(xmlGenericErrorContext,
1930 "xmlAddNotationDecl: name == NULL\n");
1931 return(NULL);
1932 }
1933 if ((PublicID == NULL) && (SystemID == NULL)) {
1934 xmlGenericError(xmlGenericErrorContext,
1935 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001936 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001937 }
1938
1939 /*
1940 * Create the Notation table if needed.
1941 */
1942 table = (xmlNotationTablePtr) dtd->notations;
1943 if (table == NULL)
1944 dtd->notations = table = xmlCreateNotationTable();
1945 if (table == NULL) {
1946 xmlGenericError(xmlGenericErrorContext,
1947 "xmlAddNotationDecl: Table creation failed!\n");
1948 return(NULL);
1949 }
1950
1951 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1952 if (ret == NULL) {
1953 xmlGenericError(xmlGenericErrorContext,
1954 "xmlAddNotationDecl: out of memory\n");
1955 return(NULL);
1956 }
1957 memset(ret, 0, sizeof(xmlNotation));
1958
1959 /*
1960 * fill the structure.
1961 */
1962 ret->name = xmlStrdup(name);
1963 if (SystemID != NULL)
1964 ret->SystemID = xmlStrdup(SystemID);
1965 if (PublicID != NULL)
1966 ret->PublicID = xmlStrdup(PublicID);
1967
1968 /*
1969 * Validity Check:
1970 * Check the DTD for previous declarations of the ATTLIST
1971 */
1972 if (xmlHashAddEntry(table, name, ret)) {
1973 xmlGenericError(xmlGenericErrorContext,
1974 "xmlAddNotationDecl: %s already defined\n", name);
1975 xmlFreeNotation(ret);
1976 return(NULL);
1977 }
1978 return(ret);
1979}
1980
1981/**
1982 * xmlFreeNotationTable:
1983 * @table: An notation table
1984 *
1985 * Deallocate the memory used by an entities hash table.
1986 */
1987void
1988xmlFreeNotationTable(xmlNotationTablePtr table) {
1989 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1990}
1991
1992/**
1993 * xmlCopyNotation:
1994 * @nota: A notation
1995 *
1996 * Build a copy of a notation.
1997 *
1998 * Returns the new xmlNotationPtr or NULL in case of error.
1999 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002000static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002001xmlCopyNotation(xmlNotationPtr nota) {
2002 xmlNotationPtr cur;
2003
2004 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
2005 if (cur == NULL) {
2006 xmlGenericError(xmlGenericErrorContext,
2007 "xmlCopyNotation: out of memory !\n");
2008 return(NULL);
2009 }
2010 if (nota->name != NULL)
2011 cur->name = xmlStrdup(nota->name);
2012 else
2013 cur->name = NULL;
2014 if (nota->PublicID != NULL)
2015 cur->PublicID = xmlStrdup(nota->PublicID);
2016 else
2017 cur->PublicID = NULL;
2018 if (nota->SystemID != NULL)
2019 cur->SystemID = xmlStrdup(nota->SystemID);
2020 else
2021 cur->SystemID = NULL;
2022 return(cur);
2023}
2024
2025/**
2026 * xmlCopyNotationTable:
2027 * @table: A notation table
2028 *
2029 * Build a copy of a notation table.
2030 *
2031 * Returns the new xmlNotationTablePtr or NULL in case of error.
2032 */
2033xmlNotationTablePtr
2034xmlCopyNotationTable(xmlNotationTablePtr table) {
2035 return((xmlNotationTablePtr) xmlHashCopy(table,
2036 (xmlHashCopier) xmlCopyNotation));
2037}
2038
2039/**
2040 * xmlDumpNotationDecl:
2041 * @buf: the XML buffer output
2042 * @nota: A notation declaration
2043 *
2044 * This will dump the content the notation declaration as an XML DTD definition
2045 */
2046void
2047xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2048 xmlBufferWriteChar(buf, "<!NOTATION ");
2049 xmlBufferWriteCHAR(buf, nota->name);
2050 if (nota->PublicID != NULL) {
2051 xmlBufferWriteChar(buf, " PUBLIC ");
2052 xmlBufferWriteQuotedString(buf, nota->PublicID);
2053 if (nota->SystemID != NULL) {
2054 xmlBufferWriteChar(buf, " ");
2055 xmlBufferWriteCHAR(buf, nota->SystemID);
2056 }
2057 } else {
2058 xmlBufferWriteChar(buf, " SYSTEM ");
2059 xmlBufferWriteCHAR(buf, nota->SystemID);
2060 }
2061 xmlBufferWriteChar(buf, " >\n");
2062}
2063
2064/**
2065 * xmlDumpNotationTable:
2066 * @buf: the XML buffer output
2067 * @table: A notation table
2068 *
2069 * This will dump the content of the notation table as an XML DTD definition
2070 */
2071void
2072xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2073 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2074}
2075
2076/************************************************************************
2077 * *
2078 * IDs *
2079 * *
2080 ************************************************************************/
2081/**
2082 * xmlCreateIDTable:
2083 *
2084 * create and initialize an empty id hash table.
2085 *
2086 * Returns the xmlIDTablePtr just created or NULL in case
2087 * of error.
2088 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002089static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002090xmlCreateIDTable(void) {
2091 return(xmlHashCreate(0));
2092}
2093
2094/**
2095 * xmlFreeID:
2096 * @not: A id
2097 *
2098 * Deallocate the memory used by an id definition
2099 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002100static void
Owen Taylor3473f882001-02-23 17:55:21 +00002101xmlFreeID(xmlIDPtr id) {
2102 if (id == NULL) return;
2103 if (id->value != NULL)
2104 xmlFree((xmlChar *) id->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002105 if (id->name != NULL)
2106 xmlFree((xmlChar *) id->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002107 xmlFree(id);
2108}
2109
2110/**
2111 * xmlAddID:
2112 * @ctxt: the validation context
2113 * @doc: pointer to the document
2114 * @value: the value name
2115 * @attr: the attribute holding the ID
2116 *
2117 * Register a new id declaration
2118 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002119 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002120 */
2121xmlIDPtr
2122xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2123 xmlAttrPtr attr) {
2124 xmlIDPtr ret;
2125 xmlIDTablePtr table;
2126
2127 if (doc == NULL) {
2128 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002129 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002130 return(NULL);
2131 }
2132 if (value == NULL) {
2133 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002134 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002135 return(NULL);
2136 }
2137 if (attr == NULL) {
2138 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002139 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002140 return(NULL);
2141 }
2142
2143 /*
2144 * Create the ID table if needed.
2145 */
2146 table = (xmlIDTablePtr) doc->ids;
2147 if (table == NULL)
2148 doc->ids = table = xmlCreateIDTable();
2149 if (table == NULL) {
2150 xmlGenericError(xmlGenericErrorContext,
2151 "xmlAddID: Table creation failed!\n");
2152 return(NULL);
2153 }
2154
2155 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2156 if (ret == NULL) {
2157 xmlGenericError(xmlGenericErrorContext,
2158 "xmlAddID: out of memory\n");
2159 return(NULL);
2160 }
2161
2162 /*
2163 * fill the structure.
2164 */
2165 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002166 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2167 /*
2168 * Operating in streaming mode, attr is gonna disapear
2169 */
2170 ret->name = xmlStrdup(attr->name);
2171 ret->attr = NULL;
2172 } else {
2173 ret->attr = attr;
2174 ret->name = NULL;
2175 }
2176 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002177
2178 if (xmlHashAddEntry(table, value, ret) < 0) {
2179 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002180 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002181 */
Daniel Veillard76575762002-09-05 14:21:15 +00002182 if (ctxt != NULL) {
2183 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002184 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002185 }
Owen Taylor3473f882001-02-23 17:55:21 +00002186 xmlFreeID(ret);
2187 return(NULL);
2188 }
Daniel Veillard249d7bb2003-03-19 21:02:29 +00002189 if (attr != NULL)
2190 attr->atype = XML_ATTRIBUTE_ID;
Owen Taylor3473f882001-02-23 17:55:21 +00002191 return(ret);
2192}
2193
2194/**
2195 * xmlFreeIDTable:
2196 * @table: An id table
2197 *
2198 * Deallocate the memory used by an ID hash table.
2199 */
2200void
2201xmlFreeIDTable(xmlIDTablePtr table) {
2202 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2203}
2204
2205/**
2206 * xmlIsID:
2207 * @doc: the document
2208 * @elem: the element carrying the attribute
2209 * @attr: the attribute
2210 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002211 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002212 * then this is done if DTD loading has been requested. In the case
2213 * of HTML documents parsed with the HTML parser, then ID detection is
2214 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002215 *
2216 * Returns 0 or 1 depending on the lookup result
2217 */
2218int
2219xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2220 if (doc == NULL) return(0);
2221 if (attr == NULL) return(0);
2222 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2223 return(0);
2224 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2225 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2226 (xmlStrEqual(BAD_CAST "name", attr->name)))
2227 return(1);
2228 return(0);
2229 } else {
2230 xmlAttributePtr attrDecl;
2231
2232 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002233 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00002234 xmlChar fn[50];
Daniel Veillard37f961d2002-07-06 17:53:56 +00002235 xmlChar *fullname;
Daniel Veillardc00cda82003-04-07 10:22:39 +00002236
2237 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002238 if (fullname == NULL)
2239 return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002240 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2241 attr->name);
2242 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2243 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2244 attr->name);
Daniel Veillardc00cda82003-04-07 10:22:39 +00002245 if ((fullname != fn) && (fullname != elem->name))
2246 xmlFree(fullname);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002247 } else {
2248 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2249 attr->name);
2250 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2251 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2252 attr->name);
2253 }
Owen Taylor3473f882001-02-23 17:55:21 +00002254
2255 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2256 return(1);
2257 }
2258 return(0);
2259}
2260
2261/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002262 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002263 * @doc: the document
2264 * @attr: the attribute
2265 *
2266 * Remove the given attribute from the ID table maintained internally.
2267 *
2268 * Returns -1 if the lookup failed and 0 otherwise
2269 */
2270int
2271xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2272 xmlAttrPtr cur;
2273 xmlIDTablePtr table;
2274 xmlChar *ID;
2275
2276 if (doc == NULL) return(-1);
2277 if (attr == NULL) return(-1);
2278 table = (xmlIDTablePtr) doc->ids;
2279 if (table == NULL)
2280 return(-1);
2281
2282 if (attr == NULL)
2283 return(-1);
2284 ID = xmlNodeListGetString(doc, attr->children, 1);
2285 if (ID == NULL)
2286 return(-1);
2287 cur = xmlHashLookup(table, ID);
2288 if (cur != attr) {
2289 xmlFree(ID);
2290 return(-1);
2291 }
2292 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2293 xmlFree(ID);
2294 return(0);
2295}
2296
2297/**
2298 * xmlGetID:
2299 * @doc: pointer to the document
2300 * @ID: the ID value
2301 *
2302 * Search the attribute declaring the given ID
2303 *
2304 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2305 */
2306xmlAttrPtr
2307xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2308 xmlIDTablePtr table;
2309 xmlIDPtr id;
2310
2311 if (doc == NULL) {
2312 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2313 return(NULL);
2314 }
2315
2316 if (ID == NULL) {
2317 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2318 return(NULL);
2319 }
2320
2321 table = (xmlIDTablePtr) doc->ids;
2322 if (table == NULL)
2323 return(NULL);
2324
2325 id = xmlHashLookup(table, ID);
2326 if (id == NULL)
2327 return(NULL);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002328 if (id->attr == NULL) {
2329 /*
2330 * We are operating on a stream, return a well known reference
2331 * since the attribute node doesn't exist anymore
2332 */
2333 return((xmlAttrPtr) doc);
2334 }
Owen Taylor3473f882001-02-23 17:55:21 +00002335 return(id->attr);
2336}
2337
2338/************************************************************************
2339 * *
2340 * Refs *
2341 * *
2342 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002343typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002344{
2345 xmlListPtr l;
2346 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002347} xmlRemoveMemo;
2348
2349typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2350
2351typedef struct xmlValidateMemo_t
2352{
2353 xmlValidCtxtPtr ctxt;
2354 const xmlChar *name;
2355} xmlValidateMemo;
2356
2357typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002358
2359/**
2360 * xmlCreateRefTable:
2361 *
2362 * create and initialize an empty ref hash table.
2363 *
2364 * Returns the xmlRefTablePtr just created or NULL in case
2365 * of error.
2366 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002367static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002368xmlCreateRefTable(void) {
2369 return(xmlHashCreate(0));
2370}
2371
2372/**
2373 * xmlFreeRef:
2374 * @lk: A list link
2375 *
2376 * Deallocate the memory used by a ref definition
2377 */
2378static void
2379xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002380 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2381 if (ref == NULL) return;
2382 if (ref->value != NULL)
2383 xmlFree((xmlChar *)ref->value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002384 if (ref->name != NULL)
2385 xmlFree((xmlChar *)ref->name);
Daniel Veillard37721922001-05-04 15:21:12 +00002386 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002387}
2388
2389/**
2390 * xmlFreeRefList:
2391 * @list_ref: A list of references.
2392 *
2393 * Deallocate the memory used by a list of references
2394 */
2395static void
2396xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002397 if (list_ref == NULL) return;
2398 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002399}
2400
2401/**
2402 * xmlWalkRemoveRef:
2403 * @data: Contents of current link
2404 * @user: Value supplied by the user
2405 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002406 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002407 */
2408static int
2409xmlWalkRemoveRef(const void *data, const void *user)
2410{
Daniel Veillard37721922001-05-04 15:21:12 +00002411 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2412 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2413 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002414
Daniel Veillard37721922001-05-04 15:21:12 +00002415 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2416 xmlListRemoveFirst(ref_list, (void *)data);
2417 return 0;
2418 }
2419 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002420}
2421
2422/**
2423 * xmlAddRef:
2424 * @ctxt: the validation context
2425 * @doc: pointer to the document
2426 * @value: the value name
2427 * @attr: the attribute holding the Ref
2428 *
2429 * Register a new ref declaration
2430 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002431 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002432 */
2433xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002434xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002435 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002436 xmlRefPtr ret;
2437 xmlRefTablePtr table;
2438 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002439
Daniel Veillard37721922001-05-04 15:21:12 +00002440 if (doc == NULL) {
2441 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002442 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002443 return(NULL);
2444 }
2445 if (value == NULL) {
2446 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002447 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002448 return(NULL);
2449 }
2450 if (attr == NULL) {
2451 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002452 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002453 return(NULL);
2454 }
Owen Taylor3473f882001-02-23 17:55:21 +00002455
Daniel Veillard37721922001-05-04 15:21:12 +00002456 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002457 * Create the Ref table if needed.
2458 */
Daniel Veillard37721922001-05-04 15:21:12 +00002459 table = (xmlRefTablePtr) doc->refs;
2460 if (table == NULL)
2461 doc->refs = table = xmlCreateRefTable();
2462 if (table == NULL) {
2463 xmlGenericError(xmlGenericErrorContext,
2464 "xmlAddRef: Table creation failed!\n");
2465 return(NULL);
2466 }
Owen Taylor3473f882001-02-23 17:55:21 +00002467
Daniel Veillard37721922001-05-04 15:21:12 +00002468 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2469 if (ret == NULL) {
2470 xmlGenericError(xmlGenericErrorContext,
2471 "xmlAddRef: out of memory\n");
2472 return(NULL);
2473 }
Owen Taylor3473f882001-02-23 17:55:21 +00002474
Daniel Veillard37721922001-05-04 15:21:12 +00002475 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002476 * fill the structure.
2477 */
Daniel Veillard37721922001-05-04 15:21:12 +00002478 ret->value = xmlStrdup(value);
Daniel Veillardea7751d2002-12-20 00:16:24 +00002479 if ((ctxt != NULL) && (ctxt->vstateNr != 0)) {
2480 /*
2481 * Operating in streaming mode, attr is gonna disapear
2482 */
2483 ret->name = xmlStrdup(attr->name);
2484 ret->attr = NULL;
2485 } else {
2486 ret->name = NULL;
2487 ret->attr = attr;
2488 }
2489 ret->lineno = xmlGetLineNo(attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00002490
Daniel Veillard37721922001-05-04 15:21:12 +00002491 /* To add a reference :-
2492 * References are maintained as a list of references,
2493 * Lookup the entry, if no entry create new nodelist
2494 * Add the owning node to the NodeList
2495 * Return the ref
2496 */
Owen Taylor3473f882001-02-23 17:55:21 +00002497
Daniel Veillard37721922001-05-04 15:21:12 +00002498 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2499 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2500 xmlGenericError(xmlGenericErrorContext,
2501 "xmlAddRef: Reference list creation failed!\n");
2502 return(NULL);
2503 }
2504 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2505 xmlListDelete(ref_list);
2506 xmlGenericError(xmlGenericErrorContext,
2507 "xmlAddRef: Reference list insertion failed!\n");
2508 return(NULL);
2509 }
2510 }
2511 xmlListInsert(ref_list, ret);
2512 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002513}
2514
2515/**
2516 * xmlFreeRefTable:
2517 * @table: An ref table
2518 *
2519 * Deallocate the memory used by an Ref hash table.
2520 */
2521void
2522xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002523 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002524}
2525
2526/**
2527 * xmlIsRef:
2528 * @doc: the document
2529 * @elem: the element carrying the attribute
2530 * @attr: the attribute
2531 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002532 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002533 * then this is simple, otherwise we use an heuristic: name Ref (upper
2534 * or lowercase).
2535 *
2536 * Returns 0 or 1 depending on the lookup result
2537 */
2538int
2539xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002540 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2541 return(0);
2542 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2543 /* TODO @@@ */
2544 return(0);
2545 } else {
2546 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002547
Daniel Veillard37721922001-05-04 15:21:12 +00002548 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2549 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2550 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2551 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002552
Daniel Veillard37721922001-05-04 15:21:12 +00002553 if ((attrDecl != NULL) &&
2554 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2555 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2556 return(1);
2557 }
2558 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002559}
2560
2561/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002562 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002563 * @doc: the document
2564 * @attr: the attribute
2565 *
2566 * Remove the given attribute from the Ref table maintained internally.
2567 *
2568 * Returns -1 if the lookup failed and 0 otherwise
2569 */
2570int
2571xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002572 xmlListPtr ref_list;
2573 xmlRefTablePtr table;
2574 xmlChar *ID;
2575 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002576
Daniel Veillard37721922001-05-04 15:21:12 +00002577 if (doc == NULL) return(-1);
2578 if (attr == NULL) return(-1);
2579 table = (xmlRefTablePtr) doc->refs;
2580 if (table == NULL)
2581 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002582
Daniel Veillard37721922001-05-04 15:21:12 +00002583 if (attr == NULL)
2584 return(-1);
2585 ID = xmlNodeListGetString(doc, attr->children, 1);
2586 if (ID == NULL)
2587 return(-1);
2588 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002589
Daniel Veillard37721922001-05-04 15:21:12 +00002590 if(ref_list == NULL) {
2591 xmlFree(ID);
2592 return (-1);
2593 }
2594 /* At this point, ref_list refers to a list of references which
2595 * have the same key as the supplied attr. Our list of references
2596 * is ordered by reference address and we don't have that information
2597 * here to use when removing. We'll have to walk the list and
2598 * check for a matching attribute, when we find one stop the walk
2599 * and remove the entry.
2600 * The list is ordered by reference, so that means we don't have the
2601 * key. Passing the list and the reference to the walker means we
2602 * will have enough data to be able to remove the entry.
2603 */
2604 target.l = ref_list;
2605 target.ap = attr;
2606
2607 /* Remove the supplied attr from our list */
2608 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002609
Daniel Veillard37721922001-05-04 15:21:12 +00002610 /*If the list is empty then remove the list entry in the hash */
2611 if (xmlListEmpty(ref_list))
2612 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2613 xmlFreeRefList);
2614 xmlFree(ID);
2615 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002616}
2617
2618/**
2619 * xmlGetRefs:
2620 * @doc: pointer to the document
2621 * @ID: the ID value
2622 *
2623 * Find the set of references for the supplied ID.
2624 *
2625 * Returns NULL if not found, otherwise node set for the ID.
2626 */
2627xmlListPtr
2628xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002629 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002630
Daniel Veillard37721922001-05-04 15:21:12 +00002631 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002632 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002633 return(NULL);
2634 }
Owen Taylor3473f882001-02-23 17:55:21 +00002635
Daniel Veillard37721922001-05-04 15:21:12 +00002636 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002637 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002638 return(NULL);
2639 }
Owen Taylor3473f882001-02-23 17:55:21 +00002640
Daniel Veillard37721922001-05-04 15:21:12 +00002641 table = (xmlRefTablePtr) doc->refs;
2642 if (table == NULL)
2643 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002644
Daniel Veillard37721922001-05-04 15:21:12 +00002645 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002646}
2647
2648/************************************************************************
2649 * *
2650 * Routines for validity checking *
2651 * *
2652 ************************************************************************/
2653
2654/**
2655 * xmlGetDtdElementDesc:
2656 * @dtd: a pointer to the DtD to search
2657 * @name: the element name
2658 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002659 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002660 *
2661 * returns the xmlElementPtr if found or NULL
2662 */
2663
2664xmlElementPtr
2665xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2666 xmlElementTablePtr table;
2667 xmlElementPtr cur;
2668 xmlChar *uqname = NULL, *prefix = NULL;
2669
2670 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002671 if (dtd->elements == NULL)
2672 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002673 table = (xmlElementTablePtr) dtd->elements;
2674
2675 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002676 if (uqname != NULL)
2677 name = uqname;
2678 cur = xmlHashLookup2(table, name, prefix);
2679 if (prefix != NULL) xmlFree(prefix);
2680 if (uqname != NULL) xmlFree(uqname);
2681 return(cur);
2682}
2683/**
2684 * xmlGetDtdElementDesc2:
2685 * @dtd: a pointer to the DtD to search
2686 * @name: the element name
2687 * @create: create an empty description if not found
2688 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002689 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002690 *
2691 * returns the xmlElementPtr if found or NULL
2692 */
2693
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002694static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002695xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2696 xmlElementTablePtr table;
2697 xmlElementPtr cur;
2698 xmlChar *uqname = NULL, *prefix = NULL;
2699
2700 if (dtd == NULL) return(NULL);
2701 if (dtd->elements == NULL) {
2702 if (!create)
2703 return(NULL);
2704 /*
2705 * Create the Element table if needed.
2706 */
2707 table = (xmlElementTablePtr) dtd->elements;
2708 if (table == NULL) {
2709 table = xmlCreateElementTable();
2710 dtd->elements = (void *) table;
2711 }
2712 if (table == NULL) {
2713 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002714 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002715 return(NULL);
2716 }
2717 }
2718 table = (xmlElementTablePtr) dtd->elements;
2719
2720 uqname = xmlSplitQName2(name, &prefix);
2721 if (uqname != NULL)
2722 name = uqname;
2723 cur = xmlHashLookup2(table, name, prefix);
2724 if ((cur == NULL) && (create)) {
2725 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2726 if (cur == NULL) {
2727 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002728 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002729 return(NULL);
2730 }
2731 memset(cur, 0, sizeof(xmlElement));
2732 cur->type = XML_ELEMENT_DECL;
2733
2734 /*
2735 * fill the structure.
2736 */
2737 cur->name = xmlStrdup(name);
2738 cur->prefix = xmlStrdup(prefix);
2739 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2740
2741 xmlHashAddEntry2(table, name, prefix, cur);
2742 }
2743 if (prefix != NULL) xmlFree(prefix);
2744 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002745 return(cur);
2746}
2747
2748/**
2749 * xmlGetDtdQElementDesc:
2750 * @dtd: a pointer to the DtD to search
2751 * @name: the element name
2752 * @prefix: the element namespace prefix
2753 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002754 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002755 *
2756 * returns the xmlElementPtr if found or NULL
2757 */
2758
Daniel Veillard48da9102001-08-07 01:10:10 +00002759xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002760xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2761 const xmlChar *prefix) {
2762 xmlElementTablePtr table;
2763
2764 if (dtd == NULL) return(NULL);
2765 if (dtd->elements == NULL) return(NULL);
2766 table = (xmlElementTablePtr) dtd->elements;
2767
2768 return(xmlHashLookup2(table, name, prefix));
2769}
2770
2771/**
2772 * xmlGetDtdAttrDesc:
2773 * @dtd: a pointer to the DtD to search
2774 * @elem: the element name
2775 * @name: the attribute name
2776 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002777 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002778 * this element.
2779 *
2780 * returns the xmlAttributePtr if found or NULL
2781 */
2782
2783xmlAttributePtr
2784xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2785 xmlAttributeTablePtr table;
2786 xmlAttributePtr cur;
2787 xmlChar *uqname = NULL, *prefix = NULL;
2788
2789 if (dtd == NULL) return(NULL);
2790 if (dtd->attributes == NULL) return(NULL);
2791
2792 table = (xmlAttributeTablePtr) dtd->attributes;
2793 if (table == NULL)
2794 return(NULL);
2795
2796 uqname = xmlSplitQName2(name, &prefix);
2797
2798 if (uqname != NULL) {
2799 cur = xmlHashLookup3(table, uqname, prefix, elem);
2800 if (prefix != NULL) xmlFree(prefix);
2801 if (uqname != NULL) xmlFree(uqname);
2802 } else
2803 cur = xmlHashLookup3(table, name, NULL, elem);
2804 return(cur);
2805}
2806
2807/**
2808 * xmlGetDtdQAttrDesc:
2809 * @dtd: a pointer to the DtD to search
2810 * @elem: the element name
2811 * @name: the attribute name
2812 * @prefix: the attribute namespace prefix
2813 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002814 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002815 * this element.
2816 *
2817 * returns the xmlAttributePtr if found or NULL
2818 */
2819
Daniel Veillard48da9102001-08-07 01:10:10 +00002820xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002821xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2822 const xmlChar *prefix) {
2823 xmlAttributeTablePtr table;
2824
2825 if (dtd == NULL) return(NULL);
2826 if (dtd->attributes == NULL) return(NULL);
2827 table = (xmlAttributeTablePtr) dtd->attributes;
2828
2829 return(xmlHashLookup3(table, name, prefix, elem));
2830}
2831
2832/**
2833 * xmlGetDtdNotationDesc:
2834 * @dtd: a pointer to the DtD to search
2835 * @name: the notation name
2836 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002837 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002838 *
2839 * returns the xmlNotationPtr if found or NULL
2840 */
2841
2842xmlNotationPtr
2843xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2844 xmlNotationTablePtr table;
2845
2846 if (dtd == NULL) return(NULL);
2847 if (dtd->notations == NULL) return(NULL);
2848 table = (xmlNotationTablePtr) dtd->notations;
2849
2850 return(xmlHashLookup(table, name));
2851}
2852
2853/**
2854 * xmlValidateNotationUse:
2855 * @ctxt: the validation context
2856 * @doc: the document
2857 * @notationName: the notation name to check
2858 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002859 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002860 * - [ VC: Notation Declared ]
2861 *
2862 * returns 1 if valid or 0 otherwise
2863 */
2864
2865int
2866xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2867 const xmlChar *notationName) {
2868 xmlNotationPtr notaDecl;
2869 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2870
2871 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2872 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2873 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2874
Daniel Veillarde637c4a2003-03-30 21:10:09 +00002875 if ((notaDecl == NULL) && (ctxt != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00002876 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2877 notationName);
2878 return(0);
2879 }
2880 return(1);
2881}
2882
2883/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002884 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00002885 * @doc: the document
2886 * @name: the element name
2887 *
2888 * Search in the DtDs whether an element accept Mixed content (or ANY)
2889 * basically if it is supposed to accept text childs
2890 *
2891 * returns 0 if no, 1 if yes, and -1 if no element description is available
2892 */
2893
2894int
2895xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2896 xmlElementPtr elemDecl;
2897
2898 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2899
2900 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2901 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2902 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2903 if (elemDecl == NULL) return(-1);
2904 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002905 case XML_ELEMENT_TYPE_UNDEFINED:
2906 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002907 case XML_ELEMENT_TYPE_ELEMENT:
2908 return(0);
2909 case XML_ELEMENT_TYPE_EMPTY:
2910 /*
2911 * return 1 for EMPTY since we want VC error to pop up
2912 * on <empty> </empty> for example
2913 */
2914 case XML_ELEMENT_TYPE_ANY:
2915 case XML_ELEMENT_TYPE_MIXED:
2916 return(1);
2917 }
2918 return(1);
2919}
2920
2921/**
2922 * xmlValidateNameValue:
2923 * @value: an Name value
2924 *
2925 * Validate that the given value match Name production
2926 *
2927 * returns 1 if valid or 0 otherwise
2928 */
2929
Daniel Veillard9b731d72002-04-14 12:56:08 +00002930int
Owen Taylor3473f882001-02-23 17:55:21 +00002931xmlValidateNameValue(const xmlChar *value) {
2932 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002933 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002934
2935 if (value == NULL) return(0);
2936 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002937 val = xmlStringCurrentChar(NULL, cur, &len);
2938 cur += len;
2939 if (!IS_LETTER(val) && (val != '_') &&
2940 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002941 return(0);
2942 }
2943
Daniel Veillardd8224e02002-01-13 15:43:22 +00002944 val = xmlStringCurrentChar(NULL, cur, &len);
2945 cur += len;
2946 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2947 (val == '.') || (val == '-') ||
2948 (val == '_') || (val == ':') ||
2949 (IS_COMBINING(val)) ||
2950 (IS_EXTENDER(val))) {
2951 val = xmlStringCurrentChar(NULL, cur, &len);
2952 cur += len;
2953 }
Owen Taylor3473f882001-02-23 17:55:21 +00002954
Daniel Veillardd8224e02002-01-13 15:43:22 +00002955 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002956
2957 return(1);
2958}
2959
2960/**
2961 * xmlValidateNamesValue:
2962 * @value: an Names value
2963 *
2964 * Validate that the given value match Names production
2965 *
2966 * returns 1 if valid or 0 otherwise
2967 */
2968
Daniel Veillard9b731d72002-04-14 12:56:08 +00002969int
Owen Taylor3473f882001-02-23 17:55:21 +00002970xmlValidateNamesValue(const xmlChar *value) {
2971 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002972 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002973
2974 if (value == NULL) return(0);
2975 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002976 val = xmlStringCurrentChar(NULL, cur, &len);
2977 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002978
Daniel Veillardd8224e02002-01-13 15:43:22 +00002979 if (!IS_LETTER(val) && (val != '_') &&
2980 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002981 return(0);
2982 }
2983
Daniel Veillardd8224e02002-01-13 15:43:22 +00002984 val = xmlStringCurrentChar(NULL, cur, &len);
2985 cur += len;
2986 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2987 (val == '.') || (val == '-') ||
2988 (val == '_') || (val == ':') ||
2989 (IS_COMBINING(val)) ||
2990 (IS_EXTENDER(val))) {
2991 val = xmlStringCurrentChar(NULL, cur, &len);
2992 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002993 }
2994
Daniel Veillardd8224e02002-01-13 15:43:22 +00002995 while (IS_BLANK(val)) {
2996 while (IS_BLANK(val)) {
2997 val = xmlStringCurrentChar(NULL, cur, &len);
2998 cur += len;
2999 }
3000
3001 if (!IS_LETTER(val) && (val != '_') &&
3002 (val != ':')) {
3003 return(0);
3004 }
3005 val = xmlStringCurrentChar(NULL, cur, &len);
3006 cur += len;
3007
3008 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3009 (val == '.') || (val == '-') ||
3010 (val == '_') || (val == ':') ||
3011 (IS_COMBINING(val)) ||
3012 (IS_EXTENDER(val))) {
3013 val = xmlStringCurrentChar(NULL, cur, &len);
3014 cur += len;
3015 }
3016 }
3017
3018 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003019
3020 return(1);
3021}
3022
3023/**
3024 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003025 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00003026 *
3027 * Validate that the given value match Nmtoken production
3028 *
3029 * [ VC: Name Token ]
3030 *
3031 * returns 1 if valid or 0 otherwise
3032 */
3033
Daniel Veillard9b731d72002-04-14 12:56:08 +00003034int
Owen Taylor3473f882001-02-23 17:55:21 +00003035xmlValidateNmtokenValue(const xmlChar *value) {
3036 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003037 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003038
3039 if (value == NULL) return(0);
3040 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003041 val = xmlStringCurrentChar(NULL, cur, &len);
3042 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003043
Daniel Veillardd8224e02002-01-13 15:43:22 +00003044 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3045 (val != '.') && (val != '-') &&
3046 (val != '_') && (val != ':') &&
3047 (!IS_COMBINING(val)) &&
3048 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00003049 return(0);
3050
Daniel Veillardd8224e02002-01-13 15:43:22 +00003051 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3052 (val == '.') || (val == '-') ||
3053 (val == '_') || (val == ':') ||
3054 (IS_COMBINING(val)) ||
3055 (IS_EXTENDER(val))) {
3056 val = xmlStringCurrentChar(NULL, cur, &len);
3057 cur += len;
3058 }
Owen Taylor3473f882001-02-23 17:55:21 +00003059
Daniel Veillardd8224e02002-01-13 15:43:22 +00003060 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003061
3062 return(1);
3063}
3064
3065/**
3066 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003067 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003068 *
3069 * Validate that the given value match Nmtokens production
3070 *
3071 * [ VC: Name Token ]
3072 *
3073 * returns 1 if valid or 0 otherwise
3074 */
3075
Daniel Veillard9b731d72002-04-14 12:56:08 +00003076int
Owen Taylor3473f882001-02-23 17:55:21 +00003077xmlValidateNmtokensValue(const xmlChar *value) {
3078 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003079 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003080
3081 if (value == NULL) return(0);
3082 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003083 val = xmlStringCurrentChar(NULL, cur, &len);
3084 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003085
Daniel Veillardd8224e02002-01-13 15:43:22 +00003086 while (IS_BLANK(val)) {
3087 val = xmlStringCurrentChar(NULL, cur, &len);
3088 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003089 }
3090
Daniel Veillardd8224e02002-01-13 15:43:22 +00003091 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3092 (val != '.') && (val != '-') &&
3093 (val != '_') && (val != ':') &&
3094 (!IS_COMBINING(val)) &&
3095 (!IS_EXTENDER(val)))
3096 return(0);
3097
3098 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3099 (val == '.') || (val == '-') ||
3100 (val == '_') || (val == ':') ||
3101 (IS_COMBINING(val)) ||
3102 (IS_EXTENDER(val))) {
3103 val = xmlStringCurrentChar(NULL, cur, &len);
3104 cur += len;
3105 }
3106
3107 while (IS_BLANK(val)) {
3108 while (IS_BLANK(val)) {
3109 val = xmlStringCurrentChar(NULL, cur, &len);
3110 cur += len;
3111 }
3112 if (val == 0) return(1);
3113
3114 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3115 (val != '.') && (val != '-') &&
3116 (val != '_') && (val != ':') &&
3117 (!IS_COMBINING(val)) &&
3118 (!IS_EXTENDER(val)))
3119 return(0);
3120
3121 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3122 (val == '.') || (val == '-') ||
3123 (val == '_') || (val == ':') ||
3124 (IS_COMBINING(val)) ||
3125 (IS_EXTENDER(val))) {
3126 val = xmlStringCurrentChar(NULL, cur, &len);
3127 cur += len;
3128 }
3129 }
3130
3131 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003132
3133 return(1);
3134}
3135
3136/**
3137 * xmlValidateNotationDecl:
3138 * @ctxt: the validation context
3139 * @doc: a document instance
3140 * @nota: a notation definition
3141 *
3142 * Try to validate a single notation definition
3143 * basically it does the following checks as described by the
3144 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003145 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003146 * But this function get called anyway ...
3147 *
3148 * returns 1 if valid or 0 otherwise
3149 */
3150
3151int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003152xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3153 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003154 int ret = 1;
3155
3156 return(ret);
3157}
3158
3159/**
3160 * xmlValidateAttributeValue:
3161 * @type: an attribute type
3162 * @value: an attribute value
3163 *
3164 * Validate that the given attribute value match the proper production
3165 *
3166 * [ VC: ID ]
3167 * Values of type ID must match the Name production....
3168 *
3169 * [ VC: IDREF ]
3170 * Values of type IDREF must match the Name production, and values
3171 * of type IDREFS must match Names ...
3172 *
3173 * [ VC: Entity Name ]
3174 * Values of type ENTITY must match the Name production, values
3175 * of type ENTITIES must match Names ...
3176 *
3177 * [ VC: Name Token ]
3178 * Values of type NMTOKEN must match the Nmtoken production; values
3179 * of type NMTOKENS must match Nmtokens.
3180 *
3181 * returns 1 if valid or 0 otherwise
3182 */
3183
3184int
3185xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3186 switch (type) {
3187 case XML_ATTRIBUTE_ENTITIES:
3188 case XML_ATTRIBUTE_IDREFS:
3189 return(xmlValidateNamesValue(value));
3190 case XML_ATTRIBUTE_ENTITY:
3191 case XML_ATTRIBUTE_IDREF:
3192 case XML_ATTRIBUTE_ID:
3193 case XML_ATTRIBUTE_NOTATION:
3194 return(xmlValidateNameValue(value));
3195 case XML_ATTRIBUTE_NMTOKENS:
3196 case XML_ATTRIBUTE_ENUMERATION:
3197 return(xmlValidateNmtokensValue(value));
3198 case XML_ATTRIBUTE_NMTOKEN:
3199 return(xmlValidateNmtokenValue(value));
3200 case XML_ATTRIBUTE_CDATA:
3201 break;
3202 }
3203 return(1);
3204}
3205
3206/**
3207 * xmlValidateAttributeValue2:
3208 * @ctxt: the validation context
3209 * @doc: the document
3210 * @name: the attribute name (used for error reporting only)
3211 * @type: the attribute type
3212 * @value: the attribute value
3213 *
3214 * Validate that the given attribute value match a given type.
3215 * This typically cannot be done before having finished parsing
3216 * the subsets.
3217 *
3218 * [ VC: IDREF ]
3219 * Values of type IDREF must match one of the declared IDs
3220 * Values of type IDREFS must match a sequence of the declared IDs
3221 * each Name must match the value of an ID attribute on some element
3222 * in the XML document; i.e. IDREF values must match the value of
3223 * some ID attribute
3224 *
3225 * [ VC: Entity Name ]
3226 * Values of type ENTITY must match one declared entity
3227 * Values of type ENTITIES must match a sequence of declared entities
3228 *
3229 * [ VC: Notation Attributes ]
3230 * all notation names in the declaration must be declared.
3231 *
3232 * returns 1 if valid or 0 otherwise
3233 */
3234
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003235static int
Owen Taylor3473f882001-02-23 17:55:21 +00003236xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3237 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3238 int ret = 1;
3239 switch (type) {
3240 case XML_ATTRIBUTE_IDREFS:
3241 case XML_ATTRIBUTE_IDREF:
3242 case XML_ATTRIBUTE_ID:
3243 case XML_ATTRIBUTE_NMTOKENS:
3244 case XML_ATTRIBUTE_ENUMERATION:
3245 case XML_ATTRIBUTE_NMTOKEN:
3246 case XML_ATTRIBUTE_CDATA:
3247 break;
3248 case XML_ATTRIBUTE_ENTITY: {
3249 xmlEntityPtr ent;
3250
3251 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003252 if ((ent == NULL) && (doc->standalone == 1)) {
3253 doc->standalone = 0;
3254 ent = xmlGetDocEntity(doc, value);
3255 if (ent != NULL) {
3256 VERROR(ctxt->userData,
3257"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3258 name, value);
3259 /* WAIT to get answer from the Core WG on this
3260 ret = 0;
3261 */
3262 }
3263 }
Owen Taylor3473f882001-02-23 17:55:21 +00003264 if (ent == NULL) {
3265 VERROR(ctxt->userData,
3266 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3267 name, value);
3268 ret = 0;
3269 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3270 VERROR(ctxt->userData,
3271 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3272 name, value);
3273 ret = 0;
3274 }
3275 break;
3276 }
3277 case XML_ATTRIBUTE_ENTITIES: {
3278 xmlChar *dup, *nam = NULL, *cur, save;
3279 xmlEntityPtr ent;
3280
3281 dup = xmlStrdup(value);
3282 if (dup == NULL)
3283 return(0);
3284 cur = dup;
3285 while (*cur != 0) {
3286 nam = cur;
3287 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3288 save = *cur;
3289 *cur = 0;
3290 ent = xmlGetDocEntity(doc, nam);
3291 if (ent == NULL) {
3292 VERROR(ctxt->userData,
3293 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3294 name, nam);
3295 ret = 0;
3296 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3297 VERROR(ctxt->userData,
3298 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3299 name, nam);
3300 ret = 0;
3301 }
3302 if (save == 0)
3303 break;
3304 *cur = save;
3305 while (IS_BLANK(*cur)) cur++;
3306 }
3307 xmlFree(dup);
3308 break;
3309 }
3310 case XML_ATTRIBUTE_NOTATION: {
3311 xmlNotationPtr nota;
3312
3313 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3314 if ((nota == NULL) && (doc->extSubset != NULL))
3315 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3316
3317 if (nota == NULL) {
3318 VERROR(ctxt->userData,
3319 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3320 name, value);
3321 ret = 0;
3322 }
3323 break;
3324 }
3325 }
3326 return(ret);
3327}
3328
3329/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003330 * xmlValidCtxtNormalizeAttributeValue:
3331 * @ctxt: the validation context
3332 * @doc: the document
3333 * @elem: the parent
3334 * @name: the attribute name
3335 * @value: the attribute value
3336 * @ctxt: the validation context or NULL
3337 *
3338 * Does the validation related extra step of the normalization of attribute
3339 * values:
3340 *
3341 * If the declared value is not CDATA, then the XML processor must further
3342 * process the normalized attribute value by discarding any leading and
3343 * trailing space (#x20) characters, and by replacing sequences of space
3344 * (#x20) characters by single space (#x20) character.
3345 *
3346 * Also check VC: Standalone Document Declaration in P32, and update
3347 * ctxt->valid accordingly
3348 *
3349 * returns a new normalized string if normalization is needed, NULL otherwise
3350 * the caller must free the returned value.
3351 */
3352
3353xmlChar *
3354xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3355 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3356 xmlChar *ret, *dst;
3357 const xmlChar *src;
3358 xmlAttributePtr attrDecl = NULL;
3359 int extsubset = 0;
3360
3361 if (doc == NULL) return(NULL);
3362 if (elem == NULL) return(NULL);
3363 if (name == NULL) return(NULL);
3364 if (value == NULL) return(NULL);
3365
3366 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003367 xmlChar fn[50];
3368 xmlChar *fullname;
3369
3370 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3371 if (fullname == NULL)
3372 return(0);
3373 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003374 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003375 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003376 if (attrDecl != NULL)
3377 extsubset = 1;
3378 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003379 if ((fullname != fn) && (fullname != elem->name))
3380 xmlFree(fullname);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003381 }
3382 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3383 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3384 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3385 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3386 if (attrDecl != NULL)
3387 extsubset = 1;
3388 }
3389
3390 if (attrDecl == NULL)
3391 return(NULL);
3392 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3393 return(NULL);
3394
3395 ret = xmlStrdup(value);
3396 if (ret == NULL)
3397 return(NULL);
3398 src = value;
3399 dst = ret;
3400 while (*src == 0x20) src++;
3401 while (*src != 0) {
3402 if (*src == 0x20) {
3403 while (*src == 0x20) src++;
3404 if (*src != 0)
3405 *dst++ = 0x20;
3406 } else {
3407 *dst++ = *src++;
3408 }
3409 }
3410 *dst = 0;
3411 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3412 VERROR(ctxt->userData,
3413"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3414 name, elem->name);
3415 ctxt->valid = 0;
3416 }
3417 return(ret);
3418}
3419
3420/**
Owen Taylor3473f882001-02-23 17:55:21 +00003421 * xmlValidNormalizeAttributeValue:
3422 * @doc: the document
3423 * @elem: the parent
3424 * @name: the attribute name
3425 * @value: the attribute value
3426 *
3427 * Does the validation related extra step of the normalization of attribute
3428 * values:
3429 *
3430 * If the declared value is not CDATA, then the XML processor must further
3431 * process the normalized attribute value by discarding any leading and
3432 * trailing space (#x20) characters, and by replacing sequences of space
3433 * (#x20) characters by single space (#x20) character.
3434 *
3435 * returns a new normalized string if normalization is needed, NULL otherwise
3436 * the caller must free the returned value.
3437 */
3438
3439xmlChar *
3440xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3441 const xmlChar *name, const xmlChar *value) {
3442 xmlChar *ret, *dst;
3443 const xmlChar *src;
3444 xmlAttributePtr attrDecl = NULL;
3445
3446 if (doc == NULL) return(NULL);
3447 if (elem == NULL) return(NULL);
3448 if (name == NULL) return(NULL);
3449 if (value == NULL) return(NULL);
3450
3451 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003452 xmlChar fn[50];
3453 xmlChar *fullname;
3454
3455 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3456 if (fullname == NULL)
3457 return(0);
3458 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name);
Owen Taylor3473f882001-02-23 17:55:21 +00003459 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003460 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name);
3461 if ((fullname != fn) && (fullname != elem->name))
3462 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003463 }
3464 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3465 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3466 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3467
3468 if (attrDecl == NULL)
3469 return(NULL);
3470 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3471 return(NULL);
3472
3473 ret = xmlStrdup(value);
3474 if (ret == NULL)
3475 return(NULL);
3476 src = value;
3477 dst = ret;
3478 while (*src == 0x20) src++;
3479 while (*src != 0) {
3480 if (*src == 0x20) {
3481 while (*src == 0x20) src++;
3482 if (*src != 0)
3483 *dst++ = 0x20;
3484 } else {
3485 *dst++ = *src++;
3486 }
3487 }
3488 *dst = 0;
3489 return(ret);
3490}
3491
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003492static void
Owen Taylor3473f882001-02-23 17:55:21 +00003493xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003494 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003495 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3496}
3497
3498/**
3499 * xmlValidateAttributeDecl:
3500 * @ctxt: the validation context
3501 * @doc: a document instance
3502 * @attr: an attribute definition
3503 *
3504 * Try to validate a single attribute definition
3505 * basically it does the following checks as described by the
3506 * XML-1.0 recommendation:
3507 * - [ VC: Attribute Default Legal ]
3508 * - [ VC: Enumeration ]
3509 * - [ VC: ID Attribute Default ]
3510 *
3511 * The ID/IDREF uniqueness and matching are done separately
3512 *
3513 * returns 1 if valid or 0 otherwise
3514 */
3515
3516int
3517xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3518 xmlAttributePtr attr) {
3519 int ret = 1;
3520 int val;
3521 CHECK_DTD;
3522 if(attr == NULL) return(1);
3523
3524 /* Attribute Default Legal */
3525 /* Enumeration */
3526 if (attr->defaultValue != NULL) {
3527 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3528 if (val == 0) {
3529 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003530 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003531 attr->name, attr->elem);
3532 }
3533 ret &= val;
3534 }
3535
3536 /* ID Attribute Default */
3537 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3538 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3539 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3540 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003541 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003542 attr->name, attr->elem);
3543 ret = 0;
3544 }
3545
3546 /* One ID per Element Type */
3547 if (attr->atype == XML_ATTRIBUTE_ID) {
3548 int nbId;
3549
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003550 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003551 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3552 attr->elem);
3553 if (elem != NULL) {
3554 nbId = xmlScanIDAttributeDecl(NULL, elem);
3555 } else {
3556 xmlAttributeTablePtr table;
3557
3558 /*
3559 * The attribute may be declared in the internal subset and the
3560 * element in the external subset.
3561 */
3562 nbId = 0;
3563 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3564 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3565 xmlValidateAttributeIdCallback, &nbId);
3566 }
3567 if (nbId > 1) {
3568 VERROR(ctxt->userData,
3569 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3570 attr->elem, nbId, attr->name);
3571 } else if (doc->extSubset != NULL) {
3572 int extId = 0;
3573 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3574 if (elem != NULL) {
3575 extId = xmlScanIDAttributeDecl(NULL, elem);
3576 }
3577 if (extId > 1) {
3578 VERROR(ctxt->userData,
3579 "Element %s has %d ID attribute defined in the external subset : %s\n",
3580 attr->elem, extId, attr->name);
3581 } else if (extId + nbId > 1) {
3582 VERROR(ctxt->userData,
3583"Element %s has ID attributes defined in the internal and external subset : %s\n",
3584 attr->elem, attr->name);
3585 }
3586 }
3587 }
3588
3589 /* Validity Constraint: Enumeration */
3590 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3591 xmlEnumerationPtr tree = attr->tree;
3592 while (tree != NULL) {
3593 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3594 tree = tree->next;
3595 }
3596 if (tree == NULL) {
3597 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003598"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003599 attr->defaultValue, attr->name, attr->elem);
3600 ret = 0;
3601 }
3602 }
3603
3604 return(ret);
3605}
3606
3607/**
3608 * xmlValidateElementDecl:
3609 * @ctxt: the validation context
3610 * @doc: a document instance
3611 * @elem: an element definition
3612 *
3613 * Try to validate a single element definition
3614 * basically it does the following checks as described by the
3615 * XML-1.0 recommendation:
3616 * - [ VC: One ID per Element Type ]
3617 * - [ VC: No Duplicate Types ]
3618 * - [ VC: Unique Element Type Declaration ]
3619 *
3620 * returns 1 if valid or 0 otherwise
3621 */
3622
3623int
3624xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3625 xmlElementPtr elem) {
3626 int ret = 1;
3627 xmlElementPtr tst;
3628
3629 CHECK_DTD;
3630
3631 if (elem == NULL) return(1);
3632
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003633#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003634#ifdef LIBXML_REGEXP_ENABLED
3635 /* Build the regexp associated to the content model */
3636 ret = xmlValidBuildContentModel(ctxt, elem);
3637#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003638#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003639
Owen Taylor3473f882001-02-23 17:55:21 +00003640 /* No Duplicate Types */
3641 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3642 xmlElementContentPtr cur, next;
3643 const xmlChar *name;
3644
3645 cur = elem->content;
3646 while (cur != NULL) {
3647 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3648 if (cur->c1 == NULL) break;
3649 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3650 name = cur->c1->name;
3651 next = cur->c2;
3652 while (next != NULL) {
3653 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3654 if (xmlStrEqual(next->name, name)) {
3655 VERROR(ctxt->userData,
3656 "Definition of %s has duplicate references of %s\n",
3657 elem->name, name);
3658 ret = 0;
3659 }
3660 break;
3661 }
3662 if (next->c1 == NULL) break;
3663 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3664 if (xmlStrEqual(next->c1->name, name)) {
3665 VERROR(ctxt->userData,
3666 "Definition of %s has duplicate references of %s\n",
3667 elem->name, name);
3668 ret = 0;
3669 }
3670 next = next->c2;
3671 }
3672 }
3673 cur = cur->c2;
3674 }
3675 }
3676
3677 /* VC: Unique Element Type Declaration */
3678 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003679 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003680 ((tst->prefix == elem->prefix) ||
3681 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003682 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003683 VERROR(ctxt->userData, "Redefinition of element %s\n",
3684 elem->name);
3685 ret = 0;
3686 }
3687 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003688 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003689 ((tst->prefix == elem->prefix) ||
3690 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003691 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003692 VERROR(ctxt->userData, "Redefinition of element %s\n",
3693 elem->name);
3694 ret = 0;
3695 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003696 /* One ID per Element Type
3697 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003698 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3699 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003700 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003701 return(ret);
3702}
3703
3704/**
3705 * xmlValidateOneAttribute:
3706 * @ctxt: the validation context
3707 * @doc: a document instance
3708 * @elem: an element instance
3709 * @attr: an attribute instance
3710 * @value: the attribute value (without entities processing)
3711 *
3712 * Try to validate a single attribute for an element
3713 * basically it does the following checks as described by the
3714 * XML-1.0 recommendation:
3715 * - [ VC: Attribute Value Type ]
3716 * - [ VC: Fixed Attribute Default ]
3717 * - [ VC: Entity Name ]
3718 * - [ VC: Name Token ]
3719 * - [ VC: ID ]
3720 * - [ VC: IDREF ]
3721 * - [ VC: Entity Name ]
3722 * - [ VC: Notation Attributes ]
3723 *
3724 * The ID/IDREF uniqueness and matching are done separately
3725 *
3726 * returns 1 if valid or 0 otherwise
3727 */
3728
3729int
3730xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3731 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3732 /* xmlElementPtr elemDecl; */
3733 xmlAttributePtr attrDecl = NULL;
3734 int val;
3735 int ret = 1;
3736
3737 CHECK_DTD;
3738 if ((elem == NULL) || (elem->name == NULL)) return(0);
3739 if ((attr == NULL) || (attr->name == NULL)) return(0);
3740
3741 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003742 xmlChar fn[50];
3743 xmlChar *fullname;
3744
3745 fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50);
3746 if (fullname == NULL)
3747 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003748 if (attr->ns != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003749 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003750 attr->name, attr->ns->prefix);
3751 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003752 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Owen Taylor3473f882001-02-23 17:55:21 +00003753 attr->name, attr->ns->prefix);
3754 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003755 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003756 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3757 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
Daniel Veillardc00cda82003-04-07 10:22:39 +00003758 fullname, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003759 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003760 if ((fullname != fn) && (fullname != elem->name))
3761 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00003762 }
3763 if (attrDecl == NULL) {
3764 if (attr->ns != NULL) {
3765 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3766 attr->name, attr->ns->prefix);
3767 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3768 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3769 attr->name, attr->ns->prefix);
3770 } else {
3771 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3772 elem->name, attr->name);
3773 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3774 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3775 elem->name, attr->name);
3776 }
3777 }
3778
3779
3780 /* Validity Constraint: Attribute Value Type */
3781 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003782 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003783 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003784 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003785 attr->name, elem->name);
3786 return(0);
3787 }
3788 attr->atype = attrDecl->atype;
3789
3790 val = xmlValidateAttributeValue(attrDecl->atype, value);
3791 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003792 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003793 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003794 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003795 attr->name, elem->name);
3796 ret = 0;
3797 }
3798
3799 /* Validity constraint: Fixed Attribute Default */
3800 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3801 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003802 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003803 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003804 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003805 attr->name, elem->name, attrDecl->defaultValue);
3806 ret = 0;
3807 }
3808 }
3809
3810 /* Validity Constraint: ID uniqueness */
3811 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3812 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3813 ret = 0;
3814 }
3815
3816 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3817 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3818 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3819 ret = 0;
3820 }
3821
3822 /* Validity Constraint: Notation Attributes */
3823 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3824 xmlEnumerationPtr tree = attrDecl->tree;
3825 xmlNotationPtr nota;
3826
3827 /* First check that the given NOTATION was declared */
3828 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3829 if (nota == NULL)
3830 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3831
3832 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003833 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003834 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003835 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003836 value, attr->name, elem->name);
3837 ret = 0;
3838 }
3839
3840 /* Second, verify that it's among the list */
3841 while (tree != NULL) {
3842 if (xmlStrEqual(tree->name, value)) break;
3843 tree = tree->next;
3844 }
3845 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003846 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003847 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003848"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003849 value, attr->name, elem->name);
3850 ret = 0;
3851 }
3852 }
3853
3854 /* Validity Constraint: Enumeration */
3855 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3856 xmlEnumerationPtr tree = attrDecl->tree;
3857 while (tree != NULL) {
3858 if (xmlStrEqual(tree->name, value)) break;
3859 tree = tree->next;
3860 }
3861 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003862 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003863 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003864 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003865 value, attr->name, elem->name);
3866 ret = 0;
3867 }
3868 }
3869
3870 /* Fixed Attribute Default */
3871 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3872 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003873 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003874 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003875 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003876 attr->name, elem->name, attrDecl->defaultValue);
3877 ret = 0;
3878 }
3879
3880 /* Extra check for the attribute value */
3881 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3882 attrDecl->atype, value);
3883
3884 return(ret);
3885}
3886
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003887/**
3888 * xmlValidateOneNamespace:
3889 * @ctxt: the validation context
3890 * @doc: a document instance
3891 * @elem: an element instance
Daniel Veillarda9b66d02002-12-11 14:23:49 +00003892 * @prefix: the namespace prefix
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003893 * @ns: an namespace declaration instance
3894 * @value: the attribute value (without entities processing)
3895 *
3896 * Try to validate a single namespace declaration for an element
3897 * basically it does the following checks as described by the
3898 * XML-1.0 recommendation:
3899 * - [ VC: Attribute Value Type ]
3900 * - [ VC: Fixed Attribute Default ]
3901 * - [ VC: Entity Name ]
3902 * - [ VC: Name Token ]
3903 * - [ VC: ID ]
3904 * - [ VC: IDREF ]
3905 * - [ VC: Entity Name ]
3906 * - [ VC: Notation Attributes ]
3907 *
3908 * The ID/IDREF uniqueness and matching are done separately
3909 *
3910 * returns 1 if valid or 0 otherwise
3911 */
3912
3913int
3914xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3915xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
3916 /* xmlElementPtr elemDecl; */
3917 xmlAttributePtr attrDecl = NULL;
3918 int val;
3919 int ret = 1;
3920
3921 CHECK_DTD;
3922 if ((elem == NULL) || (elem->name == NULL)) return(0);
3923 if ((ns == NULL) || (ns->href == NULL)) return(0);
3924
3925 if (prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003926 xmlChar fn[50];
3927 xmlChar *fullname;
3928
3929 fullname = xmlBuildQName(elem->name, prefix, fn, 50);
3930 if (fullname == NULL) {
3931 VERROR(ctxt->userData, "Out of memory\n");
3932 return(0);
3933 }
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003934 if (ns->prefix != NULL) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003935 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003936 ns->prefix, BAD_CAST "xmlns");
3937 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003938 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003939 ns->prefix, BAD_CAST "xmlns");
3940 } else {
Daniel Veillardc00cda82003-04-07 10:22:39 +00003941 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003942 BAD_CAST "xmlns");
3943 if ((attrDecl == NULL) && (doc->extSubset != NULL))
Daniel Veillardc00cda82003-04-07 10:22:39 +00003944 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003945 BAD_CAST "xmlns");
3946 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00003947 if ((fullname != fn) && (fullname != elem->name))
3948 xmlFree(fullname);
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003949 }
3950 if (attrDecl == NULL) {
3951 if (ns->prefix != NULL) {
3952 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3953 ns->prefix, BAD_CAST "xmlns");
3954 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3955 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3956 ns->prefix, BAD_CAST "xmlns");
3957 } else {
3958 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3959 elem->name, BAD_CAST "xmlns");
3960 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3961 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3962 elem->name, BAD_CAST "xmlns");
3963 }
3964 }
3965
3966
3967 /* Validity Constraint: Attribute Value Type */
3968 if (attrDecl == NULL) {
3969 VECTXT(ctxt, elem);
3970 if (ns->prefix != NULL) {
3971 VERROR(ctxt->userData,
3972 "No declaration for attribute xmlns:%s of element %s\n",
3973 ns->prefix, elem->name);
3974 } else {
3975 VERROR(ctxt->userData,
3976 "No declaration for attribute xmlns of element %s\n",
3977 elem->name);
3978 }
3979 return(0);
3980 }
3981
3982 val = xmlValidateAttributeValue(attrDecl->atype, value);
3983 if (val == 0) {
3984 VECTXT(ctxt, elem);
3985 if (ns->prefix != NULL) {
3986 VERROR(ctxt->userData,
3987 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
3988 ns->prefix, elem->name);
3989 } else {
3990 VERROR(ctxt->userData,
3991 "Syntax of value for attribute xmlns of %s is not valid\n",
3992 elem->name);
3993 }
3994 ret = 0;
3995 }
3996
3997 /* Validity constraint: Fixed Attribute Default */
3998 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3999 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
4000 VECTXT(ctxt, elem);
4001 if (ns->prefix != NULL) {
4002 VERROR(ctxt->userData,
4003 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
4004 ns->prefix, elem->name, attrDecl->defaultValue);
4005 } else {
4006 VERROR(ctxt->userData,
4007 "Value for attribute xmlns of %s is different from default \"%s\"\n",
4008 elem->name, attrDecl->defaultValue);
4009 }
4010 ret = 0;
4011 }
4012 }
4013
4014 /* Validity Constraint: ID uniqueness */
4015 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
4016 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4017 ret = 0;
4018 }
4019
4020 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
4021 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
4022 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
4023 ret = 0;
4024 }
4025
4026 /* Validity Constraint: Notation Attributes */
4027 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
4028 xmlEnumerationPtr tree = attrDecl->tree;
4029 xmlNotationPtr nota;
4030
4031 /* First check that the given NOTATION was declared */
4032 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
4033 if (nota == NULL)
4034 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
4035
4036 if (nota == NULL) {
4037 VECTXT(ctxt, elem);
4038 if (ns->prefix != NULL) {
4039 VERROR(ctxt->userData,
4040 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
4041 value, ns->prefix, elem->name);
4042 } else {
4043 VERROR(ctxt->userData,
4044 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
4045 value, elem->name);
4046 }
4047 ret = 0;
4048 }
4049
4050 /* Second, verify that it's among the list */
4051 while (tree != NULL) {
4052 if (xmlStrEqual(tree->name, value)) break;
4053 tree = tree->next;
4054 }
4055 if (tree == NULL) {
4056 VECTXT(ctxt, elem);
4057 if (ns->prefix != NULL) {
4058 VERROR(ctxt->userData,
4059"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
4060 value, ns->prefix, elem->name);
4061 } else {
4062 VERROR(ctxt->userData,
4063"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
4064 value, elem->name);
4065 }
4066 ret = 0;
4067 }
4068 }
4069
4070 /* Validity Constraint: Enumeration */
4071 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4072 xmlEnumerationPtr tree = attrDecl->tree;
4073 while (tree != NULL) {
4074 if (xmlStrEqual(tree->name, value)) break;
4075 tree = tree->next;
4076 }
4077 if (tree == NULL) {
4078 VECTXT(ctxt, elem);
4079 if (ns->prefix != NULL) {
4080 VERROR(ctxt->userData,
4081"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4082 value, ns->prefix, elem->name);
4083 } else {
4084 VERROR(ctxt->userData,
4085"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4086 value, elem->name);
4087 }
4088 ret = 0;
4089 }
4090 }
4091
4092 /* Fixed Attribute Default */
4093 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4094 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4095 VECTXT(ctxt, elem);
4096 if (ns->prefix != NULL) {
4097 VERROR(ctxt->userData,
4098 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4099 ns->prefix, elem->name, attrDecl->defaultValue);
4100 } else {
4101 VERROR(ctxt->userData,
4102 "Value for attribute xmlns of %s must be \"%s\"\n",
4103 elem->name, attrDecl->defaultValue);
4104 }
4105 ret = 0;
4106 }
4107
4108 /* Extra check for the attribute value */
4109 if (ns->prefix != NULL) {
4110 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4111 attrDecl->atype, value);
4112 } else {
4113 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4114 attrDecl->atype, value);
4115 }
4116
4117 return(ret);
4118}
4119
Daniel Veillard118aed72002-09-24 14:13:13 +00004120#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004121/**
4122 * xmlValidateSkipIgnorable:
4123 * @ctxt: the validation context
4124 * @child: the child list
4125 *
4126 * Skip ignorable elements w.r.t. the validation process
4127 *
4128 * returns the first element to consider for validation of the content model
4129 */
4130
4131static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004132xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004133 while (child != NULL) {
4134 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004135 /* These things are ignored (skipped) during validation. */
4136 case XML_PI_NODE:
4137 case XML_COMMENT_NODE:
4138 case XML_XINCLUDE_START:
4139 case XML_XINCLUDE_END:
4140 child = child->next;
4141 break;
4142 case XML_TEXT_NODE:
4143 if (xmlIsBlankNode(child))
4144 child = child->next;
4145 else
4146 return(child);
4147 break;
4148 /* keep current node */
4149 default:
4150 return(child);
4151 }
4152 }
4153 return(child);
4154}
4155
4156/**
4157 * xmlValidateElementType:
4158 * @ctxt: the validation context
4159 *
4160 * Try to validate the content model of an element internal function
4161 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004162 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4163 * reference is found and -3 if the validation succeeded but
4164 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004165 */
4166
4167static int
4168xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004169 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004170 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004171
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004172 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004173 if ((NODE == NULL) && (CONT == NULL))
4174 return(1);
4175 if ((NODE == NULL) &&
4176 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4177 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4178 return(1);
4179 }
4180 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004181 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004182 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004183
4184 /*
4185 * We arrive here when more states need to be examined
4186 */
4187cont:
4188
4189 /*
4190 * We just recovered from a rollback generated by a possible
4191 * epsilon transition, go directly to the analysis phase
4192 */
4193 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004194 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004195 DEBUG_VALID_STATE(NODE, CONT)
4196 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004197 goto analyze;
4198 }
4199
4200 DEBUG_VALID_STATE(NODE, CONT)
4201 /*
4202 * we may have to save a backup state here. This is the equivalent
4203 * of handling epsilon transition in NFAs.
4204 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004205 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004206 ((CONT->parent == NULL) ||
4207 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004208 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004209 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004210 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004211 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004212 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4213 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004214 }
4215
4216
4217 /*
4218 * Check first if the content matches
4219 */
4220 switch (CONT->type) {
4221 case XML_ELEMENT_CONTENT_PCDATA:
4222 if (NODE == NULL) {
4223 DEBUG_VALID_MSG("pcdata failed no node");
4224 ret = 0;
4225 break;
4226 }
4227 if (NODE->type == XML_TEXT_NODE) {
4228 DEBUG_VALID_MSG("pcdata found, skip to next");
4229 /*
4230 * go to next element in the content model
4231 * skipping ignorable elems
4232 */
4233 do {
4234 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004235 NODE = xmlValidateSkipIgnorable(NODE);
4236 if ((NODE != NULL) &&
4237 (NODE->type == XML_ENTITY_REF_NODE))
4238 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004239 } while ((NODE != NULL) &&
4240 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004241 (NODE->type != XML_TEXT_NODE) &&
4242 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004243 ret = 1;
4244 break;
4245 } else {
4246 DEBUG_VALID_MSG("pcdata failed");
4247 ret = 0;
4248 break;
4249 }
4250 break;
4251 case XML_ELEMENT_CONTENT_ELEMENT:
4252 if (NODE == NULL) {
4253 DEBUG_VALID_MSG("element failed no node");
4254 ret = 0;
4255 break;
4256 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004257 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4258 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004259 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004260 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4261 ret = (CONT->prefix == NULL);
4262 } else if (CONT->prefix == NULL) {
4263 ret = 0;
4264 } else {
4265 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4266 }
4267 }
4268 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004269 DEBUG_VALID_MSG("element found, skip to next");
4270 /*
4271 * go to next element in the content model
4272 * skipping ignorable elems
4273 */
4274 do {
4275 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004276 NODE = xmlValidateSkipIgnorable(NODE);
4277 if ((NODE != NULL) &&
4278 (NODE->type == XML_ENTITY_REF_NODE))
4279 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004280 } while ((NODE != NULL) &&
4281 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004282 (NODE->type != XML_TEXT_NODE) &&
4283 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004284 } else {
4285 DEBUG_VALID_MSG("element failed");
4286 ret = 0;
4287 break;
4288 }
4289 break;
4290 case XML_ELEMENT_CONTENT_OR:
4291 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004292 * Small optimization.
4293 */
4294 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4295 if ((NODE == NULL) ||
4296 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4297 DEPTH++;
4298 CONT = CONT->c2;
4299 goto cont;
4300 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004301 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4302 ret = (CONT->c1->prefix == NULL);
4303 } else if (CONT->c1->prefix == NULL) {
4304 ret = 0;
4305 } else {
4306 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4307 }
4308 if (ret == 0) {
4309 DEPTH++;
4310 CONT = CONT->c2;
4311 goto cont;
4312 }
Daniel Veillard85349052001-04-20 13:48:21 +00004313 }
4314
4315 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004316 * save the second branch 'or' branch
4317 */
4318 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004319 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4320 OCCURS, ROLLBACK_OR) < 0)
4321 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004322 DEPTH++;
4323 CONT = CONT->c1;
4324 goto cont;
4325 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004326 /*
4327 * Small optimization.
4328 */
4329 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4330 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4331 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4332 if ((NODE == NULL) ||
4333 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4334 DEPTH++;
4335 CONT = CONT->c2;
4336 goto cont;
4337 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004338 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4339 ret = (CONT->c1->prefix == NULL);
4340 } else if (CONT->c1->prefix == NULL) {
4341 ret = 0;
4342 } else {
4343 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4344 }
4345 if (ret == 0) {
4346 DEPTH++;
4347 CONT = CONT->c2;
4348 goto cont;
4349 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004350 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004351 DEPTH++;
4352 CONT = CONT->c1;
4353 goto cont;
4354 }
4355
4356 /*
4357 * At this point handle going up in the tree
4358 */
4359 if (ret == -1) {
4360 DEBUG_VALID_MSG("error found returning");
4361 return(ret);
4362 }
4363analyze:
4364 while (CONT != NULL) {
4365 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004366 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004367 * this level.
4368 */
4369 if (ret == 0) {
4370 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004371 xmlNodePtr cur;
4372
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004373 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004374 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004375 DEBUG_VALID_MSG("Once branch failed, rollback");
4376 if (vstateVPop(ctxt) < 0 ) {
4377 DEBUG_VALID_MSG("exhaustion, failed");
4378 return(0);
4379 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004380 if (cur != ctxt->vstate->node)
4381 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004382 goto cont;
4383 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004384 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004385 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004386 DEBUG_VALID_MSG("Plus branch failed, rollback");
4387 if (vstateVPop(ctxt) < 0 ) {
4388 DEBUG_VALID_MSG("exhaustion, failed");
4389 return(0);
4390 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004391 if (cur != ctxt->vstate->node)
4392 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004393 goto cont;
4394 }
4395 DEBUG_VALID_MSG("Plus branch found");
4396 ret = 1;
4397 break;
4398 case XML_ELEMENT_CONTENT_MULT:
4399#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004400 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004401 DEBUG_VALID_MSG("Mult branch failed");
4402 } else {
4403 DEBUG_VALID_MSG("Mult branch found");
4404 }
4405#endif
4406 ret = 1;
4407 break;
4408 case XML_ELEMENT_CONTENT_OPT:
4409 DEBUG_VALID_MSG("Option branch failed");
4410 ret = 1;
4411 break;
4412 }
4413 } else {
4414 switch (CONT->ocur) {
4415 case XML_ELEMENT_CONTENT_OPT:
4416 DEBUG_VALID_MSG("Option branch succeeded");
4417 ret = 1;
4418 break;
4419 case XML_ELEMENT_CONTENT_ONCE:
4420 DEBUG_VALID_MSG("Once branch succeeded");
4421 ret = 1;
4422 break;
4423 case XML_ELEMENT_CONTENT_PLUS:
4424 if (STATE == ROLLBACK_PARENT) {
4425 DEBUG_VALID_MSG("Plus branch rollback");
4426 ret = 1;
4427 break;
4428 }
4429 if (NODE == NULL) {
4430 DEBUG_VALID_MSG("Plus branch exhausted");
4431 ret = 1;
4432 break;
4433 }
4434 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004435 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004436 goto cont;
4437 case XML_ELEMENT_CONTENT_MULT:
4438 if (STATE == ROLLBACK_PARENT) {
4439 DEBUG_VALID_MSG("Mult branch rollback");
4440 ret = 1;
4441 break;
4442 }
4443 if (NODE == NULL) {
4444 DEBUG_VALID_MSG("Mult branch exhausted");
4445 ret = 1;
4446 break;
4447 }
4448 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004449 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004450 goto cont;
4451 }
4452 }
4453 STATE = 0;
4454
4455 /*
4456 * Then act accordingly at the parent level
4457 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004458 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004459 if (CONT->parent == NULL)
4460 break;
4461
4462 switch (CONT->parent->type) {
4463 case XML_ELEMENT_CONTENT_PCDATA:
4464 DEBUG_VALID_MSG("Error: parent pcdata");
4465 return(-1);
4466 case XML_ELEMENT_CONTENT_ELEMENT:
4467 DEBUG_VALID_MSG("Error: parent element");
4468 return(-1);
4469 case XML_ELEMENT_CONTENT_OR:
4470 if (ret == 1) {
4471 DEBUG_VALID_MSG("Or succeeded");
4472 CONT = CONT->parent;
4473 DEPTH--;
4474 } else {
4475 DEBUG_VALID_MSG("Or failed");
4476 CONT = CONT->parent;
4477 DEPTH--;
4478 }
4479 break;
4480 case XML_ELEMENT_CONTENT_SEQ:
4481 if (ret == 0) {
4482 DEBUG_VALID_MSG("Sequence failed");
4483 CONT = CONT->parent;
4484 DEPTH--;
4485 } else if (CONT == CONT->parent->c1) {
4486 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4487 CONT = CONT->parent->c2;
4488 goto cont;
4489 } else {
4490 DEBUG_VALID_MSG("Sequence succeeded");
4491 CONT = CONT->parent;
4492 DEPTH--;
4493 }
4494 }
4495 }
4496 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004497 xmlNodePtr cur;
4498
4499 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004500 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4501 if (vstateVPop(ctxt) < 0 ) {
4502 DEBUG_VALID_MSG("exhaustion, failed");
4503 return(0);
4504 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004505 if (cur != ctxt->vstate->node)
4506 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004507 goto cont;
4508 }
4509 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004510 xmlNodePtr cur;
4511
4512 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004513 DEBUG_VALID_MSG("Failure, rollback");
4514 if (vstateVPop(ctxt) < 0 ) {
4515 DEBUG_VALID_MSG("exhaustion, failed");
4516 return(0);
4517 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004518 if (cur != ctxt->vstate->node)
4519 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004520 goto cont;
4521 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004522 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004523}
Daniel Veillard23e73572002-09-19 19:56:43 +00004524#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004525
4526/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004527 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004528 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004529 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004530 * @content: An element
4531 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4532 *
4533 * This will dump the list of elements to the buffer
4534 * Intended just for the debug routine
4535 */
4536static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004537xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004538 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004539 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004540
4541 if (node == NULL) return;
4542 if (glob) strcat(buf, "(");
4543 cur = node;
4544 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004545 len = strlen(buf);
4546 if (size - len < 50) {
4547 if ((size - len > 4) && (buf[len - 1] != '.'))
4548 strcat(buf, " ...");
4549 return;
4550 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004551 switch (cur->type) {
4552 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004553 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004554 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004555 if ((size - len > 4) && (buf[len - 1] != '.'))
4556 strcat(buf, " ...");
4557 return;
4558 }
4559 strcat(buf, (char *) cur->ns->prefix);
4560 strcat(buf, ":");
4561 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004562 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004563 if ((size - len > 4) && (buf[len - 1] != '.'))
4564 strcat(buf, " ...");
4565 return;
4566 }
4567 strcat(buf, (char *) cur->name);
4568 if (cur->next != NULL)
4569 strcat(buf, " ");
4570 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004571 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004572 if (xmlIsBlankNode(cur))
4573 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004574 case XML_CDATA_SECTION_NODE:
4575 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004576 strcat(buf, "CDATA");
4577 if (cur->next != NULL)
4578 strcat(buf, " ");
4579 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004580 case XML_ATTRIBUTE_NODE:
4581 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004582#ifdef LIBXML_DOCB_ENABLED
4583 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004584#endif
4585 case XML_HTML_DOCUMENT_NODE:
4586 case XML_DOCUMENT_TYPE_NODE:
4587 case XML_DOCUMENT_FRAG_NODE:
4588 case XML_NOTATION_NODE:
4589 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004590 strcat(buf, "???");
4591 if (cur->next != NULL)
4592 strcat(buf, " ");
4593 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004594 case XML_ENTITY_NODE:
4595 case XML_PI_NODE:
4596 case XML_DTD_NODE:
4597 case XML_COMMENT_NODE:
4598 case XML_ELEMENT_DECL:
4599 case XML_ATTRIBUTE_DECL:
4600 case XML_ENTITY_DECL:
4601 case XML_XINCLUDE_START:
4602 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004603 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004604 }
4605 cur = cur->next;
4606 }
4607 if (glob) strcat(buf, ")");
4608}
4609
4610/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004611 * xmlValidateElementContent:
4612 * @ctxt: the validation context
4613 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004614 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004615 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004616 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004617 *
4618 * Try to validate the content model of an element
4619 *
4620 * returns 1 if valid or 0 if not and -1 in case of error
4621 */
4622
4623static int
4624xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004625 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004626 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004627#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004628 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004629#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004630 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004631 xmlElementContentPtr cont;
4632 const xmlChar *name;
4633
4634 if (elemDecl == NULL)
4635 return(-1);
4636 cont = elemDecl->content;
4637 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004638
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004639#ifdef LIBXML_REGEXP_ENABLED
4640 /* Build the regexp associated to the content model */
4641 if (elemDecl->contModel == NULL)
4642 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4643 if (elemDecl->contModel == NULL) {
4644 ret = -1;
4645 } else {
4646 xmlRegExecCtxtPtr exec;
4647
Daniel Veillardec498e12003-02-05 11:01:50 +00004648 if (!xmlRegexpIsDeterminist(elemDecl->contModel)) {
4649 return(-1);
4650 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004651 ctxt->nodeMax = 0;
4652 ctxt->nodeNr = 0;
4653 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004654 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4655 if (exec != NULL) {
4656 cur = child;
4657 while (cur != NULL) {
4658 switch (cur->type) {
4659 case XML_ENTITY_REF_NODE:
4660 /*
4661 * Push the current node to be able to roll back
4662 * and process within the entity
4663 */
4664 if ((cur->children != NULL) &&
4665 (cur->children->children != NULL)) {
4666 nodeVPush(ctxt, cur);
4667 cur = cur->children->children;
4668 continue;
4669 }
4670 break;
4671 case XML_TEXT_NODE:
4672 if (xmlIsBlankNode(cur))
4673 break;
4674 ret = 0;
4675 goto fail;
4676 case XML_CDATA_SECTION_NODE:
Daniel Veillardea7751d2002-12-20 00:16:24 +00004677 /* TODO */
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004678 ret = 0;
4679 goto fail;
4680 case XML_ELEMENT_NODE:
4681 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00004682 xmlChar fn[50];
4683 xmlChar *fullname;
4684
4685 fullname = xmlBuildQName(cur->name,
4686 cur->ns->prefix, fn, 50);
4687 if (fullname == NULL) {
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004688 ret = -1;
4689 goto fail;
4690 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00004691 ret = xmlRegExecPushString(exec, fullname, NULL);
4692 if ((fullname != fn) && (fullname != cur->name))
4693 xmlFree(fullname);
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004694 } else {
4695 ret = xmlRegExecPushString(exec, cur->name, NULL);
4696 }
4697 break;
4698 default:
4699 break;
4700 }
4701 /*
4702 * Switch to next element
4703 */
4704 cur = cur->next;
4705 while (cur == NULL) {
4706 cur = nodeVPop(ctxt);
4707 if (cur == NULL)
4708 break;
4709 cur = cur->next;
4710 }
4711 }
4712 ret = xmlRegExecPushString(exec, NULL, NULL);
4713fail:
4714 xmlRegFreeExecCtxt(exec);
4715 }
4716 }
4717#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004718 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004719 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004720 */
4721 ctxt->vstateMax = 8;
4722 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4723 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4724 if (ctxt->vstateTab == NULL) {
4725 xmlGenericError(xmlGenericErrorContext,
4726 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004727 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004728 }
4729 /*
4730 * The first entry in the stack is reserved to the current state
4731 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004732 ctxt->nodeMax = 0;
4733 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004734 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004735 ctxt->vstate = &ctxt->vstateTab[0];
4736 ctxt->vstateNr = 1;
4737 CONT = cont;
4738 NODE = child;
4739 DEPTH = 0;
4740 OCCURS = 0;
4741 STATE = 0;
4742 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004743 if ((ret == -3) && (warn)) {
4744 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004745 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004746 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004747 /*
4748 * An entities reference appeared at this level.
4749 * Buid a minimal representation of this node content
4750 * sufficient to run the validation process on it
4751 */
4752 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004753 cur = child;
4754 while (cur != NULL) {
4755 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004756 case XML_ENTITY_REF_NODE:
4757 /*
4758 * Push the current node to be able to roll back
4759 * and process within the entity
4760 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004761 if ((cur->children != NULL) &&
4762 (cur->children->children != NULL)) {
4763 nodeVPush(ctxt, cur);
4764 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004765 continue;
4766 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004767 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004768 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004769 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004770 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004771 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004772 case XML_CDATA_SECTION_NODE:
4773 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004774 case XML_ELEMENT_NODE:
4775 /*
4776 * Allocate a new node and minimally fills in
4777 * what's required
4778 */
4779 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4780 if (tmp == NULL) {
4781 xmlGenericError(xmlGenericErrorContext,
4782 "xmlValidateElementContent : malloc failed\n");
4783 xmlFreeNodeList(repl);
4784 ret = -1;
4785 goto done;
4786 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004787 tmp->type = cur->type;
4788 tmp->name = cur->name;
4789 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004790 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004791 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004792 if (repl == NULL)
4793 repl = last = tmp;
4794 else {
4795 last->next = tmp;
4796 last = tmp;
4797 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004798 if (cur->type == XML_CDATA_SECTION_NODE) {
4799 /*
4800 * E59 spaces in CDATA does not match the
4801 * nonterminal S
4802 */
4803 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4804 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004805 break;
4806 default:
4807 break;
4808 }
4809 /*
4810 * Switch to next element
4811 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004812 cur = cur->next;
4813 while (cur == NULL) {
4814 cur = nodeVPop(ctxt);
4815 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004816 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004817 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004818 }
4819 }
4820
4821 /*
4822 * Relaunch the validation
4823 */
4824 ctxt->vstate = &ctxt->vstateTab[0];
4825 ctxt->vstateNr = 1;
4826 CONT = cont;
4827 NODE = repl;
4828 DEPTH = 0;
4829 OCCURS = 0;
4830 STATE = 0;
4831 ret = xmlValidateElementType(ctxt);
4832 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004833#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004834 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004835 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4836 char expr[5000];
4837 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004838
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004839 expr[0] = 0;
4840 xmlSnprintfElementContent(expr, 5000, cont, 1);
4841 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004842#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004843 if (repl != NULL)
4844 xmlSnprintfElements(list, 5000, repl, 1);
4845 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004846#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004847 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004848
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004849 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004850 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004851 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004852 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004853 name, expr, list);
4854 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004855 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004856 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004857 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004858 expr, list);
4859 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004860 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004861 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004862 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004863 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004864 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004865 name);
4866 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004867 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004868 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004869 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004870 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004871 }
4872 ret = 0;
4873 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004874 if (ret == -3)
4875 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004876
Daniel Veillard23e73572002-09-19 19:56:43 +00004877#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004878done:
4879 /*
4880 * Deallocate the copy if done, and free up the validation stack
4881 */
4882 while (repl != NULL) {
4883 tmp = repl->next;
4884 xmlFree(repl);
4885 repl = tmp;
4886 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004887 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004888 if (ctxt->vstateTab != NULL) {
4889 xmlFree(ctxt->vstateTab);
4890 ctxt->vstateTab = NULL;
4891 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004892#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004893 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004894 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004895 if (ctxt->nodeTab != NULL) {
4896 xmlFree(ctxt->nodeTab);
4897 ctxt->nodeTab = NULL;
4898 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004899 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004900
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004901}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004902
Owen Taylor3473f882001-02-23 17:55:21 +00004903/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004904 * xmlValidateCdataElement:
4905 * @ctxt: the validation context
4906 * @doc: a document instance
4907 * @elem: an element instance
4908 *
4909 * Check that an element follows #CDATA
4910 *
4911 * returns 1 if valid or 0 otherwise
4912 */
4913static int
4914xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4915 xmlNodePtr elem) {
4916 int ret = 1;
4917 xmlNodePtr cur, child;
4918
Daniel Veillardceb09b92002-10-04 11:46:37 +00004919 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004920 return(0);
4921
4922 child = elem->children;
4923
4924 cur = child;
4925 while (cur != NULL) {
4926 switch (cur->type) {
4927 case XML_ENTITY_REF_NODE:
4928 /*
4929 * Push the current node to be able to roll back
4930 * and process within the entity
4931 */
4932 if ((cur->children != NULL) &&
4933 (cur->children->children != NULL)) {
4934 nodeVPush(ctxt, cur);
4935 cur = cur->children->children;
4936 continue;
4937 }
4938 break;
4939 case XML_COMMENT_NODE:
4940 case XML_PI_NODE:
4941 case XML_TEXT_NODE:
4942 case XML_CDATA_SECTION_NODE:
4943 break;
4944 default:
4945 ret = 0;
4946 goto done;
4947 }
4948 /*
4949 * Switch to next element
4950 */
4951 cur = cur->next;
4952 while (cur == NULL) {
4953 cur = nodeVPop(ctxt);
4954 if (cur == NULL)
4955 break;
4956 cur = cur->next;
4957 }
4958 }
4959done:
4960 ctxt->nodeMax = 0;
4961 ctxt->nodeNr = 0;
4962 if (ctxt->nodeTab != NULL) {
4963 xmlFree(ctxt->nodeTab);
4964 ctxt->nodeTab = NULL;
4965 }
4966 return(ret);
4967}
4968
4969/**
Daniel Veillardea7751d2002-12-20 00:16:24 +00004970 * xmlValidateCheckMixed:
4971 * @ctxt: the validation context
4972 * @cont: the mixed content model
4973 * @qname: the qualified name as appearing in the serialization
4974 *
4975 * Check if the given node is part of the content model.
4976 *
4977 * Returns 1 if yes, 0 if no, -1 in case of error
4978 */
4979static int
4980xmlValidateCheckMixed(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED,
4981 xmlElementContentPtr cont, const xmlChar *qname) {
4982 while (cont != NULL) {
4983 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4984 if (xmlStrEqual(cont->name, qname))
4985 return(1);
4986 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4987 (cont->c1 != NULL) &&
4988 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4989 if (xmlStrEqual(cont->c1->name, qname))
4990 return(1);
4991 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4992 (cont->c1 == NULL) ||
4993 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4994 /* Internal error !!! */
4995 xmlGenericError(xmlGenericErrorContext,
4996 "Internal: MIXED struct bad\n");
4997 break;
4998 }
4999 cont = cont->c2;
5000 }
5001 return(0);
5002}
5003
5004/**
5005 * xmlValidGetElemDecl:
5006 * @ctxt: the validation context
5007 * @doc: a document instance
5008 * @elem: an element instance
5009 * @extsubset: pointer, (out) indicate if the declaration was found
5010 * in the external subset.
5011 *
5012 * Finds a declaration associated to an element in the document.
5013 *
5014 * returns the pointer to the declaration or NULL if not found.
5015 */
5016static xmlElementPtr
5017xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5018 xmlNodePtr elem, int *extsubset) {
5019 xmlElementPtr elemDecl = NULL;
5020 const xmlChar *prefix = NULL;
5021
5022 if ((elem == NULL) || (elem->name == NULL)) return(NULL);
5023 if (extsubset != NULL)
5024 *extsubset = 0;
5025
5026 /*
5027 * Fetch the declaration for the qualified name
5028 */
5029 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5030 prefix = elem->ns->prefix;
5031
5032 if (prefix != NULL) {
5033 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
5034 elem->name, prefix);
5035 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5036 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
5037 elem->name, prefix);
5038 if ((elemDecl != NULL) && (extsubset != NULL))
5039 *extsubset = 1;
5040 }
5041 }
5042
5043 /*
5044 * Fetch the declaration for the non qualified name
5045 * This is "non-strict" validation should be done on the
5046 * full QName but in that case being flexible makes sense.
5047 */
5048 if (elemDecl == NULL) {
5049 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
5050 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
5051 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
5052 if ((elemDecl != NULL) && (extsubset != NULL))
5053 *extsubset = 1;
5054 }
5055 }
5056 if (elemDecl == NULL) {
5057 VECTXT(ctxt, elem);
5058 VERROR(ctxt->userData, "No declaration for element %s\n",
5059 elem->name);
5060 }
5061 return(elemDecl);
5062}
5063
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005064#ifdef LIBXML_REGEXP_ENABLED
Daniel Veillardea7751d2002-12-20 00:16:24 +00005065/**
5066 * xmlValidatePushElement:
5067 * @ctxt: the validation context
5068 * @doc: a document instance
5069 * @elem: an element instance
5070 * @qname: the qualified name as appearing in the serialization
5071 *
5072 * Push a new element start on the validation stack.
5073 *
5074 * returns 1 if no validation problem was found or 0 otherwise
5075 */
5076int
5077xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5078 xmlNodePtr elem, const xmlChar *qname) {
5079 int ret = 1;
5080 xmlElementPtr eDecl;
5081 int extsubset = 0;
5082
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005083/* printf("PushElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005084 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5085 xmlValidStatePtr state = ctxt->vstate;
5086 xmlElementPtr elemDecl;
5087
5088 /*
5089 * Check the new element agaisnt the content model of the new elem.
5090 */
5091 if (state->elemDecl != NULL) {
5092 elemDecl = state->elemDecl;
5093
5094 switch(elemDecl->etype) {
5095 case XML_ELEMENT_TYPE_UNDEFINED:
5096 ret = 0;
5097 break;
5098 case XML_ELEMENT_TYPE_EMPTY:
5099 VECTXT(ctxt, state->node);
5100 VERROR(ctxt->userData,
5101 "Element %s was declared EMPTY this one has content\n",
5102 state->node->name);
5103 ret = 0;
5104 break;
5105 case XML_ELEMENT_TYPE_ANY:
5106 /* I don't think anything is required then */
5107 break;
5108 case XML_ELEMENT_TYPE_MIXED:
5109 /* simple case of declared as #PCDATA */
5110 if ((elemDecl->content != NULL) &&
5111 (elemDecl->content->type ==
5112 XML_ELEMENT_CONTENT_PCDATA)) {
5113 VECTXT(ctxt, state->node);
5114 VERROR(ctxt->userData,
5115 "Element %s was declared #PCDATA but contains non text nodes\n",
5116 state->node->name);
5117 ret = 0;
5118 } else {
5119 ret = xmlValidateCheckMixed(ctxt, elemDecl->content,
5120 qname);
5121 if (ret != 1) {
5122 VECTXT(ctxt, state->node);
5123 VERROR(ctxt->userData,
5124 "Element %s is not declared in %s list of possible children\n",
5125 qname, state->node->name);
5126 }
5127 }
5128 break;
5129 case XML_ELEMENT_TYPE_ELEMENT:
5130 /*
5131 * TODO:
5132 * VC: Standalone Document Declaration
5133 * - element types with element content, if white space
5134 * occurs directly within any instance of those types.
5135 */
5136 if (state->exec != NULL) {
5137 ret = xmlRegExecPushString(state->exec, qname, NULL);
5138 if (ret < 0) {
5139 VECTXT(ctxt, state->node);
5140 VERROR(ctxt->userData,
5141 "Element %s content does not follow the DTD\nMisplaced %s\n",
5142 state->node->name, qname);
5143 ret = 0;
5144 } else {
5145 ret = 1;
5146 }
5147 }
5148 break;
5149 }
5150 }
5151 }
5152 eDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5153 vstateVPush(ctxt, eDecl, elem);
5154 return(ret);
5155}
5156
5157/**
5158 * xmlValidatePushCData:
5159 * @ctxt: the validation context
5160 * @data: some character data read
5161 * @len: the lenght of the data
5162 *
5163 * check the CData parsed for validation in the current stack
5164 *
5165 * returns 1 if no validation problem was found or 0 otherwise
5166 */
5167int
5168xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) {
5169 int ret = 1;
5170
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005171/* printf("CDATA %s %d\n", data, len); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005172 if (len <= 0)
5173 return(ret);
5174 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5175 xmlValidStatePtr state = ctxt->vstate;
5176 xmlElementPtr elemDecl;
5177
5178 /*
5179 * Check the new element agaisnt the content model of the new elem.
5180 */
5181 if (state->elemDecl != NULL) {
5182 elemDecl = state->elemDecl;
5183
5184 switch(elemDecl->etype) {
5185 case XML_ELEMENT_TYPE_UNDEFINED:
5186 ret = 0;
5187 break;
5188 case XML_ELEMENT_TYPE_EMPTY:
5189 VECTXT(ctxt, state->node);
5190 VERROR(ctxt->userData,
5191 "Element %s was declared EMPTY this one has content\n",
5192 state->node->name);
5193 ret = 0;
5194 break;
5195 case XML_ELEMENT_TYPE_ANY:
5196 break;
5197 case XML_ELEMENT_TYPE_MIXED:
5198 break;
5199 case XML_ELEMENT_TYPE_ELEMENT:
5200 if (len > 0) {
5201 int i;
5202
5203 for (i = 0;i < len;i++) {
5204 if (!IS_BLANK(data[i])) {
5205 VECTXT(ctxt, state->node);
5206 VERROR(ctxt->userData,
5207 "Element %s content does not follow the DTD\nText not allowed\n",
5208 state->node->name);
5209 ret = 0;
5210 goto done;
5211 }
5212 }
5213 /*
5214 * TODO:
5215 * VC: Standalone Document Declaration
5216 * element types with element content, if white space
5217 * occurs directly within any instance of those types.
5218 */
5219 }
5220 break;
5221 }
5222 }
5223 }
5224done:
5225 return(ret);
5226}
5227
5228/**
5229 * xmlValidatePopElement:
5230 * @ctxt: the validation context
5231 * @doc: a document instance
5232 * @elem: an element instance
5233 * @qname: the qualified name as appearing in the serialization
5234 *
5235 * Pop the element end from the validation stack.
5236 *
5237 * returns 1 if no validation problem was found or 0 otherwise
5238 */
5239int
5240xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED,
Daniel Veillard580ced82003-03-21 21:22:48 +00005241 xmlNodePtr elem ATTRIBUTE_UNUSED,
5242 const xmlChar *qname ATTRIBUTE_UNUSED) {
Daniel Veillardea7751d2002-12-20 00:16:24 +00005243 int ret = 1;
5244
Daniel Veillardef8dd7b2003-03-23 12:02:56 +00005245/* printf("PopElem %s\n", qname); */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005246 if ((ctxt->vstateNr > 0) && (ctxt->vstate != NULL)) {
5247 xmlValidStatePtr state = ctxt->vstate;
5248 xmlElementPtr elemDecl;
5249
5250 /*
5251 * Check the new element agaisnt the content model of the new elem.
5252 */
5253 if (state->elemDecl != NULL) {
5254 elemDecl = state->elemDecl;
5255
5256 if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) {
5257 if (state->exec != NULL) {
5258 ret = xmlRegExecPushString(state->exec, NULL, NULL);
5259 if (ret == 0) {
5260 VECTXT(ctxt, state->node);
5261 VERROR(ctxt->userData,
5262 "Element %s content does not follow the DTD\nExpecting more child\n",
5263 state->node->name);
5264 } else {
5265 /*
5266 * previous validation errors should not generate
5267 * a new one here
5268 */
5269 ret = 1;
5270 }
5271 }
5272 }
5273 }
5274 vstateVPop(ctxt);
5275 }
5276 return(ret);
5277}
Daniel Veillard0e298ad2003-02-04 16:14:33 +00005278#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005279
5280/**
Owen Taylor3473f882001-02-23 17:55:21 +00005281 * xmlValidateOneElement:
5282 * @ctxt: the validation context
5283 * @doc: a document instance
5284 * @elem: an element instance
5285 *
5286 * Try to validate a single element and it's attributes,
5287 * basically it does the following checks as described by the
5288 * XML-1.0 recommendation:
5289 * - [ VC: Element Valid ]
5290 * - [ VC: Required Attribute ]
5291 * Then call xmlValidateOneAttribute() for each attribute present.
5292 *
5293 * The ID/IDREF checkings are done separately
5294 *
5295 * returns 1 if valid or 0 otherwise
5296 */
5297
5298int
5299xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
5300 xmlNodePtr elem) {
5301 xmlElementPtr elemDecl = NULL;
5302 xmlElementContentPtr cont;
5303 xmlAttributePtr attr;
5304 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005305 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005306 const xmlChar *name;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005307 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005308
5309 CHECK_DTD;
5310
5311 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005312 switch (elem->type) {
5313 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005314 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005315 VERROR(ctxt->userData,
5316 "Attribute element not expected here\n");
5317 return(0);
5318 case XML_TEXT_NODE:
5319 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005320 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005321 VERROR(ctxt->userData, "Text element has childs !\n");
5322 return(0);
5323 }
5324 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005325 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005326 VERROR(ctxt->userData, "Text element has attributes !\n");
5327 return(0);
5328 }
5329 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005330 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005331 VERROR(ctxt->userData, "Text element has namespace !\n");
5332 return(0);
5333 }
5334 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005335 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005336 VERROR(ctxt->userData,
5337 "Text element carries namespace definitions !\n");
5338 return(0);
5339 }
5340 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005341 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005342 VERROR(ctxt->userData,
5343 "Text element has no content !\n");
5344 return(0);
5345 }
5346 return(1);
5347 case XML_XINCLUDE_START:
5348 case XML_XINCLUDE_END:
5349 return(1);
5350 case XML_CDATA_SECTION_NODE:
5351 case XML_ENTITY_REF_NODE:
5352 case XML_PI_NODE:
5353 case XML_COMMENT_NODE:
5354 return(1);
5355 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005356 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005357 VERROR(ctxt->userData,
5358 "Entity element not expected here\n");
5359 return(0);
5360 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005361 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005362 VERROR(ctxt->userData,
5363 "Notation element not expected here\n");
5364 return(0);
5365 case XML_DOCUMENT_NODE:
5366 case XML_DOCUMENT_TYPE_NODE:
5367 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005368 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005369 VERROR(ctxt->userData,
5370 "Document element not expected here\n");
5371 return(0);
5372 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005373 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005374 VERROR(ctxt->userData,
5375 "\n");
5376 return(0);
5377 case XML_ELEMENT_NODE:
5378 break;
5379 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005380 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005381 VERROR(ctxt->userData,
5382 "unknown element type %d\n", elem->type);
5383 return(0);
5384 }
Owen Taylor3473f882001-02-23 17:55:21 +00005385
5386 /*
Daniel Veillardea7751d2002-12-20 00:16:24 +00005387 * Fetch the declaration
Owen Taylor3473f882001-02-23 17:55:21 +00005388 */
Daniel Veillardea7751d2002-12-20 00:16:24 +00005389 elemDecl = xmlValidGetElemDecl(ctxt, doc, elem, &extsubset);
5390 if (elemDecl == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005391 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005392
Daniel Veillardea7751d2002-12-20 00:16:24 +00005393 /*
5394 * If vstateNr is not zero that means continuous validation is
5395 * activated, do not try to check the content model at that level.
5396 */
5397 if (ctxt->vstateNr == 0) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005398 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005399 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005400 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005401 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005402 VERROR(ctxt->userData, "No declaration for element %s\n",
5403 elem->name);
5404 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005405 case XML_ELEMENT_TYPE_EMPTY:
5406 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005407 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005408 VERROR(ctxt->userData,
5409 "Element %s was declared EMPTY this one has content\n",
5410 elem->name);
5411 ret = 0;
5412 }
5413 break;
5414 case XML_ELEMENT_TYPE_ANY:
5415 /* I don't think anything is required then */
5416 break;
5417 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005418
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005419 /* simple case of declared as #PCDATA */
5420 if ((elemDecl->content != NULL) &&
5421 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5422 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5423 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005424 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005425 VERROR(ctxt->userData,
5426 "Element %s was declared #PCDATA but contains non text nodes\n",
5427 elem->name);
5428 }
5429 break;
5430 }
Owen Taylor3473f882001-02-23 17:55:21 +00005431 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005432 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005433 while (child != NULL) {
5434 if (child->type == XML_ELEMENT_NODE) {
5435 name = child->name;
5436 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005437 xmlChar fn[50];
5438 xmlChar *fullname;
5439
5440 fullname = xmlBuildQName(child->name, child->ns->prefix,
5441 fn, 50);
5442 if (fullname == NULL)
5443 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005444 cont = elemDecl->content;
5445 while (cont != NULL) {
5446 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005447 if (xmlStrEqual(cont->name, fullname))
5448 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005449 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5450 (cont->c1 != NULL) &&
5451 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillardc00cda82003-04-07 10:22:39 +00005452 if (xmlStrEqual(cont->c1->name, fullname))
5453 break;
Owen Taylor3473f882001-02-23 17:55:21 +00005454 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5455 (cont->c1 == NULL) ||
5456 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5457 /* Internal error !!! */
5458 xmlGenericError(xmlGenericErrorContext,
5459 "Internal: MIXED struct bad\n");
5460 break;
5461 }
5462 cont = cont->c2;
5463 }
Daniel Veillardc00cda82003-04-07 10:22:39 +00005464 if ((fullname != fn) && (fullname != child->name))
5465 xmlFree(fullname);
Owen Taylor3473f882001-02-23 17:55:21 +00005466 if (cont != NULL)
5467 goto child_ok;
5468 }
5469 cont = elemDecl->content;
5470 while (cont != NULL) {
5471 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5472 if (xmlStrEqual(cont->name, name)) break;
5473 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5474 (cont->c1 != NULL) &&
5475 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5476 if (xmlStrEqual(cont->c1->name, name)) break;
5477 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5478 (cont->c1 == NULL) ||
5479 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5480 /* Internal error !!! */
5481 xmlGenericError(xmlGenericErrorContext,
5482 "Internal: MIXED struct bad\n");
5483 break;
5484 }
5485 cont = cont->c2;
5486 }
5487 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005488 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005489 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005490 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005491 name, elem->name);
5492 ret = 0;
5493 }
5494 }
5495child_ok:
5496 child = child->next;
5497 }
5498 break;
5499 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005500 if ((doc->standalone == 1) && (extsubset == 1)) {
5501 /*
5502 * VC: Standalone Document Declaration
5503 * - element types with element content, if white space
5504 * occurs directly within any instance of those types.
5505 */
5506 child = elem->children;
5507 while (child != NULL) {
5508 if (child->type == XML_TEXT_NODE) {
5509 const xmlChar *content = child->content;
5510
5511 while (IS_BLANK(*content))
5512 content++;
5513 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005514 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005515 VERROR(ctxt->userData,
5516"standalone: %s declared in the external subset contains white spaces nodes\n",
5517 elem->name);
5518 ret = 0;
5519 break;
5520 }
5521 }
5522 child =child->next;
5523 }
5524 }
Owen Taylor3473f882001-02-23 17:55:21 +00005525 child = elem->children;
5526 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005527 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005528 if (tmp <= 0)
5529 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005530 break;
5531 }
Daniel Veillardea7751d2002-12-20 00:16:24 +00005532 } /* not continuous */
Owen Taylor3473f882001-02-23 17:55:21 +00005533
5534 /* [ VC: Required Attribute ] */
5535 attr = elemDecl->attributes;
5536 while (attr != NULL) {
5537 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005538 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005539
Daniel Veillarde4301c82002-02-13 13:32:35 +00005540 if ((attr->prefix == NULL) &&
5541 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5542 xmlNsPtr ns;
5543
5544 ns = elem->nsDef;
5545 while (ns != NULL) {
5546 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005547 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005548 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005549 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005550 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5551 xmlNsPtr ns;
5552
5553 ns = elem->nsDef;
5554 while (ns != NULL) {
5555 if (xmlStrEqual(attr->name, ns->prefix))
5556 goto found;
5557 ns = ns->next;
5558 }
5559 } else {
5560 xmlAttrPtr attrib;
5561
5562 attrib = elem->properties;
5563 while (attrib != NULL) {
5564 if (xmlStrEqual(attrib->name, attr->name)) {
5565 if (attr->prefix != NULL) {
5566 xmlNsPtr nameSpace = attrib->ns;
5567
5568 if (nameSpace == NULL)
5569 nameSpace = elem->ns;
5570 /*
5571 * qualified names handling is problematic, having a
5572 * different prefix should be possible but DTDs don't
5573 * allow to define the URI instead of the prefix :-(
5574 */
5575 if (nameSpace == NULL) {
5576 if (qualified < 0)
5577 qualified = 0;
5578 } else if (!xmlStrEqual(nameSpace->prefix,
5579 attr->prefix)) {
5580 if (qualified < 1)
5581 qualified = 1;
5582 } else
5583 goto found;
5584 } else {
5585 /*
5586 * We should allow applications to define namespaces
5587 * for their application even if the DTD doesn't
5588 * carry one, otherwise, basically we would always
5589 * break.
5590 */
5591 goto found;
5592 }
5593 }
5594 attrib = attrib->next;
5595 }
Owen Taylor3473f882001-02-23 17:55:21 +00005596 }
5597 if (qualified == -1) {
5598 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005599 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005600 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005601 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005602 elem->name, attr->name);
5603 ret = 0;
5604 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005605 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005606 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005607 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005608 elem->name, attr->prefix,attr->name);
5609 ret = 0;
5610 }
5611 } else if (qualified == 0) {
5612 VWARNING(ctxt->userData,
5613 "Element %s required attribute %s:%s has no prefix\n",
5614 elem->name, attr->prefix,attr->name);
5615 } else if (qualified == 1) {
5616 VWARNING(ctxt->userData,
5617 "Element %s required attribute %s:%s has different prefix\n",
5618 elem->name, attr->prefix,attr->name);
5619 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005620 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5621 /*
5622 * Special tests checking #FIXED namespace declarations
5623 * have the right value since this is not done as an
5624 * attribute checking
5625 */
5626 if ((attr->prefix == NULL) &&
5627 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5628 xmlNsPtr ns;
5629
5630 ns = elem->nsDef;
5631 while (ns != NULL) {
5632 if (ns->prefix == NULL) {
5633 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005634 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005635 VERROR(ctxt->userData,
5636 "Element %s namespace name for default namespace does not match the DTD\n",
5637 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005638 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005639 }
5640 goto found;
5641 }
5642 ns = ns->next;
5643 }
5644 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5645 xmlNsPtr ns;
5646
5647 ns = elem->nsDef;
5648 while (ns != NULL) {
5649 if (xmlStrEqual(attr->name, ns->prefix)) {
5650 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005651 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005652 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005653 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005654 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005655 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005656 }
5657 goto found;
5658 }
5659 ns = ns->next;
5660 }
5661 }
Owen Taylor3473f882001-02-23 17:55:21 +00005662 }
5663found:
5664 attr = attr->nexth;
5665 }
5666 return(ret);
5667}
5668
5669/**
5670 * xmlValidateRoot:
5671 * @ctxt: the validation context
5672 * @doc: a document instance
5673 *
5674 * Try to validate a the root element
5675 * basically it does the following check as described by the
5676 * XML-1.0 recommendation:
5677 * - [ VC: Root Element Type ]
5678 * it doesn't try to recurse or apply other check to the element
5679 *
5680 * returns 1 if valid or 0 otherwise
5681 */
5682
5683int
5684xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5685 xmlNodePtr root;
Daniel Veillardc00cda82003-04-07 10:22:39 +00005686 int ret;
5687
Owen Taylor3473f882001-02-23 17:55:21 +00005688 if (doc == NULL) return(0);
5689
5690 root = xmlDocGetRootElement(doc);
5691 if ((root == NULL) || (root->name == NULL)) {
5692 VERROR(ctxt->userData, "Not valid: no root element\n");
5693 return(0);
5694 }
5695
5696 /*
5697 * When doing post validation against a separate DTD, those may
5698 * no internal subset has been generated
5699 */
5700 if ((doc->intSubset != NULL) &&
5701 (doc->intSubset->name != NULL)) {
5702 /*
5703 * Check first the document root against the NQName
5704 */
5705 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5706 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
Daniel Veillardc00cda82003-04-07 10:22:39 +00005707 xmlChar fn[50];
5708 xmlChar *fullname;
5709
5710 fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50);
5711 if (fullname == NULL) {
5712 VERROR(ctxt->userData, "Out of memory\n");
5713 return(0);
5714 }
5715 ret = xmlStrEqual(doc->intSubset->name, fullname);
5716 if ((fullname != fn) && (fullname != root->name))
5717 xmlFree(fullname);
5718 if (ret == 1)
Owen Taylor3473f882001-02-23 17:55:21 +00005719 goto name_ok;
5720 }
5721 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5722 (xmlStrEqual(root->name, BAD_CAST "html")))
5723 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005724 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005725 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005726 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005727 root->name, doc->intSubset->name);
5728 return(0);
5729
5730 }
5731 }
5732name_ok:
5733 return(1);
5734}
5735
5736
5737/**
5738 * xmlValidateElement:
5739 * @ctxt: the validation context
5740 * @doc: a document instance
5741 * @elem: an element instance
5742 *
5743 * Try to validate the subtree under an element
5744 *
5745 * returns 1 if valid or 0 otherwise
5746 */
5747
5748int
5749xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5750 xmlNodePtr child;
5751 xmlAttrPtr attr;
5752 xmlChar *value;
5753 int ret = 1;
5754
5755 if (elem == NULL) return(0);
5756
5757 /*
5758 * XInclude elements were added after parsing in the infoset,
5759 * they don't really mean anything validation wise.
5760 */
5761 if ((elem->type == XML_XINCLUDE_START) ||
5762 (elem->type == XML_XINCLUDE_END))
5763 return(1);
5764
5765 CHECK_DTD;
5766
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005767 /*
5768 * Entities references have to be handled separately
5769 */
5770 if (elem->type == XML_ENTITY_REF_NODE) {
5771 return(1);
5772 }
5773
Owen Taylor3473f882001-02-23 17:55:21 +00005774 ret &= xmlValidateOneElement(ctxt, doc, elem);
5775 attr = elem->properties;
5776 while(attr != NULL) {
5777 value = xmlNodeListGetString(doc, attr->children, 0);
5778 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5779 if (value != NULL)
5780 xmlFree(value);
5781 attr= attr->next;
5782 }
5783 child = elem->children;
5784 while (child != NULL) {
5785 ret &= xmlValidateElement(ctxt, doc, child);
5786 child = child->next;
5787 }
5788
5789 return(ret);
5790}
5791
Daniel Veillard8730c562001-02-26 10:49:57 +00005792/**
5793 * xmlValidateRef:
5794 * @ref: A reference to be validated
5795 * @ctxt: Validation context
5796 * @name: Name of ID we are searching for
5797 *
5798 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005799static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005800xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005801 const xmlChar *name) {
5802 xmlAttrPtr id;
5803 xmlAttrPtr attr;
5804
5805 if (ref == NULL)
5806 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005807 if ((ref->attr == NULL) && (ref->name == NULL))
Owen Taylor3473f882001-02-23 17:55:21 +00005808 return;
Daniel Veillardea7751d2002-12-20 00:16:24 +00005809 attr = ref->attr;
5810 if (attr == NULL) {
5811 xmlChar *dup, *str = NULL, *cur, save;
5812
5813 dup = xmlStrdup(name);
5814 if (dup == NULL) {
5815 ctxt->valid = 0;
5816 return;
5817 }
5818 cur = dup;
5819 while (*cur != 0) {
5820 str = cur;
5821 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5822 save = *cur;
5823 *cur = 0;
5824 id = xmlGetID(ctxt->doc, str);
5825 if (id == NULL) {
5826 VERROR(ctxt->userData,
5827 "attribute %s line %d references an unknown ID \"%s\"\n",
5828 ref->name, ref->lineno, str);
5829 ctxt->valid = 0;
5830 }
5831 if (save == 0)
5832 break;
5833 *cur = save;
5834 while (IS_BLANK(*cur)) cur++;
5835 }
5836 xmlFree(dup);
5837 } else if (attr->atype == XML_ATTRIBUTE_IDREF) {
Owen Taylor3473f882001-02-23 17:55:21 +00005838 id = xmlGetID(ctxt->doc, name);
5839 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005840 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005841 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005842 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005843 attr->name, name);
5844 ctxt->valid = 0;
5845 }
5846 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5847 xmlChar *dup, *str = NULL, *cur, save;
5848
5849 dup = xmlStrdup(name);
5850 if (dup == NULL) {
5851 ctxt->valid = 0;
5852 return;
5853 }
5854 cur = dup;
5855 while (*cur != 0) {
5856 str = cur;
5857 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5858 save = *cur;
5859 *cur = 0;
5860 id = xmlGetID(ctxt->doc, str);
5861 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005862 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005863 VERROR(ctxt->userData,
Daniel Veillardea7751d2002-12-20 00:16:24 +00005864 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005865 attr->name, str);
5866 ctxt->valid = 0;
5867 }
5868 if (save == 0)
5869 break;
5870 *cur = save;
5871 while (IS_BLANK(*cur)) cur++;
5872 }
5873 xmlFree(dup);
5874 }
5875}
5876
5877/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005878 * xmlWalkValidateList:
5879 * @data: Contents of current link
5880 * @user: Value supplied by the user
5881 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005882 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005883 */
5884static int
5885xmlWalkValidateList(const void *data, const void *user)
5886{
5887 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5888 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5889 return 1;
5890}
5891
5892/**
5893 * xmlValidateCheckRefCallback:
5894 * @ref_list: List of references
5895 * @ctxt: Validation context
5896 * @name: Name of ID we are searching for
5897 *
5898 */
5899static void
5900xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5901 const xmlChar *name) {
5902 xmlValidateMemo memo;
5903
5904 if (ref_list == NULL)
5905 return;
5906 memo.ctxt = ctxt;
5907 memo.name = name;
5908
5909 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5910
5911}
5912
5913/**
Owen Taylor3473f882001-02-23 17:55:21 +00005914 * xmlValidateDocumentFinal:
5915 * @ctxt: the validation context
5916 * @doc: a document instance
5917 *
5918 * Does the final step for the document validation once all the
5919 * incremental validation steps have been completed
5920 *
5921 * basically it does the following checks described by the XML Rec
5922 *
Daniel Veillardc3da18a2003-03-18 00:31:04 +00005923 * Check all the IDREF/IDREFS attributes definition for validity
Owen Taylor3473f882001-02-23 17:55:21 +00005924 *
5925 * returns 1 if valid or 0 otherwise
5926 */
5927
5928int
5929xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5930 xmlRefTablePtr table;
5931
5932 if (doc == NULL) {
5933 xmlGenericError(xmlGenericErrorContext,
5934 "xmlValidateDocumentFinal: doc == NULL\n");
5935 return(0);
5936 }
5937
5938 /*
5939 * Check all the NOTATION/NOTATIONS attributes
5940 */
5941 /*
5942 * Check all the ENTITY/ENTITIES attributes definition for validity
5943 */
5944 /*
5945 * Check all the IDREF/IDREFS attributes definition for validity
5946 */
5947 table = (xmlRefTablePtr) doc->refs;
5948 ctxt->doc = doc;
5949 ctxt->valid = 1;
5950 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
5951 return(ctxt->valid);
5952}
5953
5954/**
5955 * xmlValidateDtd:
5956 * @ctxt: the validation context
5957 * @doc: a document instance
5958 * @dtd: a dtd instance
5959 *
5960 * Try to validate the document against the dtd instance
5961 *
5962 * basically it does check all the definitions in the DtD.
5963 *
5964 * returns 1 if valid or 0 otherwise
5965 */
5966
5967int
5968xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
5969 int ret;
5970 xmlDtdPtr oldExt;
5971 xmlNodePtr root;
5972
5973 if (dtd == NULL) return(0);
5974 if (doc == NULL) return(0);
5975 oldExt = doc->extSubset;
5976 doc->extSubset = dtd;
5977 ret = xmlValidateRoot(ctxt, doc);
5978 if (ret == 0) {
5979 doc->extSubset = oldExt;
5980 return(ret);
5981 }
5982 if (doc->ids != NULL) {
5983 xmlFreeIDTable(doc->ids);
5984 doc->ids = NULL;
5985 }
5986 if (doc->refs != NULL) {
5987 xmlFreeRefTable(doc->refs);
5988 doc->refs = NULL;
5989 }
5990 root = xmlDocGetRootElement(doc);
5991 ret = xmlValidateElement(ctxt, doc, root);
5992 ret &= xmlValidateDocumentFinal(ctxt, doc);
5993 doc->extSubset = oldExt;
5994 return(ret);
5995}
5996
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005997static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005998xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
5999 const xmlChar *name ATTRIBUTE_UNUSED) {
6000 if (cur == NULL)
6001 return;
6002 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
6003 xmlChar *notation = cur->content;
6004
Daniel Veillard878eab02002-02-19 13:46:09 +00006005 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006006 int ret;
6007
6008 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
6009 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00006010 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006011 }
6012 }
6013 }
6014}
6015
6016static void
Owen Taylor3473f882001-02-23 17:55:21 +00006017xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00006018 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006019 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00006020 xmlDocPtr doc;
6021 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006022
Owen Taylor3473f882001-02-23 17:55:21 +00006023 if (cur == NULL)
6024 return;
6025 switch (cur->atype) {
6026 case XML_ATTRIBUTE_CDATA:
6027 case XML_ATTRIBUTE_ID:
6028 case XML_ATTRIBUTE_IDREF :
6029 case XML_ATTRIBUTE_IDREFS:
6030 case XML_ATTRIBUTE_NMTOKEN:
6031 case XML_ATTRIBUTE_NMTOKENS:
6032 case XML_ATTRIBUTE_ENUMERATION:
6033 break;
6034 case XML_ATTRIBUTE_ENTITY:
6035 case XML_ATTRIBUTE_ENTITIES:
6036 case XML_ATTRIBUTE_NOTATION:
6037 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006038
6039 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
6040 cur->atype, cur->defaultValue);
6041 if ((ret == 0) && (ctxt->valid == 1))
6042 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006043 }
6044 if (cur->tree != NULL) {
6045 xmlEnumerationPtr tree = cur->tree;
6046 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006047 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00006048 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006049 if ((ret == 0) && (ctxt->valid == 1))
6050 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00006051 tree = tree->next;
6052 }
6053 }
6054 }
Daniel Veillard878eab02002-02-19 13:46:09 +00006055 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
6056 doc = cur->doc;
6057 if ((doc == NULL) || (cur->elem == NULL)) {
6058 VERROR(ctxt->userData,
6059 "xmlValidateAttributeCallback(%s): internal error\n",
6060 cur->name);
6061 return;
6062 }
6063 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
6064 if (elem == NULL)
6065 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
6066 if (elem == NULL) {
6067 VERROR(ctxt->userData,
6068 "attribute %s: could not find decl for element %s\n",
6069 cur->name, cur->elem);
6070 return;
6071 }
6072 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
6073 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00006074 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00006075 cur->name, cur->elem);
6076 ctxt->valid = 0;
6077 }
6078 }
Owen Taylor3473f882001-02-23 17:55:21 +00006079}
6080
6081/**
6082 * xmlValidateDtdFinal:
6083 * @ctxt: the validation context
6084 * @doc: a document instance
6085 *
6086 * Does the final step for the dtds validation once all the
6087 * subsets have been parsed
6088 *
6089 * basically it does the following checks described by the XML Rec
6090 * - check that ENTITY and ENTITIES type attributes default or
6091 * possible values matches one of the defined entities.
6092 * - check that NOTATION type attributes default or
6093 * possible values matches one of the defined notations.
6094 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006095 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00006096 */
6097
6098int
6099xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00006100 xmlDtdPtr dtd;
6101 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006102 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00006103
6104 if (doc == NULL) return(0);
6105 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
6106 return(0);
6107 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006108 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00006109 dtd = doc->intSubset;
6110 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6111 table = (xmlAttributeTablePtr) dtd->attributes;
6112 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006113 }
6114 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006115 entities = (xmlEntitiesTablePtr) dtd->entities;
6116 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6117 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006118 }
6119 dtd = doc->extSubset;
6120 if ((dtd != NULL) && (dtd->attributes != NULL)) {
6121 table = (xmlAttributeTablePtr) dtd->attributes;
6122 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00006123 }
6124 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00006125 entities = (xmlEntitiesTablePtr) dtd->entities;
6126 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
6127 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00006128 }
6129 return(ctxt->valid);
6130}
6131
6132/**
6133 * xmlValidateDocument:
6134 * @ctxt: the validation context
6135 * @doc: a document instance
6136 *
6137 * Try to validate the document instance
6138 *
6139 * basically it does the all the checks described by the XML Rec
6140 * i.e. validates the internal and external subset (if present)
6141 * and validate the document tree.
6142 *
6143 * returns 1 if valid or 0 otherwise
6144 */
6145
6146int
6147xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
6148 int ret;
6149 xmlNodePtr root;
6150
Daniel Veillard2fd85422002-10-16 14:32:41 +00006151 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
6152 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00006153 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00006154 }
Owen Taylor3473f882001-02-23 17:55:21 +00006155 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
6156 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
6157 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
6158 doc->intSubset->SystemID);
6159 if (doc->extSubset == NULL) {
6160 if (doc->intSubset->SystemID != NULL) {
6161 VERROR(ctxt->userData,
6162 "Could not load the external subset \"%s\"\n",
6163 doc->intSubset->SystemID);
6164 } else {
6165 VERROR(ctxt->userData,
6166 "Could not load the external subset \"%s\"\n",
6167 doc->intSubset->ExternalID);
6168 }
6169 return(0);
6170 }
6171 }
6172
6173 if (doc->ids != NULL) {
6174 xmlFreeIDTable(doc->ids);
6175 doc->ids = NULL;
6176 }
6177 if (doc->refs != NULL) {
6178 xmlFreeRefTable(doc->refs);
6179 doc->refs = NULL;
6180 }
6181 ret = xmlValidateDtdFinal(ctxt, doc);
6182 if (!xmlValidateRoot(ctxt, doc)) return(0);
6183
6184 root = xmlDocGetRootElement(doc);
6185 ret &= xmlValidateElement(ctxt, doc, root);
6186 ret &= xmlValidateDocumentFinal(ctxt, doc);
6187 return(ret);
6188}
6189
6190
6191/************************************************************************
6192 * *
6193 * Routines for dynamic validation editing *
6194 * *
6195 ************************************************************************/
6196
6197/**
6198 * xmlValidGetPotentialChildren:
6199 * @ctree: an element content tree
6200 * @list: an array to store the list of child names
6201 * @len: a pointer to the number of element in the list
6202 * @max: the size of the array
6203 *
6204 * Build/extend a list of potential children allowed by the content tree
6205 *
6206 * returns the number of element in the list, or -1 in case of error.
6207 */
6208
6209int
6210xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
6211 int *len, int max) {
6212 int i;
6213
6214 if ((ctree == NULL) || (list == NULL) || (len == NULL))
6215 return(-1);
6216 if (*len >= max) return(*len);
6217
6218 switch (ctree->type) {
6219 case XML_ELEMENT_CONTENT_PCDATA:
6220 for (i = 0; i < *len;i++)
6221 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
6222 list[(*len)++] = BAD_CAST "#PCDATA";
6223 break;
6224 case XML_ELEMENT_CONTENT_ELEMENT:
6225 for (i = 0; i < *len;i++)
6226 if (xmlStrEqual(ctree->name, list[i])) return(*len);
6227 list[(*len)++] = ctree->name;
6228 break;
6229 case XML_ELEMENT_CONTENT_SEQ:
6230 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6231 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6232 break;
6233 case XML_ELEMENT_CONTENT_OR:
6234 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
6235 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
6236 break;
6237 }
6238
6239 return(*len);
6240}
6241
6242/**
6243 * xmlValidGetValidElements:
6244 * @prev: an element to insert after
6245 * @next: an element to insert next
6246 * @list: an array to store the list of child names
6247 * @max: the size of the array
6248 *
6249 * This function returns the list of authorized children to insert
6250 * within an existing tree while respecting the validity constraints
6251 * forced by the Dtd. The insertion point is defined using @prev and
6252 * @next in the following ways:
6253 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
6254 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
6255 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
6256 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
6257 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
6258 *
6259 * pointers to the element names are inserted at the beginning of the array
6260 * and do not need to be freed.
6261 *
6262 * returns the number of element in the list, or -1 in case of error. If
6263 * the function returns the value @max the caller is invited to grow the
6264 * receiving array and retry.
6265 */
6266
6267int
6268xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
6269 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006270 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00006271 int nb_valid_elements = 0;
6272 const xmlChar *elements[256];
6273 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00006274 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00006275
6276 xmlNode *ref_node;
6277 xmlNode *parent;
6278 xmlNode *test_node;
6279
6280 xmlNode *prev_next;
6281 xmlNode *next_prev;
6282 xmlNode *parent_childs;
6283 xmlNode *parent_last;
6284
6285 xmlElement *element_desc;
6286
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00006287 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006288
Owen Taylor3473f882001-02-23 17:55:21 +00006289 if (prev == NULL && next == NULL)
6290 return(-1);
6291
6292 if (list == NULL) return(-1);
6293 if (max <= 0) return(-1);
6294
6295 nb_valid_elements = 0;
6296 ref_node = prev ? prev : next;
6297 parent = ref_node->parent;
6298
6299 /*
6300 * Retrieves the parent element declaration
6301 */
6302 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
6303 parent->name);
6304 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
6305 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
6306 parent->name);
6307 if (element_desc == NULL) return(-1);
6308
6309 /*
6310 * Do a backup of the current tree structure
6311 */
6312 prev_next = prev ? prev->next : NULL;
6313 next_prev = next ? next->prev : NULL;
6314 parent_childs = parent->children;
6315 parent_last = parent->last;
6316
6317 /*
6318 * Creates a dummy node and insert it into the tree
6319 */
6320 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
6321 test_node->doc = ref_node->doc;
6322 test_node->parent = parent;
6323 test_node->prev = prev;
6324 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00006325 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00006326
6327 if (prev) prev->next = test_node;
6328 else parent->children = test_node;
6329
6330 if (next) next->prev = test_node;
6331 else parent->last = test_node;
6332
6333 /*
6334 * Insert each potential child node and check if the parent is
6335 * still valid
6336 */
6337 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
6338 elements, &nb_elements, 256);
6339
6340 for (i = 0;i < nb_elements;i++) {
6341 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00006342 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00006343 int j;
6344
6345 for (j = 0; j < nb_valid_elements;j++)
6346 if (xmlStrEqual(elements[i], list[j])) break;
6347 list[nb_valid_elements++] = elements[i];
6348 if (nb_valid_elements >= max) break;
6349 }
6350 }
6351
6352 /*
6353 * Restore the tree structure
6354 */
6355 if (prev) prev->next = prev_next;
6356 if (next) next->prev = next_prev;
6357 parent->children = parent_childs;
6358 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00006359
6360 /*
6361 * Free up the dummy node
6362 */
6363 test_node->name = name;
6364 xmlFreeNode(test_node);
6365
Owen Taylor3473f882001-02-23 17:55:21 +00006366 return(nb_valid_elements);
6367}