blob: 75121a88fb5157acbbca609522854c8c30e333d9 [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
Owen Taylor3473f882001-02-23 17:55:21 +000036
Daniel Veillard1c732d22002-11-30 11:22:59 +000037#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +000038/*
Daniel Veillard1c732d22002-11-30 11:22:59 +000039 * If regexp are not enabled, it uses a home made algorithm less
40 * complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +000041 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +000042 * only restriction is on the deepness of the tree limited by the
43 * size of the occurs bitfield
44 *
45 * this is the content of a saved state for rollbacks
46 */
47
48#define ROLLBACK_OR 0
49#define ROLLBACK_PARENT 1
50
Daniel Veillardb44025c2001-10-11 22:55:55 +000051typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +000052 xmlElementContentPtr cont; /* pointer to the content model subtree */
53 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +000054 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +000055 unsigned char depth; /* current depth in the overall tree */
56 unsigned char state; /* ROLLBACK_XXX */
57} _xmlValidState;
58
Daniel Veillardfc57b412002-04-29 15:50:14 +000059#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +000060#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
61#define CONT ctxt->vstate->cont
62#define NODE ctxt->vstate->node
63#define DEPTH ctxt->vstate->depth
64#define OCCURS ctxt->vstate->occurs
65#define STATE ctxt->vstate->state
66
Daniel Veillard5344c602001-12-31 16:37:34 +000067#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
68#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +000069
Daniel Veillard5344c602001-12-31 16:37:34 +000070#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
71#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +000072
73static int
74vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
75 xmlNodePtr node, unsigned char depth, long occurs,
76 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +000077 int i = ctxt->vstateNr - 1;
78
Daniel Veillard940492d2002-04-15 10:15:25 +000079 if (ctxt->vstateNr > MAX_RECURSE) {
80 return(-1);
81 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +000082 if (ctxt->vstateNr >= ctxt->vstateMax) {
83 ctxt->vstateMax *= 2;
84 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
85 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
86 if (ctxt->vstateTab == NULL) {
87 xmlGenericError(xmlGenericErrorContext,
88 "realloc failed !n");
Daniel Veillard940492d2002-04-15 10:15:25 +000089 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +000090 }
Daniel Veillard06803992001-04-22 10:35:56 +000091 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +000092 }
Daniel Veillardbed7b052001-05-19 14:59:49 +000093 /*
94 * Don't push on the stack a state already here
95 */
96 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
97 (ctxt->vstateTab[i].node == node) &&
98 (ctxt->vstateTab[i].depth == depth) &&
99 (ctxt->vstateTab[i].occurs == occurs) &&
100 (ctxt->vstateTab[i].state == state))
101 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000102 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
103 ctxt->vstateTab[ctxt->vstateNr].node = node;
104 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
105 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
106 ctxt->vstateTab[ctxt->vstateNr].state = state;
107 return(ctxt->vstateNr++);
108}
109
110static int
111vstateVPop(xmlValidCtxtPtr ctxt) {
112 if (ctxt->vstateNr <= 1) return(-1);
113 ctxt->vstateNr--;
114 ctxt->vstate = &ctxt->vstateTab[0];
115 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
116 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
117 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
118 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
119 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
120 return(ctxt->vstateNr);
121}
122
Daniel Veillard118aed72002-09-24 14:13:13 +0000123#endif /* LIBXML_REGEXP_ENABLED */
124
Daniel Veillard1c732d22002-11-30 11:22:59 +0000125static int
126nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value)
127{
128 if (ctxt->nodeMax <= 0) {
129 ctxt->nodeMax = 4;
130 ctxt->nodeTab =
131 (xmlNodePtr *) xmlMalloc(ctxt->nodeMax *
132 sizeof(ctxt->nodeTab[0]));
133 if (ctxt->nodeTab == NULL) {
134 xmlGenericError(xmlGenericErrorContext, "malloc failed !\n");
135 ctxt->nodeMax = 0;
136 return (0);
137 }
138 }
139 if (ctxt->nodeNr >= ctxt->nodeMax) {
140 ctxt->nodeMax *= 2;
141 ctxt->nodeTab =
142 (xmlNodePtr *) xmlRealloc(ctxt->nodeTab,
143 ctxt->nodeMax *
144 sizeof(ctxt->nodeTab[0]));
145 if (ctxt->nodeTab == NULL) {
146 xmlGenericError(xmlGenericErrorContext, "realloc failed !\n");
147 return (0);
148 }
149 }
150 ctxt->nodeTab[ctxt->nodeNr] = value;
151 ctxt->node = value;
152 return (ctxt->nodeNr++);
153}
154static xmlNodePtr
155nodeVPop(xmlValidCtxtPtr ctxt)
156{
157 xmlNodePtr ret;
158
159 if (ctxt->nodeNr <= 0)
160 return (0);
161 ctxt->nodeNr--;
162 if (ctxt->nodeNr > 0)
163 ctxt->node = ctxt->nodeTab[ctxt->nodeNr - 1];
164 else
165 ctxt->node = NULL;
166 ret = ctxt->nodeTab[ctxt->nodeNr];
167 ctxt->nodeTab[ctxt->nodeNr] = 0;
168 return (ret);
169}
Owen Taylor3473f882001-02-23 17:55:21 +0000170
Owen Taylor3473f882001-02-23 17:55:21 +0000171#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000172static void
173xmlValidPrintNode(xmlNodePtr cur) {
174 if (cur == NULL) {
175 xmlGenericError(xmlGenericErrorContext, "null");
176 return;
177 }
178 switch (cur->type) {
179 case XML_ELEMENT_NODE:
180 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
181 break;
182 case XML_TEXT_NODE:
183 xmlGenericError(xmlGenericErrorContext, "text ");
184 break;
185 case XML_CDATA_SECTION_NODE:
186 xmlGenericError(xmlGenericErrorContext, "cdata ");
187 break;
188 case XML_ENTITY_REF_NODE:
189 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
190 break;
191 case XML_PI_NODE:
192 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
193 break;
194 case XML_COMMENT_NODE:
195 xmlGenericError(xmlGenericErrorContext, "comment ");
196 break;
197 case XML_ATTRIBUTE_NODE:
198 xmlGenericError(xmlGenericErrorContext, "?attr? ");
199 break;
200 case XML_ENTITY_NODE:
201 xmlGenericError(xmlGenericErrorContext, "?ent? ");
202 break;
203 case XML_DOCUMENT_NODE:
204 xmlGenericError(xmlGenericErrorContext, "?doc? ");
205 break;
206 case XML_DOCUMENT_TYPE_NODE:
207 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
208 break;
209 case XML_DOCUMENT_FRAG_NODE:
210 xmlGenericError(xmlGenericErrorContext, "?frag? ");
211 break;
212 case XML_NOTATION_NODE:
213 xmlGenericError(xmlGenericErrorContext, "?nota? ");
214 break;
215 case XML_HTML_DOCUMENT_NODE:
216 xmlGenericError(xmlGenericErrorContext, "?html? ");
217 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000218#ifdef LIBXML_DOCB_ENABLED
219 case XML_DOCB_DOCUMENT_NODE:
220 xmlGenericError(xmlGenericErrorContext, "?docb? ");
221 break;
222#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000223 case XML_DTD_NODE:
224 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
225 break;
226 case XML_ELEMENT_DECL:
227 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
228 break;
229 case XML_ATTRIBUTE_DECL:
230 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
231 break;
232 case XML_ENTITY_DECL:
233 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
234 break;
235 case XML_NAMESPACE_DECL:
236 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
237 break;
238 case XML_XINCLUDE_START:
239 xmlGenericError(xmlGenericErrorContext, "incstart ");
240 break;
241 case XML_XINCLUDE_END:
242 xmlGenericError(xmlGenericErrorContext, "incend ");
243 break;
244 }
245}
246
247static void
248xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000249 if (cur == NULL)
250 xmlGenericError(xmlGenericErrorContext, "null ");
251 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000252 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000253 cur = cur->next;
254 }
255}
256
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000257static void
258xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000259 char expr[1000];
260
261 expr[0] = 0;
262 xmlGenericError(xmlGenericErrorContext, "valid: ");
263 xmlValidPrintNodeList(cur);
264 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000265 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000266 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
267}
268
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000269static void
270xmlValidDebugState(xmlValidStatePtr state) {
271 xmlGenericError(xmlGenericErrorContext, "(");
272 if (state->cont == NULL)
273 xmlGenericError(xmlGenericErrorContext, "null,");
274 else
275 switch (state->cont->type) {
276 case XML_ELEMENT_CONTENT_PCDATA:
277 xmlGenericError(xmlGenericErrorContext, "pcdata,");
278 break;
279 case XML_ELEMENT_CONTENT_ELEMENT:
280 xmlGenericError(xmlGenericErrorContext, "%s,",
281 state->cont->name);
282 break;
283 case XML_ELEMENT_CONTENT_SEQ:
284 xmlGenericError(xmlGenericErrorContext, "seq,");
285 break;
286 case XML_ELEMENT_CONTENT_OR:
287 xmlGenericError(xmlGenericErrorContext, "or,");
288 break;
289 }
290 xmlValidPrintNode(state->node);
291 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
292 state->depth, state->occurs, state->state);
293}
294
295static void
296xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
297 int i, j;
298
299 xmlGenericError(xmlGenericErrorContext, "state: ");
300 xmlValidDebugState(ctxt->vstate);
301 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
302 ctxt->vstateNr - 1);
303 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
304 xmlValidDebugState(&ctxt->vstateTab[j]);
305 xmlGenericError(xmlGenericErrorContext, "\n");
306}
307
308/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000309#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000310 *****/
311
312#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000313#define DEBUG_VALID_MSG(m) \
314 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
315
Owen Taylor3473f882001-02-23 17:55:21 +0000316#else
317#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000318#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000319#endif
320
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000321/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000322
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000323#define VECTXT(ctxt, node) \
324 if ((ctxt != NULL) && (ctxt->error != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000325 (node != NULL)) { \
326 xmlChar *base = xmlNodeGetBase(NULL,node); \
327 if (base != NULL) { \
328 ctxt->error(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000329 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000330 xmlFree(base); \
331 } else \
332 ctxt->error(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000333 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000334 }
335
336#define VWCTXT(ctxt, node) \
337 if ((ctxt != NULL) && (ctxt->warning != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000338 (node != NULL)) { \
339 xmlChar *base = xmlNodeGetBase(NULL,node); \
340 if (base != NULL) { \
341 ctxt->warning(ctxt->userData, "%s:%d: ", base, \
Daniel Veillard366a9152002-10-23 20:43:53 +0000342 (int) (long) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000343 xmlFree(base); \
344 } else \
345 ctxt->warning(ctxt->userData, ":%d: ", \
Daniel Veillard366a9152002-10-23 20:43:53 +0000346 (int) (long) node->content); \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000347 }
348
Owen Taylor3473f882001-02-23 17:55:21 +0000349#define VERROR \
350 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
351
352#define VWARNING \
353 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
354
355#define CHECK_DTD \
356 if (doc == NULL) return(0); \
357 else if ((doc->intSubset == NULL) && \
358 (doc->extSubset == NULL)) return(0)
359
Daniel Veillarda10efa82001-04-18 13:09:01 +0000360static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
361 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000362xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
363
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000364#ifdef LIBXML_REGEXP_ENABLED
365
366/************************************************************************
367 * *
368 * Content model validation based on the regexps *
369 * *
370 ************************************************************************/
371
372/**
373 * xmlValidBuildAContentModel:
374 * @content: the content model
375 * @ctxt: the schema parser context
376 * @name: the element name whose content is being built
377 *
378 * Generate the automata sequence needed for that type
379 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000380 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000381 */
382static int
383xmlValidBuildAContentModel(xmlElementContentPtr content,
384 xmlValidCtxtPtr ctxt,
385 const xmlChar *name) {
386 if (content == NULL) {
387 VERROR(ctxt->userData,
388 "Found unexpected type = NULL in %s content model\n", name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000389 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000390 }
391 switch (content->type) {
392 case XML_ELEMENT_CONTENT_PCDATA:
393 VERROR(ctxt->userData, "ContentModel found PCDATA for element %s\n",
394 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000395 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000396 break;
397 case XML_ELEMENT_CONTENT_ELEMENT: {
398 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000399 xmlChar *QName = NULL;
400 const xmlChar *fname = content->name;
401
402 if (content->prefix != NULL) {
403 int len;
404
405 len = xmlStrlen(content->name) +
406 xmlStrlen(content->prefix) + 2;
407 QName = xmlMalloc(len);
408 if (QName == NULL) {
409 VERROR(ctxt->userData,
410 "ContentModel %s : alloc failed\n", name);
411 return(0);
412 }
413 snprintf((char *) QName, len, "%s:%s",
414 (char *)content->prefix,
415 (char *)content->name);
416 fname = QName;
417 }
418
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000419 switch (content->ocur) {
420 case XML_ELEMENT_CONTENT_ONCE:
421 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000422 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000423 break;
424 case XML_ELEMENT_CONTENT_OPT:
425 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000426 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000427 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
428 break;
429 case XML_ELEMENT_CONTENT_PLUS:
430 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000431 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000432 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000433 ctxt->state, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000434 break;
435 case XML_ELEMENT_CONTENT_MULT:
436 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000437 ctxt->state, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000438 break;
439 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000440 if (QName != NULL)
441 xmlFree(QName);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000442 break;
443 }
444 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000445 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000446 xmlElementContentOccur ocur;
447
448 /*
449 * Simply iterate over the content
450 */
451 oldstate = ctxt->state;
452 ocur = content->ocur;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000453 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000454 xmlValidBuildAContentModel(content->c1, ctxt, name);
455 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000456 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
457 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
458 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000459 oldend = ctxt->state;
460 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000461 switch (ocur) {
462 case XML_ELEMENT_CONTENT_ONCE:
463 break;
464 case XML_ELEMENT_CONTENT_OPT:
465 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
466 break;
467 case XML_ELEMENT_CONTENT_MULT:
468 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000469 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000470 break;
471 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000472 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000473 break;
474 }
475 break;
476 }
477 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000478 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000479 xmlElementContentOccur ocur;
480
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000481 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000482 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
483 (ocur == XML_ELEMENT_CONTENT_MULT)) {
484 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
485 ctxt->state, NULL);
486 }
487 oldstate = ctxt->state;
488 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000489
490 /*
491 * iterate over the subtypes and remerge the end with an
492 * epsilon transition
493 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000494 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000495 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000496 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000497 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000498 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000499 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
500 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000501 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000502 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000503 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
504 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000505 switch (ocur) {
506 case XML_ELEMENT_CONTENT_ONCE:
507 break;
508 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000509 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000510 break;
511 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000512 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
513 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000514 break;
515 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000516 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000517 break;
518 }
519 break;
520 }
521 default:
522 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
523 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000524 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000525 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000526 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000527}
528/**
529 * xmlValidBuildContentModel:
530 * @ctxt: a validation context
531 * @elem: an element declaration node
532 *
533 * (Re)Build the automata associated to the content model of this
534 * element
535 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000536 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000537 */
538int
539xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
540 xmlAutomataStatePtr start;
541
542 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000543 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000544 if (elem->type != XML_ELEMENT_DECL)
545 return(0);
546 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
547 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000548 /* TODO: should we rebuild in this case ? */
549 if (elem->contModel != NULL)
Daniel Veillard84d70a42002-09-16 10:51:38 +0000550 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000551
552 ctxt->am = xmlNewAutomata();
553 if (ctxt->am == NULL) {
554 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
555 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000556 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000557 }
558 start = ctxt->state = xmlAutomataGetInitState(ctxt->am);
559 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
560 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000561 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillard23e73572002-09-19 19:56:43 +0000562 if (!xmlRegexpIsDeterminist(elem->contModel)) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000563 char expr[5000];
564 expr[0] = 0;
565 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
566 VERROR(ctxt->userData, "Content model of %s is not determinist: %s\n",
567 elem->name, expr);
568#ifdef DEBUG_REGEXP_ALGO
569 xmlRegexpPrint(stderr, elem->contModel);
570#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000571 ctxt->valid = 0;
572 }
573 ctxt->state = NULL;
574 xmlFreeAutomata(ctxt->am);
575 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000576 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000577}
578
579#endif /* LIBXML_REGEXP_ENABLED */
580
Owen Taylor3473f882001-02-23 17:55:21 +0000581/************************************************************************
582 * *
583 * QName handling helper *
584 * *
585 ************************************************************************/
586
587/**
588 * xmlSplitQName2:
589 * @name: an XML parser context
590 * @prefix: a xmlChar **
591 *
592 * parse an XML qualified name string
593 *
594 * [NS 5] QName ::= (Prefix ':')? LocalPart
595 *
596 * [NS 6] Prefix ::= NCName
597 *
598 * [NS 7] LocalPart ::= NCName
599 *
600 * Returns NULL if not a QName, otherwise the local part, and prefix
601 * is updated to get the Prefix if any.
602 */
603
604xmlChar *
605xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
606 int len = 0;
607 xmlChar *ret = NULL;
608
609 *prefix = NULL;
610
Daniel Veillardf4309d72001-10-02 09:28:58 +0000611#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000612 /* xml: prefix is not really a namespace */
613 if ((name[0] == 'x') && (name[1] == 'm') &&
614 (name[2] == 'l') && (name[3] == ':'))
615 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000616#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000617
618 /* nasty but valid */
619 if (name[0] == ':')
620 return(NULL);
621
622 /*
623 * we are not trying to validate but just to cut, and yes it will
624 * work even if this is as set of UTF-8 encoded chars
625 */
626 while ((name[len] != 0) && (name[len] != ':'))
627 len++;
628
629 if (name[len] == 0)
630 return(NULL);
631
632 *prefix = xmlStrndup(name, len);
633 ret = xmlStrdup(&name[len + 1]);
634
635 return(ret);
636}
637
638/****************************************************************
639 * *
640 * Util functions for data allocation/deallocation *
641 * *
642 ****************************************************************/
643
644/**
645 * xmlNewElementContent:
646 * @name: the subelement name or NULL
647 * @type: the type of element content decl
648 *
649 * Allocate an element content structure.
650 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000651 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000652 */
653xmlElementContentPtr
654xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
655 xmlElementContentPtr ret;
656
657 switch(type) {
658 case XML_ELEMENT_CONTENT_ELEMENT:
659 if (name == NULL) {
660 xmlGenericError(xmlGenericErrorContext,
661 "xmlNewElementContent : name == NULL !\n");
662 }
663 break;
664 case XML_ELEMENT_CONTENT_PCDATA:
665 case XML_ELEMENT_CONTENT_SEQ:
666 case XML_ELEMENT_CONTENT_OR:
667 if (name != NULL) {
668 xmlGenericError(xmlGenericErrorContext,
669 "xmlNewElementContent : name != NULL !\n");
670 }
671 break;
672 default:
673 xmlGenericError(xmlGenericErrorContext,
674 "xmlNewElementContent: unknown type %d\n", type);
675 return(NULL);
676 }
677 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
678 if (ret == NULL) {
679 xmlGenericError(xmlGenericErrorContext,
680 "xmlNewElementContent : out of memory!\n");
681 return(NULL);
682 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000683 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000684 ret->type = type;
685 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000686 if (name != NULL) {
687 xmlChar *prefix = NULL;
688 ret->name = xmlSplitQName2(name, &prefix);
689 if (ret->name == NULL)
690 ret->name = xmlStrdup(name);
691 ret->prefix = prefix;
692 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000693 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000694 ret->prefix = NULL;
695 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000696 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000697 return(ret);
698}
699
700/**
701 * xmlCopyElementContent:
702 * @content: An element content pointer.
703 *
704 * Build a copy of an element content description.
705 *
706 * Returns the new xmlElementContentPtr or NULL in case of error.
707 */
708xmlElementContentPtr
709xmlCopyElementContent(xmlElementContentPtr cur) {
710 xmlElementContentPtr ret;
711
712 if (cur == NULL) return(NULL);
713 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
714 if (ret == NULL) {
715 xmlGenericError(xmlGenericErrorContext,
716 "xmlCopyElementContent : out of memory\n");
717 return(NULL);
718 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000719 if (cur->prefix != NULL)
720 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000721 ret->ocur = cur->ocur;
722 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000723 if (ret->c1 != NULL)
724 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000725 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000726 if (ret->c2 != NULL)
727 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000728 return(ret);
729}
730
731/**
732 * xmlFreeElementContent:
733 * @cur: the element content tree to free
734 *
735 * Free an element content structure. This is a recursive call !
736 */
737void
738xmlFreeElementContent(xmlElementContentPtr cur) {
739 if (cur == NULL) return;
740 switch (cur->type) {
741 case XML_ELEMENT_CONTENT_PCDATA:
742 case XML_ELEMENT_CONTENT_ELEMENT:
743 case XML_ELEMENT_CONTENT_SEQ:
744 case XML_ELEMENT_CONTENT_OR:
745 break;
746 default:
747 xmlGenericError(xmlGenericErrorContext,
748 "xmlFreeElementContent : type %d\n", cur->type);
749 return;
750 }
751 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
752 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
753 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000754 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000755 xmlFree(cur);
756}
757
758/**
759 * xmlDumpElementContent:
760 * @buf: An XML buffer
761 * @content: An element table
762 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
763 *
764 * This will dump the content of the element table as an XML DTD definition
765 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000766static void
Owen Taylor3473f882001-02-23 17:55:21 +0000767xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
768 if (content == NULL) return;
769
770 if (glob) xmlBufferWriteChar(buf, "(");
771 switch (content->type) {
772 case XML_ELEMENT_CONTENT_PCDATA:
773 xmlBufferWriteChar(buf, "#PCDATA");
774 break;
775 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000776 if (content->prefix != NULL) {
777 xmlBufferWriteCHAR(buf, content->prefix);
778 xmlBufferWriteChar(buf, ":");
779 }
Owen Taylor3473f882001-02-23 17:55:21 +0000780 xmlBufferWriteCHAR(buf, content->name);
781 break;
782 case XML_ELEMENT_CONTENT_SEQ:
783 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
784 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
785 xmlDumpElementContent(buf, content->c1, 1);
786 else
787 xmlDumpElementContent(buf, content->c1, 0);
788 xmlBufferWriteChar(buf, " , ");
789 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
790 xmlDumpElementContent(buf, content->c2, 1);
791 else
792 xmlDumpElementContent(buf, content->c2, 0);
793 break;
794 case XML_ELEMENT_CONTENT_OR:
795 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
796 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
797 xmlDumpElementContent(buf, content->c1, 1);
798 else
799 xmlDumpElementContent(buf, content->c1, 0);
800 xmlBufferWriteChar(buf, " | ");
801 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
802 xmlDumpElementContent(buf, content->c2, 1);
803 else
804 xmlDumpElementContent(buf, content->c2, 0);
805 break;
806 default:
807 xmlGenericError(xmlGenericErrorContext,
808 "xmlDumpElementContent: unknown type %d\n",
809 content->type);
810 }
811 if (glob)
812 xmlBufferWriteChar(buf, ")");
813 switch (content->ocur) {
814 case XML_ELEMENT_CONTENT_ONCE:
815 break;
816 case XML_ELEMENT_CONTENT_OPT:
817 xmlBufferWriteChar(buf, "?");
818 break;
819 case XML_ELEMENT_CONTENT_MULT:
820 xmlBufferWriteChar(buf, "*");
821 break;
822 case XML_ELEMENT_CONTENT_PLUS:
823 xmlBufferWriteChar(buf, "+");
824 break;
825 }
826}
827
828/**
829 * xmlSprintfElementContent:
830 * @buf: an output buffer
831 * @content: An element table
832 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
833 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000834 * Deprecated, unsafe, use xmlSnprintfElementContent
835 */
836void
837xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
838 xmlElementContentPtr content ATTRIBUTE_UNUSED,
839 int glob ATTRIBUTE_UNUSED) {
840}
841
842/**
843 * xmlSnprintfElementContent:
844 * @buf: an output buffer
845 * @size: the buffer size
846 * @content: An element table
847 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
848 *
Owen Taylor3473f882001-02-23 17:55:21 +0000849 * This will dump the content of the element content definition
850 * Intended just for the debug routine
851 */
852void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000853xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
854 int len;
855
Owen Taylor3473f882001-02-23 17:55:21 +0000856 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000857 len = strlen(buf);
858 if (size - len < 50) {
859 if ((size - len > 4) && (buf[len - 1] != '.'))
860 strcat(buf, " ...");
861 return;
862 }
Owen Taylor3473f882001-02-23 17:55:21 +0000863 if (glob) strcat(buf, "(");
864 switch (content->type) {
865 case XML_ELEMENT_CONTENT_PCDATA:
866 strcat(buf, "#PCDATA");
867 break;
868 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000869 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000870 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000871 strcat(buf, " ...");
872 return;
873 }
874 strcat(buf, (char *) content->prefix);
875 strcat(buf, ":");
876 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000877 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000878 strcat(buf, " ...");
879 return;
880 }
Owen Taylor3473f882001-02-23 17:55:21 +0000881 strcat(buf, (char *) content->name);
882 break;
883 case XML_ELEMENT_CONTENT_SEQ:
884 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
885 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000886 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000887 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000888 xmlSnprintfElementContent(buf, size, content->c1, 0);
889 len = strlen(buf);
890 if (size - len < 50) {
891 if ((size - len > 4) && (buf[len - 1] != '.'))
892 strcat(buf, " ...");
893 return;
894 }
Owen Taylor3473f882001-02-23 17:55:21 +0000895 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000896 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
897 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
898 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000899 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000900 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000901 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000902 break;
903 case XML_ELEMENT_CONTENT_OR:
904 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
905 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000906 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000907 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000908 xmlSnprintfElementContent(buf, size, content->c1, 0);
909 len = strlen(buf);
910 if (size - len < 50) {
911 if ((size - len > 4) && (buf[len - 1] != '.'))
912 strcat(buf, " ...");
913 return;
914 }
Owen Taylor3473f882001-02-23 17:55:21 +0000915 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000916 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
917 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
918 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000919 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000920 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000921 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000922 break;
923 }
924 if (glob)
925 strcat(buf, ")");
926 switch (content->ocur) {
927 case XML_ELEMENT_CONTENT_ONCE:
928 break;
929 case XML_ELEMENT_CONTENT_OPT:
930 strcat(buf, "?");
931 break;
932 case XML_ELEMENT_CONTENT_MULT:
933 strcat(buf, "*");
934 break;
935 case XML_ELEMENT_CONTENT_PLUS:
936 strcat(buf, "+");
937 break;
938 }
939}
940
941/****************************************************************
942 * *
943 * Registration of DTD declarations *
944 * *
945 ****************************************************************/
946
947/**
948 * xmlCreateElementTable:
949 *
950 * create and initialize an empty element hash table.
951 *
952 * Returns the xmlElementTablePtr just created or NULL in case of error.
953 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000954static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000955xmlCreateElementTable(void) {
956 return(xmlHashCreate(0));
957}
958
959/**
960 * xmlFreeElement:
961 * @elem: An element
962 *
963 * Deallocate the memory used by an element definition
964 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000965static void
Owen Taylor3473f882001-02-23 17:55:21 +0000966xmlFreeElement(xmlElementPtr elem) {
967 if (elem == NULL) return;
968 xmlUnlinkNode((xmlNodePtr) elem);
969 xmlFreeElementContent(elem->content);
970 if (elem->name != NULL)
971 xmlFree((xmlChar *) elem->name);
972 if (elem->prefix != NULL)
973 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000974#ifdef LIBXML_REGEXP_ENABLED
975 if (elem->contModel != NULL)
976 xmlRegFreeRegexp(elem->contModel);
977#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000978 xmlFree(elem);
979}
980
981
982/**
983 * xmlAddElementDecl:
984 * @ctxt: the validation context
985 * @dtd: pointer to the DTD
986 * @name: the entity name
987 * @type: the element type
988 * @content: the element content tree or NULL
989 *
990 * Register a new element declaration
991 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000992 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +0000993 */
994xmlElementPtr
995xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
996 xmlElementTypeVal type,
997 xmlElementContentPtr content) {
998 xmlElementPtr ret;
999 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +00001000 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00001001 xmlChar *ns, *uqname;
1002
1003 if (dtd == NULL) {
1004 xmlGenericError(xmlGenericErrorContext,
1005 "xmlAddElementDecl: dtd == NULL\n");
1006 return(NULL);
1007 }
1008 if (name == NULL) {
1009 xmlGenericError(xmlGenericErrorContext,
1010 "xmlAddElementDecl: name == NULL\n");
1011 return(NULL);
1012 }
1013 switch (type) {
1014 case XML_ELEMENT_TYPE_EMPTY:
1015 if (content != NULL) {
1016 xmlGenericError(xmlGenericErrorContext,
1017 "xmlAddElementDecl: content != NULL for EMPTY\n");
1018 return(NULL);
1019 }
1020 break;
1021 case XML_ELEMENT_TYPE_ANY:
1022 if (content != NULL) {
1023 xmlGenericError(xmlGenericErrorContext,
1024 "xmlAddElementDecl: content != NULL for ANY\n");
1025 return(NULL);
1026 }
1027 break;
1028 case XML_ELEMENT_TYPE_MIXED:
1029 if (content == NULL) {
1030 xmlGenericError(xmlGenericErrorContext,
1031 "xmlAddElementDecl: content == NULL for MIXED\n");
1032 return(NULL);
1033 }
1034 break;
1035 case XML_ELEMENT_TYPE_ELEMENT:
1036 if (content == NULL) {
1037 xmlGenericError(xmlGenericErrorContext,
1038 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1039 return(NULL);
1040 }
1041 break;
1042 default:
1043 xmlGenericError(xmlGenericErrorContext,
1044 "xmlAddElementDecl: unknown type %d\n", type);
1045 return(NULL);
1046 }
1047
1048 /*
1049 * check if name is a QName
1050 */
1051 uqname = xmlSplitQName2(name, &ns);
1052 if (uqname != NULL)
1053 name = uqname;
1054
1055 /*
1056 * Create the Element table if needed.
1057 */
1058 table = (xmlElementTablePtr) dtd->elements;
1059 if (table == NULL) {
1060 table = xmlCreateElementTable();
1061 dtd->elements = (void *) table;
1062 }
1063 if (table == NULL) {
1064 xmlGenericError(xmlGenericErrorContext,
1065 "xmlAddElementDecl: Table creation failed!\n");
1066 return(NULL);
1067 }
1068
Daniel Veillarda10efa82001-04-18 13:09:01 +00001069 /*
1070 * lookup old attributes inserted on an undefined element in the
1071 * internal subset.
1072 */
1073 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1074 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1075 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1076 oldAttributes = ret->attributes;
1077 ret->attributes = NULL;
1078 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1079 xmlFreeElement(ret);
1080 }
Owen Taylor3473f882001-02-23 17:55:21 +00001081 }
Owen Taylor3473f882001-02-23 17:55:21 +00001082
1083 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001084 * The element may already be present if one of its attribute
1085 * was registered first
1086 */
1087 ret = xmlHashLookup2(table, name, ns);
1088 if (ret != NULL) {
1089 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1090 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001091 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001092 */
1093 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1094 if (uqname != NULL)
1095 xmlFree(uqname);
1096 return(NULL);
1097 }
1098 } else {
1099 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1100 if (ret == NULL) {
1101 xmlGenericError(xmlGenericErrorContext,
1102 "xmlAddElementDecl: out of memory\n");
1103 return(NULL);
1104 }
1105 memset(ret, 0, sizeof(xmlElement));
1106 ret->type = XML_ELEMENT_DECL;
1107
1108 /*
1109 * fill the structure.
1110 */
1111 ret->name = xmlStrdup(name);
1112 ret->prefix = ns;
1113
1114 /*
1115 * Validity Check:
1116 * Insertion must not fail
1117 */
1118 if (xmlHashAddEntry2(table, name, ns, ret)) {
1119 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001120 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001121 */
1122 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1123 xmlFreeElement(ret);
1124 if (uqname != NULL)
1125 xmlFree(uqname);
1126 return(NULL);
1127 }
1128 }
1129
1130 /*
1131 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001132 */
1133 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001134 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001135 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001136
1137 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001138 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001139 */
1140 ret->parent = dtd;
1141 ret->doc = dtd->doc;
1142 if (dtd->last == NULL) {
1143 dtd->children = dtd->last = (xmlNodePtr) ret;
1144 } else {
1145 dtd->last->next = (xmlNodePtr) ret;
1146 ret->prev = dtd->last;
1147 dtd->last = (xmlNodePtr) ret;
1148 }
1149 if (uqname != NULL)
1150 xmlFree(uqname);
1151 return(ret);
1152}
1153
1154/**
1155 * xmlFreeElementTable:
1156 * @table: An element table
1157 *
1158 * Deallocate the memory used by an element hash table.
1159 */
1160void
1161xmlFreeElementTable(xmlElementTablePtr table) {
1162 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1163}
1164
1165/**
1166 * xmlCopyElement:
1167 * @elem: An element
1168 *
1169 * Build a copy of an element.
1170 *
1171 * Returns the new xmlElementPtr or NULL in case of error.
1172 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001173static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001174xmlCopyElement(xmlElementPtr elem) {
1175 xmlElementPtr cur;
1176
1177 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1178 if (cur == NULL) {
1179 xmlGenericError(xmlGenericErrorContext,
1180 "xmlCopyElement: out of memory !\n");
1181 return(NULL);
1182 }
1183 memset(cur, 0, sizeof(xmlElement));
1184 cur->type = XML_ELEMENT_DECL;
1185 cur->etype = elem->etype;
1186 if (elem->name != NULL)
1187 cur->name = xmlStrdup(elem->name);
1188 else
1189 cur->name = NULL;
1190 if (elem->prefix != NULL)
1191 cur->prefix = xmlStrdup(elem->prefix);
1192 else
1193 cur->prefix = NULL;
1194 cur->content = xmlCopyElementContent(elem->content);
1195 /* TODO : rebuild the attribute list on the copy */
1196 cur->attributes = NULL;
1197 return(cur);
1198}
1199
1200/**
1201 * xmlCopyElementTable:
1202 * @table: An element table
1203 *
1204 * Build a copy of an element table.
1205 *
1206 * Returns the new xmlElementTablePtr or NULL in case of error.
1207 */
1208xmlElementTablePtr
1209xmlCopyElementTable(xmlElementTablePtr table) {
1210 return((xmlElementTablePtr) xmlHashCopy(table,
1211 (xmlHashCopier) xmlCopyElement));
1212}
1213
1214/**
1215 * xmlDumpElementDecl:
1216 * @buf: the XML buffer output
1217 * @elem: An element table
1218 *
1219 * This will dump the content of the element declaration as an XML
1220 * DTD definition
1221 */
1222void
1223xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1224 switch (elem->etype) {
1225 case XML_ELEMENT_TYPE_EMPTY:
1226 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001227 if (elem->prefix != NULL) {
1228 xmlBufferWriteCHAR(buf, elem->prefix);
1229 xmlBufferWriteChar(buf, ":");
1230 }
Owen Taylor3473f882001-02-23 17:55:21 +00001231 xmlBufferWriteCHAR(buf, elem->name);
1232 xmlBufferWriteChar(buf, " EMPTY>\n");
1233 break;
1234 case XML_ELEMENT_TYPE_ANY:
1235 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001236 if (elem->prefix != NULL) {
1237 xmlBufferWriteCHAR(buf, elem->prefix);
1238 xmlBufferWriteChar(buf, ":");
1239 }
Owen Taylor3473f882001-02-23 17:55:21 +00001240 xmlBufferWriteCHAR(buf, elem->name);
1241 xmlBufferWriteChar(buf, " ANY>\n");
1242 break;
1243 case XML_ELEMENT_TYPE_MIXED:
1244 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001245 if (elem->prefix != NULL) {
1246 xmlBufferWriteCHAR(buf, elem->prefix);
1247 xmlBufferWriteChar(buf, ":");
1248 }
Owen Taylor3473f882001-02-23 17:55:21 +00001249 xmlBufferWriteCHAR(buf, elem->name);
1250 xmlBufferWriteChar(buf, " ");
1251 xmlDumpElementContent(buf, elem->content, 1);
1252 xmlBufferWriteChar(buf, ">\n");
1253 break;
1254 case XML_ELEMENT_TYPE_ELEMENT:
1255 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001256 if (elem->prefix != NULL) {
1257 xmlBufferWriteCHAR(buf, elem->prefix);
1258 xmlBufferWriteChar(buf, ":");
1259 }
Owen Taylor3473f882001-02-23 17:55:21 +00001260 xmlBufferWriteCHAR(buf, elem->name);
1261 xmlBufferWriteChar(buf, " ");
1262 xmlDumpElementContent(buf, elem->content, 1);
1263 xmlBufferWriteChar(buf, ">\n");
1264 break;
1265 default:
1266 xmlGenericError(xmlGenericErrorContext,
1267 "xmlDumpElementDecl: internal: unknown type %d\n",
1268 elem->etype);
1269 }
1270}
1271
1272/**
1273 * xmlDumpElementTable:
1274 * @buf: the XML buffer output
1275 * @table: An element table
1276 *
1277 * This will dump the content of the element table as an XML DTD definition
1278 */
1279void
1280xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1281 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1282}
1283
1284/**
1285 * xmlCreateEnumeration:
1286 * @name: the enumeration name or NULL
1287 *
1288 * create and initialize an enumeration attribute node.
1289 *
1290 * Returns the xmlEnumerationPtr just created or NULL in case
1291 * of error.
1292 */
1293xmlEnumerationPtr
1294xmlCreateEnumeration(xmlChar *name) {
1295 xmlEnumerationPtr ret;
1296
1297 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1298 if (ret == NULL) {
1299 xmlGenericError(xmlGenericErrorContext,
1300 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1301 (long)sizeof(xmlEnumeration));
1302 return(NULL);
1303 }
1304 memset(ret, 0, sizeof(xmlEnumeration));
1305
1306 if (name != NULL)
1307 ret->name = xmlStrdup(name);
1308 return(ret);
1309}
1310
1311/**
1312 * xmlFreeEnumeration:
1313 * @cur: the tree to free.
1314 *
1315 * free an enumeration attribute node (recursive).
1316 */
1317void
1318xmlFreeEnumeration(xmlEnumerationPtr cur) {
1319 if (cur == NULL) return;
1320
1321 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1322
1323 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001324 xmlFree(cur);
1325}
1326
1327/**
1328 * xmlCopyEnumeration:
1329 * @cur: the tree to copy.
1330 *
1331 * Copy an enumeration attribute node (recursive).
1332 *
1333 * Returns the xmlEnumerationPtr just created or NULL in case
1334 * of error.
1335 */
1336xmlEnumerationPtr
1337xmlCopyEnumeration(xmlEnumerationPtr cur) {
1338 xmlEnumerationPtr ret;
1339
1340 if (cur == NULL) return(NULL);
1341 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1342
1343 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1344 else ret->next = NULL;
1345
1346 return(ret);
1347}
1348
1349/**
1350 * xmlDumpEnumeration:
1351 * @buf: the XML buffer output
1352 * @enum: An enumeration
1353 *
1354 * This will dump the content of the enumeration
1355 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001356static void
Owen Taylor3473f882001-02-23 17:55:21 +00001357xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1358 if (cur == NULL) return;
1359
1360 xmlBufferWriteCHAR(buf, cur->name);
1361 if (cur->next == NULL)
1362 xmlBufferWriteChar(buf, ")");
1363 else {
1364 xmlBufferWriteChar(buf, " | ");
1365 xmlDumpEnumeration(buf, cur->next);
1366 }
1367}
1368
1369/**
1370 * xmlCreateAttributeTable:
1371 *
1372 * create and initialize an empty attribute hash table.
1373 *
1374 * Returns the xmlAttributeTablePtr just created or NULL in case
1375 * of error.
1376 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001377static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001378xmlCreateAttributeTable(void) {
1379 return(xmlHashCreate(0));
1380}
1381
1382/**
1383 * xmlScanAttributeDeclCallback:
1384 * @attr: the attribute decl
1385 * @list: the list to update
1386 *
1387 * Callback called by xmlScanAttributeDecl when a new attribute
1388 * has to be entered in the list.
1389 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001390static void
Owen Taylor3473f882001-02-23 17:55:21 +00001391xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001392 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001393 attr->nexth = *list;
1394 *list = attr;
1395}
1396
1397/**
1398 * xmlScanAttributeDecl:
1399 * @dtd: pointer to the DTD
1400 * @elem: the element name
1401 *
1402 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001403 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001404 *
1405 * Returns the pointer to the first attribute decl in the chain,
1406 * possibly NULL.
1407 */
1408xmlAttributePtr
1409xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1410 xmlAttributePtr ret = NULL;
1411 xmlAttributeTablePtr table;
1412
1413 if (dtd == NULL) {
1414 xmlGenericError(xmlGenericErrorContext,
1415 "xmlScanAttributeDecl: dtd == NULL\n");
1416 return(NULL);
1417 }
1418 if (elem == NULL) {
1419 xmlGenericError(xmlGenericErrorContext,
1420 "xmlScanAttributeDecl: elem == NULL\n");
1421 return(NULL);
1422 }
1423 table = (xmlAttributeTablePtr) dtd->attributes;
1424 if (table == NULL)
1425 return(NULL);
1426
1427 /* WRONG !!! */
1428 xmlHashScan3(table, NULL, NULL, elem,
1429 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1430 return(ret);
1431}
1432
1433/**
1434 * xmlScanIDAttributeDecl:
1435 * @ctxt: the validation context
1436 * @elem: the element name
1437 *
1438 * Verify that the element don't have too many ID attributes
1439 * declared.
1440 *
1441 * Returns the number of ID attributes found.
1442 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001443static int
Owen Taylor3473f882001-02-23 17:55:21 +00001444xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1445 xmlAttributePtr cur;
1446 int ret = 0;
1447
1448 if (elem == NULL) return(0);
1449 cur = elem->attributes;
1450 while (cur != NULL) {
1451 if (cur->atype == XML_ATTRIBUTE_ID) {
1452 ret ++;
1453 if (ret > 1)
1454 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001455 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001456 elem->name, cur->name);
1457 }
1458 cur = cur->nexth;
1459 }
1460 return(ret);
1461}
1462
1463/**
1464 * xmlFreeAttribute:
1465 * @elem: An attribute
1466 *
1467 * Deallocate the memory used by an attribute definition
1468 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001469static void
Owen Taylor3473f882001-02-23 17:55:21 +00001470xmlFreeAttribute(xmlAttributePtr attr) {
1471 if (attr == NULL) return;
1472 xmlUnlinkNode((xmlNodePtr) attr);
1473 if (attr->tree != NULL)
1474 xmlFreeEnumeration(attr->tree);
1475 if (attr->elem != NULL)
1476 xmlFree((xmlChar *) attr->elem);
1477 if (attr->name != NULL)
1478 xmlFree((xmlChar *) attr->name);
1479 if (attr->defaultValue != NULL)
1480 xmlFree((xmlChar *) attr->defaultValue);
1481 if (attr->prefix != NULL)
1482 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001483 xmlFree(attr);
1484}
1485
1486
1487/**
1488 * xmlAddAttributeDecl:
1489 * @ctxt: the validation context
1490 * @dtd: pointer to the DTD
1491 * @elem: the element name
1492 * @name: the attribute name
1493 * @ns: the attribute namespace prefix
1494 * @type: the attribute type
1495 * @def: the attribute default type
1496 * @defaultValue: the attribute default value
1497 * @tree: if it's an enumeration, the associated list
1498 *
1499 * Register a new attribute declaration
1500 * Note that @tree becomes the ownership of the DTD
1501 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001502 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001503 */
1504xmlAttributePtr
1505xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1506 const xmlChar *name, const xmlChar *ns,
1507 xmlAttributeType type, xmlAttributeDefault def,
1508 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1509 xmlAttributePtr ret;
1510 xmlAttributeTablePtr table;
1511 xmlElementPtr elemDef;
1512
1513 if (dtd == NULL) {
1514 xmlGenericError(xmlGenericErrorContext,
1515 "xmlAddAttributeDecl: dtd == NULL\n");
1516 xmlFreeEnumeration(tree);
1517 return(NULL);
1518 }
1519 if (name == NULL) {
1520 xmlGenericError(xmlGenericErrorContext,
1521 "xmlAddAttributeDecl: name == NULL\n");
1522 xmlFreeEnumeration(tree);
1523 return(NULL);
1524 }
1525 if (elem == NULL) {
1526 xmlGenericError(xmlGenericErrorContext,
1527 "xmlAddAttributeDecl: elem == NULL\n");
1528 xmlFreeEnumeration(tree);
1529 return(NULL);
1530 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001531
Owen Taylor3473f882001-02-23 17:55:21 +00001532 /*
1533 * Check the type and possibly the default value.
1534 */
1535 switch (type) {
1536 case XML_ATTRIBUTE_CDATA:
1537 break;
1538 case XML_ATTRIBUTE_ID:
1539 break;
1540 case XML_ATTRIBUTE_IDREF:
1541 break;
1542 case XML_ATTRIBUTE_IDREFS:
1543 break;
1544 case XML_ATTRIBUTE_ENTITY:
1545 break;
1546 case XML_ATTRIBUTE_ENTITIES:
1547 break;
1548 case XML_ATTRIBUTE_NMTOKEN:
1549 break;
1550 case XML_ATTRIBUTE_NMTOKENS:
1551 break;
1552 case XML_ATTRIBUTE_ENUMERATION:
1553 break;
1554 case XML_ATTRIBUTE_NOTATION:
1555 break;
1556 default:
1557 xmlGenericError(xmlGenericErrorContext,
1558 "xmlAddAttributeDecl: unknown type %d\n", type);
1559 xmlFreeEnumeration(tree);
1560 return(NULL);
1561 }
1562 if ((defaultValue != NULL) &&
1563 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001564 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001565 elem, name, defaultValue);
1566 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001567 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001568 }
1569
1570 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001571 * Check first that an attribute defined in the external subset wasn't
1572 * already defined in the internal subset
1573 */
1574 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1575 (dtd->doc->intSubset != NULL) &&
1576 (dtd->doc->intSubset->attributes != NULL)) {
1577 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1578 if (ret != NULL)
1579 return(NULL);
1580 }
1581
1582 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001583 * Create the Attribute table if needed.
1584 */
1585 table = (xmlAttributeTablePtr) dtd->attributes;
1586 if (table == NULL) {
1587 table = xmlCreateAttributeTable();
1588 dtd->attributes = (void *) table;
1589 }
1590 if (table == NULL) {
1591 xmlGenericError(xmlGenericErrorContext,
1592 "xmlAddAttributeDecl: Table creation failed!\n");
1593 return(NULL);
1594 }
1595
1596
1597 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1598 if (ret == NULL) {
1599 xmlGenericError(xmlGenericErrorContext,
1600 "xmlAddAttributeDecl: out of memory\n");
1601 return(NULL);
1602 }
1603 memset(ret, 0, sizeof(xmlAttribute));
1604 ret->type = XML_ATTRIBUTE_DECL;
1605
1606 /*
1607 * fill the structure.
1608 */
1609 ret->atype = type;
1610 ret->name = xmlStrdup(name);
1611 ret->prefix = xmlStrdup(ns);
1612 ret->elem = xmlStrdup(elem);
1613 ret->def = def;
1614 ret->tree = tree;
1615 if (defaultValue != NULL)
1616 ret->defaultValue = xmlStrdup(defaultValue);
1617
1618 /*
1619 * Validity Check:
1620 * Search the DTD for previous declarations of the ATTLIST
1621 */
1622 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1623 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001624 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001625 */
1626 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001627 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001628 name, elem);
1629 xmlFreeAttribute(ret);
1630 return(NULL);
1631 }
1632
1633 /*
1634 * Validity Check:
1635 * Multiple ID per element
1636 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001637 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001638 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001639
Owen Taylor3473f882001-02-23 17:55:21 +00001640 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001641 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001642 VERROR(ctxt->userData,
1643 "Element %s has too may ID attributes defined : %s\n",
1644 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001645 ctxt->valid = 0;
1646 }
1647
Daniel Veillard48da9102001-08-07 01:10:10 +00001648 /*
1649 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001650 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001651 */
1652 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1653 ((ret->prefix != NULL &&
1654 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1655 ret->nexth = elemDef->attributes;
1656 elemDef->attributes = ret;
1657 } else {
1658 xmlAttributePtr tmp = elemDef->attributes;
1659
1660 while ((tmp != NULL) &&
1661 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1662 ((ret->prefix != NULL &&
1663 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1664 if (tmp->nexth == NULL)
1665 break;
1666 tmp = tmp->nexth;
1667 }
1668 if (tmp != NULL) {
1669 ret->nexth = tmp->nexth;
1670 tmp->nexth = ret;
1671 } else {
1672 ret->nexth = elemDef->attributes;
1673 elemDef->attributes = ret;
1674 }
1675 }
Owen Taylor3473f882001-02-23 17:55:21 +00001676 }
1677
1678 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001679 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001680 */
1681 ret->parent = dtd;
1682 ret->doc = dtd->doc;
1683 if (dtd->last == NULL) {
1684 dtd->children = dtd->last = (xmlNodePtr) ret;
1685 } else {
1686 dtd->last->next = (xmlNodePtr) ret;
1687 ret->prev = dtd->last;
1688 dtd->last = (xmlNodePtr) ret;
1689 }
1690 return(ret);
1691}
1692
1693/**
1694 * xmlFreeAttributeTable:
1695 * @table: An attribute table
1696 *
1697 * Deallocate the memory used by an entities hash table.
1698 */
1699void
1700xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1701 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1702}
1703
1704/**
1705 * xmlCopyAttribute:
1706 * @attr: An attribute
1707 *
1708 * Build a copy of an attribute.
1709 *
1710 * Returns the new xmlAttributePtr or NULL in case of error.
1711 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001712static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001713xmlCopyAttribute(xmlAttributePtr attr) {
1714 xmlAttributePtr cur;
1715
1716 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1717 if (cur == NULL) {
1718 xmlGenericError(xmlGenericErrorContext,
1719 "xmlCopyAttribute: out of memory !\n");
1720 return(NULL);
1721 }
1722 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001723 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001724 cur->atype = attr->atype;
1725 cur->def = attr->def;
1726 cur->tree = xmlCopyEnumeration(attr->tree);
1727 if (attr->elem != NULL)
1728 cur->elem = xmlStrdup(attr->elem);
1729 if (attr->name != NULL)
1730 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001731 if (attr->prefix != NULL)
1732 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001733 if (attr->defaultValue != NULL)
1734 cur->defaultValue = xmlStrdup(attr->defaultValue);
1735 return(cur);
1736}
1737
1738/**
1739 * xmlCopyAttributeTable:
1740 * @table: An attribute table
1741 *
1742 * Build a copy of an attribute table.
1743 *
1744 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1745 */
1746xmlAttributeTablePtr
1747xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1748 return((xmlAttributeTablePtr) xmlHashCopy(table,
1749 (xmlHashCopier) xmlCopyAttribute));
1750}
1751
1752/**
1753 * xmlDumpAttributeDecl:
1754 * @buf: the XML buffer output
1755 * @attr: An attribute declaration
1756 *
1757 * This will dump the content of the attribute declaration as an XML
1758 * DTD definition
1759 */
1760void
1761xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1762 xmlBufferWriteChar(buf, "<!ATTLIST ");
1763 xmlBufferWriteCHAR(buf, attr->elem);
1764 xmlBufferWriteChar(buf, " ");
1765 if (attr->prefix != NULL) {
1766 xmlBufferWriteCHAR(buf, attr->prefix);
1767 xmlBufferWriteChar(buf, ":");
1768 }
1769 xmlBufferWriteCHAR(buf, attr->name);
1770 switch (attr->atype) {
1771 case XML_ATTRIBUTE_CDATA:
1772 xmlBufferWriteChar(buf, " CDATA");
1773 break;
1774 case XML_ATTRIBUTE_ID:
1775 xmlBufferWriteChar(buf, " ID");
1776 break;
1777 case XML_ATTRIBUTE_IDREF:
1778 xmlBufferWriteChar(buf, " IDREF");
1779 break;
1780 case XML_ATTRIBUTE_IDREFS:
1781 xmlBufferWriteChar(buf, " IDREFS");
1782 break;
1783 case XML_ATTRIBUTE_ENTITY:
1784 xmlBufferWriteChar(buf, " ENTITY");
1785 break;
1786 case XML_ATTRIBUTE_ENTITIES:
1787 xmlBufferWriteChar(buf, " ENTITIES");
1788 break;
1789 case XML_ATTRIBUTE_NMTOKEN:
1790 xmlBufferWriteChar(buf, " NMTOKEN");
1791 break;
1792 case XML_ATTRIBUTE_NMTOKENS:
1793 xmlBufferWriteChar(buf, " NMTOKENS");
1794 break;
1795 case XML_ATTRIBUTE_ENUMERATION:
1796 xmlBufferWriteChar(buf, " (");
1797 xmlDumpEnumeration(buf, attr->tree);
1798 break;
1799 case XML_ATTRIBUTE_NOTATION:
1800 xmlBufferWriteChar(buf, " NOTATION (");
1801 xmlDumpEnumeration(buf, attr->tree);
1802 break;
1803 default:
1804 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001805 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001806 attr->atype);
1807 }
1808 switch (attr->def) {
1809 case XML_ATTRIBUTE_NONE:
1810 break;
1811 case XML_ATTRIBUTE_REQUIRED:
1812 xmlBufferWriteChar(buf, " #REQUIRED");
1813 break;
1814 case XML_ATTRIBUTE_IMPLIED:
1815 xmlBufferWriteChar(buf, " #IMPLIED");
1816 break;
1817 case XML_ATTRIBUTE_FIXED:
1818 xmlBufferWriteChar(buf, " #FIXED");
1819 break;
1820 default:
1821 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001822 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001823 attr->def);
1824 }
1825 if (attr->defaultValue != NULL) {
1826 xmlBufferWriteChar(buf, " ");
1827 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1828 }
1829 xmlBufferWriteChar(buf, ">\n");
1830}
1831
1832/**
1833 * xmlDumpAttributeTable:
1834 * @buf: the XML buffer output
1835 * @table: An attribute table
1836 *
1837 * This will dump the content of the attribute table as an XML DTD definition
1838 */
1839void
1840xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1841 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1842}
1843
1844/************************************************************************
1845 * *
1846 * NOTATIONs *
1847 * *
1848 ************************************************************************/
1849/**
1850 * xmlCreateNotationTable:
1851 *
1852 * create and initialize an empty notation hash table.
1853 *
1854 * Returns the xmlNotationTablePtr just created or NULL in case
1855 * of error.
1856 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001857static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001858xmlCreateNotationTable(void) {
1859 return(xmlHashCreate(0));
1860}
1861
1862/**
1863 * xmlFreeNotation:
1864 * @not: A notation
1865 *
1866 * Deallocate the memory used by an notation definition
1867 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001868static void
Owen Taylor3473f882001-02-23 17:55:21 +00001869xmlFreeNotation(xmlNotationPtr nota) {
1870 if (nota == NULL) return;
1871 if (nota->name != NULL)
1872 xmlFree((xmlChar *) nota->name);
1873 if (nota->PublicID != NULL)
1874 xmlFree((xmlChar *) nota->PublicID);
1875 if (nota->SystemID != NULL)
1876 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001877 xmlFree(nota);
1878}
1879
1880
1881/**
1882 * xmlAddNotationDecl:
1883 * @dtd: pointer to the DTD
1884 * @ctxt: the validation context
1885 * @name: the entity name
1886 * @PublicID: the public identifier or NULL
1887 * @SystemID: the system identifier or NULL
1888 *
1889 * Register a new notation declaration
1890 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001891 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001892 */
1893xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001894xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001895 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001896 const xmlChar *PublicID, const xmlChar *SystemID) {
1897 xmlNotationPtr ret;
1898 xmlNotationTablePtr table;
1899
1900 if (dtd == NULL) {
1901 xmlGenericError(xmlGenericErrorContext,
1902 "xmlAddNotationDecl: dtd == NULL\n");
1903 return(NULL);
1904 }
1905 if (name == NULL) {
1906 xmlGenericError(xmlGenericErrorContext,
1907 "xmlAddNotationDecl: name == NULL\n");
1908 return(NULL);
1909 }
1910 if ((PublicID == NULL) && (SystemID == NULL)) {
1911 xmlGenericError(xmlGenericErrorContext,
1912 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001913 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001914 }
1915
1916 /*
1917 * Create the Notation table if needed.
1918 */
1919 table = (xmlNotationTablePtr) dtd->notations;
1920 if (table == NULL)
1921 dtd->notations = table = xmlCreateNotationTable();
1922 if (table == NULL) {
1923 xmlGenericError(xmlGenericErrorContext,
1924 "xmlAddNotationDecl: Table creation failed!\n");
1925 return(NULL);
1926 }
1927
1928 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1929 if (ret == NULL) {
1930 xmlGenericError(xmlGenericErrorContext,
1931 "xmlAddNotationDecl: out of memory\n");
1932 return(NULL);
1933 }
1934 memset(ret, 0, sizeof(xmlNotation));
1935
1936 /*
1937 * fill the structure.
1938 */
1939 ret->name = xmlStrdup(name);
1940 if (SystemID != NULL)
1941 ret->SystemID = xmlStrdup(SystemID);
1942 if (PublicID != NULL)
1943 ret->PublicID = xmlStrdup(PublicID);
1944
1945 /*
1946 * Validity Check:
1947 * Check the DTD for previous declarations of the ATTLIST
1948 */
1949 if (xmlHashAddEntry(table, name, ret)) {
1950 xmlGenericError(xmlGenericErrorContext,
1951 "xmlAddNotationDecl: %s already defined\n", name);
1952 xmlFreeNotation(ret);
1953 return(NULL);
1954 }
1955 return(ret);
1956}
1957
1958/**
1959 * xmlFreeNotationTable:
1960 * @table: An notation table
1961 *
1962 * Deallocate the memory used by an entities hash table.
1963 */
1964void
1965xmlFreeNotationTable(xmlNotationTablePtr table) {
1966 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1967}
1968
1969/**
1970 * xmlCopyNotation:
1971 * @nota: A notation
1972 *
1973 * Build a copy of a notation.
1974 *
1975 * Returns the new xmlNotationPtr or NULL in case of error.
1976 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001977static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001978xmlCopyNotation(xmlNotationPtr nota) {
1979 xmlNotationPtr cur;
1980
1981 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1982 if (cur == NULL) {
1983 xmlGenericError(xmlGenericErrorContext,
1984 "xmlCopyNotation: out of memory !\n");
1985 return(NULL);
1986 }
1987 if (nota->name != NULL)
1988 cur->name = xmlStrdup(nota->name);
1989 else
1990 cur->name = NULL;
1991 if (nota->PublicID != NULL)
1992 cur->PublicID = xmlStrdup(nota->PublicID);
1993 else
1994 cur->PublicID = NULL;
1995 if (nota->SystemID != NULL)
1996 cur->SystemID = xmlStrdup(nota->SystemID);
1997 else
1998 cur->SystemID = NULL;
1999 return(cur);
2000}
2001
2002/**
2003 * xmlCopyNotationTable:
2004 * @table: A notation table
2005 *
2006 * Build a copy of a notation table.
2007 *
2008 * Returns the new xmlNotationTablePtr or NULL in case of error.
2009 */
2010xmlNotationTablePtr
2011xmlCopyNotationTable(xmlNotationTablePtr table) {
2012 return((xmlNotationTablePtr) xmlHashCopy(table,
2013 (xmlHashCopier) xmlCopyNotation));
2014}
2015
2016/**
2017 * xmlDumpNotationDecl:
2018 * @buf: the XML buffer output
2019 * @nota: A notation declaration
2020 *
2021 * This will dump the content the notation declaration as an XML DTD definition
2022 */
2023void
2024xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2025 xmlBufferWriteChar(buf, "<!NOTATION ");
2026 xmlBufferWriteCHAR(buf, nota->name);
2027 if (nota->PublicID != NULL) {
2028 xmlBufferWriteChar(buf, " PUBLIC ");
2029 xmlBufferWriteQuotedString(buf, nota->PublicID);
2030 if (nota->SystemID != NULL) {
2031 xmlBufferWriteChar(buf, " ");
2032 xmlBufferWriteCHAR(buf, nota->SystemID);
2033 }
2034 } else {
2035 xmlBufferWriteChar(buf, " SYSTEM ");
2036 xmlBufferWriteCHAR(buf, nota->SystemID);
2037 }
2038 xmlBufferWriteChar(buf, " >\n");
2039}
2040
2041/**
2042 * xmlDumpNotationTable:
2043 * @buf: the XML buffer output
2044 * @table: A notation table
2045 *
2046 * This will dump the content of the notation table as an XML DTD definition
2047 */
2048void
2049xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2050 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2051}
2052
2053/************************************************************************
2054 * *
2055 * IDs *
2056 * *
2057 ************************************************************************/
2058/**
2059 * xmlCreateIDTable:
2060 *
2061 * create and initialize an empty id hash table.
2062 *
2063 * Returns the xmlIDTablePtr just created or NULL in case
2064 * of error.
2065 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002066static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002067xmlCreateIDTable(void) {
2068 return(xmlHashCreate(0));
2069}
2070
2071/**
2072 * xmlFreeID:
2073 * @not: A id
2074 *
2075 * Deallocate the memory used by an id definition
2076 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002077static void
Owen Taylor3473f882001-02-23 17:55:21 +00002078xmlFreeID(xmlIDPtr id) {
2079 if (id == NULL) return;
2080 if (id->value != NULL)
2081 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00002082 xmlFree(id);
2083}
2084
2085/**
2086 * xmlAddID:
2087 * @ctxt: the validation context
2088 * @doc: pointer to the document
2089 * @value: the value name
2090 * @attr: the attribute holding the ID
2091 *
2092 * Register a new id declaration
2093 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002094 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002095 */
2096xmlIDPtr
2097xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2098 xmlAttrPtr attr) {
2099 xmlIDPtr ret;
2100 xmlIDTablePtr table;
2101
2102 if (doc == NULL) {
2103 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002104 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002105 return(NULL);
2106 }
2107 if (value == NULL) {
2108 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002109 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002110 return(NULL);
2111 }
2112 if (attr == NULL) {
2113 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002114 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002115 return(NULL);
2116 }
2117
2118 /*
2119 * Create the ID table if needed.
2120 */
2121 table = (xmlIDTablePtr) doc->ids;
2122 if (table == NULL)
2123 doc->ids = table = xmlCreateIDTable();
2124 if (table == NULL) {
2125 xmlGenericError(xmlGenericErrorContext,
2126 "xmlAddID: Table creation failed!\n");
2127 return(NULL);
2128 }
2129
2130 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2131 if (ret == NULL) {
2132 xmlGenericError(xmlGenericErrorContext,
2133 "xmlAddID: out of memory\n");
2134 return(NULL);
2135 }
2136
2137 /*
2138 * fill the structure.
2139 */
2140 ret->value = xmlStrdup(value);
2141 ret->attr = attr;
2142
2143 if (xmlHashAddEntry(table, value, ret) < 0) {
2144 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002145 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002146 */
Daniel Veillard76575762002-09-05 14:21:15 +00002147 if (ctxt != NULL) {
2148 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002149 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002150 }
Owen Taylor3473f882001-02-23 17:55:21 +00002151 xmlFreeID(ret);
2152 return(NULL);
2153 }
2154 return(ret);
2155}
2156
2157/**
2158 * xmlFreeIDTable:
2159 * @table: An id table
2160 *
2161 * Deallocate the memory used by an ID hash table.
2162 */
2163void
2164xmlFreeIDTable(xmlIDTablePtr table) {
2165 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2166}
2167
2168/**
2169 * xmlIsID:
2170 * @doc: the document
2171 * @elem: the element carrying the attribute
2172 * @attr: the attribute
2173 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002174 * Determine whether an attribute is of type ID. In case we have DTD(s)
Daniel Veillard9dc1cf12002-10-08 08:26:11 +00002175 * then this is done if DTD loading has been requested. In the case
2176 * of HTML documents parsed with the HTML parser, then ID detection is
2177 * done systematically.
Owen Taylor3473f882001-02-23 17:55:21 +00002178 *
2179 * Returns 0 or 1 depending on the lookup result
2180 */
2181int
2182xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2183 if (doc == NULL) return(0);
2184 if (attr == NULL) return(0);
2185 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2186 return(0);
2187 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2188 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2189 (xmlStrEqual(BAD_CAST "name", attr->name)))
2190 return(1);
2191 return(0);
2192 } else {
2193 xmlAttributePtr attrDecl;
2194
2195 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002196 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2197 /*
2198 * TODO: this sucks ... recomputing this every time is stupid
2199 */
2200 int len = xmlStrlen(elem->name) + xmlStrlen(elem->ns->prefix) + 2;
2201 xmlChar *fullname;
2202
2203 fullname = xmlMalloc(len);
2204 if (fullname == NULL)
2205 return(0);
2206 snprintf((char *) fullname, len, "%s:%s", (char *) elem->ns->prefix,
2207 (char *) elem->name);
2208 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2209 attr->name);
2210 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2211 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2212 attr->name);
2213 xmlFree(fullname);
2214 } else {
2215 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2216 attr->name);
2217 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2218 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2219 attr->name);
2220 }
Owen Taylor3473f882001-02-23 17:55:21 +00002221
2222 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2223 return(1);
2224 }
2225 return(0);
2226}
2227
2228/**
2229 * xmlRemoveID
2230 * @doc: the document
2231 * @attr: the attribute
2232 *
2233 * Remove the given attribute from the ID table maintained internally.
2234 *
2235 * Returns -1 if the lookup failed and 0 otherwise
2236 */
2237int
2238xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2239 xmlAttrPtr cur;
2240 xmlIDTablePtr table;
2241 xmlChar *ID;
2242
2243 if (doc == NULL) return(-1);
2244 if (attr == NULL) return(-1);
2245 table = (xmlIDTablePtr) doc->ids;
2246 if (table == NULL)
2247 return(-1);
2248
2249 if (attr == NULL)
2250 return(-1);
2251 ID = xmlNodeListGetString(doc, attr->children, 1);
2252 if (ID == NULL)
2253 return(-1);
2254 cur = xmlHashLookup(table, ID);
2255 if (cur != attr) {
2256 xmlFree(ID);
2257 return(-1);
2258 }
2259 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2260 xmlFree(ID);
2261 return(0);
2262}
2263
2264/**
2265 * xmlGetID:
2266 * @doc: pointer to the document
2267 * @ID: the ID value
2268 *
2269 * Search the attribute declaring the given ID
2270 *
2271 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2272 */
2273xmlAttrPtr
2274xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2275 xmlIDTablePtr table;
2276 xmlIDPtr id;
2277
2278 if (doc == NULL) {
2279 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2280 return(NULL);
2281 }
2282
2283 if (ID == NULL) {
2284 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2285 return(NULL);
2286 }
2287
2288 table = (xmlIDTablePtr) doc->ids;
2289 if (table == NULL)
2290 return(NULL);
2291
2292 id = xmlHashLookup(table, ID);
2293 if (id == NULL)
2294 return(NULL);
2295 return(id->attr);
2296}
2297
2298/************************************************************************
2299 * *
2300 * Refs *
2301 * *
2302 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002303typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002304{
2305 xmlListPtr l;
2306 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002307} xmlRemoveMemo;
2308
2309typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2310
2311typedef struct xmlValidateMemo_t
2312{
2313 xmlValidCtxtPtr ctxt;
2314 const xmlChar *name;
2315} xmlValidateMemo;
2316
2317typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002318
2319/**
2320 * xmlCreateRefTable:
2321 *
2322 * create and initialize an empty ref hash table.
2323 *
2324 * Returns the xmlRefTablePtr just created or NULL in case
2325 * of error.
2326 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002327static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002328xmlCreateRefTable(void) {
2329 return(xmlHashCreate(0));
2330}
2331
2332/**
2333 * xmlFreeRef:
2334 * @lk: A list link
2335 *
2336 * Deallocate the memory used by a ref definition
2337 */
2338static void
2339xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002340 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2341 if (ref == NULL) return;
2342 if (ref->value != NULL)
2343 xmlFree((xmlChar *)ref->value);
2344 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002345}
2346
2347/**
2348 * xmlFreeRefList:
2349 * @list_ref: A list of references.
2350 *
2351 * Deallocate the memory used by a list of references
2352 */
2353static void
2354xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002355 if (list_ref == NULL) return;
2356 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002357}
2358
2359/**
2360 * xmlWalkRemoveRef:
2361 * @data: Contents of current link
2362 * @user: Value supplied by the user
2363 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002364 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002365 */
2366static int
2367xmlWalkRemoveRef(const void *data, const void *user)
2368{
Daniel Veillard37721922001-05-04 15:21:12 +00002369 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2370 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2371 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002372
Daniel Veillard37721922001-05-04 15:21:12 +00002373 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2374 xmlListRemoveFirst(ref_list, (void *)data);
2375 return 0;
2376 }
2377 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002378}
2379
2380/**
2381 * xmlAddRef:
2382 * @ctxt: the validation context
2383 * @doc: pointer to the document
2384 * @value: the value name
2385 * @attr: the attribute holding the Ref
2386 *
2387 * Register a new ref declaration
2388 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002389 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002390 */
2391xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002392xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002393 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002394 xmlRefPtr ret;
2395 xmlRefTablePtr table;
2396 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002397
Daniel Veillard37721922001-05-04 15:21:12 +00002398 if (doc == NULL) {
2399 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002400 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002401 return(NULL);
2402 }
2403 if (value == NULL) {
2404 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002405 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002406 return(NULL);
2407 }
2408 if (attr == NULL) {
2409 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002410 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002411 return(NULL);
2412 }
Owen Taylor3473f882001-02-23 17:55:21 +00002413
Daniel Veillard37721922001-05-04 15:21:12 +00002414 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002415 * Create the Ref table if needed.
2416 */
Daniel Veillard37721922001-05-04 15:21:12 +00002417 table = (xmlRefTablePtr) doc->refs;
2418 if (table == NULL)
2419 doc->refs = table = xmlCreateRefTable();
2420 if (table == NULL) {
2421 xmlGenericError(xmlGenericErrorContext,
2422 "xmlAddRef: Table creation failed!\n");
2423 return(NULL);
2424 }
Owen Taylor3473f882001-02-23 17:55:21 +00002425
Daniel Veillard37721922001-05-04 15:21:12 +00002426 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2427 if (ret == NULL) {
2428 xmlGenericError(xmlGenericErrorContext,
2429 "xmlAddRef: out of memory\n");
2430 return(NULL);
2431 }
Owen Taylor3473f882001-02-23 17:55:21 +00002432
Daniel Veillard37721922001-05-04 15:21:12 +00002433 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002434 * fill the structure.
2435 */
Daniel Veillard37721922001-05-04 15:21:12 +00002436 ret->value = xmlStrdup(value);
2437 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002438
Daniel Veillard37721922001-05-04 15:21:12 +00002439 /* To add a reference :-
2440 * References are maintained as a list of references,
2441 * Lookup the entry, if no entry create new nodelist
2442 * Add the owning node to the NodeList
2443 * Return the ref
2444 */
Owen Taylor3473f882001-02-23 17:55:21 +00002445
Daniel Veillard37721922001-05-04 15:21:12 +00002446 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2447 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2448 xmlGenericError(xmlGenericErrorContext,
2449 "xmlAddRef: Reference list creation failed!\n");
2450 return(NULL);
2451 }
2452 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2453 xmlListDelete(ref_list);
2454 xmlGenericError(xmlGenericErrorContext,
2455 "xmlAddRef: Reference list insertion failed!\n");
2456 return(NULL);
2457 }
2458 }
2459 xmlListInsert(ref_list, ret);
2460 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002461}
2462
2463/**
2464 * xmlFreeRefTable:
2465 * @table: An ref table
2466 *
2467 * Deallocate the memory used by an Ref hash table.
2468 */
2469void
2470xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002471 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002472}
2473
2474/**
2475 * xmlIsRef:
2476 * @doc: the document
2477 * @elem: the element carrying the attribute
2478 * @attr: the attribute
2479 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002480 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002481 * then this is simple, otherwise we use an heuristic: name Ref (upper
2482 * or lowercase).
2483 *
2484 * Returns 0 or 1 depending on the lookup result
2485 */
2486int
2487xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002488 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2489 return(0);
2490 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2491 /* TODO @@@ */
2492 return(0);
2493 } else {
2494 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002495
Daniel Veillard37721922001-05-04 15:21:12 +00002496 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2497 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2498 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2499 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002500
Daniel Veillard37721922001-05-04 15:21:12 +00002501 if ((attrDecl != NULL) &&
2502 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2503 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2504 return(1);
2505 }
2506 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002507}
2508
2509/**
2510 * xmlRemoveRef
2511 * @doc: the document
2512 * @attr: the attribute
2513 *
2514 * Remove the given attribute from the Ref table maintained internally.
2515 *
2516 * Returns -1 if the lookup failed and 0 otherwise
2517 */
2518int
2519xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002520 xmlListPtr ref_list;
2521 xmlRefTablePtr table;
2522 xmlChar *ID;
2523 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002524
Daniel Veillard37721922001-05-04 15:21:12 +00002525 if (doc == NULL) return(-1);
2526 if (attr == NULL) return(-1);
2527 table = (xmlRefTablePtr) doc->refs;
2528 if (table == NULL)
2529 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002530
Daniel Veillard37721922001-05-04 15:21:12 +00002531 if (attr == NULL)
2532 return(-1);
2533 ID = xmlNodeListGetString(doc, attr->children, 1);
2534 if (ID == NULL)
2535 return(-1);
2536 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002537
Daniel Veillard37721922001-05-04 15:21:12 +00002538 if(ref_list == NULL) {
2539 xmlFree(ID);
2540 return (-1);
2541 }
2542 /* At this point, ref_list refers to a list of references which
2543 * have the same key as the supplied attr. Our list of references
2544 * is ordered by reference address and we don't have that information
2545 * here to use when removing. We'll have to walk the list and
2546 * check for a matching attribute, when we find one stop the walk
2547 * and remove the entry.
2548 * The list is ordered by reference, so that means we don't have the
2549 * key. Passing the list and the reference to the walker means we
2550 * will have enough data to be able to remove the entry.
2551 */
2552 target.l = ref_list;
2553 target.ap = attr;
2554
2555 /* Remove the supplied attr from our list */
2556 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002557
Daniel Veillard37721922001-05-04 15:21:12 +00002558 /*If the list is empty then remove the list entry in the hash */
2559 if (xmlListEmpty(ref_list))
2560 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2561 xmlFreeRefList);
2562 xmlFree(ID);
2563 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002564}
2565
2566/**
2567 * xmlGetRefs:
2568 * @doc: pointer to the document
2569 * @ID: the ID value
2570 *
2571 * Find the set of references for the supplied ID.
2572 *
2573 * Returns NULL if not found, otherwise node set for the ID.
2574 */
2575xmlListPtr
2576xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002577 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002578
Daniel Veillard37721922001-05-04 15:21:12 +00002579 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002580 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002581 return(NULL);
2582 }
Owen Taylor3473f882001-02-23 17:55:21 +00002583
Daniel Veillard37721922001-05-04 15:21:12 +00002584 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002585 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002586 return(NULL);
2587 }
Owen Taylor3473f882001-02-23 17:55:21 +00002588
Daniel Veillard37721922001-05-04 15:21:12 +00002589 table = (xmlRefTablePtr) doc->refs;
2590 if (table == NULL)
2591 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002592
Daniel Veillard37721922001-05-04 15:21:12 +00002593 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002594}
2595
2596/************************************************************************
2597 * *
2598 * Routines for validity checking *
2599 * *
2600 ************************************************************************/
2601
2602/**
2603 * xmlGetDtdElementDesc:
2604 * @dtd: a pointer to the DtD to search
2605 * @name: the element name
2606 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002607 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002608 *
2609 * returns the xmlElementPtr if found or NULL
2610 */
2611
2612xmlElementPtr
2613xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2614 xmlElementTablePtr table;
2615 xmlElementPtr cur;
2616 xmlChar *uqname = NULL, *prefix = NULL;
2617
2618 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002619 if (dtd->elements == NULL)
2620 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002621 table = (xmlElementTablePtr) dtd->elements;
2622
2623 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002624 if (uqname != NULL)
2625 name = uqname;
2626 cur = xmlHashLookup2(table, name, prefix);
2627 if (prefix != NULL) xmlFree(prefix);
2628 if (uqname != NULL) xmlFree(uqname);
2629 return(cur);
2630}
2631/**
2632 * xmlGetDtdElementDesc2:
2633 * @dtd: a pointer to the DtD to search
2634 * @name: the element name
2635 * @create: create an empty description if not found
2636 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002637 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002638 *
2639 * returns the xmlElementPtr if found or NULL
2640 */
2641
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002642static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002643xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2644 xmlElementTablePtr table;
2645 xmlElementPtr cur;
2646 xmlChar *uqname = NULL, *prefix = NULL;
2647
2648 if (dtd == NULL) return(NULL);
2649 if (dtd->elements == NULL) {
2650 if (!create)
2651 return(NULL);
2652 /*
2653 * Create the Element table if needed.
2654 */
2655 table = (xmlElementTablePtr) dtd->elements;
2656 if (table == NULL) {
2657 table = xmlCreateElementTable();
2658 dtd->elements = (void *) table;
2659 }
2660 if (table == NULL) {
2661 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002662 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002663 return(NULL);
2664 }
2665 }
2666 table = (xmlElementTablePtr) dtd->elements;
2667
2668 uqname = xmlSplitQName2(name, &prefix);
2669 if (uqname != NULL)
2670 name = uqname;
2671 cur = xmlHashLookup2(table, name, prefix);
2672 if ((cur == NULL) && (create)) {
2673 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2674 if (cur == NULL) {
2675 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002676 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002677 return(NULL);
2678 }
2679 memset(cur, 0, sizeof(xmlElement));
2680 cur->type = XML_ELEMENT_DECL;
2681
2682 /*
2683 * fill the structure.
2684 */
2685 cur->name = xmlStrdup(name);
2686 cur->prefix = xmlStrdup(prefix);
2687 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2688
2689 xmlHashAddEntry2(table, name, prefix, cur);
2690 }
2691 if (prefix != NULL) xmlFree(prefix);
2692 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002693 return(cur);
2694}
2695
2696/**
2697 * xmlGetDtdQElementDesc:
2698 * @dtd: a pointer to the DtD to search
2699 * @name: the element name
2700 * @prefix: the element namespace prefix
2701 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002702 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002703 *
2704 * returns the xmlElementPtr if found or NULL
2705 */
2706
Daniel Veillard48da9102001-08-07 01:10:10 +00002707xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002708xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2709 const xmlChar *prefix) {
2710 xmlElementTablePtr table;
2711
2712 if (dtd == NULL) return(NULL);
2713 if (dtd->elements == NULL) return(NULL);
2714 table = (xmlElementTablePtr) dtd->elements;
2715
2716 return(xmlHashLookup2(table, name, prefix));
2717}
2718
2719/**
2720 * xmlGetDtdAttrDesc:
2721 * @dtd: a pointer to the DtD to search
2722 * @elem: the element name
2723 * @name: the attribute name
2724 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002725 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002726 * this element.
2727 *
2728 * returns the xmlAttributePtr if found or NULL
2729 */
2730
2731xmlAttributePtr
2732xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2733 xmlAttributeTablePtr table;
2734 xmlAttributePtr cur;
2735 xmlChar *uqname = NULL, *prefix = NULL;
2736
2737 if (dtd == NULL) return(NULL);
2738 if (dtd->attributes == NULL) return(NULL);
2739
2740 table = (xmlAttributeTablePtr) dtd->attributes;
2741 if (table == NULL)
2742 return(NULL);
2743
2744 uqname = xmlSplitQName2(name, &prefix);
2745
2746 if (uqname != NULL) {
2747 cur = xmlHashLookup3(table, uqname, prefix, elem);
2748 if (prefix != NULL) xmlFree(prefix);
2749 if (uqname != NULL) xmlFree(uqname);
2750 } else
2751 cur = xmlHashLookup3(table, name, NULL, elem);
2752 return(cur);
2753}
2754
2755/**
2756 * xmlGetDtdQAttrDesc:
2757 * @dtd: a pointer to the DtD to search
2758 * @elem: the element name
2759 * @name: the attribute name
2760 * @prefix: the attribute namespace prefix
2761 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002762 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002763 * this element.
2764 *
2765 * returns the xmlAttributePtr if found or NULL
2766 */
2767
Daniel Veillard48da9102001-08-07 01:10:10 +00002768xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002769xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2770 const xmlChar *prefix) {
2771 xmlAttributeTablePtr table;
2772
2773 if (dtd == NULL) return(NULL);
2774 if (dtd->attributes == NULL) return(NULL);
2775 table = (xmlAttributeTablePtr) dtd->attributes;
2776
2777 return(xmlHashLookup3(table, name, prefix, elem));
2778}
2779
2780/**
2781 * xmlGetDtdNotationDesc:
2782 * @dtd: a pointer to the DtD to search
2783 * @name: the notation name
2784 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002785 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002786 *
2787 * returns the xmlNotationPtr if found or NULL
2788 */
2789
2790xmlNotationPtr
2791xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2792 xmlNotationTablePtr table;
2793
2794 if (dtd == NULL) return(NULL);
2795 if (dtd->notations == NULL) return(NULL);
2796 table = (xmlNotationTablePtr) dtd->notations;
2797
2798 return(xmlHashLookup(table, name));
2799}
2800
2801/**
2802 * xmlValidateNotationUse:
2803 * @ctxt: the validation context
2804 * @doc: the document
2805 * @notationName: the notation name to check
2806 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002807 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002808 * - [ VC: Notation Declared ]
2809 *
2810 * returns 1 if valid or 0 otherwise
2811 */
2812
2813int
2814xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2815 const xmlChar *notationName) {
2816 xmlNotationPtr notaDecl;
2817 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2818
2819 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2820 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2821 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2822
2823 if (notaDecl == NULL) {
2824 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2825 notationName);
2826 return(0);
2827 }
2828 return(1);
2829}
2830
2831/**
2832 * xmlIsMixedElement
2833 * @doc: the document
2834 * @name: the element name
2835 *
2836 * Search in the DtDs whether an element accept Mixed content (or ANY)
2837 * basically if it is supposed to accept text childs
2838 *
2839 * returns 0 if no, 1 if yes, and -1 if no element description is available
2840 */
2841
2842int
2843xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2844 xmlElementPtr elemDecl;
2845
2846 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2847
2848 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2849 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2850 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2851 if (elemDecl == NULL) return(-1);
2852 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002853 case XML_ELEMENT_TYPE_UNDEFINED:
2854 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002855 case XML_ELEMENT_TYPE_ELEMENT:
2856 return(0);
2857 case XML_ELEMENT_TYPE_EMPTY:
2858 /*
2859 * return 1 for EMPTY since we want VC error to pop up
2860 * on <empty> </empty> for example
2861 */
2862 case XML_ELEMENT_TYPE_ANY:
2863 case XML_ELEMENT_TYPE_MIXED:
2864 return(1);
2865 }
2866 return(1);
2867}
2868
2869/**
2870 * xmlValidateNameValue:
2871 * @value: an Name value
2872 *
2873 * Validate that the given value match Name production
2874 *
2875 * returns 1 if valid or 0 otherwise
2876 */
2877
Daniel Veillard9b731d72002-04-14 12:56:08 +00002878int
Owen Taylor3473f882001-02-23 17:55:21 +00002879xmlValidateNameValue(const xmlChar *value) {
2880 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002881 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002882
2883 if (value == NULL) return(0);
2884 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002885 val = xmlStringCurrentChar(NULL, cur, &len);
2886 cur += len;
2887 if (!IS_LETTER(val) && (val != '_') &&
2888 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002889 return(0);
2890 }
2891
Daniel Veillardd8224e02002-01-13 15:43:22 +00002892 val = xmlStringCurrentChar(NULL, cur, &len);
2893 cur += len;
2894 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2895 (val == '.') || (val == '-') ||
2896 (val == '_') || (val == ':') ||
2897 (IS_COMBINING(val)) ||
2898 (IS_EXTENDER(val))) {
2899 val = xmlStringCurrentChar(NULL, cur, &len);
2900 cur += len;
2901 }
Owen Taylor3473f882001-02-23 17:55:21 +00002902
Daniel Veillardd8224e02002-01-13 15:43:22 +00002903 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002904
2905 return(1);
2906}
2907
2908/**
2909 * xmlValidateNamesValue:
2910 * @value: an Names value
2911 *
2912 * Validate that the given value match Names production
2913 *
2914 * returns 1 if valid or 0 otherwise
2915 */
2916
Daniel Veillard9b731d72002-04-14 12:56:08 +00002917int
Owen Taylor3473f882001-02-23 17:55:21 +00002918xmlValidateNamesValue(const xmlChar *value) {
2919 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002920 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002921
2922 if (value == NULL) return(0);
2923 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002924 val = xmlStringCurrentChar(NULL, cur, &len);
2925 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002926
Daniel Veillardd8224e02002-01-13 15:43:22 +00002927 if (!IS_LETTER(val) && (val != '_') &&
2928 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002929 return(0);
2930 }
2931
Daniel Veillardd8224e02002-01-13 15:43:22 +00002932 val = xmlStringCurrentChar(NULL, cur, &len);
2933 cur += len;
2934 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2935 (val == '.') || (val == '-') ||
2936 (val == '_') || (val == ':') ||
2937 (IS_COMBINING(val)) ||
2938 (IS_EXTENDER(val))) {
2939 val = xmlStringCurrentChar(NULL, cur, &len);
2940 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002941 }
2942
Daniel Veillardd8224e02002-01-13 15:43:22 +00002943 while (IS_BLANK(val)) {
2944 while (IS_BLANK(val)) {
2945 val = xmlStringCurrentChar(NULL, cur, &len);
2946 cur += len;
2947 }
2948
2949 if (!IS_LETTER(val) && (val != '_') &&
2950 (val != ':')) {
2951 return(0);
2952 }
2953 val = xmlStringCurrentChar(NULL, cur, &len);
2954 cur += len;
2955
2956 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2957 (val == '.') || (val == '-') ||
2958 (val == '_') || (val == ':') ||
2959 (IS_COMBINING(val)) ||
2960 (IS_EXTENDER(val))) {
2961 val = xmlStringCurrentChar(NULL, cur, &len);
2962 cur += len;
2963 }
2964 }
2965
2966 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002967
2968 return(1);
2969}
2970
2971/**
2972 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002973 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002974 *
2975 * Validate that the given value match Nmtoken production
2976 *
2977 * [ VC: Name Token ]
2978 *
2979 * returns 1 if valid or 0 otherwise
2980 */
2981
Daniel Veillard9b731d72002-04-14 12:56:08 +00002982int
Owen Taylor3473f882001-02-23 17:55:21 +00002983xmlValidateNmtokenValue(const xmlChar *value) {
2984 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002985 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002986
2987 if (value == NULL) return(0);
2988 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002989 val = xmlStringCurrentChar(NULL, cur, &len);
2990 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002991
Daniel Veillardd8224e02002-01-13 15:43:22 +00002992 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2993 (val != '.') && (val != '-') &&
2994 (val != '_') && (val != ':') &&
2995 (!IS_COMBINING(val)) &&
2996 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002997 return(0);
2998
Daniel Veillardd8224e02002-01-13 15:43:22 +00002999 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3000 (val == '.') || (val == '-') ||
3001 (val == '_') || (val == ':') ||
3002 (IS_COMBINING(val)) ||
3003 (IS_EXTENDER(val))) {
3004 val = xmlStringCurrentChar(NULL, cur, &len);
3005 cur += len;
3006 }
Owen Taylor3473f882001-02-23 17:55:21 +00003007
Daniel Veillardd8224e02002-01-13 15:43:22 +00003008 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003009
3010 return(1);
3011}
3012
3013/**
3014 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003015 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003016 *
3017 * Validate that the given value match Nmtokens production
3018 *
3019 * [ VC: Name Token ]
3020 *
3021 * returns 1 if valid or 0 otherwise
3022 */
3023
Daniel Veillard9b731d72002-04-14 12:56:08 +00003024int
Owen Taylor3473f882001-02-23 17:55:21 +00003025xmlValidateNmtokensValue(const xmlChar *value) {
3026 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003027 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003028
3029 if (value == NULL) return(0);
3030 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003031 val = xmlStringCurrentChar(NULL, cur, &len);
3032 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003033
Daniel Veillardd8224e02002-01-13 15:43:22 +00003034 while (IS_BLANK(val)) {
3035 val = xmlStringCurrentChar(NULL, cur, &len);
3036 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003037 }
3038
Daniel Veillardd8224e02002-01-13 15:43:22 +00003039 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3040 (val != '.') && (val != '-') &&
3041 (val != '_') && (val != ':') &&
3042 (!IS_COMBINING(val)) &&
3043 (!IS_EXTENDER(val)))
3044 return(0);
3045
3046 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3047 (val == '.') || (val == '-') ||
3048 (val == '_') || (val == ':') ||
3049 (IS_COMBINING(val)) ||
3050 (IS_EXTENDER(val))) {
3051 val = xmlStringCurrentChar(NULL, cur, &len);
3052 cur += len;
3053 }
3054
3055 while (IS_BLANK(val)) {
3056 while (IS_BLANK(val)) {
3057 val = xmlStringCurrentChar(NULL, cur, &len);
3058 cur += len;
3059 }
3060 if (val == 0) return(1);
3061
3062 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3063 (val != '.') && (val != '-') &&
3064 (val != '_') && (val != ':') &&
3065 (!IS_COMBINING(val)) &&
3066 (!IS_EXTENDER(val)))
3067 return(0);
3068
3069 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3070 (val == '.') || (val == '-') ||
3071 (val == '_') || (val == ':') ||
3072 (IS_COMBINING(val)) ||
3073 (IS_EXTENDER(val))) {
3074 val = xmlStringCurrentChar(NULL, cur, &len);
3075 cur += len;
3076 }
3077 }
3078
3079 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003080
3081 return(1);
3082}
3083
3084/**
3085 * xmlValidateNotationDecl:
3086 * @ctxt: the validation context
3087 * @doc: a document instance
3088 * @nota: a notation definition
3089 *
3090 * Try to validate a single notation definition
3091 * basically it does the following checks as described by the
3092 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003093 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003094 * But this function get called anyway ...
3095 *
3096 * returns 1 if valid or 0 otherwise
3097 */
3098
3099int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003100xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3101 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003102 int ret = 1;
3103
3104 return(ret);
3105}
3106
3107/**
3108 * xmlValidateAttributeValue:
3109 * @type: an attribute type
3110 * @value: an attribute value
3111 *
3112 * Validate that the given attribute value match the proper production
3113 *
3114 * [ VC: ID ]
3115 * Values of type ID must match the Name production....
3116 *
3117 * [ VC: IDREF ]
3118 * Values of type IDREF must match the Name production, and values
3119 * of type IDREFS must match Names ...
3120 *
3121 * [ VC: Entity Name ]
3122 * Values of type ENTITY must match the Name production, values
3123 * of type ENTITIES must match Names ...
3124 *
3125 * [ VC: Name Token ]
3126 * Values of type NMTOKEN must match the Nmtoken production; values
3127 * of type NMTOKENS must match Nmtokens.
3128 *
3129 * returns 1 if valid or 0 otherwise
3130 */
3131
3132int
3133xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3134 switch (type) {
3135 case XML_ATTRIBUTE_ENTITIES:
3136 case XML_ATTRIBUTE_IDREFS:
3137 return(xmlValidateNamesValue(value));
3138 case XML_ATTRIBUTE_ENTITY:
3139 case XML_ATTRIBUTE_IDREF:
3140 case XML_ATTRIBUTE_ID:
3141 case XML_ATTRIBUTE_NOTATION:
3142 return(xmlValidateNameValue(value));
3143 case XML_ATTRIBUTE_NMTOKENS:
3144 case XML_ATTRIBUTE_ENUMERATION:
3145 return(xmlValidateNmtokensValue(value));
3146 case XML_ATTRIBUTE_NMTOKEN:
3147 return(xmlValidateNmtokenValue(value));
3148 case XML_ATTRIBUTE_CDATA:
3149 break;
3150 }
3151 return(1);
3152}
3153
3154/**
3155 * xmlValidateAttributeValue2:
3156 * @ctxt: the validation context
3157 * @doc: the document
3158 * @name: the attribute name (used for error reporting only)
3159 * @type: the attribute type
3160 * @value: the attribute value
3161 *
3162 * Validate that the given attribute value match a given type.
3163 * This typically cannot be done before having finished parsing
3164 * the subsets.
3165 *
3166 * [ VC: IDREF ]
3167 * Values of type IDREF must match one of the declared IDs
3168 * Values of type IDREFS must match a sequence of the declared IDs
3169 * each Name must match the value of an ID attribute on some element
3170 * in the XML document; i.e. IDREF values must match the value of
3171 * some ID attribute
3172 *
3173 * [ VC: Entity Name ]
3174 * Values of type ENTITY must match one declared entity
3175 * Values of type ENTITIES must match a sequence of declared entities
3176 *
3177 * [ VC: Notation Attributes ]
3178 * all notation names in the declaration must be declared.
3179 *
3180 * returns 1 if valid or 0 otherwise
3181 */
3182
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003183static int
Owen Taylor3473f882001-02-23 17:55:21 +00003184xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3185 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3186 int ret = 1;
3187 switch (type) {
3188 case XML_ATTRIBUTE_IDREFS:
3189 case XML_ATTRIBUTE_IDREF:
3190 case XML_ATTRIBUTE_ID:
3191 case XML_ATTRIBUTE_NMTOKENS:
3192 case XML_ATTRIBUTE_ENUMERATION:
3193 case XML_ATTRIBUTE_NMTOKEN:
3194 case XML_ATTRIBUTE_CDATA:
3195 break;
3196 case XML_ATTRIBUTE_ENTITY: {
3197 xmlEntityPtr ent;
3198
3199 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003200 if ((ent == NULL) && (doc->standalone == 1)) {
3201 doc->standalone = 0;
3202 ent = xmlGetDocEntity(doc, value);
3203 if (ent != NULL) {
3204 VERROR(ctxt->userData,
3205"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3206 name, value);
3207 /* WAIT to get answer from the Core WG on this
3208 ret = 0;
3209 */
3210 }
3211 }
Owen Taylor3473f882001-02-23 17:55:21 +00003212 if (ent == NULL) {
3213 VERROR(ctxt->userData,
3214 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3215 name, value);
3216 ret = 0;
3217 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3218 VERROR(ctxt->userData,
3219 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3220 name, value);
3221 ret = 0;
3222 }
3223 break;
3224 }
3225 case XML_ATTRIBUTE_ENTITIES: {
3226 xmlChar *dup, *nam = NULL, *cur, save;
3227 xmlEntityPtr ent;
3228
3229 dup = xmlStrdup(value);
3230 if (dup == NULL)
3231 return(0);
3232 cur = dup;
3233 while (*cur != 0) {
3234 nam = cur;
3235 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3236 save = *cur;
3237 *cur = 0;
3238 ent = xmlGetDocEntity(doc, nam);
3239 if (ent == NULL) {
3240 VERROR(ctxt->userData,
3241 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3242 name, nam);
3243 ret = 0;
3244 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3245 VERROR(ctxt->userData,
3246 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3247 name, nam);
3248 ret = 0;
3249 }
3250 if (save == 0)
3251 break;
3252 *cur = save;
3253 while (IS_BLANK(*cur)) cur++;
3254 }
3255 xmlFree(dup);
3256 break;
3257 }
3258 case XML_ATTRIBUTE_NOTATION: {
3259 xmlNotationPtr nota;
3260
3261 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3262 if ((nota == NULL) && (doc->extSubset != NULL))
3263 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3264
3265 if (nota == NULL) {
3266 VERROR(ctxt->userData,
3267 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3268 name, value);
3269 ret = 0;
3270 }
3271 break;
3272 }
3273 }
3274 return(ret);
3275}
3276
3277/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003278 * xmlValidCtxtNormalizeAttributeValue:
3279 * @ctxt: the validation context
3280 * @doc: the document
3281 * @elem: the parent
3282 * @name: the attribute name
3283 * @value: the attribute value
3284 * @ctxt: the validation context or NULL
3285 *
3286 * Does the validation related extra step of the normalization of attribute
3287 * values:
3288 *
3289 * If the declared value is not CDATA, then the XML processor must further
3290 * process the normalized attribute value by discarding any leading and
3291 * trailing space (#x20) characters, and by replacing sequences of space
3292 * (#x20) characters by single space (#x20) character.
3293 *
3294 * Also check VC: Standalone Document Declaration in P32, and update
3295 * ctxt->valid accordingly
3296 *
3297 * returns a new normalized string if normalization is needed, NULL otherwise
3298 * the caller must free the returned value.
3299 */
3300
3301xmlChar *
3302xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3303 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3304 xmlChar *ret, *dst;
3305 const xmlChar *src;
3306 xmlAttributePtr attrDecl = NULL;
3307 int extsubset = 0;
3308
3309 if (doc == NULL) return(NULL);
3310 if (elem == NULL) return(NULL);
3311 if (name == NULL) return(NULL);
3312 if (value == NULL) return(NULL);
3313
3314 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3315 xmlChar qname[500];
3316 snprintf((char *) qname, sizeof(qname), "%s:%s",
3317 elem->ns->prefix, elem->name);
3318 qname[sizeof(qname) - 1] = 0;
3319 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3320 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3321 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3322 if (attrDecl != NULL)
3323 extsubset = 1;
3324 }
3325 }
3326 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3327 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3328 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3329 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3330 if (attrDecl != NULL)
3331 extsubset = 1;
3332 }
3333
3334 if (attrDecl == NULL)
3335 return(NULL);
3336 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3337 return(NULL);
3338
3339 ret = xmlStrdup(value);
3340 if (ret == NULL)
3341 return(NULL);
3342 src = value;
3343 dst = ret;
3344 while (*src == 0x20) src++;
3345 while (*src != 0) {
3346 if (*src == 0x20) {
3347 while (*src == 0x20) src++;
3348 if (*src != 0)
3349 *dst++ = 0x20;
3350 } else {
3351 *dst++ = *src++;
3352 }
3353 }
3354 *dst = 0;
3355 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3356 VERROR(ctxt->userData,
3357"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3358 name, elem->name);
3359 ctxt->valid = 0;
3360 }
3361 return(ret);
3362}
3363
3364/**
Owen Taylor3473f882001-02-23 17:55:21 +00003365 * xmlValidNormalizeAttributeValue:
3366 * @doc: the document
3367 * @elem: the parent
3368 * @name: the attribute name
3369 * @value: the attribute value
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003370 * @ctxt: the validation context or NULL
Owen Taylor3473f882001-02-23 17:55:21 +00003371 *
3372 * Does the validation related extra step of the normalization of attribute
3373 * values:
3374 *
3375 * If the declared value is not CDATA, then the XML processor must further
3376 * process the normalized attribute value by discarding any leading and
3377 * trailing space (#x20) characters, and by replacing sequences of space
3378 * (#x20) characters by single space (#x20) character.
3379 *
3380 * returns a new normalized string if normalization is needed, NULL otherwise
3381 * the caller must free the returned value.
3382 */
3383
3384xmlChar *
3385xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3386 const xmlChar *name, const xmlChar *value) {
3387 xmlChar *ret, *dst;
3388 const xmlChar *src;
3389 xmlAttributePtr attrDecl = NULL;
3390
3391 if (doc == NULL) return(NULL);
3392 if (elem == NULL) return(NULL);
3393 if (name == NULL) return(NULL);
3394 if (value == NULL) return(NULL);
3395
3396 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3397 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003398 snprintf((char *) qname, sizeof(qname), "%s:%s",
3399 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003400 qname[sizeof(qname) - 1] = 0;
3401 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3402 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3403 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3404 }
3405 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3406 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3407 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3408
3409 if (attrDecl == NULL)
3410 return(NULL);
3411 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3412 return(NULL);
3413
3414 ret = xmlStrdup(value);
3415 if (ret == NULL)
3416 return(NULL);
3417 src = value;
3418 dst = ret;
3419 while (*src == 0x20) src++;
3420 while (*src != 0) {
3421 if (*src == 0x20) {
3422 while (*src == 0x20) src++;
3423 if (*src != 0)
3424 *dst++ = 0x20;
3425 } else {
3426 *dst++ = *src++;
3427 }
3428 }
3429 *dst = 0;
3430 return(ret);
3431}
3432
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003433static void
Owen Taylor3473f882001-02-23 17:55:21 +00003434xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003435 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003436 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3437}
3438
3439/**
3440 * xmlValidateAttributeDecl:
3441 * @ctxt: the validation context
3442 * @doc: a document instance
3443 * @attr: an attribute definition
3444 *
3445 * Try to validate a single attribute definition
3446 * basically it does the following checks as described by the
3447 * XML-1.0 recommendation:
3448 * - [ VC: Attribute Default Legal ]
3449 * - [ VC: Enumeration ]
3450 * - [ VC: ID Attribute Default ]
3451 *
3452 * The ID/IDREF uniqueness and matching are done separately
3453 *
3454 * returns 1 if valid or 0 otherwise
3455 */
3456
3457int
3458xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3459 xmlAttributePtr attr) {
3460 int ret = 1;
3461 int val;
3462 CHECK_DTD;
3463 if(attr == NULL) return(1);
3464
3465 /* Attribute Default Legal */
3466 /* Enumeration */
3467 if (attr->defaultValue != NULL) {
3468 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3469 if (val == 0) {
3470 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003471 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003472 attr->name, attr->elem);
3473 }
3474 ret &= val;
3475 }
3476
3477 /* ID Attribute Default */
3478 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3479 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3480 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3481 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003482 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003483 attr->name, attr->elem);
3484 ret = 0;
3485 }
3486
3487 /* One ID per Element Type */
3488 if (attr->atype == XML_ATTRIBUTE_ID) {
3489 int nbId;
3490
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003491 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003492 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3493 attr->elem);
3494 if (elem != NULL) {
3495 nbId = xmlScanIDAttributeDecl(NULL, elem);
3496 } else {
3497 xmlAttributeTablePtr table;
3498
3499 /*
3500 * The attribute may be declared in the internal subset and the
3501 * element in the external subset.
3502 */
3503 nbId = 0;
3504 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3505 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3506 xmlValidateAttributeIdCallback, &nbId);
3507 }
3508 if (nbId > 1) {
3509 VERROR(ctxt->userData,
3510 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3511 attr->elem, nbId, attr->name);
3512 } else if (doc->extSubset != NULL) {
3513 int extId = 0;
3514 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3515 if (elem != NULL) {
3516 extId = xmlScanIDAttributeDecl(NULL, elem);
3517 }
3518 if (extId > 1) {
3519 VERROR(ctxt->userData,
3520 "Element %s has %d ID attribute defined in the external subset : %s\n",
3521 attr->elem, extId, attr->name);
3522 } else if (extId + nbId > 1) {
3523 VERROR(ctxt->userData,
3524"Element %s has ID attributes defined in the internal and external subset : %s\n",
3525 attr->elem, attr->name);
3526 }
3527 }
3528 }
3529
3530 /* Validity Constraint: Enumeration */
3531 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3532 xmlEnumerationPtr tree = attr->tree;
3533 while (tree != NULL) {
3534 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3535 tree = tree->next;
3536 }
3537 if (tree == NULL) {
3538 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003539"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003540 attr->defaultValue, attr->name, attr->elem);
3541 ret = 0;
3542 }
3543 }
3544
3545 return(ret);
3546}
3547
3548/**
3549 * xmlValidateElementDecl:
3550 * @ctxt: the validation context
3551 * @doc: a document instance
3552 * @elem: an element definition
3553 *
3554 * Try to validate a single element definition
3555 * basically it does the following checks as described by the
3556 * XML-1.0 recommendation:
3557 * - [ VC: One ID per Element Type ]
3558 * - [ VC: No Duplicate Types ]
3559 * - [ VC: Unique Element Type Declaration ]
3560 *
3561 * returns 1 if valid or 0 otherwise
3562 */
3563
3564int
3565xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3566 xmlElementPtr elem) {
3567 int ret = 1;
3568 xmlElementPtr tst;
3569
3570 CHECK_DTD;
3571
3572 if (elem == NULL) return(1);
3573
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003574#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003575#ifdef LIBXML_REGEXP_ENABLED
3576 /* Build the regexp associated to the content model */
3577 ret = xmlValidBuildContentModel(ctxt, elem);
3578#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003579#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003580
Owen Taylor3473f882001-02-23 17:55:21 +00003581 /* No Duplicate Types */
3582 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3583 xmlElementContentPtr cur, next;
3584 const xmlChar *name;
3585
3586 cur = elem->content;
3587 while (cur != NULL) {
3588 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3589 if (cur->c1 == NULL) break;
3590 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3591 name = cur->c1->name;
3592 next = cur->c2;
3593 while (next != NULL) {
3594 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3595 if (xmlStrEqual(next->name, name)) {
3596 VERROR(ctxt->userData,
3597 "Definition of %s has duplicate references of %s\n",
3598 elem->name, name);
3599 ret = 0;
3600 }
3601 break;
3602 }
3603 if (next->c1 == NULL) break;
3604 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3605 if (xmlStrEqual(next->c1->name, name)) {
3606 VERROR(ctxt->userData,
3607 "Definition of %s has duplicate references of %s\n",
3608 elem->name, name);
3609 ret = 0;
3610 }
3611 next = next->c2;
3612 }
3613 }
3614 cur = cur->c2;
3615 }
3616 }
3617
3618 /* VC: Unique Element Type Declaration */
3619 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003620 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003621 ((tst->prefix == elem->prefix) ||
3622 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003623 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003624 VERROR(ctxt->userData, "Redefinition of element %s\n",
3625 elem->name);
3626 ret = 0;
3627 }
3628 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003629 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003630 ((tst->prefix == elem->prefix) ||
3631 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003632 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003633 VERROR(ctxt->userData, "Redefinition of element %s\n",
3634 elem->name);
3635 ret = 0;
3636 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003637 /* One ID per Element Type
3638 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003639 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3640 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003641 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003642 return(ret);
3643}
3644
3645/**
3646 * xmlValidateOneAttribute:
3647 * @ctxt: the validation context
3648 * @doc: a document instance
3649 * @elem: an element instance
3650 * @attr: an attribute instance
3651 * @value: the attribute value (without entities processing)
3652 *
3653 * Try to validate a single attribute for an element
3654 * basically it does the following checks as described by the
3655 * XML-1.0 recommendation:
3656 * - [ VC: Attribute Value Type ]
3657 * - [ VC: Fixed Attribute Default ]
3658 * - [ VC: Entity Name ]
3659 * - [ VC: Name Token ]
3660 * - [ VC: ID ]
3661 * - [ VC: IDREF ]
3662 * - [ VC: Entity Name ]
3663 * - [ VC: Notation Attributes ]
3664 *
3665 * The ID/IDREF uniqueness and matching are done separately
3666 *
3667 * returns 1 if valid or 0 otherwise
3668 */
3669
3670int
3671xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3672 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3673 /* xmlElementPtr elemDecl; */
3674 xmlAttributePtr attrDecl = NULL;
3675 int val;
3676 int ret = 1;
3677
3678 CHECK_DTD;
3679 if ((elem == NULL) || (elem->name == NULL)) return(0);
3680 if ((attr == NULL) || (attr->name == NULL)) return(0);
3681
3682 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3683 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003684 snprintf((char *) qname, sizeof(qname), "%s:%s",
3685 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003686 qname[sizeof(qname) - 1] = 0;
3687 if (attr->ns != NULL) {
3688 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3689 attr->name, attr->ns->prefix);
3690 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3691 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3692 attr->name, attr->ns->prefix);
3693 } else {
3694 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3695 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3696 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3697 qname, attr->name);
3698 }
3699 }
3700 if (attrDecl == NULL) {
3701 if (attr->ns != NULL) {
3702 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3703 attr->name, attr->ns->prefix);
3704 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3705 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3706 attr->name, attr->ns->prefix);
3707 } else {
3708 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3709 elem->name, attr->name);
3710 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3711 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3712 elem->name, attr->name);
3713 }
3714 }
3715
3716
3717 /* Validity Constraint: Attribute Value Type */
3718 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003719 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003720 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003721 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003722 attr->name, elem->name);
3723 return(0);
3724 }
3725 attr->atype = attrDecl->atype;
3726
3727 val = xmlValidateAttributeValue(attrDecl->atype, value);
3728 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003729 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003730 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003731 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003732 attr->name, elem->name);
3733 ret = 0;
3734 }
3735
3736 /* Validity constraint: Fixed Attribute Default */
3737 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3738 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003739 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003740 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003741 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003742 attr->name, elem->name, attrDecl->defaultValue);
3743 ret = 0;
3744 }
3745 }
3746
3747 /* Validity Constraint: ID uniqueness */
3748 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3749 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3750 ret = 0;
3751 }
3752
3753 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3754 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3755 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3756 ret = 0;
3757 }
3758
3759 /* Validity Constraint: Notation Attributes */
3760 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3761 xmlEnumerationPtr tree = attrDecl->tree;
3762 xmlNotationPtr nota;
3763
3764 /* First check that the given NOTATION was declared */
3765 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3766 if (nota == NULL)
3767 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3768
3769 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003770 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003771 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003772 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003773 value, attr->name, elem->name);
3774 ret = 0;
3775 }
3776
3777 /* Second, verify that it's among the list */
3778 while (tree != NULL) {
3779 if (xmlStrEqual(tree->name, value)) break;
3780 tree = tree->next;
3781 }
3782 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003783 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003784 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003785"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003786 value, attr->name, elem->name);
3787 ret = 0;
3788 }
3789 }
3790
3791 /* Validity Constraint: Enumeration */
3792 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3793 xmlEnumerationPtr tree = attrDecl->tree;
3794 while (tree != NULL) {
3795 if (xmlStrEqual(tree->name, value)) break;
3796 tree = tree->next;
3797 }
3798 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003799 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003800 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003801 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003802 value, attr->name, elem->name);
3803 ret = 0;
3804 }
3805 }
3806
3807 /* Fixed Attribute Default */
3808 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3809 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003810 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003811 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003812 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003813 attr->name, elem->name, attrDecl->defaultValue);
3814 ret = 0;
3815 }
3816
3817 /* Extra check for the attribute value */
3818 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3819 attrDecl->atype, value);
3820
3821 return(ret);
3822}
3823
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003824/**
3825 * xmlValidateOneNamespace:
3826 * @ctxt: the validation context
3827 * @doc: a document instance
3828 * @elem: an element instance
3829 * @ns: an namespace declaration instance
3830 * @value: the attribute value (without entities processing)
3831 *
3832 * Try to validate a single namespace declaration for an element
3833 * basically it does the following checks as described by the
3834 * XML-1.0 recommendation:
3835 * - [ VC: Attribute Value Type ]
3836 * - [ VC: Fixed Attribute Default ]
3837 * - [ VC: Entity Name ]
3838 * - [ VC: Name Token ]
3839 * - [ VC: ID ]
3840 * - [ VC: IDREF ]
3841 * - [ VC: Entity Name ]
3842 * - [ VC: Notation Attributes ]
3843 *
3844 * The ID/IDREF uniqueness and matching are done separately
3845 *
3846 * returns 1 if valid or 0 otherwise
3847 */
3848
3849int
3850xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3851xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
3852 /* xmlElementPtr elemDecl; */
3853 xmlAttributePtr attrDecl = NULL;
3854 int val;
3855 int ret = 1;
3856
3857 CHECK_DTD;
3858 if ((elem == NULL) || (elem->name == NULL)) return(0);
3859 if ((ns == NULL) || (ns->href == NULL)) return(0);
3860
3861 if (prefix != NULL) {
3862 xmlChar qname[500];
3863 snprintf((char *) qname, sizeof(qname), "%s:%s",
3864 prefix, elem->name);
3865 qname[sizeof(qname) - 1] = 0;
3866 if (ns->prefix != NULL) {
3867 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3868 ns->prefix, BAD_CAST "xmlns");
3869 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3870 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3871 ns->prefix, BAD_CAST "xmlns");
3872 } else {
3873 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname,
3874 BAD_CAST "xmlns");
3875 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3876 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname,
3877 BAD_CAST "xmlns");
3878 }
3879 }
3880 if (attrDecl == NULL) {
3881 if (ns->prefix != NULL) {
3882 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3883 ns->prefix, BAD_CAST "xmlns");
3884 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3885 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3886 ns->prefix, BAD_CAST "xmlns");
3887 } else {
3888 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3889 elem->name, BAD_CAST "xmlns");
3890 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3891 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3892 elem->name, BAD_CAST "xmlns");
3893 }
3894 }
3895
3896
3897 /* Validity Constraint: Attribute Value Type */
3898 if (attrDecl == NULL) {
3899 VECTXT(ctxt, elem);
3900 if (ns->prefix != NULL) {
3901 VERROR(ctxt->userData,
3902 "No declaration for attribute xmlns:%s of element %s\n",
3903 ns->prefix, elem->name);
3904 } else {
3905 VERROR(ctxt->userData,
3906 "No declaration for attribute xmlns of element %s\n",
3907 elem->name);
3908 }
3909 return(0);
3910 }
3911
3912 val = xmlValidateAttributeValue(attrDecl->atype, value);
3913 if (val == 0) {
3914 VECTXT(ctxt, elem);
3915 if (ns->prefix != NULL) {
3916 VERROR(ctxt->userData,
3917 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
3918 ns->prefix, elem->name);
3919 } else {
3920 VERROR(ctxt->userData,
3921 "Syntax of value for attribute xmlns of %s is not valid\n",
3922 elem->name);
3923 }
3924 ret = 0;
3925 }
3926
3927 /* Validity constraint: Fixed Attribute Default */
3928 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3929 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3930 VECTXT(ctxt, elem);
3931 if (ns->prefix != NULL) {
3932 VERROR(ctxt->userData,
3933 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
3934 ns->prefix, elem->name, attrDecl->defaultValue);
3935 } else {
3936 VERROR(ctxt->userData,
3937 "Value for attribute xmlns of %s is different from default \"%s\"\n",
3938 elem->name, attrDecl->defaultValue);
3939 }
3940 ret = 0;
3941 }
3942 }
3943
3944 /* Validity Constraint: ID uniqueness */
3945 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3946 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
3947 ret = 0;
3948 }
3949
3950 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3951 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3952 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
3953 ret = 0;
3954 }
3955
3956 /* Validity Constraint: Notation Attributes */
3957 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3958 xmlEnumerationPtr tree = attrDecl->tree;
3959 xmlNotationPtr nota;
3960
3961 /* First check that the given NOTATION was declared */
3962 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3963 if (nota == NULL)
3964 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3965
3966 if (nota == NULL) {
3967 VECTXT(ctxt, elem);
3968 if (ns->prefix != NULL) {
3969 VERROR(ctxt->userData,
3970 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
3971 value, ns->prefix, elem->name);
3972 } else {
3973 VERROR(ctxt->userData,
3974 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
3975 value, elem->name);
3976 }
3977 ret = 0;
3978 }
3979
3980 /* Second, verify that it's among the list */
3981 while (tree != NULL) {
3982 if (xmlStrEqual(tree->name, value)) break;
3983 tree = tree->next;
3984 }
3985 if (tree == NULL) {
3986 VECTXT(ctxt, elem);
3987 if (ns->prefix != NULL) {
3988 VERROR(ctxt->userData,
3989"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
3990 value, ns->prefix, elem->name);
3991 } else {
3992 VERROR(ctxt->userData,
3993"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
3994 value, elem->name);
3995 }
3996 ret = 0;
3997 }
3998 }
3999
4000 /* Validity Constraint: Enumeration */
4001 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4002 xmlEnumerationPtr tree = attrDecl->tree;
4003 while (tree != NULL) {
4004 if (xmlStrEqual(tree->name, value)) break;
4005 tree = tree->next;
4006 }
4007 if (tree == NULL) {
4008 VECTXT(ctxt, elem);
4009 if (ns->prefix != NULL) {
4010 VERROR(ctxt->userData,
4011"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4012 value, ns->prefix, elem->name);
4013 } else {
4014 VERROR(ctxt->userData,
4015"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4016 value, elem->name);
4017 }
4018 ret = 0;
4019 }
4020 }
4021
4022 /* Fixed Attribute Default */
4023 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4024 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4025 VECTXT(ctxt, elem);
4026 if (ns->prefix != NULL) {
4027 VERROR(ctxt->userData,
4028 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4029 ns->prefix, elem->name, attrDecl->defaultValue);
4030 } else {
4031 VERROR(ctxt->userData,
4032 "Value for attribute xmlns of %s must be \"%s\"\n",
4033 elem->name, attrDecl->defaultValue);
4034 }
4035 ret = 0;
4036 }
4037
4038 /* Extra check for the attribute value */
4039 if (ns->prefix != NULL) {
4040 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4041 attrDecl->atype, value);
4042 } else {
4043 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4044 attrDecl->atype, value);
4045 }
4046
4047 return(ret);
4048}
4049
Daniel Veillard118aed72002-09-24 14:13:13 +00004050#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004051/**
4052 * xmlValidateSkipIgnorable:
4053 * @ctxt: the validation context
4054 * @child: the child list
4055 *
4056 * Skip ignorable elements w.r.t. the validation process
4057 *
4058 * returns the first element to consider for validation of the content model
4059 */
4060
4061static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004062xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004063 while (child != NULL) {
4064 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004065 /* These things are ignored (skipped) during validation. */
4066 case XML_PI_NODE:
4067 case XML_COMMENT_NODE:
4068 case XML_XINCLUDE_START:
4069 case XML_XINCLUDE_END:
4070 child = child->next;
4071 break;
4072 case XML_TEXT_NODE:
4073 if (xmlIsBlankNode(child))
4074 child = child->next;
4075 else
4076 return(child);
4077 break;
4078 /* keep current node */
4079 default:
4080 return(child);
4081 }
4082 }
4083 return(child);
4084}
4085
4086/**
4087 * xmlValidateElementType:
4088 * @ctxt: the validation context
4089 *
4090 * Try to validate the content model of an element internal function
4091 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004092 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4093 * reference is found and -3 if the validation succeeded but
4094 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004095 */
4096
4097static int
4098xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004099 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004100 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004101
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004102 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004103 if ((NODE == NULL) && (CONT == NULL))
4104 return(1);
4105 if ((NODE == NULL) &&
4106 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4107 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4108 return(1);
4109 }
4110 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004111 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004112 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004113
4114 /*
4115 * We arrive here when more states need to be examined
4116 */
4117cont:
4118
4119 /*
4120 * We just recovered from a rollback generated by a possible
4121 * epsilon transition, go directly to the analysis phase
4122 */
4123 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004124 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004125 DEBUG_VALID_STATE(NODE, CONT)
4126 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004127 goto analyze;
4128 }
4129
4130 DEBUG_VALID_STATE(NODE, CONT)
4131 /*
4132 * we may have to save a backup state here. This is the equivalent
4133 * of handling epsilon transition in NFAs.
4134 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004135 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004136 ((CONT->parent == NULL) ||
4137 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004138 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004139 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004140 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004141 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004142 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4143 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004144 }
4145
4146
4147 /*
4148 * Check first if the content matches
4149 */
4150 switch (CONT->type) {
4151 case XML_ELEMENT_CONTENT_PCDATA:
4152 if (NODE == NULL) {
4153 DEBUG_VALID_MSG("pcdata failed no node");
4154 ret = 0;
4155 break;
4156 }
4157 if (NODE->type == XML_TEXT_NODE) {
4158 DEBUG_VALID_MSG("pcdata found, skip to next");
4159 /*
4160 * go to next element in the content model
4161 * skipping ignorable elems
4162 */
4163 do {
4164 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004165 NODE = xmlValidateSkipIgnorable(NODE);
4166 if ((NODE != NULL) &&
4167 (NODE->type == XML_ENTITY_REF_NODE))
4168 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004169 } while ((NODE != NULL) &&
4170 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004171 (NODE->type != XML_TEXT_NODE) &&
4172 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004173 ret = 1;
4174 break;
4175 } else {
4176 DEBUG_VALID_MSG("pcdata failed");
4177 ret = 0;
4178 break;
4179 }
4180 break;
4181 case XML_ELEMENT_CONTENT_ELEMENT:
4182 if (NODE == NULL) {
4183 DEBUG_VALID_MSG("element failed no node");
4184 ret = 0;
4185 break;
4186 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004187 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4188 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004189 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004190 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4191 ret = (CONT->prefix == NULL);
4192 } else if (CONT->prefix == NULL) {
4193 ret = 0;
4194 } else {
4195 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4196 }
4197 }
4198 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004199 DEBUG_VALID_MSG("element found, skip to next");
4200 /*
4201 * go to next element in the content model
4202 * skipping ignorable elems
4203 */
4204 do {
4205 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004206 NODE = xmlValidateSkipIgnorable(NODE);
4207 if ((NODE != NULL) &&
4208 (NODE->type == XML_ENTITY_REF_NODE))
4209 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004210 } while ((NODE != NULL) &&
4211 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004212 (NODE->type != XML_TEXT_NODE) &&
4213 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004214 } else {
4215 DEBUG_VALID_MSG("element failed");
4216 ret = 0;
4217 break;
4218 }
4219 break;
4220 case XML_ELEMENT_CONTENT_OR:
4221 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004222 * Small optimization.
4223 */
4224 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4225 if ((NODE == NULL) ||
4226 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4227 DEPTH++;
4228 CONT = CONT->c2;
4229 goto cont;
4230 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004231 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4232 ret = (CONT->c1->prefix == NULL);
4233 } else if (CONT->c1->prefix == NULL) {
4234 ret = 0;
4235 } else {
4236 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4237 }
4238 if (ret == 0) {
4239 DEPTH++;
4240 CONT = CONT->c2;
4241 goto cont;
4242 }
Daniel Veillard85349052001-04-20 13:48:21 +00004243 }
4244
4245 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004246 * save the second branch 'or' branch
4247 */
4248 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004249 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4250 OCCURS, ROLLBACK_OR) < 0)
4251 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004252 DEPTH++;
4253 CONT = CONT->c1;
4254 goto cont;
4255 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004256 /*
4257 * Small optimization.
4258 */
4259 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4260 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4261 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4262 if ((NODE == NULL) ||
4263 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4264 DEPTH++;
4265 CONT = CONT->c2;
4266 goto cont;
4267 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004268 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4269 ret = (CONT->c1->prefix == NULL);
4270 } else if (CONT->c1->prefix == NULL) {
4271 ret = 0;
4272 } else {
4273 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4274 }
4275 if (ret == 0) {
4276 DEPTH++;
4277 CONT = CONT->c2;
4278 goto cont;
4279 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004280 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004281 DEPTH++;
4282 CONT = CONT->c1;
4283 goto cont;
4284 }
4285
4286 /*
4287 * At this point handle going up in the tree
4288 */
4289 if (ret == -1) {
4290 DEBUG_VALID_MSG("error found returning");
4291 return(ret);
4292 }
4293analyze:
4294 while (CONT != NULL) {
4295 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004296 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004297 * this level.
4298 */
4299 if (ret == 0) {
4300 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004301 xmlNodePtr cur;
4302
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004303 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004304 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004305 DEBUG_VALID_MSG("Once branch failed, rollback");
4306 if (vstateVPop(ctxt) < 0 ) {
4307 DEBUG_VALID_MSG("exhaustion, failed");
4308 return(0);
4309 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004310 if (cur != ctxt->vstate->node)
4311 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004312 goto cont;
4313 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004314 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004315 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004316 DEBUG_VALID_MSG("Plus branch failed, rollback");
4317 if (vstateVPop(ctxt) < 0 ) {
4318 DEBUG_VALID_MSG("exhaustion, failed");
4319 return(0);
4320 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004321 if (cur != ctxt->vstate->node)
4322 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004323 goto cont;
4324 }
4325 DEBUG_VALID_MSG("Plus branch found");
4326 ret = 1;
4327 break;
4328 case XML_ELEMENT_CONTENT_MULT:
4329#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004330 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004331 DEBUG_VALID_MSG("Mult branch failed");
4332 } else {
4333 DEBUG_VALID_MSG("Mult branch found");
4334 }
4335#endif
4336 ret = 1;
4337 break;
4338 case XML_ELEMENT_CONTENT_OPT:
4339 DEBUG_VALID_MSG("Option branch failed");
4340 ret = 1;
4341 break;
4342 }
4343 } else {
4344 switch (CONT->ocur) {
4345 case XML_ELEMENT_CONTENT_OPT:
4346 DEBUG_VALID_MSG("Option branch succeeded");
4347 ret = 1;
4348 break;
4349 case XML_ELEMENT_CONTENT_ONCE:
4350 DEBUG_VALID_MSG("Once branch succeeded");
4351 ret = 1;
4352 break;
4353 case XML_ELEMENT_CONTENT_PLUS:
4354 if (STATE == ROLLBACK_PARENT) {
4355 DEBUG_VALID_MSG("Plus branch rollback");
4356 ret = 1;
4357 break;
4358 }
4359 if (NODE == NULL) {
4360 DEBUG_VALID_MSG("Plus branch exhausted");
4361 ret = 1;
4362 break;
4363 }
4364 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004365 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004366 goto cont;
4367 case XML_ELEMENT_CONTENT_MULT:
4368 if (STATE == ROLLBACK_PARENT) {
4369 DEBUG_VALID_MSG("Mult branch rollback");
4370 ret = 1;
4371 break;
4372 }
4373 if (NODE == NULL) {
4374 DEBUG_VALID_MSG("Mult branch exhausted");
4375 ret = 1;
4376 break;
4377 }
4378 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004379 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004380 goto cont;
4381 }
4382 }
4383 STATE = 0;
4384
4385 /*
4386 * Then act accordingly at the parent level
4387 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004388 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004389 if (CONT->parent == NULL)
4390 break;
4391
4392 switch (CONT->parent->type) {
4393 case XML_ELEMENT_CONTENT_PCDATA:
4394 DEBUG_VALID_MSG("Error: parent pcdata");
4395 return(-1);
4396 case XML_ELEMENT_CONTENT_ELEMENT:
4397 DEBUG_VALID_MSG("Error: parent element");
4398 return(-1);
4399 case XML_ELEMENT_CONTENT_OR:
4400 if (ret == 1) {
4401 DEBUG_VALID_MSG("Or succeeded");
4402 CONT = CONT->parent;
4403 DEPTH--;
4404 } else {
4405 DEBUG_VALID_MSG("Or failed");
4406 CONT = CONT->parent;
4407 DEPTH--;
4408 }
4409 break;
4410 case XML_ELEMENT_CONTENT_SEQ:
4411 if (ret == 0) {
4412 DEBUG_VALID_MSG("Sequence failed");
4413 CONT = CONT->parent;
4414 DEPTH--;
4415 } else if (CONT == CONT->parent->c1) {
4416 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4417 CONT = CONT->parent->c2;
4418 goto cont;
4419 } else {
4420 DEBUG_VALID_MSG("Sequence succeeded");
4421 CONT = CONT->parent;
4422 DEPTH--;
4423 }
4424 }
4425 }
4426 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004427 xmlNodePtr cur;
4428
4429 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004430 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4431 if (vstateVPop(ctxt) < 0 ) {
4432 DEBUG_VALID_MSG("exhaustion, failed");
4433 return(0);
4434 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004435 if (cur != ctxt->vstate->node)
4436 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004437 goto cont;
4438 }
4439 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004440 xmlNodePtr cur;
4441
4442 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004443 DEBUG_VALID_MSG("Failure, rollback");
4444 if (vstateVPop(ctxt) < 0 ) {
4445 DEBUG_VALID_MSG("exhaustion, failed");
4446 return(0);
4447 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004448 if (cur != ctxt->vstate->node)
4449 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004450 goto cont;
4451 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004452 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004453}
Daniel Veillard23e73572002-09-19 19:56:43 +00004454#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004455
4456/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004457 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004458 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004459 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004460 * @content: An element
4461 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4462 *
4463 * This will dump the list of elements to the buffer
4464 * Intended just for the debug routine
4465 */
4466static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004467xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004468 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004469 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004470
4471 if (node == NULL) return;
4472 if (glob) strcat(buf, "(");
4473 cur = node;
4474 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004475 len = strlen(buf);
4476 if (size - len < 50) {
4477 if ((size - len > 4) && (buf[len - 1] != '.'))
4478 strcat(buf, " ...");
4479 return;
4480 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004481 switch (cur->type) {
4482 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004483 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004484 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004485 if ((size - len > 4) && (buf[len - 1] != '.'))
4486 strcat(buf, " ...");
4487 return;
4488 }
4489 strcat(buf, (char *) cur->ns->prefix);
4490 strcat(buf, ":");
4491 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004492 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004493 if ((size - len > 4) && (buf[len - 1] != '.'))
4494 strcat(buf, " ...");
4495 return;
4496 }
4497 strcat(buf, (char *) cur->name);
4498 if (cur->next != NULL)
4499 strcat(buf, " ");
4500 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004501 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004502 if (xmlIsBlankNode(cur))
4503 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004504 case XML_CDATA_SECTION_NODE:
4505 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004506 strcat(buf, "CDATA");
4507 if (cur->next != NULL)
4508 strcat(buf, " ");
4509 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004510 case XML_ATTRIBUTE_NODE:
4511 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004512#ifdef LIBXML_DOCB_ENABLED
4513 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004514#endif
4515 case XML_HTML_DOCUMENT_NODE:
4516 case XML_DOCUMENT_TYPE_NODE:
4517 case XML_DOCUMENT_FRAG_NODE:
4518 case XML_NOTATION_NODE:
4519 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004520 strcat(buf, "???");
4521 if (cur->next != NULL)
4522 strcat(buf, " ");
4523 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004524 case XML_ENTITY_NODE:
4525 case XML_PI_NODE:
4526 case XML_DTD_NODE:
4527 case XML_COMMENT_NODE:
4528 case XML_ELEMENT_DECL:
4529 case XML_ATTRIBUTE_DECL:
4530 case XML_ENTITY_DECL:
4531 case XML_XINCLUDE_START:
4532 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004533 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004534 }
4535 cur = cur->next;
4536 }
4537 if (glob) strcat(buf, ")");
4538}
4539
4540/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004541 * xmlValidateElementContent:
4542 * @ctxt: the validation context
4543 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004544 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004545 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004546 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004547 *
4548 * Try to validate the content model of an element
4549 *
4550 * returns 1 if valid or 0 if not and -1 in case of error
4551 */
4552
4553static int
4554xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004555 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004556 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004557#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004558 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004559#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004560 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004561 xmlElementContentPtr cont;
4562 const xmlChar *name;
4563
4564 if (elemDecl == NULL)
4565 return(-1);
4566 cont = elemDecl->content;
4567 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004568
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004569#ifdef LIBXML_REGEXP_ENABLED
4570 /* Build the regexp associated to the content model */
4571 if (elemDecl->contModel == NULL)
4572 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4573 if (elemDecl->contModel == NULL) {
4574 ret = -1;
4575 } else {
4576 xmlRegExecCtxtPtr exec;
4577
Daniel Veillard01992e02002-10-09 10:20:30 +00004578 ctxt->nodeMax = 0;
4579 ctxt->nodeNr = 0;
4580 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004581 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4582 if (exec != NULL) {
4583 cur = child;
4584 while (cur != NULL) {
4585 switch (cur->type) {
4586 case XML_ENTITY_REF_NODE:
4587 /*
4588 * Push the current node to be able to roll back
4589 * and process within the entity
4590 */
4591 if ((cur->children != NULL) &&
4592 (cur->children->children != NULL)) {
4593 nodeVPush(ctxt, cur);
4594 cur = cur->children->children;
4595 continue;
4596 }
4597 break;
4598 case XML_TEXT_NODE:
4599 if (xmlIsBlankNode(cur))
4600 break;
4601 ret = 0;
4602 goto fail;
4603 case XML_CDATA_SECTION_NODE:
4604 TODO
4605 ret = 0;
4606 goto fail;
4607 case XML_ELEMENT_NODE:
4608 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
4609 xmlChar *QName;
4610 int len;
4611
4612 len = xmlStrlen(cur->name) +
4613 xmlStrlen(cur->ns->prefix) + 2;
4614 QName = xmlMalloc(len);
4615 if (QName == NULL) {
4616 ret = -1;
4617 goto fail;
4618 }
4619 snprintf((char *) QName, len, "%s:%s",
4620 (char *)cur->ns->prefix,
4621 (char *)cur->name);
4622 ret = xmlRegExecPushString(exec, QName, NULL);
4623 xmlFree(QName);
4624 } else {
4625 ret = xmlRegExecPushString(exec, cur->name, NULL);
4626 }
4627 break;
4628 default:
4629 break;
4630 }
4631 /*
4632 * Switch to next element
4633 */
4634 cur = cur->next;
4635 while (cur == NULL) {
4636 cur = nodeVPop(ctxt);
4637 if (cur == NULL)
4638 break;
4639 cur = cur->next;
4640 }
4641 }
4642 ret = xmlRegExecPushString(exec, NULL, NULL);
4643fail:
4644 xmlRegFreeExecCtxt(exec);
4645 }
4646 }
4647#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004648 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004649 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004650 */
4651 ctxt->vstateMax = 8;
4652 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4653 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4654 if (ctxt->vstateTab == NULL) {
4655 xmlGenericError(xmlGenericErrorContext,
4656 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004657 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004658 }
4659 /*
4660 * The first entry in the stack is reserved to the current state
4661 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004662 ctxt->nodeMax = 0;
4663 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004664 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004665 ctxt->vstate = &ctxt->vstateTab[0];
4666 ctxt->vstateNr = 1;
4667 CONT = cont;
4668 NODE = child;
4669 DEPTH = 0;
4670 OCCURS = 0;
4671 STATE = 0;
4672 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004673 if ((ret == -3) && (warn)) {
4674 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004675 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004676 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004677 /*
4678 * An entities reference appeared at this level.
4679 * Buid a minimal representation of this node content
4680 * sufficient to run the validation process on it
4681 */
4682 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004683 cur = child;
4684 while (cur != NULL) {
4685 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004686 case XML_ENTITY_REF_NODE:
4687 /*
4688 * Push the current node to be able to roll back
4689 * and process within the entity
4690 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004691 if ((cur->children != NULL) &&
4692 (cur->children->children != NULL)) {
4693 nodeVPush(ctxt, cur);
4694 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004695 continue;
4696 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004697 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004698 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004699 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004700 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004701 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004702 case XML_CDATA_SECTION_NODE:
4703 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004704 case XML_ELEMENT_NODE:
4705 /*
4706 * Allocate a new node and minimally fills in
4707 * what's required
4708 */
4709 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4710 if (tmp == NULL) {
4711 xmlGenericError(xmlGenericErrorContext,
4712 "xmlValidateElementContent : malloc failed\n");
4713 xmlFreeNodeList(repl);
4714 ret = -1;
4715 goto done;
4716 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004717 tmp->type = cur->type;
4718 tmp->name = cur->name;
4719 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004720 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004721 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004722 if (repl == NULL)
4723 repl = last = tmp;
4724 else {
4725 last->next = tmp;
4726 last = tmp;
4727 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004728 if (cur->type == XML_CDATA_SECTION_NODE) {
4729 /*
4730 * E59 spaces in CDATA does not match the
4731 * nonterminal S
4732 */
4733 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4734 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004735 break;
4736 default:
4737 break;
4738 }
4739 /*
4740 * Switch to next element
4741 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004742 cur = cur->next;
4743 while (cur == NULL) {
4744 cur = nodeVPop(ctxt);
4745 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004746 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004747 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004748 }
4749 }
4750
4751 /*
4752 * Relaunch the validation
4753 */
4754 ctxt->vstate = &ctxt->vstateTab[0];
4755 ctxt->vstateNr = 1;
4756 CONT = cont;
4757 NODE = repl;
4758 DEPTH = 0;
4759 OCCURS = 0;
4760 STATE = 0;
4761 ret = xmlValidateElementType(ctxt);
4762 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004763#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004764 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004765 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4766 char expr[5000];
4767 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004768
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004769 expr[0] = 0;
4770 xmlSnprintfElementContent(expr, 5000, cont, 1);
4771 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004772#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004773 if (repl != NULL)
4774 xmlSnprintfElements(list, 5000, repl, 1);
4775 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004776#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004777 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004778
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004779 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004780 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004781 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004782 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004783 name, expr, list);
4784 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004785 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004786 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004787 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004788 expr, list);
4789 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004790 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004791 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004792 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004793 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004794 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004795 name);
4796 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004797 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004798 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004799 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004800 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004801 }
4802 ret = 0;
4803 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004804 if (ret == -3)
4805 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004806
Daniel Veillard23e73572002-09-19 19:56:43 +00004807#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004808done:
4809 /*
4810 * Deallocate the copy if done, and free up the validation stack
4811 */
4812 while (repl != NULL) {
4813 tmp = repl->next;
4814 xmlFree(repl);
4815 repl = tmp;
4816 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004817 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004818 if (ctxt->vstateTab != NULL) {
4819 xmlFree(ctxt->vstateTab);
4820 ctxt->vstateTab = NULL;
4821 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004822#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004823 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004824 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004825 if (ctxt->nodeTab != NULL) {
4826 xmlFree(ctxt->nodeTab);
4827 ctxt->nodeTab = NULL;
4828 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004829 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004830
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004831}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004832
Owen Taylor3473f882001-02-23 17:55:21 +00004833/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004834 * xmlValidateCdataElement:
4835 * @ctxt: the validation context
4836 * @doc: a document instance
4837 * @elem: an element instance
4838 *
4839 * Check that an element follows #CDATA
4840 *
4841 * returns 1 if valid or 0 otherwise
4842 */
4843static int
4844xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4845 xmlNodePtr elem) {
4846 int ret = 1;
4847 xmlNodePtr cur, child;
4848
Daniel Veillardceb09b92002-10-04 11:46:37 +00004849 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004850 return(0);
4851
4852 child = elem->children;
4853
4854 cur = child;
4855 while (cur != NULL) {
4856 switch (cur->type) {
4857 case XML_ENTITY_REF_NODE:
4858 /*
4859 * Push the current node to be able to roll back
4860 * and process within the entity
4861 */
4862 if ((cur->children != NULL) &&
4863 (cur->children->children != NULL)) {
4864 nodeVPush(ctxt, cur);
4865 cur = cur->children->children;
4866 continue;
4867 }
4868 break;
4869 case XML_COMMENT_NODE:
4870 case XML_PI_NODE:
4871 case XML_TEXT_NODE:
4872 case XML_CDATA_SECTION_NODE:
4873 break;
4874 default:
4875 ret = 0;
4876 goto done;
4877 }
4878 /*
4879 * Switch to next element
4880 */
4881 cur = cur->next;
4882 while (cur == NULL) {
4883 cur = nodeVPop(ctxt);
4884 if (cur == NULL)
4885 break;
4886 cur = cur->next;
4887 }
4888 }
4889done:
4890 ctxt->nodeMax = 0;
4891 ctxt->nodeNr = 0;
4892 if (ctxt->nodeTab != NULL) {
4893 xmlFree(ctxt->nodeTab);
4894 ctxt->nodeTab = NULL;
4895 }
4896 return(ret);
4897}
4898
4899/**
Owen Taylor3473f882001-02-23 17:55:21 +00004900 * xmlValidateOneElement:
4901 * @ctxt: the validation context
4902 * @doc: a document instance
4903 * @elem: an element instance
4904 *
4905 * Try to validate a single element and it's attributes,
4906 * basically it does the following checks as described by the
4907 * XML-1.0 recommendation:
4908 * - [ VC: Element Valid ]
4909 * - [ VC: Required Attribute ]
4910 * Then call xmlValidateOneAttribute() for each attribute present.
4911 *
4912 * The ID/IDREF checkings are done separately
4913 *
4914 * returns 1 if valid or 0 otherwise
4915 */
4916
4917int
4918xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4919 xmlNodePtr elem) {
4920 xmlElementPtr elemDecl = NULL;
4921 xmlElementContentPtr cont;
4922 xmlAttributePtr attr;
4923 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004924 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004925 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004926 const xmlChar *prefix = NULL;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004927 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004928
4929 CHECK_DTD;
4930
4931 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004932 switch (elem->type) {
4933 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004934 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004935 VERROR(ctxt->userData,
4936 "Attribute element not expected here\n");
4937 return(0);
4938 case XML_TEXT_NODE:
4939 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004940 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004941 VERROR(ctxt->userData, "Text element has childs !\n");
4942 return(0);
4943 }
4944 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004945 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004946 VERROR(ctxt->userData, "Text element has attributes !\n");
4947 return(0);
4948 }
4949 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004950 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004951 VERROR(ctxt->userData, "Text element has namespace !\n");
4952 return(0);
4953 }
4954 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004955 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004956 VERROR(ctxt->userData,
4957 "Text element carries namespace definitions !\n");
4958 return(0);
4959 }
4960 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004961 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004962 VERROR(ctxt->userData,
4963 "Text element has no content !\n");
4964 return(0);
4965 }
4966 return(1);
4967 case XML_XINCLUDE_START:
4968 case XML_XINCLUDE_END:
4969 return(1);
4970 case XML_CDATA_SECTION_NODE:
4971 case XML_ENTITY_REF_NODE:
4972 case XML_PI_NODE:
4973 case XML_COMMENT_NODE:
4974 return(1);
4975 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004976 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004977 VERROR(ctxt->userData,
4978 "Entity element not expected here\n");
4979 return(0);
4980 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004981 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004982 VERROR(ctxt->userData,
4983 "Notation element not expected here\n");
4984 return(0);
4985 case XML_DOCUMENT_NODE:
4986 case XML_DOCUMENT_TYPE_NODE:
4987 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004988 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004989 VERROR(ctxt->userData,
4990 "Document element not expected here\n");
4991 return(0);
4992 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004993 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004994 VERROR(ctxt->userData,
4995 "\n");
4996 return(0);
4997 case XML_ELEMENT_NODE:
4998 break;
4999 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005000 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005001 VERROR(ctxt->userData,
5002 "unknown element type %d\n", elem->type);
5003 return(0);
5004 }
5005 if (elem->name == NULL) return(0);
5006
5007 /*
5008 * Fetch the declaration for the qualified name
5009 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005010 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5011 prefix = elem->ns->prefix;
5012
5013 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00005014 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005015 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005016 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005017 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005018 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005019 if (elemDecl != NULL)
5020 extsubset = 1;
5021 }
Owen Taylor3473f882001-02-23 17:55:21 +00005022 }
5023
5024 /*
5025 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005026 * This is "non-strict" validation should be done on the
5027 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00005028 */
5029 if (elemDecl == NULL) {
5030 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005031 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005032 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005033 if (elemDecl != NULL)
5034 extsubset = 1;
5035 }
Owen Taylor3473f882001-02-23 17:55:21 +00005036 }
5037 if (elemDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005038 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005039 VERROR(ctxt->userData, "No declaration for element %s\n",
5040 elem->name);
5041 return(0);
5042 }
5043
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005044 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005045 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005046 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005047 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005048 VERROR(ctxt->userData, "No declaration for element %s\n",
5049 elem->name);
5050 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005051 case XML_ELEMENT_TYPE_EMPTY:
5052 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005053 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005054 VERROR(ctxt->userData,
5055 "Element %s was declared EMPTY this one has content\n",
5056 elem->name);
5057 ret = 0;
5058 }
5059 break;
5060 case XML_ELEMENT_TYPE_ANY:
5061 /* I don't think anything is required then */
5062 break;
5063 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005064
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005065 /* simple case of declared as #PCDATA */
5066 if ((elemDecl->content != NULL) &&
5067 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5068 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5069 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005070 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005071 VERROR(ctxt->userData,
5072 "Element %s was declared #PCDATA but contains non text nodes\n",
5073 elem->name);
5074 }
5075 break;
5076 }
Owen Taylor3473f882001-02-23 17:55:21 +00005077 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005078 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005079 while (child != NULL) {
5080 if (child->type == XML_ELEMENT_NODE) {
5081 name = child->name;
5082 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
5083 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005084 snprintf((char *) qname, sizeof(qname), "%s:%s",
5085 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005086 qname[sizeof(qname) - 1] = 0;
5087 cont = elemDecl->content;
5088 while (cont != NULL) {
5089 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5090 if (xmlStrEqual(cont->name, qname)) break;
5091 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5092 (cont->c1 != NULL) &&
5093 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5094 if (xmlStrEqual(cont->c1->name, qname)) break;
5095 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5096 (cont->c1 == NULL) ||
5097 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5098 /* Internal error !!! */
5099 xmlGenericError(xmlGenericErrorContext,
5100 "Internal: MIXED struct bad\n");
5101 break;
5102 }
5103 cont = cont->c2;
5104 }
5105 if (cont != NULL)
5106 goto child_ok;
5107 }
5108 cont = elemDecl->content;
5109 while (cont != NULL) {
5110 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5111 if (xmlStrEqual(cont->name, name)) break;
5112 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5113 (cont->c1 != NULL) &&
5114 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5115 if (xmlStrEqual(cont->c1->name, name)) break;
5116 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5117 (cont->c1 == NULL) ||
5118 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5119 /* Internal error !!! */
5120 xmlGenericError(xmlGenericErrorContext,
5121 "Internal: MIXED struct bad\n");
5122 break;
5123 }
5124 cont = cont->c2;
5125 }
5126 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005127 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005128 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005129 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005130 name, elem->name);
5131 ret = 0;
5132 }
5133 }
5134child_ok:
5135 child = child->next;
5136 }
5137 break;
5138 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005139 if ((doc->standalone == 1) && (extsubset == 1)) {
5140 /*
5141 * VC: Standalone Document Declaration
5142 * - element types with element content, if white space
5143 * occurs directly within any instance of those types.
5144 */
5145 child = elem->children;
5146 while (child != NULL) {
5147 if (child->type == XML_TEXT_NODE) {
5148 const xmlChar *content = child->content;
5149
5150 while (IS_BLANK(*content))
5151 content++;
5152 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005153 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005154 VERROR(ctxt->userData,
5155"standalone: %s declared in the external subset contains white spaces nodes\n",
5156 elem->name);
5157 ret = 0;
5158 break;
5159 }
5160 }
5161 child =child->next;
5162 }
5163 }
Owen Taylor3473f882001-02-23 17:55:21 +00005164 child = elem->children;
5165 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005166 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005167 if (tmp <= 0)
5168 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005169 break;
5170 }
5171
5172 /* [ VC: Required Attribute ] */
5173 attr = elemDecl->attributes;
5174 while (attr != NULL) {
5175 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005176 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005177
Daniel Veillarde4301c82002-02-13 13:32:35 +00005178 if ((attr->prefix == NULL) &&
5179 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5180 xmlNsPtr ns;
5181
5182 ns = elem->nsDef;
5183 while (ns != NULL) {
5184 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005185 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005186 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005187 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005188 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5189 xmlNsPtr ns;
5190
5191 ns = elem->nsDef;
5192 while (ns != NULL) {
5193 if (xmlStrEqual(attr->name, ns->prefix))
5194 goto found;
5195 ns = ns->next;
5196 }
5197 } else {
5198 xmlAttrPtr attrib;
5199
5200 attrib = elem->properties;
5201 while (attrib != NULL) {
5202 if (xmlStrEqual(attrib->name, attr->name)) {
5203 if (attr->prefix != NULL) {
5204 xmlNsPtr nameSpace = attrib->ns;
5205
5206 if (nameSpace == NULL)
5207 nameSpace = elem->ns;
5208 /*
5209 * qualified names handling is problematic, having a
5210 * different prefix should be possible but DTDs don't
5211 * allow to define the URI instead of the prefix :-(
5212 */
5213 if (nameSpace == NULL) {
5214 if (qualified < 0)
5215 qualified = 0;
5216 } else if (!xmlStrEqual(nameSpace->prefix,
5217 attr->prefix)) {
5218 if (qualified < 1)
5219 qualified = 1;
5220 } else
5221 goto found;
5222 } else {
5223 /*
5224 * We should allow applications to define namespaces
5225 * for their application even if the DTD doesn't
5226 * carry one, otherwise, basically we would always
5227 * break.
5228 */
5229 goto found;
5230 }
5231 }
5232 attrib = attrib->next;
5233 }
Owen Taylor3473f882001-02-23 17:55:21 +00005234 }
5235 if (qualified == -1) {
5236 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005237 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005238 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005239 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005240 elem->name, attr->name);
5241 ret = 0;
5242 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005243 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005244 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005245 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005246 elem->name, attr->prefix,attr->name);
5247 ret = 0;
5248 }
5249 } else if (qualified == 0) {
5250 VWARNING(ctxt->userData,
5251 "Element %s required attribute %s:%s has no prefix\n",
5252 elem->name, attr->prefix,attr->name);
5253 } else if (qualified == 1) {
5254 VWARNING(ctxt->userData,
5255 "Element %s required attribute %s:%s has different prefix\n",
5256 elem->name, attr->prefix,attr->name);
5257 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005258 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5259 /*
5260 * Special tests checking #FIXED namespace declarations
5261 * have the right value since this is not done as an
5262 * attribute checking
5263 */
5264 if ((attr->prefix == NULL) &&
5265 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5266 xmlNsPtr ns;
5267
5268 ns = elem->nsDef;
5269 while (ns != NULL) {
5270 if (ns->prefix == NULL) {
5271 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005272 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005273 VERROR(ctxt->userData,
5274 "Element %s namespace name for default namespace does not match the DTD\n",
5275 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005276 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005277 }
5278 goto found;
5279 }
5280 ns = ns->next;
5281 }
5282 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5283 xmlNsPtr ns;
5284
5285 ns = elem->nsDef;
5286 while (ns != NULL) {
5287 if (xmlStrEqual(attr->name, ns->prefix)) {
5288 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005289 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005290 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005291 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005292 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005293 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005294 }
5295 goto found;
5296 }
5297 ns = ns->next;
5298 }
5299 }
Owen Taylor3473f882001-02-23 17:55:21 +00005300 }
5301found:
5302 attr = attr->nexth;
5303 }
5304 return(ret);
5305}
5306
5307/**
5308 * xmlValidateRoot:
5309 * @ctxt: the validation context
5310 * @doc: a document instance
5311 *
5312 * Try to validate a the root element
5313 * basically it does the following check as described by the
5314 * XML-1.0 recommendation:
5315 * - [ VC: Root Element Type ]
5316 * it doesn't try to recurse or apply other check to the element
5317 *
5318 * returns 1 if valid or 0 otherwise
5319 */
5320
5321int
5322xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5323 xmlNodePtr root;
5324 if (doc == NULL) return(0);
5325
5326 root = xmlDocGetRootElement(doc);
5327 if ((root == NULL) || (root->name == NULL)) {
5328 VERROR(ctxt->userData, "Not valid: no root element\n");
5329 return(0);
5330 }
5331
5332 /*
5333 * When doing post validation against a separate DTD, those may
5334 * no internal subset has been generated
5335 */
5336 if ((doc->intSubset != NULL) &&
5337 (doc->intSubset->name != NULL)) {
5338 /*
5339 * Check first the document root against the NQName
5340 */
5341 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5342 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
5343 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005344 snprintf((char *) qname, sizeof(qname), "%s:%s",
5345 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005346 qname[sizeof(qname) - 1] = 0;
5347 if (xmlStrEqual(doc->intSubset->name, qname))
5348 goto name_ok;
5349 }
5350 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5351 (xmlStrEqual(root->name, BAD_CAST "html")))
5352 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005353 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005354 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005355 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005356 root->name, doc->intSubset->name);
5357 return(0);
5358
5359 }
5360 }
5361name_ok:
5362 return(1);
5363}
5364
5365
5366/**
5367 * xmlValidateElement:
5368 * @ctxt: the validation context
5369 * @doc: a document instance
5370 * @elem: an element instance
5371 *
5372 * Try to validate the subtree under an element
5373 *
5374 * returns 1 if valid or 0 otherwise
5375 */
5376
5377int
5378xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5379 xmlNodePtr child;
5380 xmlAttrPtr attr;
5381 xmlChar *value;
5382 int ret = 1;
5383
5384 if (elem == NULL) return(0);
5385
5386 /*
5387 * XInclude elements were added after parsing in the infoset,
5388 * they don't really mean anything validation wise.
5389 */
5390 if ((elem->type == XML_XINCLUDE_START) ||
5391 (elem->type == XML_XINCLUDE_END))
5392 return(1);
5393
5394 CHECK_DTD;
5395
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005396 /*
5397 * Entities references have to be handled separately
5398 */
5399 if (elem->type == XML_ENTITY_REF_NODE) {
5400 return(1);
5401 }
5402
Owen Taylor3473f882001-02-23 17:55:21 +00005403 ret &= xmlValidateOneElement(ctxt, doc, elem);
5404 attr = elem->properties;
5405 while(attr != NULL) {
5406 value = xmlNodeListGetString(doc, attr->children, 0);
5407 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5408 if (value != NULL)
5409 xmlFree(value);
5410 attr= attr->next;
5411 }
5412 child = elem->children;
5413 while (child != NULL) {
5414 ret &= xmlValidateElement(ctxt, doc, child);
5415 child = child->next;
5416 }
5417
5418 return(ret);
5419}
5420
Daniel Veillard8730c562001-02-26 10:49:57 +00005421/**
5422 * xmlValidateRef:
5423 * @ref: A reference to be validated
5424 * @ctxt: Validation context
5425 * @name: Name of ID we are searching for
5426 *
5427 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005428static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005429xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005430 const xmlChar *name) {
5431 xmlAttrPtr id;
5432 xmlAttrPtr attr;
5433
5434 if (ref == NULL)
5435 return;
5436 attr = ref->attr;
5437 if (attr == NULL)
5438 return;
5439 if (attr->atype == XML_ATTRIBUTE_IDREF) {
5440 id = xmlGetID(ctxt->doc, name);
5441 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005442 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005443 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005444 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005445 attr->name, name);
5446 ctxt->valid = 0;
5447 }
5448 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5449 xmlChar *dup, *str = NULL, *cur, save;
5450
5451 dup = xmlStrdup(name);
5452 if (dup == NULL) {
5453 ctxt->valid = 0;
5454 return;
5455 }
5456 cur = dup;
5457 while (*cur != 0) {
5458 str = cur;
5459 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5460 save = *cur;
5461 *cur = 0;
5462 id = xmlGetID(ctxt->doc, str);
5463 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005464 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005465 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005466 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005467 attr->name, str);
5468 ctxt->valid = 0;
5469 }
5470 if (save == 0)
5471 break;
5472 *cur = save;
5473 while (IS_BLANK(*cur)) cur++;
5474 }
5475 xmlFree(dup);
5476 }
5477}
5478
5479/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005480 * xmlWalkValidateList:
5481 * @data: Contents of current link
5482 * @user: Value supplied by the user
5483 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005484 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005485 */
5486static int
5487xmlWalkValidateList(const void *data, const void *user)
5488{
5489 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5490 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5491 return 1;
5492}
5493
5494/**
5495 * xmlValidateCheckRefCallback:
5496 * @ref_list: List of references
5497 * @ctxt: Validation context
5498 * @name: Name of ID we are searching for
5499 *
5500 */
5501static void
5502xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5503 const xmlChar *name) {
5504 xmlValidateMemo memo;
5505
5506 if (ref_list == NULL)
5507 return;
5508 memo.ctxt = ctxt;
5509 memo.name = name;
5510
5511 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5512
5513}
5514
5515/**
Owen Taylor3473f882001-02-23 17:55:21 +00005516 * xmlValidateDocumentFinal:
5517 * @ctxt: the validation context
5518 * @doc: a document instance
5519 *
5520 * Does the final step for the document validation once all the
5521 * incremental validation steps have been completed
5522 *
5523 * basically it does the following checks described by the XML Rec
5524 *
5525 *
5526 * returns 1 if valid or 0 otherwise
5527 */
5528
5529int
5530xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5531 xmlRefTablePtr table;
5532
5533 if (doc == NULL) {
5534 xmlGenericError(xmlGenericErrorContext,
5535 "xmlValidateDocumentFinal: doc == NULL\n");
5536 return(0);
5537 }
5538
5539 /*
5540 * Check all the NOTATION/NOTATIONS attributes
5541 */
5542 /*
5543 * Check all the ENTITY/ENTITIES attributes definition for validity
5544 */
5545 /*
5546 * Check all the IDREF/IDREFS attributes definition for validity
5547 */
5548 table = (xmlRefTablePtr) doc->refs;
5549 ctxt->doc = doc;
5550 ctxt->valid = 1;
5551 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
5552 return(ctxt->valid);
5553}
5554
5555/**
5556 * xmlValidateDtd:
5557 * @ctxt: the validation context
5558 * @doc: a document instance
5559 * @dtd: a dtd instance
5560 *
5561 * Try to validate the document against the dtd instance
5562 *
5563 * basically it does check all the definitions in the DtD.
5564 *
5565 * returns 1 if valid or 0 otherwise
5566 */
5567
5568int
5569xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
5570 int ret;
5571 xmlDtdPtr oldExt;
5572 xmlNodePtr root;
5573
5574 if (dtd == NULL) return(0);
5575 if (doc == NULL) return(0);
5576 oldExt = doc->extSubset;
5577 doc->extSubset = dtd;
5578 ret = xmlValidateRoot(ctxt, doc);
5579 if (ret == 0) {
5580 doc->extSubset = oldExt;
5581 return(ret);
5582 }
5583 if (doc->ids != NULL) {
5584 xmlFreeIDTable(doc->ids);
5585 doc->ids = NULL;
5586 }
5587 if (doc->refs != NULL) {
5588 xmlFreeRefTable(doc->refs);
5589 doc->refs = NULL;
5590 }
5591 root = xmlDocGetRootElement(doc);
5592 ret = xmlValidateElement(ctxt, doc, root);
5593 ret &= xmlValidateDocumentFinal(ctxt, doc);
5594 doc->extSubset = oldExt;
5595 return(ret);
5596}
5597
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005598static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005599xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
5600 const xmlChar *name ATTRIBUTE_UNUSED) {
5601 if (cur == NULL)
5602 return;
5603 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
5604 xmlChar *notation = cur->content;
5605
Daniel Veillard878eab02002-02-19 13:46:09 +00005606 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005607 int ret;
5608
5609 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
5610 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00005611 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005612 }
5613 }
5614 }
5615}
5616
5617static void
Owen Taylor3473f882001-02-23 17:55:21 +00005618xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00005619 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005620 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00005621 xmlDocPtr doc;
5622 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005623
Owen Taylor3473f882001-02-23 17:55:21 +00005624 if (cur == NULL)
5625 return;
5626 switch (cur->atype) {
5627 case XML_ATTRIBUTE_CDATA:
5628 case XML_ATTRIBUTE_ID:
5629 case XML_ATTRIBUTE_IDREF :
5630 case XML_ATTRIBUTE_IDREFS:
5631 case XML_ATTRIBUTE_NMTOKEN:
5632 case XML_ATTRIBUTE_NMTOKENS:
5633 case XML_ATTRIBUTE_ENUMERATION:
5634 break;
5635 case XML_ATTRIBUTE_ENTITY:
5636 case XML_ATTRIBUTE_ENTITIES:
5637 case XML_ATTRIBUTE_NOTATION:
5638 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005639
5640 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
5641 cur->atype, cur->defaultValue);
5642 if ((ret == 0) && (ctxt->valid == 1))
5643 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005644 }
5645 if (cur->tree != NULL) {
5646 xmlEnumerationPtr tree = cur->tree;
5647 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005648 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00005649 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005650 if ((ret == 0) && (ctxt->valid == 1))
5651 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005652 tree = tree->next;
5653 }
5654 }
5655 }
Daniel Veillard878eab02002-02-19 13:46:09 +00005656 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
5657 doc = cur->doc;
5658 if ((doc == NULL) || (cur->elem == NULL)) {
5659 VERROR(ctxt->userData,
5660 "xmlValidateAttributeCallback(%s): internal error\n",
5661 cur->name);
5662 return;
5663 }
5664 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
5665 if (elem == NULL)
5666 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
5667 if (elem == NULL) {
5668 VERROR(ctxt->userData,
5669 "attribute %s: could not find decl for element %s\n",
5670 cur->name, cur->elem);
5671 return;
5672 }
5673 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
5674 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005675 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00005676 cur->name, cur->elem);
5677 ctxt->valid = 0;
5678 }
5679 }
Owen Taylor3473f882001-02-23 17:55:21 +00005680}
5681
5682/**
5683 * xmlValidateDtdFinal:
5684 * @ctxt: the validation context
5685 * @doc: a document instance
5686 *
5687 * Does the final step for the dtds validation once all the
5688 * subsets have been parsed
5689 *
5690 * basically it does the following checks described by the XML Rec
5691 * - check that ENTITY and ENTITIES type attributes default or
5692 * possible values matches one of the defined entities.
5693 * - check that NOTATION type attributes default or
5694 * possible values matches one of the defined notations.
5695 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005696 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00005697 */
5698
5699int
5700xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00005701 xmlDtdPtr dtd;
5702 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005703 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00005704
5705 if (doc == NULL) return(0);
5706 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5707 return(0);
5708 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005709 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005710 dtd = doc->intSubset;
5711 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5712 table = (xmlAttributeTablePtr) dtd->attributes;
5713 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005714 }
5715 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005716 entities = (xmlEntitiesTablePtr) dtd->entities;
5717 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5718 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005719 }
5720 dtd = doc->extSubset;
5721 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5722 table = (xmlAttributeTablePtr) dtd->attributes;
5723 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005724 }
5725 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005726 entities = (xmlEntitiesTablePtr) dtd->entities;
5727 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5728 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005729 }
5730 return(ctxt->valid);
5731}
5732
5733/**
5734 * xmlValidateDocument:
5735 * @ctxt: the validation context
5736 * @doc: a document instance
5737 *
5738 * Try to validate the document instance
5739 *
5740 * basically it does the all the checks described by the XML Rec
5741 * i.e. validates the internal and external subset (if present)
5742 * and validate the document tree.
5743 *
5744 * returns 1 if valid or 0 otherwise
5745 */
5746
5747int
5748xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5749 int ret;
5750 xmlNodePtr root;
5751
Daniel Veillard2fd85422002-10-16 14:32:41 +00005752 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
5753 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00005754 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00005755 }
Owen Taylor3473f882001-02-23 17:55:21 +00005756 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
5757 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
5758 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
5759 doc->intSubset->SystemID);
5760 if (doc->extSubset == NULL) {
5761 if (doc->intSubset->SystemID != NULL) {
5762 VERROR(ctxt->userData,
5763 "Could not load the external subset \"%s\"\n",
5764 doc->intSubset->SystemID);
5765 } else {
5766 VERROR(ctxt->userData,
5767 "Could not load the external subset \"%s\"\n",
5768 doc->intSubset->ExternalID);
5769 }
5770 return(0);
5771 }
5772 }
5773
5774 if (doc->ids != NULL) {
5775 xmlFreeIDTable(doc->ids);
5776 doc->ids = NULL;
5777 }
5778 if (doc->refs != NULL) {
5779 xmlFreeRefTable(doc->refs);
5780 doc->refs = NULL;
5781 }
5782 ret = xmlValidateDtdFinal(ctxt, doc);
5783 if (!xmlValidateRoot(ctxt, doc)) return(0);
5784
5785 root = xmlDocGetRootElement(doc);
5786 ret &= xmlValidateElement(ctxt, doc, root);
5787 ret &= xmlValidateDocumentFinal(ctxt, doc);
5788 return(ret);
5789}
5790
5791
5792/************************************************************************
5793 * *
5794 * Routines for dynamic validation editing *
5795 * *
5796 ************************************************************************/
5797
5798/**
5799 * xmlValidGetPotentialChildren:
5800 * @ctree: an element content tree
5801 * @list: an array to store the list of child names
5802 * @len: a pointer to the number of element in the list
5803 * @max: the size of the array
5804 *
5805 * Build/extend a list of potential children allowed by the content tree
5806 *
5807 * returns the number of element in the list, or -1 in case of error.
5808 */
5809
5810int
5811xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
5812 int *len, int max) {
5813 int i;
5814
5815 if ((ctree == NULL) || (list == NULL) || (len == NULL))
5816 return(-1);
5817 if (*len >= max) return(*len);
5818
5819 switch (ctree->type) {
5820 case XML_ELEMENT_CONTENT_PCDATA:
5821 for (i = 0; i < *len;i++)
5822 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
5823 list[(*len)++] = BAD_CAST "#PCDATA";
5824 break;
5825 case XML_ELEMENT_CONTENT_ELEMENT:
5826 for (i = 0; i < *len;i++)
5827 if (xmlStrEqual(ctree->name, list[i])) return(*len);
5828 list[(*len)++] = ctree->name;
5829 break;
5830 case XML_ELEMENT_CONTENT_SEQ:
5831 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5832 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5833 break;
5834 case XML_ELEMENT_CONTENT_OR:
5835 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5836 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5837 break;
5838 }
5839
5840 return(*len);
5841}
5842
5843/**
5844 * xmlValidGetValidElements:
5845 * @prev: an element to insert after
5846 * @next: an element to insert next
5847 * @list: an array to store the list of child names
5848 * @max: the size of the array
5849 *
5850 * This function returns the list of authorized children to insert
5851 * within an existing tree while respecting the validity constraints
5852 * forced by the Dtd. The insertion point is defined using @prev and
5853 * @next in the following ways:
5854 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
5855 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
5856 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
5857 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
5858 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
5859 *
5860 * pointers to the element names are inserted at the beginning of the array
5861 * and do not need to be freed.
5862 *
5863 * returns the number of element in the list, or -1 in case of error. If
5864 * the function returns the value @max the caller is invited to grow the
5865 * receiving array and retry.
5866 */
5867
5868int
5869xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
5870 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005871 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005872 int nb_valid_elements = 0;
5873 const xmlChar *elements[256];
5874 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005875 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005876
5877 xmlNode *ref_node;
5878 xmlNode *parent;
5879 xmlNode *test_node;
5880
5881 xmlNode *prev_next;
5882 xmlNode *next_prev;
5883 xmlNode *parent_childs;
5884 xmlNode *parent_last;
5885
5886 xmlElement *element_desc;
5887
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00005888 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005889
Owen Taylor3473f882001-02-23 17:55:21 +00005890 if (prev == NULL && next == NULL)
5891 return(-1);
5892
5893 if (list == NULL) return(-1);
5894 if (max <= 0) return(-1);
5895
5896 nb_valid_elements = 0;
5897 ref_node = prev ? prev : next;
5898 parent = ref_node->parent;
5899
5900 /*
5901 * Retrieves the parent element declaration
5902 */
5903 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5904 parent->name);
5905 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5906 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5907 parent->name);
5908 if (element_desc == NULL) return(-1);
5909
5910 /*
5911 * Do a backup of the current tree structure
5912 */
5913 prev_next = prev ? prev->next : NULL;
5914 next_prev = next ? next->prev : NULL;
5915 parent_childs = parent->children;
5916 parent_last = parent->last;
5917
5918 /*
5919 * Creates a dummy node and insert it into the tree
5920 */
5921 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5922 test_node->doc = ref_node->doc;
5923 test_node->parent = parent;
5924 test_node->prev = prev;
5925 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005926 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005927
5928 if (prev) prev->next = test_node;
5929 else parent->children = test_node;
5930
5931 if (next) next->prev = test_node;
5932 else parent->last = test_node;
5933
5934 /*
5935 * Insert each potential child node and check if the parent is
5936 * still valid
5937 */
5938 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5939 elements, &nb_elements, 256);
5940
5941 for (i = 0;i < nb_elements;i++) {
5942 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005943 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005944 int j;
5945
5946 for (j = 0; j < nb_valid_elements;j++)
5947 if (xmlStrEqual(elements[i], list[j])) break;
5948 list[nb_valid_elements++] = elements[i];
5949 if (nb_valid_elements >= max) break;
5950 }
5951 }
5952
5953 /*
5954 * Restore the tree structure
5955 */
5956 if (prev) prev->next = prev_next;
5957 if (next) next->prev = next_prev;
5958 parent->children = parent_childs;
5959 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005960
5961 /*
5962 * Free up the dummy node
5963 */
5964 test_node->name = name;
5965 xmlFreeNode(test_node);
5966
Owen Taylor3473f882001-02-23 17:55:21 +00005967 return(nb_valid_elements);
5968}