blob: 7f18a6f1c13fc8f7f58fa17e521a8a48339ac0c9 [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:
Daniel Veillard01c13b52002-12-10 15:19:08 +0000702 * @cur: An element content pointer.
Owen Taylor3473f882001-02-23 17:55:21 +0000703 *
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/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002229 * xmlRemoveID:
Owen Taylor3473f882001-02-23 17:55:21 +00002230 * @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/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002510 * xmlRemoveRef:
Owen Taylor3473f882001-02-23 17:55:21 +00002511 * @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/**
Daniel Veillard01c13b52002-12-10 15:19:08 +00002832 * xmlIsMixedElement:
Owen Taylor3473f882001-02-23 17:55:21 +00002833 * @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
3370 *
3371 * Does the validation related extra step of the normalization of attribute
3372 * values:
3373 *
3374 * If the declared value is not CDATA, then the XML processor must further
3375 * process the normalized attribute value by discarding any leading and
3376 * trailing space (#x20) characters, and by replacing sequences of space
3377 * (#x20) characters by single space (#x20) character.
3378 *
3379 * returns a new normalized string if normalization is needed, NULL otherwise
3380 * the caller must free the returned value.
3381 */
3382
3383xmlChar *
3384xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3385 const xmlChar *name, const xmlChar *value) {
3386 xmlChar *ret, *dst;
3387 const xmlChar *src;
3388 xmlAttributePtr attrDecl = NULL;
3389
3390 if (doc == NULL) return(NULL);
3391 if (elem == NULL) return(NULL);
3392 if (name == NULL) return(NULL);
3393 if (value == NULL) return(NULL);
3394
3395 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3396 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003397 snprintf((char *) qname, sizeof(qname), "%s:%s",
3398 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003399 qname[sizeof(qname) - 1] = 0;
3400 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3401 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3402 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3403 }
3404 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3405 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3406 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3407
3408 if (attrDecl == NULL)
3409 return(NULL);
3410 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3411 return(NULL);
3412
3413 ret = xmlStrdup(value);
3414 if (ret == NULL)
3415 return(NULL);
3416 src = value;
3417 dst = ret;
3418 while (*src == 0x20) src++;
3419 while (*src != 0) {
3420 if (*src == 0x20) {
3421 while (*src == 0x20) src++;
3422 if (*src != 0)
3423 *dst++ = 0x20;
3424 } else {
3425 *dst++ = *src++;
3426 }
3427 }
3428 *dst = 0;
3429 return(ret);
3430}
3431
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003432static void
Owen Taylor3473f882001-02-23 17:55:21 +00003433xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003434 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003435 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3436}
3437
3438/**
3439 * xmlValidateAttributeDecl:
3440 * @ctxt: the validation context
3441 * @doc: a document instance
3442 * @attr: an attribute definition
3443 *
3444 * Try to validate a single attribute definition
3445 * basically it does the following checks as described by the
3446 * XML-1.0 recommendation:
3447 * - [ VC: Attribute Default Legal ]
3448 * - [ VC: Enumeration ]
3449 * - [ VC: ID Attribute Default ]
3450 *
3451 * The ID/IDREF uniqueness and matching are done separately
3452 *
3453 * returns 1 if valid or 0 otherwise
3454 */
3455
3456int
3457xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3458 xmlAttributePtr attr) {
3459 int ret = 1;
3460 int val;
3461 CHECK_DTD;
3462 if(attr == NULL) return(1);
3463
3464 /* Attribute Default Legal */
3465 /* Enumeration */
3466 if (attr->defaultValue != NULL) {
3467 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3468 if (val == 0) {
3469 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003470 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003471 attr->name, attr->elem);
3472 }
3473 ret &= val;
3474 }
3475
3476 /* ID Attribute Default */
3477 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3478 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3479 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3480 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003481 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003482 attr->name, attr->elem);
3483 ret = 0;
3484 }
3485
3486 /* One ID per Element Type */
3487 if (attr->atype == XML_ATTRIBUTE_ID) {
3488 int nbId;
3489
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003490 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003491 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3492 attr->elem);
3493 if (elem != NULL) {
3494 nbId = xmlScanIDAttributeDecl(NULL, elem);
3495 } else {
3496 xmlAttributeTablePtr table;
3497
3498 /*
3499 * The attribute may be declared in the internal subset and the
3500 * element in the external subset.
3501 */
3502 nbId = 0;
3503 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3504 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3505 xmlValidateAttributeIdCallback, &nbId);
3506 }
3507 if (nbId > 1) {
3508 VERROR(ctxt->userData,
3509 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3510 attr->elem, nbId, attr->name);
3511 } else if (doc->extSubset != NULL) {
3512 int extId = 0;
3513 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3514 if (elem != NULL) {
3515 extId = xmlScanIDAttributeDecl(NULL, elem);
3516 }
3517 if (extId > 1) {
3518 VERROR(ctxt->userData,
3519 "Element %s has %d ID attribute defined in the external subset : %s\n",
3520 attr->elem, extId, attr->name);
3521 } else if (extId + nbId > 1) {
3522 VERROR(ctxt->userData,
3523"Element %s has ID attributes defined in the internal and external subset : %s\n",
3524 attr->elem, attr->name);
3525 }
3526 }
3527 }
3528
3529 /* Validity Constraint: Enumeration */
3530 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3531 xmlEnumerationPtr tree = attr->tree;
3532 while (tree != NULL) {
3533 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3534 tree = tree->next;
3535 }
3536 if (tree == NULL) {
3537 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003538"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003539 attr->defaultValue, attr->name, attr->elem);
3540 ret = 0;
3541 }
3542 }
3543
3544 return(ret);
3545}
3546
3547/**
3548 * xmlValidateElementDecl:
3549 * @ctxt: the validation context
3550 * @doc: a document instance
3551 * @elem: an element definition
3552 *
3553 * Try to validate a single element definition
3554 * basically it does the following checks as described by the
3555 * XML-1.0 recommendation:
3556 * - [ VC: One ID per Element Type ]
3557 * - [ VC: No Duplicate Types ]
3558 * - [ VC: Unique Element Type Declaration ]
3559 *
3560 * returns 1 if valid or 0 otherwise
3561 */
3562
3563int
3564xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3565 xmlElementPtr elem) {
3566 int ret = 1;
3567 xmlElementPtr tst;
3568
3569 CHECK_DTD;
3570
3571 if (elem == NULL) return(1);
3572
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003573#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003574#ifdef LIBXML_REGEXP_ENABLED
3575 /* Build the regexp associated to the content model */
3576 ret = xmlValidBuildContentModel(ctxt, elem);
3577#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003578#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003579
Owen Taylor3473f882001-02-23 17:55:21 +00003580 /* No Duplicate Types */
3581 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3582 xmlElementContentPtr cur, next;
3583 const xmlChar *name;
3584
3585 cur = elem->content;
3586 while (cur != NULL) {
3587 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3588 if (cur->c1 == NULL) break;
3589 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3590 name = cur->c1->name;
3591 next = cur->c2;
3592 while (next != NULL) {
3593 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3594 if (xmlStrEqual(next->name, name)) {
3595 VERROR(ctxt->userData,
3596 "Definition of %s has duplicate references of %s\n",
3597 elem->name, name);
3598 ret = 0;
3599 }
3600 break;
3601 }
3602 if (next->c1 == NULL) break;
3603 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3604 if (xmlStrEqual(next->c1->name, name)) {
3605 VERROR(ctxt->userData,
3606 "Definition of %s has duplicate references of %s\n",
3607 elem->name, name);
3608 ret = 0;
3609 }
3610 next = next->c2;
3611 }
3612 }
3613 cur = cur->c2;
3614 }
3615 }
3616
3617 /* VC: Unique Element Type Declaration */
3618 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003619 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003620 ((tst->prefix == elem->prefix) ||
3621 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003622 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003623 VERROR(ctxt->userData, "Redefinition of element %s\n",
3624 elem->name);
3625 ret = 0;
3626 }
3627 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003628 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003629 ((tst->prefix == elem->prefix) ||
3630 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003631 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003632 VERROR(ctxt->userData, "Redefinition of element %s\n",
3633 elem->name);
3634 ret = 0;
3635 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003636 /* One ID per Element Type
3637 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003638 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3639 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003640 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003641 return(ret);
3642}
3643
3644/**
3645 * xmlValidateOneAttribute:
3646 * @ctxt: the validation context
3647 * @doc: a document instance
3648 * @elem: an element instance
3649 * @attr: an attribute instance
3650 * @value: the attribute value (without entities processing)
3651 *
3652 * Try to validate a single attribute for an element
3653 * basically it does the following checks as described by the
3654 * XML-1.0 recommendation:
3655 * - [ VC: Attribute Value Type ]
3656 * - [ VC: Fixed Attribute Default ]
3657 * - [ VC: Entity Name ]
3658 * - [ VC: Name Token ]
3659 * - [ VC: ID ]
3660 * - [ VC: IDREF ]
3661 * - [ VC: Entity Name ]
3662 * - [ VC: Notation Attributes ]
3663 *
3664 * The ID/IDREF uniqueness and matching are done separately
3665 *
3666 * returns 1 if valid or 0 otherwise
3667 */
3668
3669int
3670xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3671 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3672 /* xmlElementPtr elemDecl; */
3673 xmlAttributePtr attrDecl = NULL;
3674 int val;
3675 int ret = 1;
3676
3677 CHECK_DTD;
3678 if ((elem == NULL) || (elem->name == NULL)) return(0);
3679 if ((attr == NULL) || (attr->name == NULL)) return(0);
3680
3681 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3682 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003683 snprintf((char *) qname, sizeof(qname), "%s:%s",
3684 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003685 qname[sizeof(qname) - 1] = 0;
3686 if (attr->ns != NULL) {
3687 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3688 attr->name, attr->ns->prefix);
3689 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3690 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3691 attr->name, attr->ns->prefix);
3692 } else {
3693 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3694 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3695 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3696 qname, attr->name);
3697 }
3698 }
3699 if (attrDecl == NULL) {
3700 if (attr->ns != NULL) {
3701 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3702 attr->name, attr->ns->prefix);
3703 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3704 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3705 attr->name, attr->ns->prefix);
3706 } else {
3707 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3708 elem->name, attr->name);
3709 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3710 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3711 elem->name, attr->name);
3712 }
3713 }
3714
3715
3716 /* Validity Constraint: Attribute Value Type */
3717 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003718 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003719 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003720 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003721 attr->name, elem->name);
3722 return(0);
3723 }
3724 attr->atype = attrDecl->atype;
3725
3726 val = xmlValidateAttributeValue(attrDecl->atype, value);
3727 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003728 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003729 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003730 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003731 attr->name, elem->name);
3732 ret = 0;
3733 }
3734
3735 /* Validity constraint: Fixed Attribute Default */
3736 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3737 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003738 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003739 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003740 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003741 attr->name, elem->name, attrDecl->defaultValue);
3742 ret = 0;
3743 }
3744 }
3745
3746 /* Validity Constraint: ID uniqueness */
3747 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3748 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3749 ret = 0;
3750 }
3751
3752 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3753 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3754 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3755 ret = 0;
3756 }
3757
3758 /* Validity Constraint: Notation Attributes */
3759 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3760 xmlEnumerationPtr tree = attrDecl->tree;
3761 xmlNotationPtr nota;
3762
3763 /* First check that the given NOTATION was declared */
3764 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3765 if (nota == NULL)
3766 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3767
3768 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003769 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003770 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003771 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003772 value, attr->name, elem->name);
3773 ret = 0;
3774 }
3775
3776 /* Second, verify that it's among the list */
3777 while (tree != NULL) {
3778 if (xmlStrEqual(tree->name, value)) break;
3779 tree = tree->next;
3780 }
3781 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003782 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003783 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003784"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003785 value, attr->name, elem->name);
3786 ret = 0;
3787 }
3788 }
3789
3790 /* Validity Constraint: Enumeration */
3791 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3792 xmlEnumerationPtr tree = attrDecl->tree;
3793 while (tree != NULL) {
3794 if (xmlStrEqual(tree->name, value)) break;
3795 tree = tree->next;
3796 }
3797 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003798 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003799 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003800 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003801 value, attr->name, elem->name);
3802 ret = 0;
3803 }
3804 }
3805
3806 /* Fixed Attribute Default */
3807 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3808 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003809 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003810 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003811 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003812 attr->name, elem->name, attrDecl->defaultValue);
3813 ret = 0;
3814 }
3815
3816 /* Extra check for the attribute value */
3817 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3818 attrDecl->atype, value);
3819
3820 return(ret);
3821}
3822
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003823/**
3824 * xmlValidateOneNamespace:
3825 * @ctxt: the validation context
3826 * @doc: a document instance
3827 * @elem: an element instance
3828 * @ns: an namespace declaration instance
3829 * @value: the attribute value (without entities processing)
3830 *
3831 * Try to validate a single namespace declaration for an element
3832 * basically it does the following checks as described by the
3833 * XML-1.0 recommendation:
3834 * - [ VC: Attribute Value Type ]
3835 * - [ VC: Fixed Attribute Default ]
3836 * - [ VC: Entity Name ]
3837 * - [ VC: Name Token ]
3838 * - [ VC: ID ]
3839 * - [ VC: IDREF ]
3840 * - [ VC: Entity Name ]
3841 * - [ VC: Notation Attributes ]
3842 *
3843 * The ID/IDREF uniqueness and matching are done separately
3844 *
3845 * returns 1 if valid or 0 otherwise
3846 */
3847
3848int
3849xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3850xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
3851 /* xmlElementPtr elemDecl; */
3852 xmlAttributePtr attrDecl = NULL;
3853 int val;
3854 int ret = 1;
3855
3856 CHECK_DTD;
3857 if ((elem == NULL) || (elem->name == NULL)) return(0);
3858 if ((ns == NULL) || (ns->href == NULL)) return(0);
3859
3860 if (prefix != NULL) {
3861 xmlChar qname[500];
3862 snprintf((char *) qname, sizeof(qname), "%s:%s",
3863 prefix, elem->name);
3864 qname[sizeof(qname) - 1] = 0;
3865 if (ns->prefix != NULL) {
3866 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3867 ns->prefix, BAD_CAST "xmlns");
3868 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3869 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3870 ns->prefix, BAD_CAST "xmlns");
3871 } else {
3872 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname,
3873 BAD_CAST "xmlns");
3874 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3875 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname,
3876 BAD_CAST "xmlns");
3877 }
3878 }
3879 if (attrDecl == NULL) {
3880 if (ns->prefix != NULL) {
3881 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3882 ns->prefix, BAD_CAST "xmlns");
3883 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3884 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3885 ns->prefix, BAD_CAST "xmlns");
3886 } else {
3887 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3888 elem->name, BAD_CAST "xmlns");
3889 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3890 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3891 elem->name, BAD_CAST "xmlns");
3892 }
3893 }
3894
3895
3896 /* Validity Constraint: Attribute Value Type */
3897 if (attrDecl == NULL) {
3898 VECTXT(ctxt, elem);
3899 if (ns->prefix != NULL) {
3900 VERROR(ctxt->userData,
3901 "No declaration for attribute xmlns:%s of element %s\n",
3902 ns->prefix, elem->name);
3903 } else {
3904 VERROR(ctxt->userData,
3905 "No declaration for attribute xmlns of element %s\n",
3906 elem->name);
3907 }
3908 return(0);
3909 }
3910
3911 val = xmlValidateAttributeValue(attrDecl->atype, value);
3912 if (val == 0) {
3913 VECTXT(ctxt, elem);
3914 if (ns->prefix != NULL) {
3915 VERROR(ctxt->userData,
3916 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
3917 ns->prefix, elem->name);
3918 } else {
3919 VERROR(ctxt->userData,
3920 "Syntax of value for attribute xmlns of %s is not valid\n",
3921 elem->name);
3922 }
3923 ret = 0;
3924 }
3925
3926 /* Validity constraint: Fixed Attribute Default */
3927 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3928 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3929 VECTXT(ctxt, elem);
3930 if (ns->prefix != NULL) {
3931 VERROR(ctxt->userData,
3932 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
3933 ns->prefix, elem->name, attrDecl->defaultValue);
3934 } else {
3935 VERROR(ctxt->userData,
3936 "Value for attribute xmlns of %s is different from default \"%s\"\n",
3937 elem->name, attrDecl->defaultValue);
3938 }
3939 ret = 0;
3940 }
3941 }
3942
3943 /* Validity Constraint: ID uniqueness */
3944 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3945 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
3946 ret = 0;
3947 }
3948
3949 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3950 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3951 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
3952 ret = 0;
3953 }
3954
3955 /* Validity Constraint: Notation Attributes */
3956 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3957 xmlEnumerationPtr tree = attrDecl->tree;
3958 xmlNotationPtr nota;
3959
3960 /* First check that the given NOTATION was declared */
3961 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3962 if (nota == NULL)
3963 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3964
3965 if (nota == NULL) {
3966 VECTXT(ctxt, elem);
3967 if (ns->prefix != NULL) {
3968 VERROR(ctxt->userData,
3969 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
3970 value, ns->prefix, elem->name);
3971 } else {
3972 VERROR(ctxt->userData,
3973 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
3974 value, elem->name);
3975 }
3976 ret = 0;
3977 }
3978
3979 /* Second, verify that it's among the list */
3980 while (tree != NULL) {
3981 if (xmlStrEqual(tree->name, value)) break;
3982 tree = tree->next;
3983 }
3984 if (tree == NULL) {
3985 VECTXT(ctxt, elem);
3986 if (ns->prefix != NULL) {
3987 VERROR(ctxt->userData,
3988"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
3989 value, ns->prefix, elem->name);
3990 } else {
3991 VERROR(ctxt->userData,
3992"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
3993 value, elem->name);
3994 }
3995 ret = 0;
3996 }
3997 }
3998
3999 /* Validity Constraint: Enumeration */
4000 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
4001 xmlEnumerationPtr tree = attrDecl->tree;
4002 while (tree != NULL) {
4003 if (xmlStrEqual(tree->name, value)) break;
4004 tree = tree->next;
4005 }
4006 if (tree == NULL) {
4007 VECTXT(ctxt, elem);
4008 if (ns->prefix != NULL) {
4009 VERROR(ctxt->userData,
4010"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4011 value, ns->prefix, elem->name);
4012 } else {
4013 VERROR(ctxt->userData,
4014"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4015 value, elem->name);
4016 }
4017 ret = 0;
4018 }
4019 }
4020
4021 /* Fixed Attribute Default */
4022 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4023 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4024 VECTXT(ctxt, elem);
4025 if (ns->prefix != NULL) {
4026 VERROR(ctxt->userData,
4027 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4028 ns->prefix, elem->name, attrDecl->defaultValue);
4029 } else {
4030 VERROR(ctxt->userData,
4031 "Value for attribute xmlns of %s must be \"%s\"\n",
4032 elem->name, attrDecl->defaultValue);
4033 }
4034 ret = 0;
4035 }
4036
4037 /* Extra check for the attribute value */
4038 if (ns->prefix != NULL) {
4039 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4040 attrDecl->atype, value);
4041 } else {
4042 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4043 attrDecl->atype, value);
4044 }
4045
4046 return(ret);
4047}
4048
Daniel Veillard118aed72002-09-24 14:13:13 +00004049#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004050/**
4051 * xmlValidateSkipIgnorable:
4052 * @ctxt: the validation context
4053 * @child: the child list
4054 *
4055 * Skip ignorable elements w.r.t. the validation process
4056 *
4057 * returns the first element to consider for validation of the content model
4058 */
4059
4060static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004061xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004062 while (child != NULL) {
4063 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004064 /* These things are ignored (skipped) during validation. */
4065 case XML_PI_NODE:
4066 case XML_COMMENT_NODE:
4067 case XML_XINCLUDE_START:
4068 case XML_XINCLUDE_END:
4069 child = child->next;
4070 break;
4071 case XML_TEXT_NODE:
4072 if (xmlIsBlankNode(child))
4073 child = child->next;
4074 else
4075 return(child);
4076 break;
4077 /* keep current node */
4078 default:
4079 return(child);
4080 }
4081 }
4082 return(child);
4083}
4084
4085/**
4086 * xmlValidateElementType:
4087 * @ctxt: the validation context
4088 *
4089 * Try to validate the content model of an element internal function
4090 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004091 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4092 * reference is found and -3 if the validation succeeded but
4093 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004094 */
4095
4096static int
4097xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004098 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004099 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004100
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004101 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004102 if ((NODE == NULL) && (CONT == NULL))
4103 return(1);
4104 if ((NODE == NULL) &&
4105 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4106 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4107 return(1);
4108 }
4109 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004110 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004111 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004112
4113 /*
4114 * We arrive here when more states need to be examined
4115 */
4116cont:
4117
4118 /*
4119 * We just recovered from a rollback generated by a possible
4120 * epsilon transition, go directly to the analysis phase
4121 */
4122 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004123 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004124 DEBUG_VALID_STATE(NODE, CONT)
4125 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004126 goto analyze;
4127 }
4128
4129 DEBUG_VALID_STATE(NODE, CONT)
4130 /*
4131 * we may have to save a backup state here. This is the equivalent
4132 * of handling epsilon transition in NFAs.
4133 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004134 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004135 ((CONT->parent == NULL) ||
4136 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004137 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004138 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004139 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004140 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004141 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4142 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004143 }
4144
4145
4146 /*
4147 * Check first if the content matches
4148 */
4149 switch (CONT->type) {
4150 case XML_ELEMENT_CONTENT_PCDATA:
4151 if (NODE == NULL) {
4152 DEBUG_VALID_MSG("pcdata failed no node");
4153 ret = 0;
4154 break;
4155 }
4156 if (NODE->type == XML_TEXT_NODE) {
4157 DEBUG_VALID_MSG("pcdata found, skip to next");
4158 /*
4159 * go to next element in the content model
4160 * skipping ignorable elems
4161 */
4162 do {
4163 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004164 NODE = xmlValidateSkipIgnorable(NODE);
4165 if ((NODE != NULL) &&
4166 (NODE->type == XML_ENTITY_REF_NODE))
4167 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004168 } while ((NODE != NULL) &&
4169 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004170 (NODE->type != XML_TEXT_NODE) &&
4171 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004172 ret = 1;
4173 break;
4174 } else {
4175 DEBUG_VALID_MSG("pcdata failed");
4176 ret = 0;
4177 break;
4178 }
4179 break;
4180 case XML_ELEMENT_CONTENT_ELEMENT:
4181 if (NODE == NULL) {
4182 DEBUG_VALID_MSG("element failed no node");
4183 ret = 0;
4184 break;
4185 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004186 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4187 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004188 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004189 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4190 ret = (CONT->prefix == NULL);
4191 } else if (CONT->prefix == NULL) {
4192 ret = 0;
4193 } else {
4194 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4195 }
4196 }
4197 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004198 DEBUG_VALID_MSG("element found, skip to next");
4199 /*
4200 * go to next element in the content model
4201 * skipping ignorable elems
4202 */
4203 do {
4204 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004205 NODE = xmlValidateSkipIgnorable(NODE);
4206 if ((NODE != NULL) &&
4207 (NODE->type == XML_ENTITY_REF_NODE))
4208 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004209 } while ((NODE != NULL) &&
4210 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004211 (NODE->type != XML_TEXT_NODE) &&
4212 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004213 } else {
4214 DEBUG_VALID_MSG("element failed");
4215 ret = 0;
4216 break;
4217 }
4218 break;
4219 case XML_ELEMENT_CONTENT_OR:
4220 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004221 * Small optimization.
4222 */
4223 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4224 if ((NODE == NULL) ||
4225 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4226 DEPTH++;
4227 CONT = CONT->c2;
4228 goto cont;
4229 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004230 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4231 ret = (CONT->c1->prefix == NULL);
4232 } else if (CONT->c1->prefix == NULL) {
4233 ret = 0;
4234 } else {
4235 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4236 }
4237 if (ret == 0) {
4238 DEPTH++;
4239 CONT = CONT->c2;
4240 goto cont;
4241 }
Daniel Veillard85349052001-04-20 13:48:21 +00004242 }
4243
4244 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004245 * save the second branch 'or' branch
4246 */
4247 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004248 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4249 OCCURS, ROLLBACK_OR) < 0)
4250 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004251 DEPTH++;
4252 CONT = CONT->c1;
4253 goto cont;
4254 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004255 /*
4256 * Small optimization.
4257 */
4258 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4259 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4260 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4261 if ((NODE == NULL) ||
4262 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4263 DEPTH++;
4264 CONT = CONT->c2;
4265 goto cont;
4266 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004267 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4268 ret = (CONT->c1->prefix == NULL);
4269 } else if (CONT->c1->prefix == NULL) {
4270 ret = 0;
4271 } else {
4272 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4273 }
4274 if (ret == 0) {
4275 DEPTH++;
4276 CONT = CONT->c2;
4277 goto cont;
4278 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004279 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004280 DEPTH++;
4281 CONT = CONT->c1;
4282 goto cont;
4283 }
4284
4285 /*
4286 * At this point handle going up in the tree
4287 */
4288 if (ret == -1) {
4289 DEBUG_VALID_MSG("error found returning");
4290 return(ret);
4291 }
4292analyze:
4293 while (CONT != NULL) {
4294 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004295 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004296 * this level.
4297 */
4298 if (ret == 0) {
4299 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004300 xmlNodePtr cur;
4301
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004302 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004303 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004304 DEBUG_VALID_MSG("Once branch failed, rollback");
4305 if (vstateVPop(ctxt) < 0 ) {
4306 DEBUG_VALID_MSG("exhaustion, failed");
4307 return(0);
4308 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004309 if (cur != ctxt->vstate->node)
4310 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004311 goto cont;
4312 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004313 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004314 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004315 DEBUG_VALID_MSG("Plus branch failed, rollback");
4316 if (vstateVPop(ctxt) < 0 ) {
4317 DEBUG_VALID_MSG("exhaustion, failed");
4318 return(0);
4319 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004320 if (cur != ctxt->vstate->node)
4321 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004322 goto cont;
4323 }
4324 DEBUG_VALID_MSG("Plus branch found");
4325 ret = 1;
4326 break;
4327 case XML_ELEMENT_CONTENT_MULT:
4328#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004329 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004330 DEBUG_VALID_MSG("Mult branch failed");
4331 } else {
4332 DEBUG_VALID_MSG("Mult branch found");
4333 }
4334#endif
4335 ret = 1;
4336 break;
4337 case XML_ELEMENT_CONTENT_OPT:
4338 DEBUG_VALID_MSG("Option branch failed");
4339 ret = 1;
4340 break;
4341 }
4342 } else {
4343 switch (CONT->ocur) {
4344 case XML_ELEMENT_CONTENT_OPT:
4345 DEBUG_VALID_MSG("Option branch succeeded");
4346 ret = 1;
4347 break;
4348 case XML_ELEMENT_CONTENT_ONCE:
4349 DEBUG_VALID_MSG("Once branch succeeded");
4350 ret = 1;
4351 break;
4352 case XML_ELEMENT_CONTENT_PLUS:
4353 if (STATE == ROLLBACK_PARENT) {
4354 DEBUG_VALID_MSG("Plus branch rollback");
4355 ret = 1;
4356 break;
4357 }
4358 if (NODE == NULL) {
4359 DEBUG_VALID_MSG("Plus branch exhausted");
4360 ret = 1;
4361 break;
4362 }
4363 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004364 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004365 goto cont;
4366 case XML_ELEMENT_CONTENT_MULT:
4367 if (STATE == ROLLBACK_PARENT) {
4368 DEBUG_VALID_MSG("Mult branch rollback");
4369 ret = 1;
4370 break;
4371 }
4372 if (NODE == NULL) {
4373 DEBUG_VALID_MSG("Mult branch exhausted");
4374 ret = 1;
4375 break;
4376 }
4377 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004378 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004379 goto cont;
4380 }
4381 }
4382 STATE = 0;
4383
4384 /*
4385 * Then act accordingly at the parent level
4386 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004387 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004388 if (CONT->parent == NULL)
4389 break;
4390
4391 switch (CONT->parent->type) {
4392 case XML_ELEMENT_CONTENT_PCDATA:
4393 DEBUG_VALID_MSG("Error: parent pcdata");
4394 return(-1);
4395 case XML_ELEMENT_CONTENT_ELEMENT:
4396 DEBUG_VALID_MSG("Error: parent element");
4397 return(-1);
4398 case XML_ELEMENT_CONTENT_OR:
4399 if (ret == 1) {
4400 DEBUG_VALID_MSG("Or succeeded");
4401 CONT = CONT->parent;
4402 DEPTH--;
4403 } else {
4404 DEBUG_VALID_MSG("Or failed");
4405 CONT = CONT->parent;
4406 DEPTH--;
4407 }
4408 break;
4409 case XML_ELEMENT_CONTENT_SEQ:
4410 if (ret == 0) {
4411 DEBUG_VALID_MSG("Sequence failed");
4412 CONT = CONT->parent;
4413 DEPTH--;
4414 } else if (CONT == CONT->parent->c1) {
4415 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4416 CONT = CONT->parent->c2;
4417 goto cont;
4418 } else {
4419 DEBUG_VALID_MSG("Sequence succeeded");
4420 CONT = CONT->parent;
4421 DEPTH--;
4422 }
4423 }
4424 }
4425 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004426 xmlNodePtr cur;
4427
4428 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004429 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4430 if (vstateVPop(ctxt) < 0 ) {
4431 DEBUG_VALID_MSG("exhaustion, failed");
4432 return(0);
4433 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004434 if (cur != ctxt->vstate->node)
4435 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004436 goto cont;
4437 }
4438 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004439 xmlNodePtr cur;
4440
4441 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004442 DEBUG_VALID_MSG("Failure, rollback");
4443 if (vstateVPop(ctxt) < 0 ) {
4444 DEBUG_VALID_MSG("exhaustion, failed");
4445 return(0);
4446 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004447 if (cur != ctxt->vstate->node)
4448 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004449 goto cont;
4450 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004451 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004452}
Daniel Veillard23e73572002-09-19 19:56:43 +00004453#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004454
4455/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004456 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004457 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004458 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004459 * @content: An element
4460 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4461 *
4462 * This will dump the list of elements to the buffer
4463 * Intended just for the debug routine
4464 */
4465static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004466xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004467 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004468 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004469
4470 if (node == NULL) return;
4471 if (glob) strcat(buf, "(");
4472 cur = node;
4473 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004474 len = strlen(buf);
4475 if (size - len < 50) {
4476 if ((size - len > 4) && (buf[len - 1] != '.'))
4477 strcat(buf, " ...");
4478 return;
4479 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004480 switch (cur->type) {
4481 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004482 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004483 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004484 if ((size - len > 4) && (buf[len - 1] != '.'))
4485 strcat(buf, " ...");
4486 return;
4487 }
4488 strcat(buf, (char *) cur->ns->prefix);
4489 strcat(buf, ":");
4490 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004491 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004492 if ((size - len > 4) && (buf[len - 1] != '.'))
4493 strcat(buf, " ...");
4494 return;
4495 }
4496 strcat(buf, (char *) cur->name);
4497 if (cur->next != NULL)
4498 strcat(buf, " ");
4499 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004500 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004501 if (xmlIsBlankNode(cur))
4502 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004503 case XML_CDATA_SECTION_NODE:
4504 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004505 strcat(buf, "CDATA");
4506 if (cur->next != NULL)
4507 strcat(buf, " ");
4508 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004509 case XML_ATTRIBUTE_NODE:
4510 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004511#ifdef LIBXML_DOCB_ENABLED
4512 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004513#endif
4514 case XML_HTML_DOCUMENT_NODE:
4515 case XML_DOCUMENT_TYPE_NODE:
4516 case XML_DOCUMENT_FRAG_NODE:
4517 case XML_NOTATION_NODE:
4518 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004519 strcat(buf, "???");
4520 if (cur->next != NULL)
4521 strcat(buf, " ");
4522 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004523 case XML_ENTITY_NODE:
4524 case XML_PI_NODE:
4525 case XML_DTD_NODE:
4526 case XML_COMMENT_NODE:
4527 case XML_ELEMENT_DECL:
4528 case XML_ATTRIBUTE_DECL:
4529 case XML_ENTITY_DECL:
4530 case XML_XINCLUDE_START:
4531 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004532 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004533 }
4534 cur = cur->next;
4535 }
4536 if (glob) strcat(buf, ")");
4537}
4538
4539/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004540 * xmlValidateElementContent:
4541 * @ctxt: the validation context
4542 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004543 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004544 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004545 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004546 *
4547 * Try to validate the content model of an element
4548 *
4549 * returns 1 if valid or 0 if not and -1 in case of error
4550 */
4551
4552static int
4553xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004554 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004555 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004556#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard01992e02002-10-09 10:20:30 +00004557 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillard23e73572002-09-19 19:56:43 +00004558#endif
Daniel Veillard01992e02002-10-09 10:20:30 +00004559 xmlNodePtr cur;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004560 xmlElementContentPtr cont;
4561 const xmlChar *name;
4562
4563 if (elemDecl == NULL)
4564 return(-1);
4565 cont = elemDecl->content;
4566 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004567
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004568#ifdef LIBXML_REGEXP_ENABLED
4569 /* Build the regexp associated to the content model */
4570 if (elemDecl->contModel == NULL)
4571 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4572 if (elemDecl->contModel == NULL) {
4573 ret = -1;
4574 } else {
4575 xmlRegExecCtxtPtr exec;
4576
Daniel Veillard01992e02002-10-09 10:20:30 +00004577 ctxt->nodeMax = 0;
4578 ctxt->nodeNr = 0;
4579 ctxt->nodeTab = NULL;
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004580 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4581 if (exec != NULL) {
4582 cur = child;
4583 while (cur != NULL) {
4584 switch (cur->type) {
4585 case XML_ENTITY_REF_NODE:
4586 /*
4587 * Push the current node to be able to roll back
4588 * and process within the entity
4589 */
4590 if ((cur->children != NULL) &&
4591 (cur->children->children != NULL)) {
4592 nodeVPush(ctxt, cur);
4593 cur = cur->children->children;
4594 continue;
4595 }
4596 break;
4597 case XML_TEXT_NODE:
4598 if (xmlIsBlankNode(cur))
4599 break;
4600 ret = 0;
4601 goto fail;
4602 case XML_CDATA_SECTION_NODE:
4603 TODO
4604 ret = 0;
4605 goto fail;
4606 case XML_ELEMENT_NODE:
4607 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
4608 xmlChar *QName;
4609 int len;
4610
4611 len = xmlStrlen(cur->name) +
4612 xmlStrlen(cur->ns->prefix) + 2;
4613 QName = xmlMalloc(len);
4614 if (QName == NULL) {
4615 ret = -1;
4616 goto fail;
4617 }
4618 snprintf((char *) QName, len, "%s:%s",
4619 (char *)cur->ns->prefix,
4620 (char *)cur->name);
4621 ret = xmlRegExecPushString(exec, QName, NULL);
4622 xmlFree(QName);
4623 } else {
4624 ret = xmlRegExecPushString(exec, cur->name, NULL);
4625 }
4626 break;
4627 default:
4628 break;
4629 }
4630 /*
4631 * Switch to next element
4632 */
4633 cur = cur->next;
4634 while (cur == NULL) {
4635 cur = nodeVPop(ctxt);
4636 if (cur == NULL)
4637 break;
4638 cur = cur->next;
4639 }
4640 }
4641 ret = xmlRegExecPushString(exec, NULL, NULL);
4642fail:
4643 xmlRegFreeExecCtxt(exec);
4644 }
4645 }
4646#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004647 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004648 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004649 */
4650 ctxt->vstateMax = 8;
4651 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4652 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4653 if (ctxt->vstateTab == NULL) {
4654 xmlGenericError(xmlGenericErrorContext,
4655 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004656 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004657 }
4658 /*
4659 * The first entry in the stack is reserved to the current state
4660 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004661 ctxt->nodeMax = 0;
4662 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004663 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004664 ctxt->vstate = &ctxt->vstateTab[0];
4665 ctxt->vstateNr = 1;
4666 CONT = cont;
4667 NODE = child;
4668 DEPTH = 0;
4669 OCCURS = 0;
4670 STATE = 0;
4671 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004672 if ((ret == -3) && (warn)) {
4673 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004674 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004675 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004676 /*
4677 * An entities reference appeared at this level.
4678 * Buid a minimal representation of this node content
4679 * sufficient to run the validation process on it
4680 */
4681 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004682 cur = child;
4683 while (cur != NULL) {
4684 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004685 case XML_ENTITY_REF_NODE:
4686 /*
4687 * Push the current node to be able to roll back
4688 * and process within the entity
4689 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004690 if ((cur->children != NULL) &&
4691 (cur->children->children != NULL)) {
4692 nodeVPush(ctxt, cur);
4693 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004694 continue;
4695 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004696 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004697 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004698 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004699 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004700 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004701 case XML_CDATA_SECTION_NODE:
4702 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004703 case XML_ELEMENT_NODE:
4704 /*
4705 * Allocate a new node and minimally fills in
4706 * what's required
4707 */
4708 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4709 if (tmp == NULL) {
4710 xmlGenericError(xmlGenericErrorContext,
4711 "xmlValidateElementContent : malloc failed\n");
4712 xmlFreeNodeList(repl);
4713 ret = -1;
4714 goto done;
4715 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004716 tmp->type = cur->type;
4717 tmp->name = cur->name;
4718 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004719 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004720 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004721 if (repl == NULL)
4722 repl = last = tmp;
4723 else {
4724 last->next = tmp;
4725 last = tmp;
4726 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004727 if (cur->type == XML_CDATA_SECTION_NODE) {
4728 /*
4729 * E59 spaces in CDATA does not match the
4730 * nonterminal S
4731 */
4732 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4733 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004734 break;
4735 default:
4736 break;
4737 }
4738 /*
4739 * Switch to next element
4740 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004741 cur = cur->next;
4742 while (cur == NULL) {
4743 cur = nodeVPop(ctxt);
4744 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004745 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004746 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004747 }
4748 }
4749
4750 /*
4751 * Relaunch the validation
4752 */
4753 ctxt->vstate = &ctxt->vstateTab[0];
4754 ctxt->vstateNr = 1;
4755 CONT = cont;
4756 NODE = repl;
4757 DEPTH = 0;
4758 OCCURS = 0;
4759 STATE = 0;
4760 ret = xmlValidateElementType(ctxt);
4761 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004762#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004763 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004764 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4765 char expr[5000];
4766 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004767
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004768 expr[0] = 0;
4769 xmlSnprintfElementContent(expr, 5000, cont, 1);
4770 list[0] = 0;
Daniel Veillard01992e02002-10-09 10:20:30 +00004771#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004772 if (repl != NULL)
4773 xmlSnprintfElements(list, 5000, repl, 1);
4774 else
Daniel Veillard01992e02002-10-09 10:20:30 +00004775#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004776 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004777
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004778 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004779 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004780 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004781 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004782 name, expr, list);
4783 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004784 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004785 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004786 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004787 expr, list);
4788 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004789 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004790 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004791 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004792 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004793 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004794 name);
4795 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004796 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004797 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004798 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004799 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004800 }
4801 ret = 0;
4802 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004803 if (ret == -3)
4804 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004805
Daniel Veillard23e73572002-09-19 19:56:43 +00004806#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004807done:
4808 /*
4809 * Deallocate the copy if done, and free up the validation stack
4810 */
4811 while (repl != NULL) {
4812 tmp = repl->next;
4813 xmlFree(repl);
4814 repl = tmp;
4815 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004816 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004817 if (ctxt->vstateTab != NULL) {
4818 xmlFree(ctxt->vstateTab);
4819 ctxt->vstateTab = NULL;
4820 }
Daniel Veillard01992e02002-10-09 10:20:30 +00004821#endif
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004822 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004823 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004824 if (ctxt->nodeTab != NULL) {
4825 xmlFree(ctxt->nodeTab);
4826 ctxt->nodeTab = NULL;
4827 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004828 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004829
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004830}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004831
Owen Taylor3473f882001-02-23 17:55:21 +00004832/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004833 * xmlValidateCdataElement:
4834 * @ctxt: the validation context
4835 * @doc: a document instance
4836 * @elem: an element instance
4837 *
4838 * Check that an element follows #CDATA
4839 *
4840 * returns 1 if valid or 0 otherwise
4841 */
4842static int
4843xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4844 xmlNodePtr elem) {
4845 int ret = 1;
4846 xmlNodePtr cur, child;
4847
Daniel Veillardceb09b92002-10-04 11:46:37 +00004848 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004849 return(0);
4850
4851 child = elem->children;
4852
4853 cur = child;
4854 while (cur != NULL) {
4855 switch (cur->type) {
4856 case XML_ENTITY_REF_NODE:
4857 /*
4858 * Push the current node to be able to roll back
4859 * and process within the entity
4860 */
4861 if ((cur->children != NULL) &&
4862 (cur->children->children != NULL)) {
4863 nodeVPush(ctxt, cur);
4864 cur = cur->children->children;
4865 continue;
4866 }
4867 break;
4868 case XML_COMMENT_NODE:
4869 case XML_PI_NODE:
4870 case XML_TEXT_NODE:
4871 case XML_CDATA_SECTION_NODE:
4872 break;
4873 default:
4874 ret = 0;
4875 goto done;
4876 }
4877 /*
4878 * Switch to next element
4879 */
4880 cur = cur->next;
4881 while (cur == NULL) {
4882 cur = nodeVPop(ctxt);
4883 if (cur == NULL)
4884 break;
4885 cur = cur->next;
4886 }
4887 }
4888done:
4889 ctxt->nodeMax = 0;
4890 ctxt->nodeNr = 0;
4891 if (ctxt->nodeTab != NULL) {
4892 xmlFree(ctxt->nodeTab);
4893 ctxt->nodeTab = NULL;
4894 }
4895 return(ret);
4896}
4897
4898/**
Owen Taylor3473f882001-02-23 17:55:21 +00004899 * xmlValidateOneElement:
4900 * @ctxt: the validation context
4901 * @doc: a document instance
4902 * @elem: an element instance
4903 *
4904 * Try to validate a single element and it's attributes,
4905 * basically it does the following checks as described by the
4906 * XML-1.0 recommendation:
4907 * - [ VC: Element Valid ]
4908 * - [ VC: Required Attribute ]
4909 * Then call xmlValidateOneAttribute() for each attribute present.
4910 *
4911 * The ID/IDREF checkings are done separately
4912 *
4913 * returns 1 if valid or 0 otherwise
4914 */
4915
4916int
4917xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4918 xmlNodePtr elem) {
4919 xmlElementPtr elemDecl = NULL;
4920 xmlElementContentPtr cont;
4921 xmlAttributePtr attr;
4922 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004923 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004924 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004925 const xmlChar *prefix = NULL;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004926 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004927
4928 CHECK_DTD;
4929
4930 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004931 switch (elem->type) {
4932 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004933 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004934 VERROR(ctxt->userData,
4935 "Attribute element not expected here\n");
4936 return(0);
4937 case XML_TEXT_NODE:
4938 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004939 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004940 VERROR(ctxt->userData, "Text element has childs !\n");
4941 return(0);
4942 }
4943 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004944 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004945 VERROR(ctxt->userData, "Text element has attributes !\n");
4946 return(0);
4947 }
4948 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004949 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004950 VERROR(ctxt->userData, "Text element has namespace !\n");
4951 return(0);
4952 }
4953 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004954 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004955 VERROR(ctxt->userData,
4956 "Text element carries namespace definitions !\n");
4957 return(0);
4958 }
4959 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004960 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004961 VERROR(ctxt->userData,
4962 "Text element has no content !\n");
4963 return(0);
4964 }
4965 return(1);
4966 case XML_XINCLUDE_START:
4967 case XML_XINCLUDE_END:
4968 return(1);
4969 case XML_CDATA_SECTION_NODE:
4970 case XML_ENTITY_REF_NODE:
4971 case XML_PI_NODE:
4972 case XML_COMMENT_NODE:
4973 return(1);
4974 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004975 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004976 VERROR(ctxt->userData,
4977 "Entity element not expected here\n");
4978 return(0);
4979 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004980 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004981 VERROR(ctxt->userData,
4982 "Notation element not expected here\n");
4983 return(0);
4984 case XML_DOCUMENT_NODE:
4985 case XML_DOCUMENT_TYPE_NODE:
4986 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004987 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004988 VERROR(ctxt->userData,
4989 "Document element not expected here\n");
4990 return(0);
4991 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004992 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004993 VERROR(ctxt->userData,
4994 "\n");
4995 return(0);
4996 case XML_ELEMENT_NODE:
4997 break;
4998 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004999 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005000 VERROR(ctxt->userData,
5001 "unknown element type %d\n", elem->type);
5002 return(0);
5003 }
5004 if (elem->name == NULL) return(0);
5005
5006 /*
5007 * Fetch the declaration for the qualified name
5008 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005009 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5010 prefix = elem->ns->prefix;
5011
5012 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00005013 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005014 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005015 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005016 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005017 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005018 if (elemDecl != NULL)
5019 extsubset = 1;
5020 }
Owen Taylor3473f882001-02-23 17:55:21 +00005021 }
5022
5023 /*
5024 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005025 * This is "non-strict" validation should be done on the
5026 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00005027 */
5028 if (elemDecl == NULL) {
5029 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005030 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005031 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005032 if (elemDecl != NULL)
5033 extsubset = 1;
5034 }
Owen Taylor3473f882001-02-23 17:55:21 +00005035 }
5036 if (elemDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005037 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005038 VERROR(ctxt->userData, "No declaration for element %s\n",
5039 elem->name);
5040 return(0);
5041 }
5042
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005043 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005044 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005045 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005046 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005047 VERROR(ctxt->userData, "No declaration for element %s\n",
5048 elem->name);
5049 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005050 case XML_ELEMENT_TYPE_EMPTY:
5051 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005052 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005053 VERROR(ctxt->userData,
5054 "Element %s was declared EMPTY this one has content\n",
5055 elem->name);
5056 ret = 0;
5057 }
5058 break;
5059 case XML_ELEMENT_TYPE_ANY:
5060 /* I don't think anything is required then */
5061 break;
5062 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005063
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005064 /* simple case of declared as #PCDATA */
5065 if ((elemDecl->content != NULL) &&
5066 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5067 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5068 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005069 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005070 VERROR(ctxt->userData,
5071 "Element %s was declared #PCDATA but contains non text nodes\n",
5072 elem->name);
5073 }
5074 break;
5075 }
Owen Taylor3473f882001-02-23 17:55:21 +00005076 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005077 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005078 while (child != NULL) {
5079 if (child->type == XML_ELEMENT_NODE) {
5080 name = child->name;
5081 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
5082 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005083 snprintf((char *) qname, sizeof(qname), "%s:%s",
5084 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005085 qname[sizeof(qname) - 1] = 0;
5086 cont = elemDecl->content;
5087 while (cont != NULL) {
5088 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5089 if (xmlStrEqual(cont->name, qname)) break;
5090 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5091 (cont->c1 != NULL) &&
5092 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5093 if (xmlStrEqual(cont->c1->name, qname)) break;
5094 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5095 (cont->c1 == NULL) ||
5096 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5097 /* Internal error !!! */
5098 xmlGenericError(xmlGenericErrorContext,
5099 "Internal: MIXED struct bad\n");
5100 break;
5101 }
5102 cont = cont->c2;
5103 }
5104 if (cont != NULL)
5105 goto child_ok;
5106 }
5107 cont = elemDecl->content;
5108 while (cont != NULL) {
5109 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5110 if (xmlStrEqual(cont->name, name)) break;
5111 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5112 (cont->c1 != NULL) &&
5113 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5114 if (xmlStrEqual(cont->c1->name, name)) break;
5115 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5116 (cont->c1 == NULL) ||
5117 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5118 /* Internal error !!! */
5119 xmlGenericError(xmlGenericErrorContext,
5120 "Internal: MIXED struct bad\n");
5121 break;
5122 }
5123 cont = cont->c2;
5124 }
5125 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005126 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005127 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005128 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005129 name, elem->name);
5130 ret = 0;
5131 }
5132 }
5133child_ok:
5134 child = child->next;
5135 }
5136 break;
5137 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005138 if ((doc->standalone == 1) && (extsubset == 1)) {
5139 /*
5140 * VC: Standalone Document Declaration
5141 * - element types with element content, if white space
5142 * occurs directly within any instance of those types.
5143 */
5144 child = elem->children;
5145 while (child != NULL) {
5146 if (child->type == XML_TEXT_NODE) {
5147 const xmlChar *content = child->content;
5148
5149 while (IS_BLANK(*content))
5150 content++;
5151 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005152 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005153 VERROR(ctxt->userData,
5154"standalone: %s declared in the external subset contains white spaces nodes\n",
5155 elem->name);
5156 ret = 0;
5157 break;
5158 }
5159 }
5160 child =child->next;
5161 }
5162 }
Owen Taylor3473f882001-02-23 17:55:21 +00005163 child = elem->children;
5164 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005165 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005166 if (tmp <= 0)
5167 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005168 break;
5169 }
5170
5171 /* [ VC: Required Attribute ] */
5172 attr = elemDecl->attributes;
5173 while (attr != NULL) {
5174 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005175 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005176
Daniel Veillarde4301c82002-02-13 13:32:35 +00005177 if ((attr->prefix == NULL) &&
5178 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5179 xmlNsPtr ns;
5180
5181 ns = elem->nsDef;
5182 while (ns != NULL) {
5183 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005184 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005185 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005186 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005187 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5188 xmlNsPtr ns;
5189
5190 ns = elem->nsDef;
5191 while (ns != NULL) {
5192 if (xmlStrEqual(attr->name, ns->prefix))
5193 goto found;
5194 ns = ns->next;
5195 }
5196 } else {
5197 xmlAttrPtr attrib;
5198
5199 attrib = elem->properties;
5200 while (attrib != NULL) {
5201 if (xmlStrEqual(attrib->name, attr->name)) {
5202 if (attr->prefix != NULL) {
5203 xmlNsPtr nameSpace = attrib->ns;
5204
5205 if (nameSpace == NULL)
5206 nameSpace = elem->ns;
5207 /*
5208 * qualified names handling is problematic, having a
5209 * different prefix should be possible but DTDs don't
5210 * allow to define the URI instead of the prefix :-(
5211 */
5212 if (nameSpace == NULL) {
5213 if (qualified < 0)
5214 qualified = 0;
5215 } else if (!xmlStrEqual(nameSpace->prefix,
5216 attr->prefix)) {
5217 if (qualified < 1)
5218 qualified = 1;
5219 } else
5220 goto found;
5221 } else {
5222 /*
5223 * We should allow applications to define namespaces
5224 * for their application even if the DTD doesn't
5225 * carry one, otherwise, basically we would always
5226 * break.
5227 */
5228 goto found;
5229 }
5230 }
5231 attrib = attrib->next;
5232 }
Owen Taylor3473f882001-02-23 17:55:21 +00005233 }
5234 if (qualified == -1) {
5235 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005236 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005237 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005238 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005239 elem->name, attr->name);
5240 ret = 0;
5241 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005242 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005243 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005244 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005245 elem->name, attr->prefix,attr->name);
5246 ret = 0;
5247 }
5248 } else if (qualified == 0) {
5249 VWARNING(ctxt->userData,
5250 "Element %s required attribute %s:%s has no prefix\n",
5251 elem->name, attr->prefix,attr->name);
5252 } else if (qualified == 1) {
5253 VWARNING(ctxt->userData,
5254 "Element %s required attribute %s:%s has different prefix\n",
5255 elem->name, attr->prefix,attr->name);
5256 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005257 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5258 /*
5259 * Special tests checking #FIXED namespace declarations
5260 * have the right value since this is not done as an
5261 * attribute checking
5262 */
5263 if ((attr->prefix == NULL) &&
5264 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5265 xmlNsPtr ns;
5266
5267 ns = elem->nsDef;
5268 while (ns != NULL) {
5269 if (ns->prefix == NULL) {
5270 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005271 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005272 VERROR(ctxt->userData,
5273 "Element %s namespace name for default namespace does not match the DTD\n",
5274 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005275 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005276 }
5277 goto found;
5278 }
5279 ns = ns->next;
5280 }
5281 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5282 xmlNsPtr ns;
5283
5284 ns = elem->nsDef;
5285 while (ns != NULL) {
5286 if (xmlStrEqual(attr->name, ns->prefix)) {
5287 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005288 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005289 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005290 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005291 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005292 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005293 }
5294 goto found;
5295 }
5296 ns = ns->next;
5297 }
5298 }
Owen Taylor3473f882001-02-23 17:55:21 +00005299 }
5300found:
5301 attr = attr->nexth;
5302 }
5303 return(ret);
5304}
5305
5306/**
5307 * xmlValidateRoot:
5308 * @ctxt: the validation context
5309 * @doc: a document instance
5310 *
5311 * Try to validate a the root element
5312 * basically it does the following check as described by the
5313 * XML-1.0 recommendation:
5314 * - [ VC: Root Element Type ]
5315 * it doesn't try to recurse or apply other check to the element
5316 *
5317 * returns 1 if valid or 0 otherwise
5318 */
5319
5320int
5321xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5322 xmlNodePtr root;
5323 if (doc == NULL) return(0);
5324
5325 root = xmlDocGetRootElement(doc);
5326 if ((root == NULL) || (root->name == NULL)) {
5327 VERROR(ctxt->userData, "Not valid: no root element\n");
5328 return(0);
5329 }
5330
5331 /*
5332 * When doing post validation against a separate DTD, those may
5333 * no internal subset has been generated
5334 */
5335 if ((doc->intSubset != NULL) &&
5336 (doc->intSubset->name != NULL)) {
5337 /*
5338 * Check first the document root against the NQName
5339 */
5340 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5341 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
5342 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005343 snprintf((char *) qname, sizeof(qname), "%s:%s",
5344 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005345 qname[sizeof(qname) - 1] = 0;
5346 if (xmlStrEqual(doc->intSubset->name, qname))
5347 goto name_ok;
5348 }
5349 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5350 (xmlStrEqual(root->name, BAD_CAST "html")))
5351 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005352 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005353 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005354 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005355 root->name, doc->intSubset->name);
5356 return(0);
5357
5358 }
5359 }
5360name_ok:
5361 return(1);
5362}
5363
5364
5365/**
5366 * xmlValidateElement:
5367 * @ctxt: the validation context
5368 * @doc: a document instance
5369 * @elem: an element instance
5370 *
5371 * Try to validate the subtree under an element
5372 *
5373 * returns 1 if valid or 0 otherwise
5374 */
5375
5376int
5377xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5378 xmlNodePtr child;
5379 xmlAttrPtr attr;
5380 xmlChar *value;
5381 int ret = 1;
5382
5383 if (elem == NULL) return(0);
5384
5385 /*
5386 * XInclude elements were added after parsing in the infoset,
5387 * they don't really mean anything validation wise.
5388 */
5389 if ((elem->type == XML_XINCLUDE_START) ||
5390 (elem->type == XML_XINCLUDE_END))
5391 return(1);
5392
5393 CHECK_DTD;
5394
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005395 /*
5396 * Entities references have to be handled separately
5397 */
5398 if (elem->type == XML_ENTITY_REF_NODE) {
5399 return(1);
5400 }
5401
Owen Taylor3473f882001-02-23 17:55:21 +00005402 ret &= xmlValidateOneElement(ctxt, doc, elem);
5403 attr = elem->properties;
5404 while(attr != NULL) {
5405 value = xmlNodeListGetString(doc, attr->children, 0);
5406 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5407 if (value != NULL)
5408 xmlFree(value);
5409 attr= attr->next;
5410 }
5411 child = elem->children;
5412 while (child != NULL) {
5413 ret &= xmlValidateElement(ctxt, doc, child);
5414 child = child->next;
5415 }
5416
5417 return(ret);
5418}
5419
Daniel Veillard8730c562001-02-26 10:49:57 +00005420/**
5421 * xmlValidateRef:
5422 * @ref: A reference to be validated
5423 * @ctxt: Validation context
5424 * @name: Name of ID we are searching for
5425 *
5426 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005427static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005428xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005429 const xmlChar *name) {
5430 xmlAttrPtr id;
5431 xmlAttrPtr attr;
5432
5433 if (ref == NULL)
5434 return;
5435 attr = ref->attr;
5436 if (attr == NULL)
5437 return;
5438 if (attr->atype == XML_ATTRIBUTE_IDREF) {
5439 id = xmlGetID(ctxt->doc, name);
5440 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005441 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005442 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005443 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005444 attr->name, name);
5445 ctxt->valid = 0;
5446 }
5447 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5448 xmlChar *dup, *str = NULL, *cur, save;
5449
5450 dup = xmlStrdup(name);
5451 if (dup == NULL) {
5452 ctxt->valid = 0;
5453 return;
5454 }
5455 cur = dup;
5456 while (*cur != 0) {
5457 str = cur;
5458 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5459 save = *cur;
5460 *cur = 0;
5461 id = xmlGetID(ctxt->doc, str);
5462 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005463 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005464 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005465 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005466 attr->name, str);
5467 ctxt->valid = 0;
5468 }
5469 if (save == 0)
5470 break;
5471 *cur = save;
5472 while (IS_BLANK(*cur)) cur++;
5473 }
5474 xmlFree(dup);
5475 }
5476}
5477
5478/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005479 * xmlWalkValidateList:
5480 * @data: Contents of current link
5481 * @user: Value supplied by the user
5482 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005483 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005484 */
5485static int
5486xmlWalkValidateList(const void *data, const void *user)
5487{
5488 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5489 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5490 return 1;
5491}
5492
5493/**
5494 * xmlValidateCheckRefCallback:
5495 * @ref_list: List of references
5496 * @ctxt: Validation context
5497 * @name: Name of ID we are searching for
5498 *
5499 */
5500static void
5501xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5502 const xmlChar *name) {
5503 xmlValidateMemo memo;
5504
5505 if (ref_list == NULL)
5506 return;
5507 memo.ctxt = ctxt;
5508 memo.name = name;
5509
5510 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5511
5512}
5513
5514/**
Owen Taylor3473f882001-02-23 17:55:21 +00005515 * xmlValidateDocumentFinal:
5516 * @ctxt: the validation context
5517 * @doc: a document instance
5518 *
5519 * Does the final step for the document validation once all the
5520 * incremental validation steps have been completed
5521 *
5522 * basically it does the following checks described by the XML Rec
5523 *
5524 *
5525 * returns 1 if valid or 0 otherwise
5526 */
5527
5528int
5529xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5530 xmlRefTablePtr table;
5531
5532 if (doc == NULL) {
5533 xmlGenericError(xmlGenericErrorContext,
5534 "xmlValidateDocumentFinal: doc == NULL\n");
5535 return(0);
5536 }
5537
5538 /*
5539 * Check all the NOTATION/NOTATIONS attributes
5540 */
5541 /*
5542 * Check all the ENTITY/ENTITIES attributes definition for validity
5543 */
5544 /*
5545 * Check all the IDREF/IDREFS attributes definition for validity
5546 */
5547 table = (xmlRefTablePtr) doc->refs;
5548 ctxt->doc = doc;
5549 ctxt->valid = 1;
5550 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
5551 return(ctxt->valid);
5552}
5553
5554/**
5555 * xmlValidateDtd:
5556 * @ctxt: the validation context
5557 * @doc: a document instance
5558 * @dtd: a dtd instance
5559 *
5560 * Try to validate the document against the dtd instance
5561 *
5562 * basically it does check all the definitions in the DtD.
5563 *
5564 * returns 1 if valid or 0 otherwise
5565 */
5566
5567int
5568xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
5569 int ret;
5570 xmlDtdPtr oldExt;
5571 xmlNodePtr root;
5572
5573 if (dtd == NULL) return(0);
5574 if (doc == NULL) return(0);
5575 oldExt = doc->extSubset;
5576 doc->extSubset = dtd;
5577 ret = xmlValidateRoot(ctxt, doc);
5578 if (ret == 0) {
5579 doc->extSubset = oldExt;
5580 return(ret);
5581 }
5582 if (doc->ids != NULL) {
5583 xmlFreeIDTable(doc->ids);
5584 doc->ids = NULL;
5585 }
5586 if (doc->refs != NULL) {
5587 xmlFreeRefTable(doc->refs);
5588 doc->refs = NULL;
5589 }
5590 root = xmlDocGetRootElement(doc);
5591 ret = xmlValidateElement(ctxt, doc, root);
5592 ret &= xmlValidateDocumentFinal(ctxt, doc);
5593 doc->extSubset = oldExt;
5594 return(ret);
5595}
5596
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005597static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005598xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
5599 const xmlChar *name ATTRIBUTE_UNUSED) {
5600 if (cur == NULL)
5601 return;
5602 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
5603 xmlChar *notation = cur->content;
5604
Daniel Veillard878eab02002-02-19 13:46:09 +00005605 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005606 int ret;
5607
5608 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
5609 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00005610 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005611 }
5612 }
5613 }
5614}
5615
5616static void
Owen Taylor3473f882001-02-23 17:55:21 +00005617xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00005618 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005619 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00005620 xmlDocPtr doc;
5621 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005622
Owen Taylor3473f882001-02-23 17:55:21 +00005623 if (cur == NULL)
5624 return;
5625 switch (cur->atype) {
5626 case XML_ATTRIBUTE_CDATA:
5627 case XML_ATTRIBUTE_ID:
5628 case XML_ATTRIBUTE_IDREF :
5629 case XML_ATTRIBUTE_IDREFS:
5630 case XML_ATTRIBUTE_NMTOKEN:
5631 case XML_ATTRIBUTE_NMTOKENS:
5632 case XML_ATTRIBUTE_ENUMERATION:
5633 break;
5634 case XML_ATTRIBUTE_ENTITY:
5635 case XML_ATTRIBUTE_ENTITIES:
5636 case XML_ATTRIBUTE_NOTATION:
5637 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005638
5639 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
5640 cur->atype, cur->defaultValue);
5641 if ((ret == 0) && (ctxt->valid == 1))
5642 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005643 }
5644 if (cur->tree != NULL) {
5645 xmlEnumerationPtr tree = cur->tree;
5646 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005647 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00005648 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005649 if ((ret == 0) && (ctxt->valid == 1))
5650 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005651 tree = tree->next;
5652 }
5653 }
5654 }
Daniel Veillard878eab02002-02-19 13:46:09 +00005655 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
5656 doc = cur->doc;
5657 if ((doc == NULL) || (cur->elem == NULL)) {
5658 VERROR(ctxt->userData,
5659 "xmlValidateAttributeCallback(%s): internal error\n",
5660 cur->name);
5661 return;
5662 }
5663 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
5664 if (elem == NULL)
5665 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
5666 if (elem == NULL) {
5667 VERROR(ctxt->userData,
5668 "attribute %s: could not find decl for element %s\n",
5669 cur->name, cur->elem);
5670 return;
5671 }
5672 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
5673 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005674 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00005675 cur->name, cur->elem);
5676 ctxt->valid = 0;
5677 }
5678 }
Owen Taylor3473f882001-02-23 17:55:21 +00005679}
5680
5681/**
5682 * xmlValidateDtdFinal:
5683 * @ctxt: the validation context
5684 * @doc: a document instance
5685 *
5686 * Does the final step for the dtds validation once all the
5687 * subsets have been parsed
5688 *
5689 * basically it does the following checks described by the XML Rec
5690 * - check that ENTITY and ENTITIES type attributes default or
5691 * possible values matches one of the defined entities.
5692 * - check that NOTATION type attributes default or
5693 * possible values matches one of the defined notations.
5694 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005695 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00005696 */
5697
5698int
5699xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00005700 xmlDtdPtr dtd;
5701 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005702 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00005703
5704 if (doc == NULL) return(0);
5705 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5706 return(0);
5707 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005708 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005709 dtd = doc->intSubset;
5710 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5711 table = (xmlAttributeTablePtr) dtd->attributes;
5712 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005713 }
5714 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005715 entities = (xmlEntitiesTablePtr) dtd->entities;
5716 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5717 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005718 }
5719 dtd = doc->extSubset;
5720 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5721 table = (xmlAttributeTablePtr) dtd->attributes;
5722 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005723 }
5724 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005725 entities = (xmlEntitiesTablePtr) dtd->entities;
5726 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5727 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005728 }
5729 return(ctxt->valid);
5730}
5731
5732/**
5733 * xmlValidateDocument:
5734 * @ctxt: the validation context
5735 * @doc: a document instance
5736 *
5737 * Try to validate the document instance
5738 *
5739 * basically it does the all the checks described by the XML Rec
5740 * i.e. validates the internal and external subset (if present)
5741 * and validate the document tree.
5742 *
5743 * returns 1 if valid or 0 otherwise
5744 */
5745
5746int
5747xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5748 int ret;
5749 xmlNodePtr root;
5750
Daniel Veillard2fd85422002-10-16 14:32:41 +00005751 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
5752 VERROR(ctxt->userData, "no DTD found!\n" );
Owen Taylor3473f882001-02-23 17:55:21 +00005753 return(0);
Daniel Veillard2fd85422002-10-16 14:32:41 +00005754 }
Owen Taylor3473f882001-02-23 17:55:21 +00005755 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
5756 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
5757 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
5758 doc->intSubset->SystemID);
5759 if (doc->extSubset == NULL) {
5760 if (doc->intSubset->SystemID != NULL) {
5761 VERROR(ctxt->userData,
5762 "Could not load the external subset \"%s\"\n",
5763 doc->intSubset->SystemID);
5764 } else {
5765 VERROR(ctxt->userData,
5766 "Could not load the external subset \"%s\"\n",
5767 doc->intSubset->ExternalID);
5768 }
5769 return(0);
5770 }
5771 }
5772
5773 if (doc->ids != NULL) {
5774 xmlFreeIDTable(doc->ids);
5775 doc->ids = NULL;
5776 }
5777 if (doc->refs != NULL) {
5778 xmlFreeRefTable(doc->refs);
5779 doc->refs = NULL;
5780 }
5781 ret = xmlValidateDtdFinal(ctxt, doc);
5782 if (!xmlValidateRoot(ctxt, doc)) return(0);
5783
5784 root = xmlDocGetRootElement(doc);
5785 ret &= xmlValidateElement(ctxt, doc, root);
5786 ret &= xmlValidateDocumentFinal(ctxt, doc);
5787 return(ret);
5788}
5789
5790
5791/************************************************************************
5792 * *
5793 * Routines for dynamic validation editing *
5794 * *
5795 ************************************************************************/
5796
5797/**
5798 * xmlValidGetPotentialChildren:
5799 * @ctree: an element content tree
5800 * @list: an array to store the list of child names
5801 * @len: a pointer to the number of element in the list
5802 * @max: the size of the array
5803 *
5804 * Build/extend a list of potential children allowed by the content tree
5805 *
5806 * returns the number of element in the list, or -1 in case of error.
5807 */
5808
5809int
5810xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
5811 int *len, int max) {
5812 int i;
5813
5814 if ((ctree == NULL) || (list == NULL) || (len == NULL))
5815 return(-1);
5816 if (*len >= max) return(*len);
5817
5818 switch (ctree->type) {
5819 case XML_ELEMENT_CONTENT_PCDATA:
5820 for (i = 0; i < *len;i++)
5821 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
5822 list[(*len)++] = BAD_CAST "#PCDATA";
5823 break;
5824 case XML_ELEMENT_CONTENT_ELEMENT:
5825 for (i = 0; i < *len;i++)
5826 if (xmlStrEqual(ctree->name, list[i])) return(*len);
5827 list[(*len)++] = ctree->name;
5828 break;
5829 case XML_ELEMENT_CONTENT_SEQ:
5830 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5831 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5832 break;
5833 case XML_ELEMENT_CONTENT_OR:
5834 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5835 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5836 break;
5837 }
5838
5839 return(*len);
5840}
5841
5842/**
5843 * xmlValidGetValidElements:
5844 * @prev: an element to insert after
5845 * @next: an element to insert next
5846 * @list: an array to store the list of child names
5847 * @max: the size of the array
5848 *
5849 * This function returns the list of authorized children to insert
5850 * within an existing tree while respecting the validity constraints
5851 * forced by the Dtd. The insertion point is defined using @prev and
5852 * @next in the following ways:
5853 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
5854 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
5855 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
5856 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
5857 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
5858 *
5859 * pointers to the element names are inserted at the beginning of the array
5860 * and do not need to be freed.
5861 *
5862 * returns the number of element in the list, or -1 in case of error. If
5863 * the function returns the value @max the caller is invited to grow the
5864 * receiving array and retry.
5865 */
5866
5867int
5868xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
5869 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005870 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005871 int nb_valid_elements = 0;
5872 const xmlChar *elements[256];
5873 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005874 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005875
5876 xmlNode *ref_node;
5877 xmlNode *parent;
5878 xmlNode *test_node;
5879
5880 xmlNode *prev_next;
5881 xmlNode *next_prev;
5882 xmlNode *parent_childs;
5883 xmlNode *parent_last;
5884
5885 xmlElement *element_desc;
5886
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00005887 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005888
Owen Taylor3473f882001-02-23 17:55:21 +00005889 if (prev == NULL && next == NULL)
5890 return(-1);
5891
5892 if (list == NULL) return(-1);
5893 if (max <= 0) return(-1);
5894
5895 nb_valid_elements = 0;
5896 ref_node = prev ? prev : next;
5897 parent = ref_node->parent;
5898
5899 /*
5900 * Retrieves the parent element declaration
5901 */
5902 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5903 parent->name);
5904 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5905 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5906 parent->name);
5907 if (element_desc == NULL) return(-1);
5908
5909 /*
5910 * Do a backup of the current tree structure
5911 */
5912 prev_next = prev ? prev->next : NULL;
5913 next_prev = next ? next->prev : NULL;
5914 parent_childs = parent->children;
5915 parent_last = parent->last;
5916
5917 /*
5918 * Creates a dummy node and insert it into the tree
5919 */
5920 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5921 test_node->doc = ref_node->doc;
5922 test_node->parent = parent;
5923 test_node->prev = prev;
5924 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005925 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005926
5927 if (prev) prev->next = test_node;
5928 else parent->children = test_node;
5929
5930 if (next) next->prev = test_node;
5931 else parent->last = test_node;
5932
5933 /*
5934 * Insert each potential child node and check if the parent is
5935 * still valid
5936 */
5937 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5938 elements, &nb_elements, 256);
5939
5940 for (i = 0;i < nb_elements;i++) {
5941 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005942 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005943 int j;
5944
5945 for (j = 0; j < nb_valid_elements;j++)
5946 if (xmlStrEqual(elements[i], list[j])) break;
5947 list[nb_valid_elements++] = elements[i];
5948 if (nb_valid_elements >= max) break;
5949 }
5950 }
5951
5952 /*
5953 * Restore the tree structure
5954 */
5955 if (prev) prev->next = prev_next;
5956 if (next) next->prev = next_prev;
5957 parent->children = parent_childs;
5958 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005959
5960 /*
5961 * Free up the dummy node
5962 */
5963 test_node->name = name;
5964 xmlFreeNode(test_node);
5965
Owen Taylor3473f882001-02-23 17:55:21 +00005966 return(nb_valid_elements);
5967}