blob: 196ea2b96e984bfe3a88b61405fd346edc8e4040 [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/*
36 * Generic function for accessing stacks in the Validity Context
37 */
38
39#define PUSH_AND_POP(scope, type, name) \
40scope int name##VPush(xmlValidCtxtPtr ctxt, type value) { \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000041 if (ctxt->name##Max <= 0) { \
42 ctxt->name##Max = 4; \
43 ctxt->name##Tab = (type *) xmlMalloc( \
44 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
45 if (ctxt->name##Tab == NULL) { \
46 xmlGenericError(xmlGenericErrorContext, \
47 "malloc failed !\n"); \
Daniel Veillarda9142e72001-06-19 11:07:54 +000048 ctxt->name##Max = 0; \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000049 return(0); \
50 } \
51 } \
Owen Taylor3473f882001-02-23 17:55:21 +000052 if (ctxt->name##Nr >= ctxt->name##Max) { \
53 ctxt->name##Max *= 2; \
54 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
55 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
56 if (ctxt->name##Tab == NULL) { \
57 xmlGenericError(xmlGenericErrorContext, \
58 "realloc failed !\n"); \
59 return(0); \
60 } \
61 } \
62 ctxt->name##Tab[ctxt->name##Nr] = value; \
63 ctxt->name = value; \
64 return(ctxt->name##Nr++); \
65} \
66scope type name##VPop(xmlValidCtxtPtr ctxt) { \
67 type ret; \
68 if (ctxt->name##Nr <= 0) return(0); \
69 ctxt->name##Nr--; \
70 if (ctxt->name##Nr > 0) \
71 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
72 else \
73 ctxt->name = NULL; \
74 ret = ctxt->name##Tab[ctxt->name##Nr]; \
75 ctxt->name##Tab[ctxt->name##Nr] = 0; \
76 return(ret); \
77} \
78
Daniel Veillarddab4cb32001-04-20 13:03:48 +000079/*
Daniel Veillardb44025c2001-10-11 22:55:55 +000080 * I use a home made algorithm less complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +000081 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +000082 * only restriction is on the deepness of the tree limited by the
83 * size of the occurs bitfield
84 *
85 * this is the content of a saved state for rollbacks
86 */
87
88#define ROLLBACK_OR 0
89#define ROLLBACK_PARENT 1
90
Daniel Veillardb44025c2001-10-11 22:55:55 +000091typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +000092 xmlElementContentPtr cont; /* pointer to the content model subtree */
93 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +000094 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +000095 unsigned char depth; /* current depth in the overall tree */
96 unsigned char state; /* ROLLBACK_XXX */
97} _xmlValidState;
98
Daniel Veillardfc57b412002-04-29 15:50:14 +000099#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000100#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
101#define CONT ctxt->vstate->cont
102#define NODE ctxt->vstate->node
103#define DEPTH ctxt->vstate->depth
104#define OCCURS ctxt->vstate->occurs
105#define STATE ctxt->vstate->state
106
Daniel Veillard5344c602001-12-31 16:37:34 +0000107#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
108#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000109
Daniel Veillard5344c602001-12-31 16:37:34 +0000110#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
111#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000112
Daniel Veillard118aed72002-09-24 14:13:13 +0000113#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000114static int
115vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
116 xmlNodePtr node, unsigned char depth, long occurs,
117 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000118 int i = ctxt->vstateNr - 1;
119
Daniel Veillard940492d2002-04-15 10:15:25 +0000120 if (ctxt->vstateNr > MAX_RECURSE) {
121 return(-1);
122 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000123 if (ctxt->vstateNr >= ctxt->vstateMax) {
124 ctxt->vstateMax *= 2;
125 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
126 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
127 if (ctxt->vstateTab == NULL) {
128 xmlGenericError(xmlGenericErrorContext,
129 "realloc failed !n");
Daniel Veillard940492d2002-04-15 10:15:25 +0000130 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000131 }
Daniel Veillard06803992001-04-22 10:35:56 +0000132 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000133 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000134 /*
135 * Don't push on the stack a state already here
136 */
137 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
138 (ctxt->vstateTab[i].node == node) &&
139 (ctxt->vstateTab[i].depth == depth) &&
140 (ctxt->vstateTab[i].occurs == occurs) &&
141 (ctxt->vstateTab[i].state == state))
142 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000143 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
144 ctxt->vstateTab[ctxt->vstateNr].node = node;
145 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
146 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
147 ctxt->vstateTab[ctxt->vstateNr].state = state;
148 return(ctxt->vstateNr++);
149}
150
151static int
152vstateVPop(xmlValidCtxtPtr ctxt) {
153 if (ctxt->vstateNr <= 1) return(-1);
154 ctxt->vstateNr--;
155 ctxt->vstate = &ctxt->vstateTab[0];
156 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
157 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
158 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
159 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
160 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
161 return(ctxt->vstateNr);
162}
163
Daniel Veillard118aed72002-09-24 14:13:13 +0000164#endif /* LIBXML_REGEXP_ENABLED */
165
Owen Taylor3473f882001-02-23 17:55:21 +0000166PUSH_AND_POP(static, xmlNodePtr, node)
167
Owen Taylor3473f882001-02-23 17:55:21 +0000168#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000169static void
170xmlValidPrintNode(xmlNodePtr cur) {
171 if (cur == NULL) {
172 xmlGenericError(xmlGenericErrorContext, "null");
173 return;
174 }
175 switch (cur->type) {
176 case XML_ELEMENT_NODE:
177 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
178 break;
179 case XML_TEXT_NODE:
180 xmlGenericError(xmlGenericErrorContext, "text ");
181 break;
182 case XML_CDATA_SECTION_NODE:
183 xmlGenericError(xmlGenericErrorContext, "cdata ");
184 break;
185 case XML_ENTITY_REF_NODE:
186 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
187 break;
188 case XML_PI_NODE:
189 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
190 break;
191 case XML_COMMENT_NODE:
192 xmlGenericError(xmlGenericErrorContext, "comment ");
193 break;
194 case XML_ATTRIBUTE_NODE:
195 xmlGenericError(xmlGenericErrorContext, "?attr? ");
196 break;
197 case XML_ENTITY_NODE:
198 xmlGenericError(xmlGenericErrorContext, "?ent? ");
199 break;
200 case XML_DOCUMENT_NODE:
201 xmlGenericError(xmlGenericErrorContext, "?doc? ");
202 break;
203 case XML_DOCUMENT_TYPE_NODE:
204 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
205 break;
206 case XML_DOCUMENT_FRAG_NODE:
207 xmlGenericError(xmlGenericErrorContext, "?frag? ");
208 break;
209 case XML_NOTATION_NODE:
210 xmlGenericError(xmlGenericErrorContext, "?nota? ");
211 break;
212 case XML_HTML_DOCUMENT_NODE:
213 xmlGenericError(xmlGenericErrorContext, "?html? ");
214 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000215#ifdef LIBXML_DOCB_ENABLED
216 case XML_DOCB_DOCUMENT_NODE:
217 xmlGenericError(xmlGenericErrorContext, "?docb? ");
218 break;
219#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000220 case XML_DTD_NODE:
221 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
222 break;
223 case XML_ELEMENT_DECL:
224 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
225 break;
226 case XML_ATTRIBUTE_DECL:
227 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
228 break;
229 case XML_ENTITY_DECL:
230 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
231 break;
232 case XML_NAMESPACE_DECL:
233 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
234 break;
235 case XML_XINCLUDE_START:
236 xmlGenericError(xmlGenericErrorContext, "incstart ");
237 break;
238 case XML_XINCLUDE_END:
239 xmlGenericError(xmlGenericErrorContext, "incend ");
240 break;
241 }
242}
243
244static void
245xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000246 if (cur == NULL)
247 xmlGenericError(xmlGenericErrorContext, "null ");
248 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000249 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000250 cur = cur->next;
251 }
252}
253
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000254static void
255xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000256 char expr[1000];
257
258 expr[0] = 0;
259 xmlGenericError(xmlGenericErrorContext, "valid: ");
260 xmlValidPrintNodeList(cur);
261 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000262 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000263 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
264}
265
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000266static void
267xmlValidDebugState(xmlValidStatePtr state) {
268 xmlGenericError(xmlGenericErrorContext, "(");
269 if (state->cont == NULL)
270 xmlGenericError(xmlGenericErrorContext, "null,");
271 else
272 switch (state->cont->type) {
273 case XML_ELEMENT_CONTENT_PCDATA:
274 xmlGenericError(xmlGenericErrorContext, "pcdata,");
275 break;
276 case XML_ELEMENT_CONTENT_ELEMENT:
277 xmlGenericError(xmlGenericErrorContext, "%s,",
278 state->cont->name);
279 break;
280 case XML_ELEMENT_CONTENT_SEQ:
281 xmlGenericError(xmlGenericErrorContext, "seq,");
282 break;
283 case XML_ELEMENT_CONTENT_OR:
284 xmlGenericError(xmlGenericErrorContext, "or,");
285 break;
286 }
287 xmlValidPrintNode(state->node);
288 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
289 state->depth, state->occurs, state->state);
290}
291
292static void
293xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
294 int i, j;
295
296 xmlGenericError(xmlGenericErrorContext, "state: ");
297 xmlValidDebugState(ctxt->vstate);
298 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
299 ctxt->vstateNr - 1);
300 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
301 xmlValidDebugState(&ctxt->vstateTab[j]);
302 xmlGenericError(xmlGenericErrorContext, "\n");
303}
304
305/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000306#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000307 *****/
308
309#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000310#define DEBUG_VALID_MSG(m) \
311 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
312
Owen Taylor3473f882001-02-23 17:55:21 +0000313#else
314#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000315#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000316#endif
317
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000318/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000319
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000320#define VECTXT(ctxt, node) \
321 if ((ctxt != NULL) && (ctxt->error != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000322 (node != NULL)) { \
323 xmlChar *base = xmlNodeGetBase(NULL,node); \
324 if (base != NULL) { \
325 ctxt->error(ctxt->userData, "%s:%d: ", base, \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000326 (int) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000327 xmlFree(base); \
328 } else \
329 ctxt->error(ctxt->userData, ":%d: ", \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000330 (int) node->content); \
331 }
332
333#define VWCTXT(ctxt, node) \
334 if ((ctxt != NULL) && (ctxt->warning != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000335 (node != NULL)) { \
336 xmlChar *base = xmlNodeGetBase(NULL,node); \
337 if (base != NULL) { \
338 ctxt->warning(ctxt->userData, "%s:%d: ", base, \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000339 (int) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000340 xmlFree(base); \
341 } else \
342 ctxt->warning(ctxt->userData, ":%d: ", \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000343 (int) node->content); \
344 }
345
Owen Taylor3473f882001-02-23 17:55:21 +0000346#define VERROR \
347 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
348
349#define VWARNING \
350 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
351
352#define CHECK_DTD \
353 if (doc == NULL) return(0); \
354 else if ((doc->intSubset == NULL) && \
355 (doc->extSubset == NULL)) return(0)
356
Daniel Veillarda10efa82001-04-18 13:09:01 +0000357static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
358 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000359xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
360
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000361#ifdef LIBXML_REGEXP_ENABLED
362
363/************************************************************************
364 * *
365 * Content model validation based on the regexps *
366 * *
367 ************************************************************************/
368
369/**
370 * xmlValidBuildAContentModel:
371 * @content: the content model
372 * @ctxt: the schema parser context
373 * @name: the element name whose content is being built
374 *
375 * Generate the automata sequence needed for that type
376 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000377 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000378 */
379static int
380xmlValidBuildAContentModel(xmlElementContentPtr content,
381 xmlValidCtxtPtr ctxt,
382 const xmlChar *name) {
383 if (content == NULL) {
384 VERROR(ctxt->userData,
385 "Found unexpected type = NULL in %s content model\n", name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000386 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000387 }
388 switch (content->type) {
389 case XML_ELEMENT_CONTENT_PCDATA:
390 VERROR(ctxt->userData, "ContentModel found PCDATA for element %s\n",
391 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000392 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000393 break;
394 case XML_ELEMENT_CONTENT_ELEMENT: {
395 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000396 xmlChar *QName = NULL;
397 const xmlChar *fname = content->name;
398
399 if (content->prefix != NULL) {
400 int len;
401
402 len = xmlStrlen(content->name) +
403 xmlStrlen(content->prefix) + 2;
404 QName = xmlMalloc(len);
405 if (QName == NULL) {
406 VERROR(ctxt->userData,
407 "ContentModel %s : alloc failed\n", name);
408 return(0);
409 }
410 snprintf((char *) QName, len, "%s:%s",
411 (char *)content->prefix,
412 (char *)content->name);
413 fname = QName;
414 }
415
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000416 switch (content->ocur) {
417 case XML_ELEMENT_CONTENT_ONCE:
418 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000419 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000420 break;
421 case XML_ELEMENT_CONTENT_OPT:
422 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000423 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000424 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
425 break;
426 case XML_ELEMENT_CONTENT_PLUS:
427 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000428 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000429 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000430 ctxt->state, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000431 break;
432 case XML_ELEMENT_CONTENT_MULT:
433 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000434 ctxt->state, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000435 break;
436 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000437 if (QName != NULL)
438 xmlFree(QName);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000439 break;
440 }
441 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000442 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000443 xmlElementContentOccur ocur;
444
445 /*
446 * Simply iterate over the content
447 */
448 oldstate = ctxt->state;
449 ocur = content->ocur;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000450 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000451 xmlValidBuildAContentModel(content->c1, ctxt, name);
452 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000453 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
454 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
455 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000456 oldend = ctxt->state;
457 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000458 switch (ocur) {
459 case XML_ELEMENT_CONTENT_ONCE:
460 break;
461 case XML_ELEMENT_CONTENT_OPT:
462 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
463 break;
464 case XML_ELEMENT_CONTENT_MULT:
465 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000466 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000467 break;
468 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000469 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000470 break;
471 }
472 break;
473 }
474 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000475 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000476 xmlElementContentOccur ocur;
477
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000478 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000479 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
480 (ocur == XML_ELEMENT_CONTENT_MULT)) {
481 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
482 ctxt->state, NULL);
483 }
484 oldstate = ctxt->state;
485 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000486
487 /*
488 * iterate over the subtypes and remerge the end with an
489 * epsilon transition
490 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000491 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000492 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000493 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000494 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000495 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000496 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
497 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000498 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000499 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000500 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
501 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000502 switch (ocur) {
503 case XML_ELEMENT_CONTENT_ONCE:
504 break;
505 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000506 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000507 break;
508 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000509 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
510 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000511 break;
512 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000513 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000514 break;
515 }
516 break;
517 }
518 default:
519 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
520 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000521 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000522 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000523 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000524}
525/**
526 * xmlValidBuildContentModel:
527 * @ctxt: a validation context
528 * @elem: an element declaration node
529 *
530 * (Re)Build the automata associated to the content model of this
531 * element
532 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000533 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000534 */
535int
536xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
537 xmlAutomataStatePtr start;
538
539 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000540 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000541 if (elem->type != XML_ELEMENT_DECL)
542 return(0);
543 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
544 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000545 /* TODO: should we rebuild in this case ? */
546 if (elem->contModel != NULL)
Daniel Veillard84d70a42002-09-16 10:51:38 +0000547 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000548
549 ctxt->am = xmlNewAutomata();
550 if (ctxt->am == NULL) {
551 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
552 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000553 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000554 }
555 start = ctxt->state = xmlAutomataGetInitState(ctxt->am);
556 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
557 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000558 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillard23e73572002-09-19 19:56:43 +0000559 if (!xmlRegexpIsDeterminist(elem->contModel)) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000560 char expr[5000];
561 expr[0] = 0;
562 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
563 VERROR(ctxt->userData, "Content model of %s is not determinist: %s\n",
564 elem->name, expr);
565#ifdef DEBUG_REGEXP_ALGO
566 xmlRegexpPrint(stderr, elem->contModel);
567#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000568 ctxt->valid = 0;
569 }
570 ctxt->state = NULL;
571 xmlFreeAutomata(ctxt->am);
572 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000573 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000574}
575
576#endif /* LIBXML_REGEXP_ENABLED */
577
Owen Taylor3473f882001-02-23 17:55:21 +0000578/************************************************************************
579 * *
580 * QName handling helper *
581 * *
582 ************************************************************************/
583
584/**
585 * xmlSplitQName2:
586 * @name: an XML parser context
587 * @prefix: a xmlChar **
588 *
589 * parse an XML qualified name string
590 *
591 * [NS 5] QName ::= (Prefix ':')? LocalPart
592 *
593 * [NS 6] Prefix ::= NCName
594 *
595 * [NS 7] LocalPart ::= NCName
596 *
597 * Returns NULL if not a QName, otherwise the local part, and prefix
598 * is updated to get the Prefix if any.
599 */
600
601xmlChar *
602xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
603 int len = 0;
604 xmlChar *ret = NULL;
605
606 *prefix = NULL;
607
Daniel Veillardf4309d72001-10-02 09:28:58 +0000608#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000609 /* xml: prefix is not really a namespace */
610 if ((name[0] == 'x') && (name[1] == 'm') &&
611 (name[2] == 'l') && (name[3] == ':'))
612 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000613#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000614
615 /* nasty but valid */
616 if (name[0] == ':')
617 return(NULL);
618
619 /*
620 * we are not trying to validate but just to cut, and yes it will
621 * work even if this is as set of UTF-8 encoded chars
622 */
623 while ((name[len] != 0) && (name[len] != ':'))
624 len++;
625
626 if (name[len] == 0)
627 return(NULL);
628
629 *prefix = xmlStrndup(name, len);
630 ret = xmlStrdup(&name[len + 1]);
631
632 return(ret);
633}
634
635/****************************************************************
636 * *
637 * Util functions for data allocation/deallocation *
638 * *
639 ****************************************************************/
640
641/**
642 * xmlNewElementContent:
643 * @name: the subelement name or NULL
644 * @type: the type of element content decl
645 *
646 * Allocate an element content structure.
647 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000648 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000649 */
650xmlElementContentPtr
651xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
652 xmlElementContentPtr ret;
653
654 switch(type) {
655 case XML_ELEMENT_CONTENT_ELEMENT:
656 if (name == NULL) {
657 xmlGenericError(xmlGenericErrorContext,
658 "xmlNewElementContent : name == NULL !\n");
659 }
660 break;
661 case XML_ELEMENT_CONTENT_PCDATA:
662 case XML_ELEMENT_CONTENT_SEQ:
663 case XML_ELEMENT_CONTENT_OR:
664 if (name != NULL) {
665 xmlGenericError(xmlGenericErrorContext,
666 "xmlNewElementContent : name != NULL !\n");
667 }
668 break;
669 default:
670 xmlGenericError(xmlGenericErrorContext,
671 "xmlNewElementContent: unknown type %d\n", type);
672 return(NULL);
673 }
674 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
675 if (ret == NULL) {
676 xmlGenericError(xmlGenericErrorContext,
677 "xmlNewElementContent : out of memory!\n");
678 return(NULL);
679 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000680 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000681 ret->type = type;
682 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000683 if (name != NULL) {
684 xmlChar *prefix = NULL;
685 ret->name = xmlSplitQName2(name, &prefix);
686 if (ret->name == NULL)
687 ret->name = xmlStrdup(name);
688 ret->prefix = prefix;
689 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000690 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000691 ret->prefix = NULL;
692 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000693 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000694 return(ret);
695}
696
697/**
698 * xmlCopyElementContent:
699 * @content: An element content pointer.
700 *
701 * Build a copy of an element content description.
702 *
703 * Returns the new xmlElementContentPtr or NULL in case of error.
704 */
705xmlElementContentPtr
706xmlCopyElementContent(xmlElementContentPtr cur) {
707 xmlElementContentPtr ret;
708
709 if (cur == NULL) return(NULL);
710 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
711 if (ret == NULL) {
712 xmlGenericError(xmlGenericErrorContext,
713 "xmlCopyElementContent : out of memory\n");
714 return(NULL);
715 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000716 if (cur->prefix != NULL)
717 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000718 ret->ocur = cur->ocur;
719 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000720 if (ret->c1 != NULL)
721 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000722 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000723 if (ret->c2 != NULL)
724 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000725 return(ret);
726}
727
728/**
729 * xmlFreeElementContent:
730 * @cur: the element content tree to free
731 *
732 * Free an element content structure. This is a recursive call !
733 */
734void
735xmlFreeElementContent(xmlElementContentPtr cur) {
736 if (cur == NULL) return;
737 switch (cur->type) {
738 case XML_ELEMENT_CONTENT_PCDATA:
739 case XML_ELEMENT_CONTENT_ELEMENT:
740 case XML_ELEMENT_CONTENT_SEQ:
741 case XML_ELEMENT_CONTENT_OR:
742 break;
743 default:
744 xmlGenericError(xmlGenericErrorContext,
745 "xmlFreeElementContent : type %d\n", cur->type);
746 return;
747 }
748 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
749 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
750 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000751 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000752 xmlFree(cur);
753}
754
755/**
756 * xmlDumpElementContent:
757 * @buf: An XML buffer
758 * @content: An element table
759 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
760 *
761 * This will dump the content of the element table as an XML DTD definition
762 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000763static void
Owen Taylor3473f882001-02-23 17:55:21 +0000764xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
765 if (content == NULL) return;
766
767 if (glob) xmlBufferWriteChar(buf, "(");
768 switch (content->type) {
769 case XML_ELEMENT_CONTENT_PCDATA:
770 xmlBufferWriteChar(buf, "#PCDATA");
771 break;
772 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000773 if (content->prefix != NULL) {
774 xmlBufferWriteCHAR(buf, content->prefix);
775 xmlBufferWriteChar(buf, ":");
776 }
Owen Taylor3473f882001-02-23 17:55:21 +0000777 xmlBufferWriteCHAR(buf, content->name);
778 break;
779 case XML_ELEMENT_CONTENT_SEQ:
780 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
781 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
782 xmlDumpElementContent(buf, content->c1, 1);
783 else
784 xmlDumpElementContent(buf, content->c1, 0);
785 xmlBufferWriteChar(buf, " , ");
786 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
787 xmlDumpElementContent(buf, content->c2, 1);
788 else
789 xmlDumpElementContent(buf, content->c2, 0);
790 break;
791 case XML_ELEMENT_CONTENT_OR:
792 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
793 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
794 xmlDumpElementContent(buf, content->c1, 1);
795 else
796 xmlDumpElementContent(buf, content->c1, 0);
797 xmlBufferWriteChar(buf, " | ");
798 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
799 xmlDumpElementContent(buf, content->c2, 1);
800 else
801 xmlDumpElementContent(buf, content->c2, 0);
802 break;
803 default:
804 xmlGenericError(xmlGenericErrorContext,
805 "xmlDumpElementContent: unknown type %d\n",
806 content->type);
807 }
808 if (glob)
809 xmlBufferWriteChar(buf, ")");
810 switch (content->ocur) {
811 case XML_ELEMENT_CONTENT_ONCE:
812 break;
813 case XML_ELEMENT_CONTENT_OPT:
814 xmlBufferWriteChar(buf, "?");
815 break;
816 case XML_ELEMENT_CONTENT_MULT:
817 xmlBufferWriteChar(buf, "*");
818 break;
819 case XML_ELEMENT_CONTENT_PLUS:
820 xmlBufferWriteChar(buf, "+");
821 break;
822 }
823}
824
825/**
826 * xmlSprintfElementContent:
827 * @buf: an output buffer
828 * @content: An element table
829 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
830 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000831 * Deprecated, unsafe, use xmlSnprintfElementContent
832 */
833void
834xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
835 xmlElementContentPtr content ATTRIBUTE_UNUSED,
836 int glob ATTRIBUTE_UNUSED) {
837}
838
839/**
840 * xmlSnprintfElementContent:
841 * @buf: an output buffer
842 * @size: the buffer size
843 * @content: An element table
844 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
845 *
Owen Taylor3473f882001-02-23 17:55:21 +0000846 * This will dump the content of the element content definition
847 * Intended just for the debug routine
848 */
849void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000850xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
851 int len;
852
Owen Taylor3473f882001-02-23 17:55:21 +0000853 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000854 len = strlen(buf);
855 if (size - len < 50) {
856 if ((size - len > 4) && (buf[len - 1] != '.'))
857 strcat(buf, " ...");
858 return;
859 }
Owen Taylor3473f882001-02-23 17:55:21 +0000860 if (glob) strcat(buf, "(");
861 switch (content->type) {
862 case XML_ELEMENT_CONTENT_PCDATA:
863 strcat(buf, "#PCDATA");
864 break;
865 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000866 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000867 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000868 strcat(buf, " ...");
869 return;
870 }
871 strcat(buf, (char *) content->prefix);
872 strcat(buf, ":");
873 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000874 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000875 strcat(buf, " ...");
876 return;
877 }
Owen Taylor3473f882001-02-23 17:55:21 +0000878 strcat(buf, (char *) content->name);
879 break;
880 case XML_ELEMENT_CONTENT_SEQ:
881 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
882 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000883 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000884 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000885 xmlSnprintfElementContent(buf, size, content->c1, 0);
886 len = strlen(buf);
887 if (size - len < 50) {
888 if ((size - len > 4) && (buf[len - 1] != '.'))
889 strcat(buf, " ...");
890 return;
891 }
Owen Taylor3473f882001-02-23 17:55:21 +0000892 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000893 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
894 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
895 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000896 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000897 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000898 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000899 break;
900 case XML_ELEMENT_CONTENT_OR:
901 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
902 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000903 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000904 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000905 xmlSnprintfElementContent(buf, size, content->c1, 0);
906 len = strlen(buf);
907 if (size - len < 50) {
908 if ((size - len > 4) && (buf[len - 1] != '.'))
909 strcat(buf, " ...");
910 return;
911 }
Owen Taylor3473f882001-02-23 17:55:21 +0000912 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000913 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
914 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
915 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000916 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000917 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000918 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000919 break;
920 }
921 if (glob)
922 strcat(buf, ")");
923 switch (content->ocur) {
924 case XML_ELEMENT_CONTENT_ONCE:
925 break;
926 case XML_ELEMENT_CONTENT_OPT:
927 strcat(buf, "?");
928 break;
929 case XML_ELEMENT_CONTENT_MULT:
930 strcat(buf, "*");
931 break;
932 case XML_ELEMENT_CONTENT_PLUS:
933 strcat(buf, "+");
934 break;
935 }
936}
937
938/****************************************************************
939 * *
940 * Registration of DTD declarations *
941 * *
942 ****************************************************************/
943
944/**
945 * xmlCreateElementTable:
946 *
947 * create and initialize an empty element hash table.
948 *
949 * Returns the xmlElementTablePtr just created or NULL in case of error.
950 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000951static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000952xmlCreateElementTable(void) {
953 return(xmlHashCreate(0));
954}
955
956/**
957 * xmlFreeElement:
958 * @elem: An element
959 *
960 * Deallocate the memory used by an element definition
961 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000962static void
Owen Taylor3473f882001-02-23 17:55:21 +0000963xmlFreeElement(xmlElementPtr elem) {
964 if (elem == NULL) return;
965 xmlUnlinkNode((xmlNodePtr) elem);
966 xmlFreeElementContent(elem->content);
967 if (elem->name != NULL)
968 xmlFree((xmlChar *) elem->name);
969 if (elem->prefix != NULL)
970 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000971#ifdef LIBXML_REGEXP_ENABLED
972 if (elem->contModel != NULL)
973 xmlRegFreeRegexp(elem->contModel);
974#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000975 xmlFree(elem);
976}
977
978
979/**
980 * xmlAddElementDecl:
981 * @ctxt: the validation context
982 * @dtd: pointer to the DTD
983 * @name: the entity name
984 * @type: the element type
985 * @content: the element content tree or NULL
986 *
987 * Register a new element declaration
988 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000989 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +0000990 */
991xmlElementPtr
992xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
993 xmlElementTypeVal type,
994 xmlElementContentPtr content) {
995 xmlElementPtr ret;
996 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000997 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000998 xmlChar *ns, *uqname;
999
1000 if (dtd == NULL) {
1001 xmlGenericError(xmlGenericErrorContext,
1002 "xmlAddElementDecl: dtd == NULL\n");
1003 return(NULL);
1004 }
1005 if (name == NULL) {
1006 xmlGenericError(xmlGenericErrorContext,
1007 "xmlAddElementDecl: name == NULL\n");
1008 return(NULL);
1009 }
1010 switch (type) {
1011 case XML_ELEMENT_TYPE_EMPTY:
1012 if (content != NULL) {
1013 xmlGenericError(xmlGenericErrorContext,
1014 "xmlAddElementDecl: content != NULL for EMPTY\n");
1015 return(NULL);
1016 }
1017 break;
1018 case XML_ELEMENT_TYPE_ANY:
1019 if (content != NULL) {
1020 xmlGenericError(xmlGenericErrorContext,
1021 "xmlAddElementDecl: content != NULL for ANY\n");
1022 return(NULL);
1023 }
1024 break;
1025 case XML_ELEMENT_TYPE_MIXED:
1026 if (content == NULL) {
1027 xmlGenericError(xmlGenericErrorContext,
1028 "xmlAddElementDecl: content == NULL for MIXED\n");
1029 return(NULL);
1030 }
1031 break;
1032 case XML_ELEMENT_TYPE_ELEMENT:
1033 if (content == NULL) {
1034 xmlGenericError(xmlGenericErrorContext,
1035 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1036 return(NULL);
1037 }
1038 break;
1039 default:
1040 xmlGenericError(xmlGenericErrorContext,
1041 "xmlAddElementDecl: unknown type %d\n", type);
1042 return(NULL);
1043 }
1044
1045 /*
1046 * check if name is a QName
1047 */
1048 uqname = xmlSplitQName2(name, &ns);
1049 if (uqname != NULL)
1050 name = uqname;
1051
1052 /*
1053 * Create the Element table if needed.
1054 */
1055 table = (xmlElementTablePtr) dtd->elements;
1056 if (table == NULL) {
1057 table = xmlCreateElementTable();
1058 dtd->elements = (void *) table;
1059 }
1060 if (table == NULL) {
1061 xmlGenericError(xmlGenericErrorContext,
1062 "xmlAddElementDecl: Table creation failed!\n");
1063 return(NULL);
1064 }
1065
Daniel Veillarda10efa82001-04-18 13:09:01 +00001066 /*
1067 * lookup old attributes inserted on an undefined element in the
1068 * internal subset.
1069 */
1070 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1071 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1072 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1073 oldAttributes = ret->attributes;
1074 ret->attributes = NULL;
1075 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1076 xmlFreeElement(ret);
1077 }
Owen Taylor3473f882001-02-23 17:55:21 +00001078 }
Owen Taylor3473f882001-02-23 17:55:21 +00001079
1080 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001081 * The element may already be present if one of its attribute
1082 * was registered first
1083 */
1084 ret = xmlHashLookup2(table, name, ns);
1085 if (ret != NULL) {
1086 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1087 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001088 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001089 */
1090 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1091 if (uqname != NULL)
1092 xmlFree(uqname);
1093 return(NULL);
1094 }
1095 } else {
1096 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1097 if (ret == NULL) {
1098 xmlGenericError(xmlGenericErrorContext,
1099 "xmlAddElementDecl: out of memory\n");
1100 return(NULL);
1101 }
1102 memset(ret, 0, sizeof(xmlElement));
1103 ret->type = XML_ELEMENT_DECL;
1104
1105 /*
1106 * fill the structure.
1107 */
1108 ret->name = xmlStrdup(name);
1109 ret->prefix = ns;
1110
1111 /*
1112 * Validity Check:
1113 * Insertion must not fail
1114 */
1115 if (xmlHashAddEntry2(table, name, ns, ret)) {
1116 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001117 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001118 */
1119 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1120 xmlFreeElement(ret);
1121 if (uqname != NULL)
1122 xmlFree(uqname);
1123 return(NULL);
1124 }
1125 }
1126
1127 /*
1128 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001129 */
1130 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001131 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001132 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001133
1134 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001135 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001136 */
1137 ret->parent = dtd;
1138 ret->doc = dtd->doc;
1139 if (dtd->last == NULL) {
1140 dtd->children = dtd->last = (xmlNodePtr) ret;
1141 } else {
1142 dtd->last->next = (xmlNodePtr) ret;
1143 ret->prev = dtd->last;
1144 dtd->last = (xmlNodePtr) ret;
1145 }
1146 if (uqname != NULL)
1147 xmlFree(uqname);
1148 return(ret);
1149}
1150
1151/**
1152 * xmlFreeElementTable:
1153 * @table: An element table
1154 *
1155 * Deallocate the memory used by an element hash table.
1156 */
1157void
1158xmlFreeElementTable(xmlElementTablePtr table) {
1159 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1160}
1161
1162/**
1163 * xmlCopyElement:
1164 * @elem: An element
1165 *
1166 * Build a copy of an element.
1167 *
1168 * Returns the new xmlElementPtr or NULL in case of error.
1169 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001170static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001171xmlCopyElement(xmlElementPtr elem) {
1172 xmlElementPtr cur;
1173
1174 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1175 if (cur == NULL) {
1176 xmlGenericError(xmlGenericErrorContext,
1177 "xmlCopyElement: out of memory !\n");
1178 return(NULL);
1179 }
1180 memset(cur, 0, sizeof(xmlElement));
1181 cur->type = XML_ELEMENT_DECL;
1182 cur->etype = elem->etype;
1183 if (elem->name != NULL)
1184 cur->name = xmlStrdup(elem->name);
1185 else
1186 cur->name = NULL;
1187 if (elem->prefix != NULL)
1188 cur->prefix = xmlStrdup(elem->prefix);
1189 else
1190 cur->prefix = NULL;
1191 cur->content = xmlCopyElementContent(elem->content);
1192 /* TODO : rebuild the attribute list on the copy */
1193 cur->attributes = NULL;
1194 return(cur);
1195}
1196
1197/**
1198 * xmlCopyElementTable:
1199 * @table: An element table
1200 *
1201 * Build a copy of an element table.
1202 *
1203 * Returns the new xmlElementTablePtr or NULL in case of error.
1204 */
1205xmlElementTablePtr
1206xmlCopyElementTable(xmlElementTablePtr table) {
1207 return((xmlElementTablePtr) xmlHashCopy(table,
1208 (xmlHashCopier) xmlCopyElement));
1209}
1210
1211/**
1212 * xmlDumpElementDecl:
1213 * @buf: the XML buffer output
1214 * @elem: An element table
1215 *
1216 * This will dump the content of the element declaration as an XML
1217 * DTD definition
1218 */
1219void
1220xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1221 switch (elem->etype) {
1222 case XML_ELEMENT_TYPE_EMPTY:
1223 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001224 if (elem->prefix != NULL) {
1225 xmlBufferWriteCHAR(buf, elem->prefix);
1226 xmlBufferWriteChar(buf, ":");
1227 }
Owen Taylor3473f882001-02-23 17:55:21 +00001228 xmlBufferWriteCHAR(buf, elem->name);
1229 xmlBufferWriteChar(buf, " EMPTY>\n");
1230 break;
1231 case XML_ELEMENT_TYPE_ANY:
1232 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001233 if (elem->prefix != NULL) {
1234 xmlBufferWriteCHAR(buf, elem->prefix);
1235 xmlBufferWriteChar(buf, ":");
1236 }
Owen Taylor3473f882001-02-23 17:55:21 +00001237 xmlBufferWriteCHAR(buf, elem->name);
1238 xmlBufferWriteChar(buf, " ANY>\n");
1239 break;
1240 case XML_ELEMENT_TYPE_MIXED:
1241 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001242 if (elem->prefix != NULL) {
1243 xmlBufferWriteCHAR(buf, elem->prefix);
1244 xmlBufferWriteChar(buf, ":");
1245 }
Owen Taylor3473f882001-02-23 17:55:21 +00001246 xmlBufferWriteCHAR(buf, elem->name);
1247 xmlBufferWriteChar(buf, " ");
1248 xmlDumpElementContent(buf, elem->content, 1);
1249 xmlBufferWriteChar(buf, ">\n");
1250 break;
1251 case XML_ELEMENT_TYPE_ELEMENT:
1252 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001253 if (elem->prefix != NULL) {
1254 xmlBufferWriteCHAR(buf, elem->prefix);
1255 xmlBufferWriteChar(buf, ":");
1256 }
Owen Taylor3473f882001-02-23 17:55:21 +00001257 xmlBufferWriteCHAR(buf, elem->name);
1258 xmlBufferWriteChar(buf, " ");
1259 xmlDumpElementContent(buf, elem->content, 1);
1260 xmlBufferWriteChar(buf, ">\n");
1261 break;
1262 default:
1263 xmlGenericError(xmlGenericErrorContext,
1264 "xmlDumpElementDecl: internal: unknown type %d\n",
1265 elem->etype);
1266 }
1267}
1268
1269/**
1270 * xmlDumpElementTable:
1271 * @buf: the XML buffer output
1272 * @table: An element table
1273 *
1274 * This will dump the content of the element table as an XML DTD definition
1275 */
1276void
1277xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1278 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1279}
1280
1281/**
1282 * xmlCreateEnumeration:
1283 * @name: the enumeration name or NULL
1284 *
1285 * create and initialize an enumeration attribute node.
1286 *
1287 * Returns the xmlEnumerationPtr just created or NULL in case
1288 * of error.
1289 */
1290xmlEnumerationPtr
1291xmlCreateEnumeration(xmlChar *name) {
1292 xmlEnumerationPtr ret;
1293
1294 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1295 if (ret == NULL) {
1296 xmlGenericError(xmlGenericErrorContext,
1297 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1298 (long)sizeof(xmlEnumeration));
1299 return(NULL);
1300 }
1301 memset(ret, 0, sizeof(xmlEnumeration));
1302
1303 if (name != NULL)
1304 ret->name = xmlStrdup(name);
1305 return(ret);
1306}
1307
1308/**
1309 * xmlFreeEnumeration:
1310 * @cur: the tree to free.
1311 *
1312 * free an enumeration attribute node (recursive).
1313 */
1314void
1315xmlFreeEnumeration(xmlEnumerationPtr cur) {
1316 if (cur == NULL) return;
1317
1318 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1319
1320 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001321 xmlFree(cur);
1322}
1323
1324/**
1325 * xmlCopyEnumeration:
1326 * @cur: the tree to copy.
1327 *
1328 * Copy an enumeration attribute node (recursive).
1329 *
1330 * Returns the xmlEnumerationPtr just created or NULL in case
1331 * of error.
1332 */
1333xmlEnumerationPtr
1334xmlCopyEnumeration(xmlEnumerationPtr cur) {
1335 xmlEnumerationPtr ret;
1336
1337 if (cur == NULL) return(NULL);
1338 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1339
1340 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1341 else ret->next = NULL;
1342
1343 return(ret);
1344}
1345
1346/**
1347 * xmlDumpEnumeration:
1348 * @buf: the XML buffer output
1349 * @enum: An enumeration
1350 *
1351 * This will dump the content of the enumeration
1352 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001353static void
Owen Taylor3473f882001-02-23 17:55:21 +00001354xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1355 if (cur == NULL) return;
1356
1357 xmlBufferWriteCHAR(buf, cur->name);
1358 if (cur->next == NULL)
1359 xmlBufferWriteChar(buf, ")");
1360 else {
1361 xmlBufferWriteChar(buf, " | ");
1362 xmlDumpEnumeration(buf, cur->next);
1363 }
1364}
1365
1366/**
1367 * xmlCreateAttributeTable:
1368 *
1369 * create and initialize an empty attribute hash table.
1370 *
1371 * Returns the xmlAttributeTablePtr just created or NULL in case
1372 * of error.
1373 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001374static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001375xmlCreateAttributeTable(void) {
1376 return(xmlHashCreate(0));
1377}
1378
1379/**
1380 * xmlScanAttributeDeclCallback:
1381 * @attr: the attribute decl
1382 * @list: the list to update
1383 *
1384 * Callback called by xmlScanAttributeDecl when a new attribute
1385 * has to be entered in the list.
1386 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001387static void
Owen Taylor3473f882001-02-23 17:55:21 +00001388xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001389 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001390 attr->nexth = *list;
1391 *list = attr;
1392}
1393
1394/**
1395 * xmlScanAttributeDecl:
1396 * @dtd: pointer to the DTD
1397 * @elem: the element name
1398 *
1399 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001400 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001401 *
1402 * Returns the pointer to the first attribute decl in the chain,
1403 * possibly NULL.
1404 */
1405xmlAttributePtr
1406xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1407 xmlAttributePtr ret = NULL;
1408 xmlAttributeTablePtr table;
1409
1410 if (dtd == NULL) {
1411 xmlGenericError(xmlGenericErrorContext,
1412 "xmlScanAttributeDecl: dtd == NULL\n");
1413 return(NULL);
1414 }
1415 if (elem == NULL) {
1416 xmlGenericError(xmlGenericErrorContext,
1417 "xmlScanAttributeDecl: elem == NULL\n");
1418 return(NULL);
1419 }
1420 table = (xmlAttributeTablePtr) dtd->attributes;
1421 if (table == NULL)
1422 return(NULL);
1423
1424 /* WRONG !!! */
1425 xmlHashScan3(table, NULL, NULL, elem,
1426 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1427 return(ret);
1428}
1429
1430/**
1431 * xmlScanIDAttributeDecl:
1432 * @ctxt: the validation context
1433 * @elem: the element name
1434 *
1435 * Verify that the element don't have too many ID attributes
1436 * declared.
1437 *
1438 * Returns the number of ID attributes found.
1439 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001440static int
Owen Taylor3473f882001-02-23 17:55:21 +00001441xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1442 xmlAttributePtr cur;
1443 int ret = 0;
1444
1445 if (elem == NULL) return(0);
1446 cur = elem->attributes;
1447 while (cur != NULL) {
1448 if (cur->atype == XML_ATTRIBUTE_ID) {
1449 ret ++;
1450 if (ret > 1)
1451 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001452 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001453 elem->name, cur->name);
1454 }
1455 cur = cur->nexth;
1456 }
1457 return(ret);
1458}
1459
1460/**
1461 * xmlFreeAttribute:
1462 * @elem: An attribute
1463 *
1464 * Deallocate the memory used by an attribute definition
1465 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001466static void
Owen Taylor3473f882001-02-23 17:55:21 +00001467xmlFreeAttribute(xmlAttributePtr attr) {
1468 if (attr == NULL) return;
1469 xmlUnlinkNode((xmlNodePtr) attr);
1470 if (attr->tree != NULL)
1471 xmlFreeEnumeration(attr->tree);
1472 if (attr->elem != NULL)
1473 xmlFree((xmlChar *) attr->elem);
1474 if (attr->name != NULL)
1475 xmlFree((xmlChar *) attr->name);
1476 if (attr->defaultValue != NULL)
1477 xmlFree((xmlChar *) attr->defaultValue);
1478 if (attr->prefix != NULL)
1479 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001480 xmlFree(attr);
1481}
1482
1483
1484/**
1485 * xmlAddAttributeDecl:
1486 * @ctxt: the validation context
1487 * @dtd: pointer to the DTD
1488 * @elem: the element name
1489 * @name: the attribute name
1490 * @ns: the attribute namespace prefix
1491 * @type: the attribute type
1492 * @def: the attribute default type
1493 * @defaultValue: the attribute default value
1494 * @tree: if it's an enumeration, the associated list
1495 *
1496 * Register a new attribute declaration
1497 * Note that @tree becomes the ownership of the DTD
1498 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001499 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001500 */
1501xmlAttributePtr
1502xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1503 const xmlChar *name, const xmlChar *ns,
1504 xmlAttributeType type, xmlAttributeDefault def,
1505 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1506 xmlAttributePtr ret;
1507 xmlAttributeTablePtr table;
1508 xmlElementPtr elemDef;
1509
1510 if (dtd == NULL) {
1511 xmlGenericError(xmlGenericErrorContext,
1512 "xmlAddAttributeDecl: dtd == NULL\n");
1513 xmlFreeEnumeration(tree);
1514 return(NULL);
1515 }
1516 if (name == NULL) {
1517 xmlGenericError(xmlGenericErrorContext,
1518 "xmlAddAttributeDecl: name == NULL\n");
1519 xmlFreeEnumeration(tree);
1520 return(NULL);
1521 }
1522 if (elem == NULL) {
1523 xmlGenericError(xmlGenericErrorContext,
1524 "xmlAddAttributeDecl: elem == NULL\n");
1525 xmlFreeEnumeration(tree);
1526 return(NULL);
1527 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001528
Owen Taylor3473f882001-02-23 17:55:21 +00001529 /*
1530 * Check the type and possibly the default value.
1531 */
1532 switch (type) {
1533 case XML_ATTRIBUTE_CDATA:
1534 break;
1535 case XML_ATTRIBUTE_ID:
1536 break;
1537 case XML_ATTRIBUTE_IDREF:
1538 break;
1539 case XML_ATTRIBUTE_IDREFS:
1540 break;
1541 case XML_ATTRIBUTE_ENTITY:
1542 break;
1543 case XML_ATTRIBUTE_ENTITIES:
1544 break;
1545 case XML_ATTRIBUTE_NMTOKEN:
1546 break;
1547 case XML_ATTRIBUTE_NMTOKENS:
1548 break;
1549 case XML_ATTRIBUTE_ENUMERATION:
1550 break;
1551 case XML_ATTRIBUTE_NOTATION:
1552 break;
1553 default:
1554 xmlGenericError(xmlGenericErrorContext,
1555 "xmlAddAttributeDecl: unknown type %d\n", type);
1556 xmlFreeEnumeration(tree);
1557 return(NULL);
1558 }
1559 if ((defaultValue != NULL) &&
1560 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001561 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001562 elem, name, defaultValue);
1563 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001564 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001565 }
1566
1567 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001568 * Check first that an attribute defined in the external subset wasn't
1569 * already defined in the internal subset
1570 */
1571 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1572 (dtd->doc->intSubset != NULL) &&
1573 (dtd->doc->intSubset->attributes != NULL)) {
1574 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1575 if (ret != NULL)
1576 return(NULL);
1577 }
1578
1579 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001580 * Create the Attribute table if needed.
1581 */
1582 table = (xmlAttributeTablePtr) dtd->attributes;
1583 if (table == NULL) {
1584 table = xmlCreateAttributeTable();
1585 dtd->attributes = (void *) table;
1586 }
1587 if (table == NULL) {
1588 xmlGenericError(xmlGenericErrorContext,
1589 "xmlAddAttributeDecl: Table creation failed!\n");
1590 return(NULL);
1591 }
1592
1593
1594 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1595 if (ret == NULL) {
1596 xmlGenericError(xmlGenericErrorContext,
1597 "xmlAddAttributeDecl: out of memory\n");
1598 return(NULL);
1599 }
1600 memset(ret, 0, sizeof(xmlAttribute));
1601 ret->type = XML_ATTRIBUTE_DECL;
1602
1603 /*
1604 * fill the structure.
1605 */
1606 ret->atype = type;
1607 ret->name = xmlStrdup(name);
1608 ret->prefix = xmlStrdup(ns);
1609 ret->elem = xmlStrdup(elem);
1610 ret->def = def;
1611 ret->tree = tree;
1612 if (defaultValue != NULL)
1613 ret->defaultValue = xmlStrdup(defaultValue);
1614
1615 /*
1616 * Validity Check:
1617 * Search the DTD for previous declarations of the ATTLIST
1618 */
1619 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1620 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001621 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001622 */
1623 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001624 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001625 name, elem);
1626 xmlFreeAttribute(ret);
1627 return(NULL);
1628 }
1629
1630 /*
1631 * Validity Check:
1632 * Multiple ID per element
1633 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001634 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001635 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001636
Owen Taylor3473f882001-02-23 17:55:21 +00001637 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001638 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001639 VERROR(ctxt->userData,
1640 "Element %s has too may ID attributes defined : %s\n",
1641 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001642 ctxt->valid = 0;
1643 }
1644
Daniel Veillard48da9102001-08-07 01:10:10 +00001645 /*
1646 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001647 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001648 */
1649 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1650 ((ret->prefix != NULL &&
1651 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1652 ret->nexth = elemDef->attributes;
1653 elemDef->attributes = ret;
1654 } else {
1655 xmlAttributePtr tmp = elemDef->attributes;
1656
1657 while ((tmp != NULL) &&
1658 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1659 ((ret->prefix != NULL &&
1660 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1661 if (tmp->nexth == NULL)
1662 break;
1663 tmp = tmp->nexth;
1664 }
1665 if (tmp != NULL) {
1666 ret->nexth = tmp->nexth;
1667 tmp->nexth = ret;
1668 } else {
1669 ret->nexth = elemDef->attributes;
1670 elemDef->attributes = ret;
1671 }
1672 }
Owen Taylor3473f882001-02-23 17:55:21 +00001673 }
1674
1675 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001676 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001677 */
1678 ret->parent = dtd;
1679 ret->doc = dtd->doc;
1680 if (dtd->last == NULL) {
1681 dtd->children = dtd->last = (xmlNodePtr) ret;
1682 } else {
1683 dtd->last->next = (xmlNodePtr) ret;
1684 ret->prev = dtd->last;
1685 dtd->last = (xmlNodePtr) ret;
1686 }
1687 return(ret);
1688}
1689
1690/**
1691 * xmlFreeAttributeTable:
1692 * @table: An attribute table
1693 *
1694 * Deallocate the memory used by an entities hash table.
1695 */
1696void
1697xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1698 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1699}
1700
1701/**
1702 * xmlCopyAttribute:
1703 * @attr: An attribute
1704 *
1705 * Build a copy of an attribute.
1706 *
1707 * Returns the new xmlAttributePtr or NULL in case of error.
1708 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001709static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001710xmlCopyAttribute(xmlAttributePtr attr) {
1711 xmlAttributePtr cur;
1712
1713 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1714 if (cur == NULL) {
1715 xmlGenericError(xmlGenericErrorContext,
1716 "xmlCopyAttribute: out of memory !\n");
1717 return(NULL);
1718 }
1719 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001720 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001721 cur->atype = attr->atype;
1722 cur->def = attr->def;
1723 cur->tree = xmlCopyEnumeration(attr->tree);
1724 if (attr->elem != NULL)
1725 cur->elem = xmlStrdup(attr->elem);
1726 if (attr->name != NULL)
1727 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001728 if (attr->prefix != NULL)
1729 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001730 if (attr->defaultValue != NULL)
1731 cur->defaultValue = xmlStrdup(attr->defaultValue);
1732 return(cur);
1733}
1734
1735/**
1736 * xmlCopyAttributeTable:
1737 * @table: An attribute table
1738 *
1739 * Build a copy of an attribute table.
1740 *
1741 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1742 */
1743xmlAttributeTablePtr
1744xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1745 return((xmlAttributeTablePtr) xmlHashCopy(table,
1746 (xmlHashCopier) xmlCopyAttribute));
1747}
1748
1749/**
1750 * xmlDumpAttributeDecl:
1751 * @buf: the XML buffer output
1752 * @attr: An attribute declaration
1753 *
1754 * This will dump the content of the attribute declaration as an XML
1755 * DTD definition
1756 */
1757void
1758xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1759 xmlBufferWriteChar(buf, "<!ATTLIST ");
1760 xmlBufferWriteCHAR(buf, attr->elem);
1761 xmlBufferWriteChar(buf, " ");
1762 if (attr->prefix != NULL) {
1763 xmlBufferWriteCHAR(buf, attr->prefix);
1764 xmlBufferWriteChar(buf, ":");
1765 }
1766 xmlBufferWriteCHAR(buf, attr->name);
1767 switch (attr->atype) {
1768 case XML_ATTRIBUTE_CDATA:
1769 xmlBufferWriteChar(buf, " CDATA");
1770 break;
1771 case XML_ATTRIBUTE_ID:
1772 xmlBufferWriteChar(buf, " ID");
1773 break;
1774 case XML_ATTRIBUTE_IDREF:
1775 xmlBufferWriteChar(buf, " IDREF");
1776 break;
1777 case XML_ATTRIBUTE_IDREFS:
1778 xmlBufferWriteChar(buf, " IDREFS");
1779 break;
1780 case XML_ATTRIBUTE_ENTITY:
1781 xmlBufferWriteChar(buf, " ENTITY");
1782 break;
1783 case XML_ATTRIBUTE_ENTITIES:
1784 xmlBufferWriteChar(buf, " ENTITIES");
1785 break;
1786 case XML_ATTRIBUTE_NMTOKEN:
1787 xmlBufferWriteChar(buf, " NMTOKEN");
1788 break;
1789 case XML_ATTRIBUTE_NMTOKENS:
1790 xmlBufferWriteChar(buf, " NMTOKENS");
1791 break;
1792 case XML_ATTRIBUTE_ENUMERATION:
1793 xmlBufferWriteChar(buf, " (");
1794 xmlDumpEnumeration(buf, attr->tree);
1795 break;
1796 case XML_ATTRIBUTE_NOTATION:
1797 xmlBufferWriteChar(buf, " NOTATION (");
1798 xmlDumpEnumeration(buf, attr->tree);
1799 break;
1800 default:
1801 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001802 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001803 attr->atype);
1804 }
1805 switch (attr->def) {
1806 case XML_ATTRIBUTE_NONE:
1807 break;
1808 case XML_ATTRIBUTE_REQUIRED:
1809 xmlBufferWriteChar(buf, " #REQUIRED");
1810 break;
1811 case XML_ATTRIBUTE_IMPLIED:
1812 xmlBufferWriteChar(buf, " #IMPLIED");
1813 break;
1814 case XML_ATTRIBUTE_FIXED:
1815 xmlBufferWriteChar(buf, " #FIXED");
1816 break;
1817 default:
1818 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001819 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001820 attr->def);
1821 }
1822 if (attr->defaultValue != NULL) {
1823 xmlBufferWriteChar(buf, " ");
1824 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1825 }
1826 xmlBufferWriteChar(buf, ">\n");
1827}
1828
1829/**
1830 * xmlDumpAttributeTable:
1831 * @buf: the XML buffer output
1832 * @table: An attribute table
1833 *
1834 * This will dump the content of the attribute table as an XML DTD definition
1835 */
1836void
1837xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1838 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1839}
1840
1841/************************************************************************
1842 * *
1843 * NOTATIONs *
1844 * *
1845 ************************************************************************/
1846/**
1847 * xmlCreateNotationTable:
1848 *
1849 * create and initialize an empty notation hash table.
1850 *
1851 * Returns the xmlNotationTablePtr just created or NULL in case
1852 * of error.
1853 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001854static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001855xmlCreateNotationTable(void) {
1856 return(xmlHashCreate(0));
1857}
1858
1859/**
1860 * xmlFreeNotation:
1861 * @not: A notation
1862 *
1863 * Deallocate the memory used by an notation definition
1864 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001865static void
Owen Taylor3473f882001-02-23 17:55:21 +00001866xmlFreeNotation(xmlNotationPtr nota) {
1867 if (nota == NULL) return;
1868 if (nota->name != NULL)
1869 xmlFree((xmlChar *) nota->name);
1870 if (nota->PublicID != NULL)
1871 xmlFree((xmlChar *) nota->PublicID);
1872 if (nota->SystemID != NULL)
1873 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001874 xmlFree(nota);
1875}
1876
1877
1878/**
1879 * xmlAddNotationDecl:
1880 * @dtd: pointer to the DTD
1881 * @ctxt: the validation context
1882 * @name: the entity name
1883 * @PublicID: the public identifier or NULL
1884 * @SystemID: the system identifier or NULL
1885 *
1886 * Register a new notation declaration
1887 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001888 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001889 */
1890xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001891xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001892 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001893 const xmlChar *PublicID, const xmlChar *SystemID) {
1894 xmlNotationPtr ret;
1895 xmlNotationTablePtr table;
1896
1897 if (dtd == NULL) {
1898 xmlGenericError(xmlGenericErrorContext,
1899 "xmlAddNotationDecl: dtd == NULL\n");
1900 return(NULL);
1901 }
1902 if (name == NULL) {
1903 xmlGenericError(xmlGenericErrorContext,
1904 "xmlAddNotationDecl: name == NULL\n");
1905 return(NULL);
1906 }
1907 if ((PublicID == NULL) && (SystemID == NULL)) {
1908 xmlGenericError(xmlGenericErrorContext,
1909 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001910 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001911 }
1912
1913 /*
1914 * Create the Notation table if needed.
1915 */
1916 table = (xmlNotationTablePtr) dtd->notations;
1917 if (table == NULL)
1918 dtd->notations = table = xmlCreateNotationTable();
1919 if (table == NULL) {
1920 xmlGenericError(xmlGenericErrorContext,
1921 "xmlAddNotationDecl: Table creation failed!\n");
1922 return(NULL);
1923 }
1924
1925 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1926 if (ret == NULL) {
1927 xmlGenericError(xmlGenericErrorContext,
1928 "xmlAddNotationDecl: out of memory\n");
1929 return(NULL);
1930 }
1931 memset(ret, 0, sizeof(xmlNotation));
1932
1933 /*
1934 * fill the structure.
1935 */
1936 ret->name = xmlStrdup(name);
1937 if (SystemID != NULL)
1938 ret->SystemID = xmlStrdup(SystemID);
1939 if (PublicID != NULL)
1940 ret->PublicID = xmlStrdup(PublicID);
1941
1942 /*
1943 * Validity Check:
1944 * Check the DTD for previous declarations of the ATTLIST
1945 */
1946 if (xmlHashAddEntry(table, name, ret)) {
1947 xmlGenericError(xmlGenericErrorContext,
1948 "xmlAddNotationDecl: %s already defined\n", name);
1949 xmlFreeNotation(ret);
1950 return(NULL);
1951 }
1952 return(ret);
1953}
1954
1955/**
1956 * xmlFreeNotationTable:
1957 * @table: An notation table
1958 *
1959 * Deallocate the memory used by an entities hash table.
1960 */
1961void
1962xmlFreeNotationTable(xmlNotationTablePtr table) {
1963 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1964}
1965
1966/**
1967 * xmlCopyNotation:
1968 * @nota: A notation
1969 *
1970 * Build a copy of a notation.
1971 *
1972 * Returns the new xmlNotationPtr or NULL in case of error.
1973 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001974static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001975xmlCopyNotation(xmlNotationPtr nota) {
1976 xmlNotationPtr cur;
1977
1978 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1979 if (cur == NULL) {
1980 xmlGenericError(xmlGenericErrorContext,
1981 "xmlCopyNotation: out of memory !\n");
1982 return(NULL);
1983 }
1984 if (nota->name != NULL)
1985 cur->name = xmlStrdup(nota->name);
1986 else
1987 cur->name = NULL;
1988 if (nota->PublicID != NULL)
1989 cur->PublicID = xmlStrdup(nota->PublicID);
1990 else
1991 cur->PublicID = NULL;
1992 if (nota->SystemID != NULL)
1993 cur->SystemID = xmlStrdup(nota->SystemID);
1994 else
1995 cur->SystemID = NULL;
1996 return(cur);
1997}
1998
1999/**
2000 * xmlCopyNotationTable:
2001 * @table: A notation table
2002 *
2003 * Build a copy of a notation table.
2004 *
2005 * Returns the new xmlNotationTablePtr or NULL in case of error.
2006 */
2007xmlNotationTablePtr
2008xmlCopyNotationTable(xmlNotationTablePtr table) {
2009 return((xmlNotationTablePtr) xmlHashCopy(table,
2010 (xmlHashCopier) xmlCopyNotation));
2011}
2012
2013/**
2014 * xmlDumpNotationDecl:
2015 * @buf: the XML buffer output
2016 * @nota: A notation declaration
2017 *
2018 * This will dump the content the notation declaration as an XML DTD definition
2019 */
2020void
2021xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2022 xmlBufferWriteChar(buf, "<!NOTATION ");
2023 xmlBufferWriteCHAR(buf, nota->name);
2024 if (nota->PublicID != NULL) {
2025 xmlBufferWriteChar(buf, " PUBLIC ");
2026 xmlBufferWriteQuotedString(buf, nota->PublicID);
2027 if (nota->SystemID != NULL) {
2028 xmlBufferWriteChar(buf, " ");
2029 xmlBufferWriteCHAR(buf, nota->SystemID);
2030 }
2031 } else {
2032 xmlBufferWriteChar(buf, " SYSTEM ");
2033 xmlBufferWriteCHAR(buf, nota->SystemID);
2034 }
2035 xmlBufferWriteChar(buf, " >\n");
2036}
2037
2038/**
2039 * xmlDumpNotationTable:
2040 * @buf: the XML buffer output
2041 * @table: A notation table
2042 *
2043 * This will dump the content of the notation table as an XML DTD definition
2044 */
2045void
2046xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2047 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2048}
2049
2050/************************************************************************
2051 * *
2052 * IDs *
2053 * *
2054 ************************************************************************/
2055/**
2056 * xmlCreateIDTable:
2057 *
2058 * create and initialize an empty id hash table.
2059 *
2060 * Returns the xmlIDTablePtr just created or NULL in case
2061 * of error.
2062 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002063static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002064xmlCreateIDTable(void) {
2065 return(xmlHashCreate(0));
2066}
2067
2068/**
2069 * xmlFreeID:
2070 * @not: A id
2071 *
2072 * Deallocate the memory used by an id definition
2073 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002074static void
Owen Taylor3473f882001-02-23 17:55:21 +00002075xmlFreeID(xmlIDPtr id) {
2076 if (id == NULL) return;
2077 if (id->value != NULL)
2078 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00002079 xmlFree(id);
2080}
2081
2082/**
2083 * xmlAddID:
2084 * @ctxt: the validation context
2085 * @doc: pointer to the document
2086 * @value: the value name
2087 * @attr: the attribute holding the ID
2088 *
2089 * Register a new id declaration
2090 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002091 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002092 */
2093xmlIDPtr
2094xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2095 xmlAttrPtr attr) {
2096 xmlIDPtr ret;
2097 xmlIDTablePtr table;
2098
2099 if (doc == NULL) {
2100 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002101 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002102 return(NULL);
2103 }
2104 if (value == NULL) {
2105 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002106 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002107 return(NULL);
2108 }
2109 if (attr == NULL) {
2110 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002111 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002112 return(NULL);
2113 }
2114
2115 /*
2116 * Create the ID table if needed.
2117 */
2118 table = (xmlIDTablePtr) doc->ids;
2119 if (table == NULL)
2120 doc->ids = table = xmlCreateIDTable();
2121 if (table == NULL) {
2122 xmlGenericError(xmlGenericErrorContext,
2123 "xmlAddID: Table creation failed!\n");
2124 return(NULL);
2125 }
2126
2127 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2128 if (ret == NULL) {
2129 xmlGenericError(xmlGenericErrorContext,
2130 "xmlAddID: out of memory\n");
2131 return(NULL);
2132 }
2133
2134 /*
2135 * fill the structure.
2136 */
2137 ret->value = xmlStrdup(value);
2138 ret->attr = attr;
2139
2140 if (xmlHashAddEntry(table, value, ret) < 0) {
2141 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002142 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002143 */
Daniel Veillard76575762002-09-05 14:21:15 +00002144 if (ctxt != NULL) {
2145 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002146 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002147 }
Owen Taylor3473f882001-02-23 17:55:21 +00002148 xmlFreeID(ret);
2149 return(NULL);
2150 }
2151 return(ret);
2152}
2153
2154/**
2155 * xmlFreeIDTable:
2156 * @table: An id table
2157 *
2158 * Deallocate the memory used by an ID hash table.
2159 */
2160void
2161xmlFreeIDTable(xmlIDTablePtr table) {
2162 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2163}
2164
2165/**
2166 * xmlIsID:
2167 * @doc: the document
2168 * @elem: the element carrying the attribute
2169 * @attr: the attribute
2170 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002171 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002172 * then this is simple, otherwise we use an heuristic: name ID (upper
2173 * or lowercase).
2174 *
2175 * Returns 0 or 1 depending on the lookup result
2176 */
2177int
2178xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2179 if (doc == NULL) return(0);
2180 if (attr == NULL) return(0);
2181 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2182 return(0);
2183 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2184 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2185 (xmlStrEqual(BAD_CAST "name", attr->name)))
2186 return(1);
2187 return(0);
2188 } else {
2189 xmlAttributePtr attrDecl;
2190
2191 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002192 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2193 /*
2194 * TODO: this sucks ... recomputing this every time is stupid
2195 */
2196 int len = xmlStrlen(elem->name) + xmlStrlen(elem->ns->prefix) + 2;
2197 xmlChar *fullname;
2198
2199 fullname = xmlMalloc(len);
2200 if (fullname == NULL)
2201 return(0);
2202 snprintf((char *) fullname, len, "%s:%s", (char *) elem->ns->prefix,
2203 (char *) elem->name);
2204 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2205 attr->name);
2206 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2207 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2208 attr->name);
2209 xmlFree(fullname);
2210 } else {
2211 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2212 attr->name);
2213 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2214 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2215 attr->name);
2216 }
Owen Taylor3473f882001-02-23 17:55:21 +00002217
2218 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2219 return(1);
2220 }
2221 return(0);
2222}
2223
2224/**
2225 * xmlRemoveID
2226 * @doc: the document
2227 * @attr: the attribute
2228 *
2229 * Remove the given attribute from the ID table maintained internally.
2230 *
2231 * Returns -1 if the lookup failed and 0 otherwise
2232 */
2233int
2234xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2235 xmlAttrPtr cur;
2236 xmlIDTablePtr table;
2237 xmlChar *ID;
2238
2239 if (doc == NULL) return(-1);
2240 if (attr == NULL) return(-1);
2241 table = (xmlIDTablePtr) doc->ids;
2242 if (table == NULL)
2243 return(-1);
2244
2245 if (attr == NULL)
2246 return(-1);
2247 ID = xmlNodeListGetString(doc, attr->children, 1);
2248 if (ID == NULL)
2249 return(-1);
2250 cur = xmlHashLookup(table, ID);
2251 if (cur != attr) {
2252 xmlFree(ID);
2253 return(-1);
2254 }
2255 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2256 xmlFree(ID);
2257 return(0);
2258}
2259
2260/**
2261 * xmlGetID:
2262 * @doc: pointer to the document
2263 * @ID: the ID value
2264 *
2265 * Search the attribute declaring the given ID
2266 *
2267 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2268 */
2269xmlAttrPtr
2270xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2271 xmlIDTablePtr table;
2272 xmlIDPtr id;
2273
2274 if (doc == NULL) {
2275 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2276 return(NULL);
2277 }
2278
2279 if (ID == NULL) {
2280 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2281 return(NULL);
2282 }
2283
2284 table = (xmlIDTablePtr) doc->ids;
2285 if (table == NULL)
2286 return(NULL);
2287
2288 id = xmlHashLookup(table, ID);
2289 if (id == NULL)
2290 return(NULL);
2291 return(id->attr);
2292}
2293
2294/************************************************************************
2295 * *
2296 * Refs *
2297 * *
2298 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002299typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002300{
2301 xmlListPtr l;
2302 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002303} xmlRemoveMemo;
2304
2305typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2306
2307typedef struct xmlValidateMemo_t
2308{
2309 xmlValidCtxtPtr ctxt;
2310 const xmlChar *name;
2311} xmlValidateMemo;
2312
2313typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002314
2315/**
2316 * xmlCreateRefTable:
2317 *
2318 * create and initialize an empty ref hash table.
2319 *
2320 * Returns the xmlRefTablePtr just created or NULL in case
2321 * of error.
2322 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002323static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002324xmlCreateRefTable(void) {
2325 return(xmlHashCreate(0));
2326}
2327
2328/**
2329 * xmlFreeRef:
2330 * @lk: A list link
2331 *
2332 * Deallocate the memory used by a ref definition
2333 */
2334static void
2335xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002336 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2337 if (ref == NULL) return;
2338 if (ref->value != NULL)
2339 xmlFree((xmlChar *)ref->value);
2340 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002341}
2342
2343/**
2344 * xmlFreeRefList:
2345 * @list_ref: A list of references.
2346 *
2347 * Deallocate the memory used by a list of references
2348 */
2349static void
2350xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002351 if (list_ref == NULL) return;
2352 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002353}
2354
2355/**
2356 * xmlWalkRemoveRef:
2357 * @data: Contents of current link
2358 * @user: Value supplied by the user
2359 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002360 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002361 */
2362static int
2363xmlWalkRemoveRef(const void *data, const void *user)
2364{
Daniel Veillard37721922001-05-04 15:21:12 +00002365 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2366 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2367 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002368
Daniel Veillard37721922001-05-04 15:21:12 +00002369 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2370 xmlListRemoveFirst(ref_list, (void *)data);
2371 return 0;
2372 }
2373 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002374}
2375
2376/**
2377 * xmlAddRef:
2378 * @ctxt: the validation context
2379 * @doc: pointer to the document
2380 * @value: the value name
2381 * @attr: the attribute holding the Ref
2382 *
2383 * Register a new ref declaration
2384 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002385 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002386 */
2387xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002388xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002389 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002390 xmlRefPtr ret;
2391 xmlRefTablePtr table;
2392 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002393
Daniel Veillard37721922001-05-04 15:21:12 +00002394 if (doc == NULL) {
2395 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002396 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002397 return(NULL);
2398 }
2399 if (value == NULL) {
2400 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002401 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002402 return(NULL);
2403 }
2404 if (attr == NULL) {
2405 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002406 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002407 return(NULL);
2408 }
Owen Taylor3473f882001-02-23 17:55:21 +00002409
Daniel Veillard37721922001-05-04 15:21:12 +00002410 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002411 * Create the Ref table if needed.
2412 */
Daniel Veillard37721922001-05-04 15:21:12 +00002413 table = (xmlRefTablePtr) doc->refs;
2414 if (table == NULL)
2415 doc->refs = table = xmlCreateRefTable();
2416 if (table == NULL) {
2417 xmlGenericError(xmlGenericErrorContext,
2418 "xmlAddRef: Table creation failed!\n");
2419 return(NULL);
2420 }
Owen Taylor3473f882001-02-23 17:55:21 +00002421
Daniel Veillard37721922001-05-04 15:21:12 +00002422 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2423 if (ret == NULL) {
2424 xmlGenericError(xmlGenericErrorContext,
2425 "xmlAddRef: out of memory\n");
2426 return(NULL);
2427 }
Owen Taylor3473f882001-02-23 17:55:21 +00002428
Daniel Veillard37721922001-05-04 15:21:12 +00002429 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002430 * fill the structure.
2431 */
Daniel Veillard37721922001-05-04 15:21:12 +00002432 ret->value = xmlStrdup(value);
2433 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002434
Daniel Veillard37721922001-05-04 15:21:12 +00002435 /* To add a reference :-
2436 * References are maintained as a list of references,
2437 * Lookup the entry, if no entry create new nodelist
2438 * Add the owning node to the NodeList
2439 * Return the ref
2440 */
Owen Taylor3473f882001-02-23 17:55:21 +00002441
Daniel Veillard37721922001-05-04 15:21:12 +00002442 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2443 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2444 xmlGenericError(xmlGenericErrorContext,
2445 "xmlAddRef: Reference list creation failed!\n");
2446 return(NULL);
2447 }
2448 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2449 xmlListDelete(ref_list);
2450 xmlGenericError(xmlGenericErrorContext,
2451 "xmlAddRef: Reference list insertion failed!\n");
2452 return(NULL);
2453 }
2454 }
2455 xmlListInsert(ref_list, ret);
2456 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002457}
2458
2459/**
2460 * xmlFreeRefTable:
2461 * @table: An ref table
2462 *
2463 * Deallocate the memory used by an Ref hash table.
2464 */
2465void
2466xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002467 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002468}
2469
2470/**
2471 * xmlIsRef:
2472 * @doc: the document
2473 * @elem: the element carrying the attribute
2474 * @attr: the attribute
2475 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002476 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002477 * then this is simple, otherwise we use an heuristic: name Ref (upper
2478 * or lowercase).
2479 *
2480 * Returns 0 or 1 depending on the lookup result
2481 */
2482int
2483xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002484 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2485 return(0);
2486 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2487 /* TODO @@@ */
2488 return(0);
2489 } else {
2490 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002491
Daniel Veillard37721922001-05-04 15:21:12 +00002492 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2493 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2494 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2495 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002496
Daniel Veillard37721922001-05-04 15:21:12 +00002497 if ((attrDecl != NULL) &&
2498 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2499 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2500 return(1);
2501 }
2502 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002503}
2504
2505/**
2506 * xmlRemoveRef
2507 * @doc: the document
2508 * @attr: the attribute
2509 *
2510 * Remove the given attribute from the Ref table maintained internally.
2511 *
2512 * Returns -1 if the lookup failed and 0 otherwise
2513 */
2514int
2515xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002516 xmlListPtr ref_list;
2517 xmlRefTablePtr table;
2518 xmlChar *ID;
2519 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002520
Daniel Veillard37721922001-05-04 15:21:12 +00002521 if (doc == NULL) return(-1);
2522 if (attr == NULL) return(-1);
2523 table = (xmlRefTablePtr) doc->refs;
2524 if (table == NULL)
2525 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002526
Daniel Veillard37721922001-05-04 15:21:12 +00002527 if (attr == NULL)
2528 return(-1);
2529 ID = xmlNodeListGetString(doc, attr->children, 1);
2530 if (ID == NULL)
2531 return(-1);
2532 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002533
Daniel Veillard37721922001-05-04 15:21:12 +00002534 if(ref_list == NULL) {
2535 xmlFree(ID);
2536 return (-1);
2537 }
2538 /* At this point, ref_list refers to a list of references which
2539 * have the same key as the supplied attr. Our list of references
2540 * is ordered by reference address and we don't have that information
2541 * here to use when removing. We'll have to walk the list and
2542 * check for a matching attribute, when we find one stop the walk
2543 * and remove the entry.
2544 * The list is ordered by reference, so that means we don't have the
2545 * key. Passing the list and the reference to the walker means we
2546 * will have enough data to be able to remove the entry.
2547 */
2548 target.l = ref_list;
2549 target.ap = attr;
2550
2551 /* Remove the supplied attr from our list */
2552 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002553
Daniel Veillard37721922001-05-04 15:21:12 +00002554 /*If the list is empty then remove the list entry in the hash */
2555 if (xmlListEmpty(ref_list))
2556 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2557 xmlFreeRefList);
2558 xmlFree(ID);
2559 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002560}
2561
2562/**
2563 * xmlGetRefs:
2564 * @doc: pointer to the document
2565 * @ID: the ID value
2566 *
2567 * Find the set of references for the supplied ID.
2568 *
2569 * Returns NULL if not found, otherwise node set for the ID.
2570 */
2571xmlListPtr
2572xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002573 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002574
Daniel Veillard37721922001-05-04 15:21:12 +00002575 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002576 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002577 return(NULL);
2578 }
Owen Taylor3473f882001-02-23 17:55:21 +00002579
Daniel Veillard37721922001-05-04 15:21:12 +00002580 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002581 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002582 return(NULL);
2583 }
Owen Taylor3473f882001-02-23 17:55:21 +00002584
Daniel Veillard37721922001-05-04 15:21:12 +00002585 table = (xmlRefTablePtr) doc->refs;
2586 if (table == NULL)
2587 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002588
Daniel Veillard37721922001-05-04 15:21:12 +00002589 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002590}
2591
2592/************************************************************************
2593 * *
2594 * Routines for validity checking *
2595 * *
2596 ************************************************************************/
2597
2598/**
2599 * xmlGetDtdElementDesc:
2600 * @dtd: a pointer to the DtD to search
2601 * @name: the element name
2602 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002603 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002604 *
2605 * returns the xmlElementPtr if found or NULL
2606 */
2607
2608xmlElementPtr
2609xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2610 xmlElementTablePtr table;
2611 xmlElementPtr cur;
2612 xmlChar *uqname = NULL, *prefix = NULL;
2613
2614 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002615 if (dtd->elements == NULL)
2616 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002617 table = (xmlElementTablePtr) dtd->elements;
2618
2619 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002620 if (uqname != NULL)
2621 name = uqname;
2622 cur = xmlHashLookup2(table, name, prefix);
2623 if (prefix != NULL) xmlFree(prefix);
2624 if (uqname != NULL) xmlFree(uqname);
2625 return(cur);
2626}
2627/**
2628 * xmlGetDtdElementDesc2:
2629 * @dtd: a pointer to the DtD to search
2630 * @name: the element name
2631 * @create: create an empty description if not found
2632 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002633 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002634 *
2635 * returns the xmlElementPtr if found or NULL
2636 */
2637
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002638static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002639xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2640 xmlElementTablePtr table;
2641 xmlElementPtr cur;
2642 xmlChar *uqname = NULL, *prefix = NULL;
2643
2644 if (dtd == NULL) return(NULL);
2645 if (dtd->elements == NULL) {
2646 if (!create)
2647 return(NULL);
2648 /*
2649 * Create the Element table if needed.
2650 */
2651 table = (xmlElementTablePtr) dtd->elements;
2652 if (table == NULL) {
2653 table = xmlCreateElementTable();
2654 dtd->elements = (void *) table;
2655 }
2656 if (table == NULL) {
2657 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002658 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002659 return(NULL);
2660 }
2661 }
2662 table = (xmlElementTablePtr) dtd->elements;
2663
2664 uqname = xmlSplitQName2(name, &prefix);
2665 if (uqname != NULL)
2666 name = uqname;
2667 cur = xmlHashLookup2(table, name, prefix);
2668 if ((cur == NULL) && (create)) {
2669 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2670 if (cur == NULL) {
2671 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002672 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002673 return(NULL);
2674 }
2675 memset(cur, 0, sizeof(xmlElement));
2676 cur->type = XML_ELEMENT_DECL;
2677
2678 /*
2679 * fill the structure.
2680 */
2681 cur->name = xmlStrdup(name);
2682 cur->prefix = xmlStrdup(prefix);
2683 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2684
2685 xmlHashAddEntry2(table, name, prefix, cur);
2686 }
2687 if (prefix != NULL) xmlFree(prefix);
2688 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002689 return(cur);
2690}
2691
2692/**
2693 * xmlGetDtdQElementDesc:
2694 * @dtd: a pointer to the DtD to search
2695 * @name: the element name
2696 * @prefix: the element namespace prefix
2697 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002698 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002699 *
2700 * returns the xmlElementPtr if found or NULL
2701 */
2702
Daniel Veillard48da9102001-08-07 01:10:10 +00002703xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002704xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2705 const xmlChar *prefix) {
2706 xmlElementTablePtr table;
2707
2708 if (dtd == NULL) return(NULL);
2709 if (dtd->elements == NULL) return(NULL);
2710 table = (xmlElementTablePtr) dtd->elements;
2711
2712 return(xmlHashLookup2(table, name, prefix));
2713}
2714
2715/**
2716 * xmlGetDtdAttrDesc:
2717 * @dtd: a pointer to the DtD to search
2718 * @elem: the element name
2719 * @name: the attribute name
2720 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002721 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002722 * this element.
2723 *
2724 * returns the xmlAttributePtr if found or NULL
2725 */
2726
2727xmlAttributePtr
2728xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2729 xmlAttributeTablePtr table;
2730 xmlAttributePtr cur;
2731 xmlChar *uqname = NULL, *prefix = NULL;
2732
2733 if (dtd == NULL) return(NULL);
2734 if (dtd->attributes == NULL) return(NULL);
2735
2736 table = (xmlAttributeTablePtr) dtd->attributes;
2737 if (table == NULL)
2738 return(NULL);
2739
2740 uqname = xmlSplitQName2(name, &prefix);
2741
2742 if (uqname != NULL) {
2743 cur = xmlHashLookup3(table, uqname, prefix, elem);
2744 if (prefix != NULL) xmlFree(prefix);
2745 if (uqname != NULL) xmlFree(uqname);
2746 } else
2747 cur = xmlHashLookup3(table, name, NULL, elem);
2748 return(cur);
2749}
2750
2751/**
2752 * xmlGetDtdQAttrDesc:
2753 * @dtd: a pointer to the DtD to search
2754 * @elem: the element name
2755 * @name: the attribute name
2756 * @prefix: the attribute namespace prefix
2757 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002758 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002759 * this element.
2760 *
2761 * returns the xmlAttributePtr if found or NULL
2762 */
2763
Daniel Veillard48da9102001-08-07 01:10:10 +00002764xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002765xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2766 const xmlChar *prefix) {
2767 xmlAttributeTablePtr table;
2768
2769 if (dtd == NULL) return(NULL);
2770 if (dtd->attributes == NULL) return(NULL);
2771 table = (xmlAttributeTablePtr) dtd->attributes;
2772
2773 return(xmlHashLookup3(table, name, prefix, elem));
2774}
2775
2776/**
2777 * xmlGetDtdNotationDesc:
2778 * @dtd: a pointer to the DtD to search
2779 * @name: the notation name
2780 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002781 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002782 *
2783 * returns the xmlNotationPtr if found or NULL
2784 */
2785
2786xmlNotationPtr
2787xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2788 xmlNotationTablePtr table;
2789
2790 if (dtd == NULL) return(NULL);
2791 if (dtd->notations == NULL) return(NULL);
2792 table = (xmlNotationTablePtr) dtd->notations;
2793
2794 return(xmlHashLookup(table, name));
2795}
2796
2797/**
2798 * xmlValidateNotationUse:
2799 * @ctxt: the validation context
2800 * @doc: the document
2801 * @notationName: the notation name to check
2802 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002803 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002804 * - [ VC: Notation Declared ]
2805 *
2806 * returns 1 if valid or 0 otherwise
2807 */
2808
2809int
2810xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2811 const xmlChar *notationName) {
2812 xmlNotationPtr notaDecl;
2813 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2814
2815 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2816 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2817 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2818
2819 if (notaDecl == NULL) {
2820 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2821 notationName);
2822 return(0);
2823 }
2824 return(1);
2825}
2826
2827/**
2828 * xmlIsMixedElement
2829 * @doc: the document
2830 * @name: the element name
2831 *
2832 * Search in the DtDs whether an element accept Mixed content (or ANY)
2833 * basically if it is supposed to accept text childs
2834 *
2835 * returns 0 if no, 1 if yes, and -1 if no element description is available
2836 */
2837
2838int
2839xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2840 xmlElementPtr elemDecl;
2841
2842 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2843
2844 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2845 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2846 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2847 if (elemDecl == NULL) return(-1);
2848 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002849 case XML_ELEMENT_TYPE_UNDEFINED:
2850 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002851 case XML_ELEMENT_TYPE_ELEMENT:
2852 return(0);
2853 case XML_ELEMENT_TYPE_EMPTY:
2854 /*
2855 * return 1 for EMPTY since we want VC error to pop up
2856 * on <empty> </empty> for example
2857 */
2858 case XML_ELEMENT_TYPE_ANY:
2859 case XML_ELEMENT_TYPE_MIXED:
2860 return(1);
2861 }
2862 return(1);
2863}
2864
2865/**
2866 * xmlValidateNameValue:
2867 * @value: an Name value
2868 *
2869 * Validate that the given value match Name production
2870 *
2871 * returns 1 if valid or 0 otherwise
2872 */
2873
Daniel Veillard9b731d72002-04-14 12:56:08 +00002874int
Owen Taylor3473f882001-02-23 17:55:21 +00002875xmlValidateNameValue(const xmlChar *value) {
2876 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002877 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002878
2879 if (value == NULL) return(0);
2880 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002881 val = xmlStringCurrentChar(NULL, cur, &len);
2882 cur += len;
2883 if (!IS_LETTER(val) && (val != '_') &&
2884 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002885 return(0);
2886 }
2887
Daniel Veillardd8224e02002-01-13 15:43:22 +00002888 val = xmlStringCurrentChar(NULL, cur, &len);
2889 cur += len;
2890 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2891 (val == '.') || (val == '-') ||
2892 (val == '_') || (val == ':') ||
2893 (IS_COMBINING(val)) ||
2894 (IS_EXTENDER(val))) {
2895 val = xmlStringCurrentChar(NULL, cur, &len);
2896 cur += len;
2897 }
Owen Taylor3473f882001-02-23 17:55:21 +00002898
Daniel Veillardd8224e02002-01-13 15:43:22 +00002899 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002900
2901 return(1);
2902}
2903
2904/**
2905 * xmlValidateNamesValue:
2906 * @value: an Names value
2907 *
2908 * Validate that the given value match Names production
2909 *
2910 * returns 1 if valid or 0 otherwise
2911 */
2912
Daniel Veillard9b731d72002-04-14 12:56:08 +00002913int
Owen Taylor3473f882001-02-23 17:55:21 +00002914xmlValidateNamesValue(const xmlChar *value) {
2915 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002916 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002917
2918 if (value == NULL) return(0);
2919 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002920 val = xmlStringCurrentChar(NULL, cur, &len);
2921 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002922
Daniel Veillardd8224e02002-01-13 15:43:22 +00002923 if (!IS_LETTER(val) && (val != '_') &&
2924 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002925 return(0);
2926 }
2927
Daniel Veillardd8224e02002-01-13 15:43:22 +00002928 val = xmlStringCurrentChar(NULL, cur, &len);
2929 cur += len;
2930 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2931 (val == '.') || (val == '-') ||
2932 (val == '_') || (val == ':') ||
2933 (IS_COMBINING(val)) ||
2934 (IS_EXTENDER(val))) {
2935 val = xmlStringCurrentChar(NULL, cur, &len);
2936 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002937 }
2938
Daniel Veillardd8224e02002-01-13 15:43:22 +00002939 while (IS_BLANK(val)) {
2940 while (IS_BLANK(val)) {
2941 val = xmlStringCurrentChar(NULL, cur, &len);
2942 cur += len;
2943 }
2944
2945 if (!IS_LETTER(val) && (val != '_') &&
2946 (val != ':')) {
2947 return(0);
2948 }
2949 val = xmlStringCurrentChar(NULL, cur, &len);
2950 cur += len;
2951
2952 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2953 (val == '.') || (val == '-') ||
2954 (val == '_') || (val == ':') ||
2955 (IS_COMBINING(val)) ||
2956 (IS_EXTENDER(val))) {
2957 val = xmlStringCurrentChar(NULL, cur, &len);
2958 cur += len;
2959 }
2960 }
2961
2962 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002963
2964 return(1);
2965}
2966
2967/**
2968 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002969 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002970 *
2971 * Validate that the given value match Nmtoken production
2972 *
2973 * [ VC: Name Token ]
2974 *
2975 * returns 1 if valid or 0 otherwise
2976 */
2977
Daniel Veillard9b731d72002-04-14 12:56:08 +00002978int
Owen Taylor3473f882001-02-23 17:55:21 +00002979xmlValidateNmtokenValue(const xmlChar *value) {
2980 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002981 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002982
2983 if (value == NULL) return(0);
2984 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002985 val = xmlStringCurrentChar(NULL, cur, &len);
2986 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002987
Daniel Veillardd8224e02002-01-13 15:43:22 +00002988 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2989 (val != '.') && (val != '-') &&
2990 (val != '_') && (val != ':') &&
2991 (!IS_COMBINING(val)) &&
2992 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002993 return(0);
2994
Daniel Veillardd8224e02002-01-13 15:43:22 +00002995 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2996 (val == '.') || (val == '-') ||
2997 (val == '_') || (val == ':') ||
2998 (IS_COMBINING(val)) ||
2999 (IS_EXTENDER(val))) {
3000 val = xmlStringCurrentChar(NULL, cur, &len);
3001 cur += len;
3002 }
Owen Taylor3473f882001-02-23 17:55:21 +00003003
Daniel Veillardd8224e02002-01-13 15:43:22 +00003004 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003005
3006 return(1);
3007}
3008
3009/**
3010 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003011 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003012 *
3013 * Validate that the given value match Nmtokens production
3014 *
3015 * [ VC: Name Token ]
3016 *
3017 * returns 1 if valid or 0 otherwise
3018 */
3019
Daniel Veillard9b731d72002-04-14 12:56:08 +00003020int
Owen Taylor3473f882001-02-23 17:55:21 +00003021xmlValidateNmtokensValue(const xmlChar *value) {
3022 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003023 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003024
3025 if (value == NULL) return(0);
3026 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003027 val = xmlStringCurrentChar(NULL, cur, &len);
3028 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003029
Daniel Veillardd8224e02002-01-13 15:43:22 +00003030 while (IS_BLANK(val)) {
3031 val = xmlStringCurrentChar(NULL, cur, &len);
3032 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003033 }
3034
Daniel Veillardd8224e02002-01-13 15:43:22 +00003035 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3036 (val != '.') && (val != '-') &&
3037 (val != '_') && (val != ':') &&
3038 (!IS_COMBINING(val)) &&
3039 (!IS_EXTENDER(val)))
3040 return(0);
3041
3042 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3043 (val == '.') || (val == '-') ||
3044 (val == '_') || (val == ':') ||
3045 (IS_COMBINING(val)) ||
3046 (IS_EXTENDER(val))) {
3047 val = xmlStringCurrentChar(NULL, cur, &len);
3048 cur += len;
3049 }
3050
3051 while (IS_BLANK(val)) {
3052 while (IS_BLANK(val)) {
3053 val = xmlStringCurrentChar(NULL, cur, &len);
3054 cur += len;
3055 }
3056 if (val == 0) return(1);
3057
3058 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3059 (val != '.') && (val != '-') &&
3060 (val != '_') && (val != ':') &&
3061 (!IS_COMBINING(val)) &&
3062 (!IS_EXTENDER(val)))
3063 return(0);
3064
3065 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3066 (val == '.') || (val == '-') ||
3067 (val == '_') || (val == ':') ||
3068 (IS_COMBINING(val)) ||
3069 (IS_EXTENDER(val))) {
3070 val = xmlStringCurrentChar(NULL, cur, &len);
3071 cur += len;
3072 }
3073 }
3074
3075 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003076
3077 return(1);
3078}
3079
3080/**
3081 * xmlValidateNotationDecl:
3082 * @ctxt: the validation context
3083 * @doc: a document instance
3084 * @nota: a notation definition
3085 *
3086 * Try to validate a single notation definition
3087 * basically it does the following checks as described by the
3088 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003089 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003090 * But this function get called anyway ...
3091 *
3092 * returns 1 if valid or 0 otherwise
3093 */
3094
3095int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003096xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3097 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003098 int ret = 1;
3099
3100 return(ret);
3101}
3102
3103/**
3104 * xmlValidateAttributeValue:
3105 * @type: an attribute type
3106 * @value: an attribute value
3107 *
3108 * Validate that the given attribute value match the proper production
3109 *
3110 * [ VC: ID ]
3111 * Values of type ID must match the Name production....
3112 *
3113 * [ VC: IDREF ]
3114 * Values of type IDREF must match the Name production, and values
3115 * of type IDREFS must match Names ...
3116 *
3117 * [ VC: Entity Name ]
3118 * Values of type ENTITY must match the Name production, values
3119 * of type ENTITIES must match Names ...
3120 *
3121 * [ VC: Name Token ]
3122 * Values of type NMTOKEN must match the Nmtoken production; values
3123 * of type NMTOKENS must match Nmtokens.
3124 *
3125 * returns 1 if valid or 0 otherwise
3126 */
3127
3128int
3129xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3130 switch (type) {
3131 case XML_ATTRIBUTE_ENTITIES:
3132 case XML_ATTRIBUTE_IDREFS:
3133 return(xmlValidateNamesValue(value));
3134 case XML_ATTRIBUTE_ENTITY:
3135 case XML_ATTRIBUTE_IDREF:
3136 case XML_ATTRIBUTE_ID:
3137 case XML_ATTRIBUTE_NOTATION:
3138 return(xmlValidateNameValue(value));
3139 case XML_ATTRIBUTE_NMTOKENS:
3140 case XML_ATTRIBUTE_ENUMERATION:
3141 return(xmlValidateNmtokensValue(value));
3142 case XML_ATTRIBUTE_NMTOKEN:
3143 return(xmlValidateNmtokenValue(value));
3144 case XML_ATTRIBUTE_CDATA:
3145 break;
3146 }
3147 return(1);
3148}
3149
3150/**
3151 * xmlValidateAttributeValue2:
3152 * @ctxt: the validation context
3153 * @doc: the document
3154 * @name: the attribute name (used for error reporting only)
3155 * @type: the attribute type
3156 * @value: the attribute value
3157 *
3158 * Validate that the given attribute value match a given type.
3159 * This typically cannot be done before having finished parsing
3160 * the subsets.
3161 *
3162 * [ VC: IDREF ]
3163 * Values of type IDREF must match one of the declared IDs
3164 * Values of type IDREFS must match a sequence of the declared IDs
3165 * each Name must match the value of an ID attribute on some element
3166 * in the XML document; i.e. IDREF values must match the value of
3167 * some ID attribute
3168 *
3169 * [ VC: Entity Name ]
3170 * Values of type ENTITY must match one declared entity
3171 * Values of type ENTITIES must match a sequence of declared entities
3172 *
3173 * [ VC: Notation Attributes ]
3174 * all notation names in the declaration must be declared.
3175 *
3176 * returns 1 if valid or 0 otherwise
3177 */
3178
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003179static int
Owen Taylor3473f882001-02-23 17:55:21 +00003180xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3181 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3182 int ret = 1;
3183 switch (type) {
3184 case XML_ATTRIBUTE_IDREFS:
3185 case XML_ATTRIBUTE_IDREF:
3186 case XML_ATTRIBUTE_ID:
3187 case XML_ATTRIBUTE_NMTOKENS:
3188 case XML_ATTRIBUTE_ENUMERATION:
3189 case XML_ATTRIBUTE_NMTOKEN:
3190 case XML_ATTRIBUTE_CDATA:
3191 break;
3192 case XML_ATTRIBUTE_ENTITY: {
3193 xmlEntityPtr ent;
3194
3195 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003196 if ((ent == NULL) && (doc->standalone == 1)) {
3197 doc->standalone = 0;
3198 ent = xmlGetDocEntity(doc, value);
3199 if (ent != NULL) {
3200 VERROR(ctxt->userData,
3201"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3202 name, value);
3203 /* WAIT to get answer from the Core WG on this
3204 ret = 0;
3205 */
3206 }
3207 }
Owen Taylor3473f882001-02-23 17:55:21 +00003208 if (ent == NULL) {
3209 VERROR(ctxt->userData,
3210 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3211 name, value);
3212 ret = 0;
3213 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3214 VERROR(ctxt->userData,
3215 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3216 name, value);
3217 ret = 0;
3218 }
3219 break;
3220 }
3221 case XML_ATTRIBUTE_ENTITIES: {
3222 xmlChar *dup, *nam = NULL, *cur, save;
3223 xmlEntityPtr ent;
3224
3225 dup = xmlStrdup(value);
3226 if (dup == NULL)
3227 return(0);
3228 cur = dup;
3229 while (*cur != 0) {
3230 nam = cur;
3231 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3232 save = *cur;
3233 *cur = 0;
3234 ent = xmlGetDocEntity(doc, nam);
3235 if (ent == NULL) {
3236 VERROR(ctxt->userData,
3237 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3238 name, nam);
3239 ret = 0;
3240 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3241 VERROR(ctxt->userData,
3242 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3243 name, nam);
3244 ret = 0;
3245 }
3246 if (save == 0)
3247 break;
3248 *cur = save;
3249 while (IS_BLANK(*cur)) cur++;
3250 }
3251 xmlFree(dup);
3252 break;
3253 }
3254 case XML_ATTRIBUTE_NOTATION: {
3255 xmlNotationPtr nota;
3256
3257 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3258 if ((nota == NULL) && (doc->extSubset != NULL))
3259 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3260
3261 if (nota == NULL) {
3262 VERROR(ctxt->userData,
3263 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3264 name, value);
3265 ret = 0;
3266 }
3267 break;
3268 }
3269 }
3270 return(ret);
3271}
3272
3273/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003274 * xmlValidCtxtNormalizeAttributeValue:
3275 * @ctxt: the validation context
3276 * @doc: the document
3277 * @elem: the parent
3278 * @name: the attribute name
3279 * @value: the attribute value
3280 * @ctxt: the validation context or NULL
3281 *
3282 * Does the validation related extra step of the normalization of attribute
3283 * values:
3284 *
3285 * If the declared value is not CDATA, then the XML processor must further
3286 * process the normalized attribute value by discarding any leading and
3287 * trailing space (#x20) characters, and by replacing sequences of space
3288 * (#x20) characters by single space (#x20) character.
3289 *
3290 * Also check VC: Standalone Document Declaration in P32, and update
3291 * ctxt->valid accordingly
3292 *
3293 * returns a new normalized string if normalization is needed, NULL otherwise
3294 * the caller must free the returned value.
3295 */
3296
3297xmlChar *
3298xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3299 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3300 xmlChar *ret, *dst;
3301 const xmlChar *src;
3302 xmlAttributePtr attrDecl = NULL;
3303 int extsubset = 0;
3304
3305 if (doc == NULL) return(NULL);
3306 if (elem == NULL) return(NULL);
3307 if (name == NULL) return(NULL);
3308 if (value == NULL) return(NULL);
3309
3310 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3311 xmlChar qname[500];
3312 snprintf((char *) qname, sizeof(qname), "%s:%s",
3313 elem->ns->prefix, elem->name);
3314 qname[sizeof(qname) - 1] = 0;
3315 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3316 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3317 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3318 if (attrDecl != NULL)
3319 extsubset = 1;
3320 }
3321 }
3322 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3323 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3324 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3325 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3326 if (attrDecl != NULL)
3327 extsubset = 1;
3328 }
3329
3330 if (attrDecl == NULL)
3331 return(NULL);
3332 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3333 return(NULL);
3334
3335 ret = xmlStrdup(value);
3336 if (ret == NULL)
3337 return(NULL);
3338 src = value;
3339 dst = ret;
3340 while (*src == 0x20) src++;
3341 while (*src != 0) {
3342 if (*src == 0x20) {
3343 while (*src == 0x20) src++;
3344 if (*src != 0)
3345 *dst++ = 0x20;
3346 } else {
3347 *dst++ = *src++;
3348 }
3349 }
3350 *dst = 0;
3351 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3352 VERROR(ctxt->userData,
3353"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3354 name, elem->name);
3355 ctxt->valid = 0;
3356 }
3357 return(ret);
3358}
3359
3360/**
Owen Taylor3473f882001-02-23 17:55:21 +00003361 * xmlValidNormalizeAttributeValue:
3362 * @doc: the document
3363 * @elem: the parent
3364 * @name: the attribute name
3365 * @value: the attribute value
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003366 * @ctxt: the validation context or NULL
Owen Taylor3473f882001-02-23 17:55:21 +00003367 *
3368 * Does the validation related extra step of the normalization of attribute
3369 * values:
3370 *
3371 * If the declared value is not CDATA, then the XML processor must further
3372 * process the normalized attribute value by discarding any leading and
3373 * trailing space (#x20) characters, and by replacing sequences of space
3374 * (#x20) characters by single space (#x20) character.
3375 *
3376 * returns a new normalized string if normalization is needed, NULL otherwise
3377 * the caller must free the returned value.
3378 */
3379
3380xmlChar *
3381xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3382 const xmlChar *name, const xmlChar *value) {
3383 xmlChar *ret, *dst;
3384 const xmlChar *src;
3385 xmlAttributePtr attrDecl = NULL;
3386
3387 if (doc == NULL) return(NULL);
3388 if (elem == NULL) return(NULL);
3389 if (name == NULL) return(NULL);
3390 if (value == NULL) return(NULL);
3391
3392 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3393 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003394 snprintf((char *) qname, sizeof(qname), "%s:%s",
3395 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003396 qname[sizeof(qname) - 1] = 0;
3397 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3398 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3399 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3400 }
3401 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3402 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3403 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3404
3405 if (attrDecl == NULL)
3406 return(NULL);
3407 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3408 return(NULL);
3409
3410 ret = xmlStrdup(value);
3411 if (ret == NULL)
3412 return(NULL);
3413 src = value;
3414 dst = ret;
3415 while (*src == 0x20) src++;
3416 while (*src != 0) {
3417 if (*src == 0x20) {
3418 while (*src == 0x20) src++;
3419 if (*src != 0)
3420 *dst++ = 0x20;
3421 } else {
3422 *dst++ = *src++;
3423 }
3424 }
3425 *dst = 0;
3426 return(ret);
3427}
3428
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003429static void
Owen Taylor3473f882001-02-23 17:55:21 +00003430xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003431 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003432 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3433}
3434
3435/**
3436 * xmlValidateAttributeDecl:
3437 * @ctxt: the validation context
3438 * @doc: a document instance
3439 * @attr: an attribute definition
3440 *
3441 * Try to validate a single attribute definition
3442 * basically it does the following checks as described by the
3443 * XML-1.0 recommendation:
3444 * - [ VC: Attribute Default Legal ]
3445 * - [ VC: Enumeration ]
3446 * - [ VC: ID Attribute Default ]
3447 *
3448 * The ID/IDREF uniqueness and matching are done separately
3449 *
3450 * returns 1 if valid or 0 otherwise
3451 */
3452
3453int
3454xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3455 xmlAttributePtr attr) {
3456 int ret = 1;
3457 int val;
3458 CHECK_DTD;
3459 if(attr == NULL) return(1);
3460
3461 /* Attribute Default Legal */
3462 /* Enumeration */
3463 if (attr->defaultValue != NULL) {
3464 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3465 if (val == 0) {
3466 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003467 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003468 attr->name, attr->elem);
3469 }
3470 ret &= val;
3471 }
3472
3473 /* ID Attribute Default */
3474 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3475 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3476 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3477 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003478 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003479 attr->name, attr->elem);
3480 ret = 0;
3481 }
3482
3483 /* One ID per Element Type */
3484 if (attr->atype == XML_ATTRIBUTE_ID) {
3485 int nbId;
3486
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003487 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003488 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3489 attr->elem);
3490 if (elem != NULL) {
3491 nbId = xmlScanIDAttributeDecl(NULL, elem);
3492 } else {
3493 xmlAttributeTablePtr table;
3494
3495 /*
3496 * The attribute may be declared in the internal subset and the
3497 * element in the external subset.
3498 */
3499 nbId = 0;
3500 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3501 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3502 xmlValidateAttributeIdCallback, &nbId);
3503 }
3504 if (nbId > 1) {
3505 VERROR(ctxt->userData,
3506 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3507 attr->elem, nbId, attr->name);
3508 } else if (doc->extSubset != NULL) {
3509 int extId = 0;
3510 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3511 if (elem != NULL) {
3512 extId = xmlScanIDAttributeDecl(NULL, elem);
3513 }
3514 if (extId > 1) {
3515 VERROR(ctxt->userData,
3516 "Element %s has %d ID attribute defined in the external subset : %s\n",
3517 attr->elem, extId, attr->name);
3518 } else if (extId + nbId > 1) {
3519 VERROR(ctxt->userData,
3520"Element %s has ID attributes defined in the internal and external subset : %s\n",
3521 attr->elem, attr->name);
3522 }
3523 }
3524 }
3525
3526 /* Validity Constraint: Enumeration */
3527 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3528 xmlEnumerationPtr tree = attr->tree;
3529 while (tree != NULL) {
3530 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3531 tree = tree->next;
3532 }
3533 if (tree == NULL) {
3534 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003535"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003536 attr->defaultValue, attr->name, attr->elem);
3537 ret = 0;
3538 }
3539 }
3540
3541 return(ret);
3542}
3543
3544/**
3545 * xmlValidateElementDecl:
3546 * @ctxt: the validation context
3547 * @doc: a document instance
3548 * @elem: an element definition
3549 *
3550 * Try to validate a single element definition
3551 * basically it does the following checks as described by the
3552 * XML-1.0 recommendation:
3553 * - [ VC: One ID per Element Type ]
3554 * - [ VC: No Duplicate Types ]
3555 * - [ VC: Unique Element Type Declaration ]
3556 *
3557 * returns 1 if valid or 0 otherwise
3558 */
3559
3560int
3561xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3562 xmlElementPtr elem) {
3563 int ret = 1;
3564 xmlElementPtr tst;
3565
3566 CHECK_DTD;
3567
3568 if (elem == NULL) return(1);
3569
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003570#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003571#ifdef LIBXML_REGEXP_ENABLED
3572 /* Build the regexp associated to the content model */
3573 ret = xmlValidBuildContentModel(ctxt, elem);
3574#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003575#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003576
Owen Taylor3473f882001-02-23 17:55:21 +00003577 /* No Duplicate Types */
3578 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3579 xmlElementContentPtr cur, next;
3580 const xmlChar *name;
3581
3582 cur = elem->content;
3583 while (cur != NULL) {
3584 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3585 if (cur->c1 == NULL) break;
3586 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3587 name = cur->c1->name;
3588 next = cur->c2;
3589 while (next != NULL) {
3590 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3591 if (xmlStrEqual(next->name, name)) {
3592 VERROR(ctxt->userData,
3593 "Definition of %s has duplicate references of %s\n",
3594 elem->name, name);
3595 ret = 0;
3596 }
3597 break;
3598 }
3599 if (next->c1 == NULL) break;
3600 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3601 if (xmlStrEqual(next->c1->name, name)) {
3602 VERROR(ctxt->userData,
3603 "Definition of %s has duplicate references of %s\n",
3604 elem->name, name);
3605 ret = 0;
3606 }
3607 next = next->c2;
3608 }
3609 }
3610 cur = cur->c2;
3611 }
3612 }
3613
3614 /* VC: Unique Element Type Declaration */
3615 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003616 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003617 ((tst->prefix == elem->prefix) ||
3618 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003619 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003620 VERROR(ctxt->userData, "Redefinition of element %s\n",
3621 elem->name);
3622 ret = 0;
3623 }
3624 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003625 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003626 ((tst->prefix == elem->prefix) ||
3627 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003628 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003629 VERROR(ctxt->userData, "Redefinition of element %s\n",
3630 elem->name);
3631 ret = 0;
3632 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003633 /* One ID per Element Type
3634 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003635 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3636 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003637 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003638 return(ret);
3639}
3640
3641/**
3642 * xmlValidateOneAttribute:
3643 * @ctxt: the validation context
3644 * @doc: a document instance
3645 * @elem: an element instance
3646 * @attr: an attribute instance
3647 * @value: the attribute value (without entities processing)
3648 *
3649 * Try to validate a single attribute for an element
3650 * basically it does the following checks as described by the
3651 * XML-1.0 recommendation:
3652 * - [ VC: Attribute Value Type ]
3653 * - [ VC: Fixed Attribute Default ]
3654 * - [ VC: Entity Name ]
3655 * - [ VC: Name Token ]
3656 * - [ VC: ID ]
3657 * - [ VC: IDREF ]
3658 * - [ VC: Entity Name ]
3659 * - [ VC: Notation Attributes ]
3660 *
3661 * The ID/IDREF uniqueness and matching are done separately
3662 *
3663 * returns 1 if valid or 0 otherwise
3664 */
3665
3666int
3667xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3668 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3669 /* xmlElementPtr elemDecl; */
3670 xmlAttributePtr attrDecl = NULL;
3671 int val;
3672 int ret = 1;
3673
3674 CHECK_DTD;
3675 if ((elem == NULL) || (elem->name == NULL)) return(0);
3676 if ((attr == NULL) || (attr->name == NULL)) return(0);
3677
3678 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3679 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003680 snprintf((char *) qname, sizeof(qname), "%s:%s",
3681 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003682 qname[sizeof(qname) - 1] = 0;
3683 if (attr->ns != NULL) {
3684 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3685 attr->name, attr->ns->prefix);
3686 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3687 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3688 attr->name, attr->ns->prefix);
3689 } else {
3690 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3691 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3692 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3693 qname, attr->name);
3694 }
3695 }
3696 if (attrDecl == NULL) {
3697 if (attr->ns != NULL) {
3698 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3699 attr->name, attr->ns->prefix);
3700 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3701 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3702 attr->name, attr->ns->prefix);
3703 } else {
3704 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3705 elem->name, attr->name);
3706 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3707 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3708 elem->name, attr->name);
3709 }
3710 }
3711
3712
3713 /* Validity Constraint: Attribute Value Type */
3714 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003715 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003716 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003717 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003718 attr->name, elem->name);
3719 return(0);
3720 }
3721 attr->atype = attrDecl->atype;
3722
3723 val = xmlValidateAttributeValue(attrDecl->atype, value);
3724 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003725 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003726 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003727 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003728 attr->name, elem->name);
3729 ret = 0;
3730 }
3731
3732 /* Validity constraint: Fixed Attribute Default */
3733 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3734 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003735 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003736 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003737 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003738 attr->name, elem->name, attrDecl->defaultValue);
3739 ret = 0;
3740 }
3741 }
3742
3743 /* Validity Constraint: ID uniqueness */
3744 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3745 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3746 ret = 0;
3747 }
3748
3749 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3750 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3751 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3752 ret = 0;
3753 }
3754
3755 /* Validity Constraint: Notation Attributes */
3756 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3757 xmlEnumerationPtr tree = attrDecl->tree;
3758 xmlNotationPtr nota;
3759
3760 /* First check that the given NOTATION was declared */
3761 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3762 if (nota == NULL)
3763 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3764
3765 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003766 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003767 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003768 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003769 value, attr->name, elem->name);
3770 ret = 0;
3771 }
3772
3773 /* Second, verify that it's among the list */
3774 while (tree != NULL) {
3775 if (xmlStrEqual(tree->name, value)) break;
3776 tree = tree->next;
3777 }
3778 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003779 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003780 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003781"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003782 value, attr->name, elem->name);
3783 ret = 0;
3784 }
3785 }
3786
3787 /* Validity Constraint: Enumeration */
3788 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3789 xmlEnumerationPtr tree = attrDecl->tree;
3790 while (tree != NULL) {
3791 if (xmlStrEqual(tree->name, value)) break;
3792 tree = tree->next;
3793 }
3794 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003795 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003796 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003797 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003798 value, attr->name, elem->name);
3799 ret = 0;
3800 }
3801 }
3802
3803 /* Fixed Attribute Default */
3804 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3805 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003806 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003807 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003808 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003809 attr->name, elem->name, attrDecl->defaultValue);
3810 ret = 0;
3811 }
3812
3813 /* Extra check for the attribute value */
3814 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3815 attrDecl->atype, value);
3816
3817 return(ret);
3818}
3819
Daniel Veillard90d68fb2002-09-26 16:10:21 +00003820/**
3821 * xmlValidateOneNamespace:
3822 * @ctxt: the validation context
3823 * @doc: a document instance
3824 * @elem: an element instance
3825 * @ns: an namespace declaration instance
3826 * @value: the attribute value (without entities processing)
3827 *
3828 * Try to validate a single namespace declaration for an element
3829 * basically it does the following checks as described by the
3830 * XML-1.0 recommendation:
3831 * - [ VC: Attribute Value Type ]
3832 * - [ VC: Fixed Attribute Default ]
3833 * - [ VC: Entity Name ]
3834 * - [ VC: Name Token ]
3835 * - [ VC: ID ]
3836 * - [ VC: IDREF ]
3837 * - [ VC: Entity Name ]
3838 * - [ VC: Notation Attributes ]
3839 *
3840 * The ID/IDREF uniqueness and matching are done separately
3841 *
3842 * returns 1 if valid or 0 otherwise
3843 */
3844
3845int
3846xmlValidateOneNamespace(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3847xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) {
3848 /* xmlElementPtr elemDecl; */
3849 xmlAttributePtr attrDecl = NULL;
3850 int val;
3851 int ret = 1;
3852
3853 CHECK_DTD;
3854 if ((elem == NULL) || (elem->name == NULL)) return(0);
3855 if ((ns == NULL) || (ns->href == NULL)) return(0);
3856
3857 if (prefix != NULL) {
3858 xmlChar qname[500];
3859 snprintf((char *) qname, sizeof(qname), "%s:%s",
3860 prefix, elem->name);
3861 qname[sizeof(qname) - 1] = 0;
3862 if (ns->prefix != NULL) {
3863 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3864 ns->prefix, BAD_CAST "xmlns");
3865 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3866 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3867 ns->prefix, BAD_CAST "xmlns");
3868 } else {
3869 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname,
3870 BAD_CAST "xmlns");
3871 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3872 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname,
3873 BAD_CAST "xmlns");
3874 }
3875 }
3876 if (attrDecl == NULL) {
3877 if (ns->prefix != NULL) {
3878 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3879 ns->prefix, BAD_CAST "xmlns");
3880 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3881 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3882 ns->prefix, BAD_CAST "xmlns");
3883 } else {
3884 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3885 elem->name, BAD_CAST "xmlns");
3886 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3887 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3888 elem->name, BAD_CAST "xmlns");
3889 }
3890 }
3891
3892
3893 /* Validity Constraint: Attribute Value Type */
3894 if (attrDecl == NULL) {
3895 VECTXT(ctxt, elem);
3896 if (ns->prefix != NULL) {
3897 VERROR(ctxt->userData,
3898 "No declaration for attribute xmlns:%s of element %s\n",
3899 ns->prefix, elem->name);
3900 } else {
3901 VERROR(ctxt->userData,
3902 "No declaration for attribute xmlns of element %s\n",
3903 elem->name);
3904 }
3905 return(0);
3906 }
3907
3908 val = xmlValidateAttributeValue(attrDecl->atype, value);
3909 if (val == 0) {
3910 VECTXT(ctxt, elem);
3911 if (ns->prefix != NULL) {
3912 VERROR(ctxt->userData,
3913 "Syntax of value for attribute xmlns:%s of %s is not valid\n",
3914 ns->prefix, elem->name);
3915 } else {
3916 VERROR(ctxt->userData,
3917 "Syntax of value for attribute xmlns of %s is not valid\n",
3918 elem->name);
3919 }
3920 ret = 0;
3921 }
3922
3923 /* Validity constraint: Fixed Attribute Default */
3924 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3925 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3926 VECTXT(ctxt, elem);
3927 if (ns->prefix != NULL) {
3928 VERROR(ctxt->userData,
3929 "Value for attribute xmlns:%s of %s is different from default \"%s\"\n",
3930 ns->prefix, elem->name, attrDecl->defaultValue);
3931 } else {
3932 VERROR(ctxt->userData,
3933 "Value for attribute xmlns of %s is different from default \"%s\"\n",
3934 elem->name, attrDecl->defaultValue);
3935 }
3936 ret = 0;
3937 }
3938 }
3939
3940 /* Validity Constraint: ID uniqueness */
3941 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3942 if (xmlAddID(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
3943 ret = 0;
3944 }
3945
3946 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3947 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3948 if (xmlAddRef(ctxt, doc, value, (xmlAttrPtr) ns) == NULL)
3949 ret = 0;
3950 }
3951
3952 /* Validity Constraint: Notation Attributes */
3953 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3954 xmlEnumerationPtr tree = attrDecl->tree;
3955 xmlNotationPtr nota;
3956
3957 /* First check that the given NOTATION was declared */
3958 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3959 if (nota == NULL)
3960 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3961
3962 if (nota == NULL) {
3963 VECTXT(ctxt, elem);
3964 if (ns->prefix != NULL) {
3965 VERROR(ctxt->userData,
3966 "Value \"%s\" for attribute xmlns:%s of %s is not a declared Notation\n",
3967 value, ns->prefix, elem->name);
3968 } else {
3969 VERROR(ctxt->userData,
3970 "Value \"%s\" for attribute xmlns of %s is not a declared Notation\n",
3971 value, elem->name);
3972 }
3973 ret = 0;
3974 }
3975
3976 /* Second, verify that it's among the list */
3977 while (tree != NULL) {
3978 if (xmlStrEqual(tree->name, value)) break;
3979 tree = tree->next;
3980 }
3981 if (tree == NULL) {
3982 VECTXT(ctxt, elem);
3983 if (ns->prefix != NULL) {
3984 VERROR(ctxt->userData,
3985"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated notations\n",
3986 value, ns->prefix, elem->name);
3987 } else {
3988 VERROR(ctxt->userData,
3989"Value \"%s\" for attribute xmlns of %s is not among the enumerated notations\n",
3990 value, elem->name);
3991 }
3992 ret = 0;
3993 }
3994 }
3995
3996 /* Validity Constraint: Enumeration */
3997 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3998 xmlEnumerationPtr tree = attrDecl->tree;
3999 while (tree != NULL) {
4000 if (xmlStrEqual(tree->name, value)) break;
4001 tree = tree->next;
4002 }
4003 if (tree == NULL) {
4004 VECTXT(ctxt, elem);
4005 if (ns->prefix != NULL) {
4006 VERROR(ctxt->userData,
4007"Value \"%s\" for attribute xmlns:%s of %s is not among the enumerated set\n",
4008 value, ns->prefix, elem->name);
4009 } else {
4010 VERROR(ctxt->userData,
4011"Value \"%s\" for attribute xmlns of %s is not among the enumerated set\n",
4012 value, elem->name);
4013 }
4014 ret = 0;
4015 }
4016 }
4017
4018 /* Fixed Attribute Default */
4019 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
4020 (!xmlStrEqual(attrDecl->defaultValue, value))) {
4021 VECTXT(ctxt, elem);
4022 if (ns->prefix != NULL) {
4023 VERROR(ctxt->userData,
4024 "Value for attribute xmlns:%s of %s must be \"%s\"\n",
4025 ns->prefix, elem->name, attrDecl->defaultValue);
4026 } else {
4027 VERROR(ctxt->userData,
4028 "Value for attribute xmlns of %s must be \"%s\"\n",
4029 elem->name, attrDecl->defaultValue);
4030 }
4031 ret = 0;
4032 }
4033
4034 /* Extra check for the attribute value */
4035 if (ns->prefix != NULL) {
4036 ret &= xmlValidateAttributeValue2(ctxt, doc, ns->prefix,
4037 attrDecl->atype, value);
4038 } else {
4039 ret &= xmlValidateAttributeValue2(ctxt, doc, BAD_CAST "xmlns",
4040 attrDecl->atype, value);
4041 }
4042
4043 return(ret);
4044}
4045
Daniel Veillard118aed72002-09-24 14:13:13 +00004046#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004047/**
4048 * xmlValidateSkipIgnorable:
4049 * @ctxt: the validation context
4050 * @child: the child list
4051 *
4052 * Skip ignorable elements w.r.t. the validation process
4053 *
4054 * returns the first element to consider for validation of the content model
4055 */
4056
4057static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004058xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004059 while (child != NULL) {
4060 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004061 /* These things are ignored (skipped) during validation. */
4062 case XML_PI_NODE:
4063 case XML_COMMENT_NODE:
4064 case XML_XINCLUDE_START:
4065 case XML_XINCLUDE_END:
4066 child = child->next;
4067 break;
4068 case XML_TEXT_NODE:
4069 if (xmlIsBlankNode(child))
4070 child = child->next;
4071 else
4072 return(child);
4073 break;
4074 /* keep current node */
4075 default:
4076 return(child);
4077 }
4078 }
4079 return(child);
4080}
4081
4082/**
4083 * xmlValidateElementType:
4084 * @ctxt: the validation context
4085 *
4086 * Try to validate the content model of an element internal function
4087 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004088 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
4089 * reference is found and -3 if the validation succeeded but
4090 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004091 */
4092
4093static int
4094xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004095 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004096 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004097
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004098 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004099 if ((NODE == NULL) && (CONT == NULL))
4100 return(1);
4101 if ((NODE == NULL) &&
4102 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
4103 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
4104 return(1);
4105 }
4106 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00004107 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004108 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004109
4110 /*
4111 * We arrive here when more states need to be examined
4112 */
4113cont:
4114
4115 /*
4116 * We just recovered from a rollback generated by a possible
4117 * epsilon transition, go directly to the analysis phase
4118 */
4119 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004120 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004121 DEBUG_VALID_STATE(NODE, CONT)
4122 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004123 goto analyze;
4124 }
4125
4126 DEBUG_VALID_STATE(NODE, CONT)
4127 /*
4128 * we may have to save a backup state here. This is the equivalent
4129 * of handling epsilon transition in NFAs.
4130 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00004131 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00004132 ((CONT->parent == NULL) ||
4133 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004134 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00004135 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00004136 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004137 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004138 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
4139 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004140 }
4141
4142
4143 /*
4144 * Check first if the content matches
4145 */
4146 switch (CONT->type) {
4147 case XML_ELEMENT_CONTENT_PCDATA:
4148 if (NODE == NULL) {
4149 DEBUG_VALID_MSG("pcdata failed no node");
4150 ret = 0;
4151 break;
4152 }
4153 if (NODE->type == XML_TEXT_NODE) {
4154 DEBUG_VALID_MSG("pcdata found, skip to next");
4155 /*
4156 * go to next element in the content model
4157 * skipping ignorable elems
4158 */
4159 do {
4160 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004161 NODE = xmlValidateSkipIgnorable(NODE);
4162 if ((NODE != NULL) &&
4163 (NODE->type == XML_ENTITY_REF_NODE))
4164 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004165 } while ((NODE != NULL) &&
4166 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004167 (NODE->type != XML_TEXT_NODE) &&
4168 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004169 ret = 1;
4170 break;
4171 } else {
4172 DEBUG_VALID_MSG("pcdata failed");
4173 ret = 0;
4174 break;
4175 }
4176 break;
4177 case XML_ELEMENT_CONTENT_ELEMENT:
4178 if (NODE == NULL) {
4179 DEBUG_VALID_MSG("element failed no node");
4180 ret = 0;
4181 break;
4182 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00004183 ret = ((NODE->type == XML_ELEMENT_NODE) &&
4184 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004185 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004186 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4187 ret = (CONT->prefix == NULL);
4188 } else if (CONT->prefix == NULL) {
4189 ret = 0;
4190 } else {
4191 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
4192 }
4193 }
4194 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004195 DEBUG_VALID_MSG("element found, skip to next");
4196 /*
4197 * go to next element in the content model
4198 * skipping ignorable elems
4199 */
4200 do {
4201 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004202 NODE = xmlValidateSkipIgnorable(NODE);
4203 if ((NODE != NULL) &&
4204 (NODE->type == XML_ENTITY_REF_NODE))
4205 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004206 } while ((NODE != NULL) &&
4207 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004208 (NODE->type != XML_TEXT_NODE) &&
4209 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004210 } else {
4211 DEBUG_VALID_MSG("element failed");
4212 ret = 0;
4213 break;
4214 }
4215 break;
4216 case XML_ELEMENT_CONTENT_OR:
4217 /*
Daniel Veillard85349052001-04-20 13:48:21 +00004218 * Small optimization.
4219 */
4220 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
4221 if ((NODE == NULL) ||
4222 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4223 DEPTH++;
4224 CONT = CONT->c2;
4225 goto cont;
4226 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004227 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4228 ret = (CONT->c1->prefix == NULL);
4229 } else if (CONT->c1->prefix == NULL) {
4230 ret = 0;
4231 } else {
4232 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4233 }
4234 if (ret == 0) {
4235 DEPTH++;
4236 CONT = CONT->c2;
4237 goto cont;
4238 }
Daniel Veillard85349052001-04-20 13:48:21 +00004239 }
4240
4241 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004242 * save the second branch 'or' branch
4243 */
4244 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004245 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4246 OCCURS, ROLLBACK_OR) < 0)
4247 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004248 DEPTH++;
4249 CONT = CONT->c1;
4250 goto cont;
4251 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004252 /*
4253 * Small optimization.
4254 */
4255 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4256 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4257 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4258 if ((NODE == NULL) ||
4259 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4260 DEPTH++;
4261 CONT = CONT->c2;
4262 goto cont;
4263 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004264 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4265 ret = (CONT->c1->prefix == NULL);
4266 } else if (CONT->c1->prefix == NULL) {
4267 ret = 0;
4268 } else {
4269 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4270 }
4271 if (ret == 0) {
4272 DEPTH++;
4273 CONT = CONT->c2;
4274 goto cont;
4275 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004276 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004277 DEPTH++;
4278 CONT = CONT->c1;
4279 goto cont;
4280 }
4281
4282 /*
4283 * At this point handle going up in the tree
4284 */
4285 if (ret == -1) {
4286 DEBUG_VALID_MSG("error found returning");
4287 return(ret);
4288 }
4289analyze:
4290 while (CONT != NULL) {
4291 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004292 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004293 * this level.
4294 */
4295 if (ret == 0) {
4296 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004297 xmlNodePtr cur;
4298
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004299 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004300 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004301 DEBUG_VALID_MSG("Once branch failed, rollback");
4302 if (vstateVPop(ctxt) < 0 ) {
4303 DEBUG_VALID_MSG("exhaustion, failed");
4304 return(0);
4305 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004306 if (cur != ctxt->vstate->node)
4307 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004308 goto cont;
4309 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004310 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004311 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004312 DEBUG_VALID_MSG("Plus branch failed, rollback");
4313 if (vstateVPop(ctxt) < 0 ) {
4314 DEBUG_VALID_MSG("exhaustion, failed");
4315 return(0);
4316 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004317 if (cur != ctxt->vstate->node)
4318 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004319 goto cont;
4320 }
4321 DEBUG_VALID_MSG("Plus branch found");
4322 ret = 1;
4323 break;
4324 case XML_ELEMENT_CONTENT_MULT:
4325#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004326 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004327 DEBUG_VALID_MSG("Mult branch failed");
4328 } else {
4329 DEBUG_VALID_MSG("Mult branch found");
4330 }
4331#endif
4332 ret = 1;
4333 break;
4334 case XML_ELEMENT_CONTENT_OPT:
4335 DEBUG_VALID_MSG("Option branch failed");
4336 ret = 1;
4337 break;
4338 }
4339 } else {
4340 switch (CONT->ocur) {
4341 case XML_ELEMENT_CONTENT_OPT:
4342 DEBUG_VALID_MSG("Option branch succeeded");
4343 ret = 1;
4344 break;
4345 case XML_ELEMENT_CONTENT_ONCE:
4346 DEBUG_VALID_MSG("Once branch succeeded");
4347 ret = 1;
4348 break;
4349 case XML_ELEMENT_CONTENT_PLUS:
4350 if (STATE == ROLLBACK_PARENT) {
4351 DEBUG_VALID_MSG("Plus branch rollback");
4352 ret = 1;
4353 break;
4354 }
4355 if (NODE == NULL) {
4356 DEBUG_VALID_MSG("Plus branch exhausted");
4357 ret = 1;
4358 break;
4359 }
4360 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004361 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004362 goto cont;
4363 case XML_ELEMENT_CONTENT_MULT:
4364 if (STATE == ROLLBACK_PARENT) {
4365 DEBUG_VALID_MSG("Mult branch rollback");
4366 ret = 1;
4367 break;
4368 }
4369 if (NODE == NULL) {
4370 DEBUG_VALID_MSG("Mult branch exhausted");
4371 ret = 1;
4372 break;
4373 }
4374 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004375 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004376 goto cont;
4377 }
4378 }
4379 STATE = 0;
4380
4381 /*
4382 * Then act accordingly at the parent level
4383 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004384 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004385 if (CONT->parent == NULL)
4386 break;
4387
4388 switch (CONT->parent->type) {
4389 case XML_ELEMENT_CONTENT_PCDATA:
4390 DEBUG_VALID_MSG("Error: parent pcdata");
4391 return(-1);
4392 case XML_ELEMENT_CONTENT_ELEMENT:
4393 DEBUG_VALID_MSG("Error: parent element");
4394 return(-1);
4395 case XML_ELEMENT_CONTENT_OR:
4396 if (ret == 1) {
4397 DEBUG_VALID_MSG("Or succeeded");
4398 CONT = CONT->parent;
4399 DEPTH--;
4400 } else {
4401 DEBUG_VALID_MSG("Or failed");
4402 CONT = CONT->parent;
4403 DEPTH--;
4404 }
4405 break;
4406 case XML_ELEMENT_CONTENT_SEQ:
4407 if (ret == 0) {
4408 DEBUG_VALID_MSG("Sequence failed");
4409 CONT = CONT->parent;
4410 DEPTH--;
4411 } else if (CONT == CONT->parent->c1) {
4412 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4413 CONT = CONT->parent->c2;
4414 goto cont;
4415 } else {
4416 DEBUG_VALID_MSG("Sequence succeeded");
4417 CONT = CONT->parent;
4418 DEPTH--;
4419 }
4420 }
4421 }
4422 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004423 xmlNodePtr cur;
4424
4425 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004426 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4427 if (vstateVPop(ctxt) < 0 ) {
4428 DEBUG_VALID_MSG("exhaustion, failed");
4429 return(0);
4430 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004431 if (cur != ctxt->vstate->node)
4432 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004433 goto cont;
4434 }
4435 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004436 xmlNodePtr cur;
4437
4438 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004439 DEBUG_VALID_MSG("Failure, rollback");
4440 if (vstateVPop(ctxt) < 0 ) {
4441 DEBUG_VALID_MSG("exhaustion, failed");
4442 return(0);
4443 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004444 if (cur != ctxt->vstate->node)
4445 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004446 goto cont;
4447 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004448 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004449}
Daniel Veillard23e73572002-09-19 19:56:43 +00004450#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004451
4452/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004453 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004454 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004455 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004456 * @content: An element
4457 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4458 *
4459 * This will dump the list of elements to the buffer
4460 * Intended just for the debug routine
4461 */
4462static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004463xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004464 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004465 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004466
4467 if (node == NULL) return;
4468 if (glob) strcat(buf, "(");
4469 cur = node;
4470 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004471 len = strlen(buf);
4472 if (size - len < 50) {
4473 if ((size - len > 4) && (buf[len - 1] != '.'))
4474 strcat(buf, " ...");
4475 return;
4476 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004477 switch (cur->type) {
4478 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004479 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004480 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004481 if ((size - len > 4) && (buf[len - 1] != '.'))
4482 strcat(buf, " ...");
4483 return;
4484 }
4485 strcat(buf, (char *) cur->ns->prefix);
4486 strcat(buf, ":");
4487 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004488 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004489 if ((size - len > 4) && (buf[len - 1] != '.'))
4490 strcat(buf, " ...");
4491 return;
4492 }
4493 strcat(buf, (char *) cur->name);
4494 if (cur->next != NULL)
4495 strcat(buf, " ");
4496 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004497 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004498 if (xmlIsBlankNode(cur))
4499 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004500 case XML_CDATA_SECTION_NODE:
4501 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004502 strcat(buf, "CDATA");
4503 if (cur->next != NULL)
4504 strcat(buf, " ");
4505 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004506 case XML_ATTRIBUTE_NODE:
4507 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004508#ifdef LIBXML_DOCB_ENABLED
4509 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004510#endif
4511 case XML_HTML_DOCUMENT_NODE:
4512 case XML_DOCUMENT_TYPE_NODE:
4513 case XML_DOCUMENT_FRAG_NODE:
4514 case XML_NOTATION_NODE:
4515 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004516 strcat(buf, "???");
4517 if (cur->next != NULL)
4518 strcat(buf, " ");
4519 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004520 case XML_ENTITY_NODE:
4521 case XML_PI_NODE:
4522 case XML_DTD_NODE:
4523 case XML_COMMENT_NODE:
4524 case XML_ELEMENT_DECL:
4525 case XML_ATTRIBUTE_DECL:
4526 case XML_ENTITY_DECL:
4527 case XML_XINCLUDE_START:
4528 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004529 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004530 }
4531 cur = cur->next;
4532 }
4533 if (glob) strcat(buf, ")");
4534}
4535
4536/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004537 * xmlValidateElementContent:
4538 * @ctxt: the validation context
4539 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004540 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004541 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004542 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004543 *
4544 * Try to validate the content model of an element
4545 *
4546 * returns 1 if valid or 0 if not and -1 in case of error
4547 */
4548
4549static int
4550xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004551 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004552 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004553#ifndef LIBXML_REGEXP_ENABLED
4554 xmlNodePtr last = NULL;
4555#endif
4556 xmlNodePtr repl = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004557 xmlElementContentPtr cont;
4558 const xmlChar *name;
4559
4560 if (elemDecl == NULL)
4561 return(-1);
4562 cont = elemDecl->content;
4563 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004564
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004565#ifdef LIBXML_REGEXP_ENABLED
4566 /* Build the regexp associated to the content model */
4567 if (elemDecl->contModel == NULL)
4568 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4569 if (elemDecl->contModel == NULL) {
4570 ret = -1;
4571 } else {
4572 xmlRegExecCtxtPtr exec;
4573
4574 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4575 if (exec != NULL) {
4576 cur = child;
4577 while (cur != NULL) {
4578 switch (cur->type) {
4579 case XML_ENTITY_REF_NODE:
4580 /*
4581 * Push the current node to be able to roll back
4582 * and process within the entity
4583 */
4584 if ((cur->children != NULL) &&
4585 (cur->children->children != NULL)) {
4586 nodeVPush(ctxt, cur);
4587 cur = cur->children->children;
4588 continue;
4589 }
4590 break;
4591 case XML_TEXT_NODE:
4592 if (xmlIsBlankNode(cur))
4593 break;
4594 ret = 0;
4595 goto fail;
4596 case XML_CDATA_SECTION_NODE:
4597 TODO
4598 ret = 0;
4599 goto fail;
4600 case XML_ELEMENT_NODE:
4601 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
4602 xmlChar *QName;
4603 int len;
4604
4605 len = xmlStrlen(cur->name) +
4606 xmlStrlen(cur->ns->prefix) + 2;
4607 QName = xmlMalloc(len);
4608 if (QName == NULL) {
4609 ret = -1;
4610 goto fail;
4611 }
4612 snprintf((char *) QName, len, "%s:%s",
4613 (char *)cur->ns->prefix,
4614 (char *)cur->name);
4615 ret = xmlRegExecPushString(exec, QName, NULL);
4616 xmlFree(QName);
4617 } else {
4618 ret = xmlRegExecPushString(exec, cur->name, NULL);
4619 }
4620 break;
4621 default:
4622 break;
4623 }
4624 /*
4625 * Switch to next element
4626 */
4627 cur = cur->next;
4628 while (cur == NULL) {
4629 cur = nodeVPop(ctxt);
4630 if (cur == NULL)
4631 break;
4632 cur = cur->next;
4633 }
4634 }
4635 ret = xmlRegExecPushString(exec, NULL, NULL);
4636fail:
4637 xmlRegFreeExecCtxt(exec);
4638 }
4639 }
4640#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004641 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004642 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004643 */
4644 ctxt->vstateMax = 8;
4645 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4646 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4647 if (ctxt->vstateTab == NULL) {
4648 xmlGenericError(xmlGenericErrorContext,
4649 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004650 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004651 }
4652 /*
4653 * The first entry in the stack is reserved to the current state
4654 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004655 ctxt->nodeMax = 0;
4656 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004657 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004658 ctxt->vstate = &ctxt->vstateTab[0];
4659 ctxt->vstateNr = 1;
4660 CONT = cont;
4661 NODE = child;
4662 DEPTH = 0;
4663 OCCURS = 0;
4664 STATE = 0;
4665 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004666 if ((ret == -3) && (warn)) {
4667 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004668 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004669 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004670 /*
4671 * An entities reference appeared at this level.
4672 * Buid a minimal representation of this node content
4673 * sufficient to run the validation process on it
4674 */
4675 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004676 cur = child;
4677 while (cur != NULL) {
4678 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004679 case XML_ENTITY_REF_NODE:
4680 /*
4681 * Push the current node to be able to roll back
4682 * and process within the entity
4683 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004684 if ((cur->children != NULL) &&
4685 (cur->children->children != NULL)) {
4686 nodeVPush(ctxt, cur);
4687 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004688 continue;
4689 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004690 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004691 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004692 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004693 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004694 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004695 case XML_CDATA_SECTION_NODE:
4696 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004697 case XML_ELEMENT_NODE:
4698 /*
4699 * Allocate a new node and minimally fills in
4700 * what's required
4701 */
4702 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4703 if (tmp == NULL) {
4704 xmlGenericError(xmlGenericErrorContext,
4705 "xmlValidateElementContent : malloc failed\n");
4706 xmlFreeNodeList(repl);
4707 ret = -1;
4708 goto done;
4709 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004710 tmp->type = cur->type;
4711 tmp->name = cur->name;
4712 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004713 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004714 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004715 if (repl == NULL)
4716 repl = last = tmp;
4717 else {
4718 last->next = tmp;
4719 last = tmp;
4720 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004721 if (cur->type == XML_CDATA_SECTION_NODE) {
4722 /*
4723 * E59 spaces in CDATA does not match the
4724 * nonterminal S
4725 */
4726 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4727 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004728 break;
4729 default:
4730 break;
4731 }
4732 /*
4733 * Switch to next element
4734 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004735 cur = cur->next;
4736 while (cur == NULL) {
4737 cur = nodeVPop(ctxt);
4738 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004739 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004740 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004741 }
4742 }
4743
4744 /*
4745 * Relaunch the validation
4746 */
4747 ctxt->vstate = &ctxt->vstateTab[0];
4748 ctxt->vstateNr = 1;
4749 CONT = cont;
4750 NODE = repl;
4751 DEPTH = 0;
4752 OCCURS = 0;
4753 STATE = 0;
4754 ret = xmlValidateElementType(ctxt);
4755 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004756#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004757 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004758 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4759 char expr[5000];
4760 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004761
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004762 expr[0] = 0;
4763 xmlSnprintfElementContent(expr, 5000, cont, 1);
4764 list[0] = 0;
4765 if (repl != NULL)
4766 xmlSnprintfElements(list, 5000, repl, 1);
4767 else
4768 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004769
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004770 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004771 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004772 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004773 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004774 name, expr, list);
4775 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004776 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004777 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004778 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004779 expr, list);
4780 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004781 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004782 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004783 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004784 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004785 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004786 name);
4787 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004788 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004789 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004790 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004791 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004792 }
4793 ret = 0;
4794 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004795 if (ret == -3)
4796 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004797
Daniel Veillard23e73572002-09-19 19:56:43 +00004798#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004799done:
Daniel Veillard23e73572002-09-19 19:56:43 +00004800#endif
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004801 /*
4802 * Deallocate the copy if done, and free up the validation stack
4803 */
4804 while (repl != NULL) {
4805 tmp = repl->next;
4806 xmlFree(repl);
4807 repl = tmp;
4808 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004809 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004810 if (ctxt->vstateTab != NULL) {
4811 xmlFree(ctxt->vstateTab);
4812 ctxt->vstateTab = NULL;
4813 }
4814 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004815 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004816 if (ctxt->nodeTab != NULL) {
4817 xmlFree(ctxt->nodeTab);
4818 ctxt->nodeTab = NULL;
4819 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004820 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004821
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004822}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004823
Owen Taylor3473f882001-02-23 17:55:21 +00004824/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004825 * xmlValidateCdataElement:
4826 * @ctxt: the validation context
4827 * @doc: a document instance
4828 * @elem: an element instance
4829 *
4830 * Check that an element follows #CDATA
4831 *
4832 * returns 1 if valid or 0 otherwise
4833 */
4834static int
4835xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4836 xmlNodePtr elem) {
4837 int ret = 1;
4838 xmlNodePtr cur, child;
4839
Daniel Veillardceb09b92002-10-04 11:46:37 +00004840 if ((ctxt == NULL) || (doc == NULL) || (elem == NULL))
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004841 return(0);
4842
4843 child = elem->children;
4844
4845 cur = child;
4846 while (cur != NULL) {
4847 switch (cur->type) {
4848 case XML_ENTITY_REF_NODE:
4849 /*
4850 * Push the current node to be able to roll back
4851 * and process within the entity
4852 */
4853 if ((cur->children != NULL) &&
4854 (cur->children->children != NULL)) {
4855 nodeVPush(ctxt, cur);
4856 cur = cur->children->children;
4857 continue;
4858 }
4859 break;
4860 case XML_COMMENT_NODE:
4861 case XML_PI_NODE:
4862 case XML_TEXT_NODE:
4863 case XML_CDATA_SECTION_NODE:
4864 break;
4865 default:
4866 ret = 0;
4867 goto done;
4868 }
4869 /*
4870 * Switch to next element
4871 */
4872 cur = cur->next;
4873 while (cur == NULL) {
4874 cur = nodeVPop(ctxt);
4875 if (cur == NULL)
4876 break;
4877 cur = cur->next;
4878 }
4879 }
4880done:
4881 ctxt->nodeMax = 0;
4882 ctxt->nodeNr = 0;
4883 if (ctxt->nodeTab != NULL) {
4884 xmlFree(ctxt->nodeTab);
4885 ctxt->nodeTab = NULL;
4886 }
4887 return(ret);
4888}
4889
4890/**
Owen Taylor3473f882001-02-23 17:55:21 +00004891 * xmlValidateOneElement:
4892 * @ctxt: the validation context
4893 * @doc: a document instance
4894 * @elem: an element instance
4895 *
4896 * Try to validate a single element and it's attributes,
4897 * basically it does the following checks as described by the
4898 * XML-1.0 recommendation:
4899 * - [ VC: Element Valid ]
4900 * - [ VC: Required Attribute ]
4901 * Then call xmlValidateOneAttribute() for each attribute present.
4902 *
4903 * The ID/IDREF checkings are done separately
4904 *
4905 * returns 1 if valid or 0 otherwise
4906 */
4907
4908int
4909xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4910 xmlNodePtr elem) {
4911 xmlElementPtr elemDecl = NULL;
4912 xmlElementContentPtr cont;
4913 xmlAttributePtr attr;
4914 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004915 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004916 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004917 const xmlChar *prefix = NULL;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004918 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004919
4920 CHECK_DTD;
4921
4922 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004923 switch (elem->type) {
4924 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004925 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004926 VERROR(ctxt->userData,
4927 "Attribute element not expected here\n");
4928 return(0);
4929 case XML_TEXT_NODE:
4930 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004931 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004932 VERROR(ctxt->userData, "Text element has childs !\n");
4933 return(0);
4934 }
4935 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004936 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004937 VERROR(ctxt->userData, "Text element has attributes !\n");
4938 return(0);
4939 }
4940 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004941 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004942 VERROR(ctxt->userData, "Text element has namespace !\n");
4943 return(0);
4944 }
4945 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004946 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004947 VERROR(ctxt->userData,
4948 "Text element carries namespace definitions !\n");
4949 return(0);
4950 }
4951 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004952 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004953 VERROR(ctxt->userData,
4954 "Text element has no content !\n");
4955 return(0);
4956 }
4957 return(1);
4958 case XML_XINCLUDE_START:
4959 case XML_XINCLUDE_END:
4960 return(1);
4961 case XML_CDATA_SECTION_NODE:
4962 case XML_ENTITY_REF_NODE:
4963 case XML_PI_NODE:
4964 case XML_COMMENT_NODE:
4965 return(1);
4966 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004967 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004968 VERROR(ctxt->userData,
4969 "Entity element not expected here\n");
4970 return(0);
4971 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004972 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004973 VERROR(ctxt->userData,
4974 "Notation element not expected here\n");
4975 return(0);
4976 case XML_DOCUMENT_NODE:
4977 case XML_DOCUMENT_TYPE_NODE:
4978 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004979 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004980 VERROR(ctxt->userData,
4981 "Document element not expected here\n");
4982 return(0);
4983 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004984 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004985 VERROR(ctxt->userData,
4986 "\n");
4987 return(0);
4988 case XML_ELEMENT_NODE:
4989 break;
4990 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004991 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004992 VERROR(ctxt->userData,
4993 "unknown element type %d\n", elem->type);
4994 return(0);
4995 }
4996 if (elem->name == NULL) return(0);
4997
4998 /*
4999 * Fetch the declaration for the qualified name
5000 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005001 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
5002 prefix = elem->ns->prefix;
5003
5004 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00005005 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005006 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005007 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005008 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005009 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005010 if (elemDecl != NULL)
5011 extsubset = 1;
5012 }
Owen Taylor3473f882001-02-23 17:55:21 +00005013 }
5014
5015 /*
5016 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00005017 * This is "non-strict" validation should be done on the
5018 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00005019 */
5020 if (elemDecl == NULL) {
5021 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005022 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005023 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005024 if (elemDecl != NULL)
5025 extsubset = 1;
5026 }
Owen Taylor3473f882001-02-23 17:55:21 +00005027 }
5028 if (elemDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005029 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005030 VERROR(ctxt->userData, "No declaration for element %s\n",
5031 elem->name);
5032 return(0);
5033 }
5034
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005035 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00005036 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00005037 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005038 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00005039 VERROR(ctxt->userData, "No declaration for element %s\n",
5040 elem->name);
5041 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00005042 case XML_ELEMENT_TYPE_EMPTY:
5043 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005044 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005045 VERROR(ctxt->userData,
5046 "Element %s was declared EMPTY this one has content\n",
5047 elem->name);
5048 ret = 0;
5049 }
5050 break;
5051 case XML_ELEMENT_TYPE_ANY:
5052 /* I don't think anything is required then */
5053 break;
5054 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005055
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005056 /* simple case of declared as #PCDATA */
5057 if ((elemDecl->content != NULL) &&
5058 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
5059 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
5060 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005061 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005062 VERROR(ctxt->userData,
5063 "Element %s was declared #PCDATA but contains non text nodes\n",
5064 elem->name);
5065 }
5066 break;
5067 }
Owen Taylor3473f882001-02-23 17:55:21 +00005068 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00005069 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00005070 while (child != NULL) {
5071 if (child->type == XML_ELEMENT_NODE) {
5072 name = child->name;
5073 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
5074 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005075 snprintf((char *) qname, sizeof(qname), "%s:%s",
5076 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005077 qname[sizeof(qname) - 1] = 0;
5078 cont = elemDecl->content;
5079 while (cont != NULL) {
5080 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5081 if (xmlStrEqual(cont->name, qname)) break;
5082 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5083 (cont->c1 != NULL) &&
5084 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
5085 if (xmlStrEqual(cont->c1->name, qname)) break;
5086 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5087 (cont->c1 == NULL) ||
5088 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
5089 /* Internal error !!! */
5090 xmlGenericError(xmlGenericErrorContext,
5091 "Internal: MIXED struct bad\n");
5092 break;
5093 }
5094 cont = cont->c2;
5095 }
5096 if (cont != NULL)
5097 goto child_ok;
5098 }
5099 cont = elemDecl->content;
5100 while (cont != NULL) {
5101 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
5102 if (xmlStrEqual(cont->name, name)) break;
5103 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
5104 (cont->c1 != NULL) &&
5105 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
5106 if (xmlStrEqual(cont->c1->name, name)) break;
5107 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
5108 (cont->c1 == NULL) ||
5109 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
5110 /* Internal error !!! */
5111 xmlGenericError(xmlGenericErrorContext,
5112 "Internal: MIXED struct bad\n");
5113 break;
5114 }
5115 cont = cont->c2;
5116 }
5117 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005118 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005119 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005120 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005121 name, elem->name);
5122 ret = 0;
5123 }
5124 }
5125child_ok:
5126 child = child->next;
5127 }
5128 break;
5129 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005130 if ((doc->standalone == 1) && (extsubset == 1)) {
5131 /*
5132 * VC: Standalone Document Declaration
5133 * - element types with element content, if white space
5134 * occurs directly within any instance of those types.
5135 */
5136 child = elem->children;
5137 while (child != NULL) {
5138 if (child->type == XML_TEXT_NODE) {
5139 const xmlChar *content = child->content;
5140
5141 while (IS_BLANK(*content))
5142 content++;
5143 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005144 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005145 VERROR(ctxt->userData,
5146"standalone: %s declared in the external subset contains white spaces nodes\n",
5147 elem->name);
5148 ret = 0;
5149 break;
5150 }
5151 }
5152 child =child->next;
5153 }
5154 }
Owen Taylor3473f882001-02-23 17:55:21 +00005155 child = elem->children;
5156 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005157 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00005158 if (tmp <= 0)
5159 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00005160 break;
5161 }
5162
5163 /* [ VC: Required Attribute ] */
5164 attr = elemDecl->attributes;
5165 while (attr != NULL) {
5166 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00005167 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00005168
Daniel Veillarde4301c82002-02-13 13:32:35 +00005169 if ((attr->prefix == NULL) &&
5170 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5171 xmlNsPtr ns;
5172
5173 ns = elem->nsDef;
5174 while (ns != NULL) {
5175 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00005176 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005177 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00005178 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005179 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5180 xmlNsPtr ns;
5181
5182 ns = elem->nsDef;
5183 while (ns != NULL) {
5184 if (xmlStrEqual(attr->name, ns->prefix))
5185 goto found;
5186 ns = ns->next;
5187 }
5188 } else {
5189 xmlAttrPtr attrib;
5190
5191 attrib = elem->properties;
5192 while (attrib != NULL) {
5193 if (xmlStrEqual(attrib->name, attr->name)) {
5194 if (attr->prefix != NULL) {
5195 xmlNsPtr nameSpace = attrib->ns;
5196
5197 if (nameSpace == NULL)
5198 nameSpace = elem->ns;
5199 /*
5200 * qualified names handling is problematic, having a
5201 * different prefix should be possible but DTDs don't
5202 * allow to define the URI instead of the prefix :-(
5203 */
5204 if (nameSpace == NULL) {
5205 if (qualified < 0)
5206 qualified = 0;
5207 } else if (!xmlStrEqual(nameSpace->prefix,
5208 attr->prefix)) {
5209 if (qualified < 1)
5210 qualified = 1;
5211 } else
5212 goto found;
5213 } else {
5214 /*
5215 * We should allow applications to define namespaces
5216 * for their application even if the DTD doesn't
5217 * carry one, otherwise, basically we would always
5218 * break.
5219 */
5220 goto found;
5221 }
5222 }
5223 attrib = attrib->next;
5224 }
Owen Taylor3473f882001-02-23 17:55:21 +00005225 }
5226 if (qualified == -1) {
5227 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005228 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005229 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005230 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005231 elem->name, attr->name);
5232 ret = 0;
5233 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005234 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005235 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005236 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005237 elem->name, attr->prefix,attr->name);
5238 ret = 0;
5239 }
5240 } else if (qualified == 0) {
5241 VWARNING(ctxt->userData,
5242 "Element %s required attribute %s:%s has no prefix\n",
5243 elem->name, attr->prefix,attr->name);
5244 } else if (qualified == 1) {
5245 VWARNING(ctxt->userData,
5246 "Element %s required attribute %s:%s has different prefix\n",
5247 elem->name, attr->prefix,attr->name);
5248 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005249 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5250 /*
5251 * Special tests checking #FIXED namespace declarations
5252 * have the right value since this is not done as an
5253 * attribute checking
5254 */
5255 if ((attr->prefix == NULL) &&
5256 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5257 xmlNsPtr ns;
5258
5259 ns = elem->nsDef;
5260 while (ns != NULL) {
5261 if (ns->prefix == NULL) {
5262 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005263 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005264 VERROR(ctxt->userData,
5265 "Element %s namespace name for default namespace does not match the DTD\n",
5266 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005267 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005268 }
5269 goto found;
5270 }
5271 ns = ns->next;
5272 }
5273 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5274 xmlNsPtr ns;
5275
5276 ns = elem->nsDef;
5277 while (ns != NULL) {
5278 if (xmlStrEqual(attr->name, ns->prefix)) {
5279 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005280 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005281 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005282 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005283 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005284 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005285 }
5286 goto found;
5287 }
5288 ns = ns->next;
5289 }
5290 }
Owen Taylor3473f882001-02-23 17:55:21 +00005291 }
5292found:
5293 attr = attr->nexth;
5294 }
5295 return(ret);
5296}
5297
5298/**
5299 * xmlValidateRoot:
5300 * @ctxt: the validation context
5301 * @doc: a document instance
5302 *
5303 * Try to validate a the root element
5304 * basically it does the following check as described by the
5305 * XML-1.0 recommendation:
5306 * - [ VC: Root Element Type ]
5307 * it doesn't try to recurse or apply other check to the element
5308 *
5309 * returns 1 if valid or 0 otherwise
5310 */
5311
5312int
5313xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5314 xmlNodePtr root;
5315 if (doc == NULL) return(0);
5316
5317 root = xmlDocGetRootElement(doc);
5318 if ((root == NULL) || (root->name == NULL)) {
5319 VERROR(ctxt->userData, "Not valid: no root element\n");
5320 return(0);
5321 }
5322
5323 /*
5324 * When doing post validation against a separate DTD, those may
5325 * no internal subset has been generated
5326 */
5327 if ((doc->intSubset != NULL) &&
5328 (doc->intSubset->name != NULL)) {
5329 /*
5330 * Check first the document root against the NQName
5331 */
5332 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5333 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
5334 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005335 snprintf((char *) qname, sizeof(qname), "%s:%s",
5336 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005337 qname[sizeof(qname) - 1] = 0;
5338 if (xmlStrEqual(doc->intSubset->name, qname))
5339 goto name_ok;
5340 }
5341 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5342 (xmlStrEqual(root->name, BAD_CAST "html")))
5343 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005344 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005345 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005346 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005347 root->name, doc->intSubset->name);
5348 return(0);
5349
5350 }
5351 }
5352name_ok:
5353 return(1);
5354}
5355
5356
5357/**
5358 * xmlValidateElement:
5359 * @ctxt: the validation context
5360 * @doc: a document instance
5361 * @elem: an element instance
5362 *
5363 * Try to validate the subtree under an element
5364 *
5365 * returns 1 if valid or 0 otherwise
5366 */
5367
5368int
5369xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5370 xmlNodePtr child;
5371 xmlAttrPtr attr;
5372 xmlChar *value;
5373 int ret = 1;
5374
5375 if (elem == NULL) return(0);
5376
5377 /*
5378 * XInclude elements were added after parsing in the infoset,
5379 * they don't really mean anything validation wise.
5380 */
5381 if ((elem->type == XML_XINCLUDE_START) ||
5382 (elem->type == XML_XINCLUDE_END))
5383 return(1);
5384
5385 CHECK_DTD;
5386
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005387 /*
5388 * Entities references have to be handled separately
5389 */
5390 if (elem->type == XML_ENTITY_REF_NODE) {
5391 return(1);
5392 }
5393
Owen Taylor3473f882001-02-23 17:55:21 +00005394 ret &= xmlValidateOneElement(ctxt, doc, elem);
5395 attr = elem->properties;
5396 while(attr != NULL) {
5397 value = xmlNodeListGetString(doc, attr->children, 0);
5398 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5399 if (value != NULL)
5400 xmlFree(value);
5401 attr= attr->next;
5402 }
5403 child = elem->children;
5404 while (child != NULL) {
5405 ret &= xmlValidateElement(ctxt, doc, child);
5406 child = child->next;
5407 }
5408
5409 return(ret);
5410}
5411
Daniel Veillard8730c562001-02-26 10:49:57 +00005412/**
5413 * xmlValidateRef:
5414 * @ref: A reference to be validated
5415 * @ctxt: Validation context
5416 * @name: Name of ID we are searching for
5417 *
5418 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005419static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005420xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005421 const xmlChar *name) {
5422 xmlAttrPtr id;
5423 xmlAttrPtr attr;
5424
5425 if (ref == NULL)
5426 return;
5427 attr = ref->attr;
5428 if (attr == NULL)
5429 return;
5430 if (attr->atype == XML_ATTRIBUTE_IDREF) {
5431 id = xmlGetID(ctxt->doc, name);
5432 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005433 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005434 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005435 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005436 attr->name, name);
5437 ctxt->valid = 0;
5438 }
5439 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5440 xmlChar *dup, *str = NULL, *cur, save;
5441
5442 dup = xmlStrdup(name);
5443 if (dup == NULL) {
5444 ctxt->valid = 0;
5445 return;
5446 }
5447 cur = dup;
5448 while (*cur != 0) {
5449 str = cur;
5450 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5451 save = *cur;
5452 *cur = 0;
5453 id = xmlGetID(ctxt->doc, str);
5454 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005455 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005456 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005457 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005458 attr->name, str);
5459 ctxt->valid = 0;
5460 }
5461 if (save == 0)
5462 break;
5463 *cur = save;
5464 while (IS_BLANK(*cur)) cur++;
5465 }
5466 xmlFree(dup);
5467 }
5468}
5469
5470/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005471 * xmlWalkValidateList:
5472 * @data: Contents of current link
5473 * @user: Value supplied by the user
5474 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005475 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005476 */
5477static int
5478xmlWalkValidateList(const void *data, const void *user)
5479{
5480 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5481 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5482 return 1;
5483}
5484
5485/**
5486 * xmlValidateCheckRefCallback:
5487 * @ref_list: List of references
5488 * @ctxt: Validation context
5489 * @name: Name of ID we are searching for
5490 *
5491 */
5492static void
5493xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5494 const xmlChar *name) {
5495 xmlValidateMemo memo;
5496
5497 if (ref_list == NULL)
5498 return;
5499 memo.ctxt = ctxt;
5500 memo.name = name;
5501
5502 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5503
5504}
5505
5506/**
Owen Taylor3473f882001-02-23 17:55:21 +00005507 * xmlValidateDocumentFinal:
5508 * @ctxt: the validation context
5509 * @doc: a document instance
5510 *
5511 * Does the final step for the document validation once all the
5512 * incremental validation steps have been completed
5513 *
5514 * basically it does the following checks described by the XML Rec
5515 *
5516 *
5517 * returns 1 if valid or 0 otherwise
5518 */
5519
5520int
5521xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5522 xmlRefTablePtr table;
5523
5524 if (doc == NULL) {
5525 xmlGenericError(xmlGenericErrorContext,
5526 "xmlValidateDocumentFinal: doc == NULL\n");
5527 return(0);
5528 }
5529
5530 /*
5531 * Check all the NOTATION/NOTATIONS attributes
5532 */
5533 /*
5534 * Check all the ENTITY/ENTITIES attributes definition for validity
5535 */
5536 /*
5537 * Check all the IDREF/IDREFS attributes definition for validity
5538 */
5539 table = (xmlRefTablePtr) doc->refs;
5540 ctxt->doc = doc;
5541 ctxt->valid = 1;
5542 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
5543 return(ctxt->valid);
5544}
5545
5546/**
5547 * xmlValidateDtd:
5548 * @ctxt: the validation context
5549 * @doc: a document instance
5550 * @dtd: a dtd instance
5551 *
5552 * Try to validate the document against the dtd instance
5553 *
5554 * basically it does check all the definitions in the DtD.
5555 *
5556 * returns 1 if valid or 0 otherwise
5557 */
5558
5559int
5560xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
5561 int ret;
5562 xmlDtdPtr oldExt;
5563 xmlNodePtr root;
5564
5565 if (dtd == NULL) return(0);
5566 if (doc == NULL) return(0);
5567 oldExt = doc->extSubset;
5568 doc->extSubset = dtd;
5569 ret = xmlValidateRoot(ctxt, doc);
5570 if (ret == 0) {
5571 doc->extSubset = oldExt;
5572 return(ret);
5573 }
5574 if (doc->ids != NULL) {
5575 xmlFreeIDTable(doc->ids);
5576 doc->ids = NULL;
5577 }
5578 if (doc->refs != NULL) {
5579 xmlFreeRefTable(doc->refs);
5580 doc->refs = NULL;
5581 }
5582 root = xmlDocGetRootElement(doc);
5583 ret = xmlValidateElement(ctxt, doc, root);
5584 ret &= xmlValidateDocumentFinal(ctxt, doc);
5585 doc->extSubset = oldExt;
5586 return(ret);
5587}
5588
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005589static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005590xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
5591 const xmlChar *name ATTRIBUTE_UNUSED) {
5592 if (cur == NULL)
5593 return;
5594 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
5595 xmlChar *notation = cur->content;
5596
Daniel Veillard878eab02002-02-19 13:46:09 +00005597 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005598 int ret;
5599
5600 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
5601 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00005602 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005603 }
5604 }
5605 }
5606}
5607
5608static void
Owen Taylor3473f882001-02-23 17:55:21 +00005609xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00005610 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005611 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00005612 xmlDocPtr doc;
5613 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005614
Owen Taylor3473f882001-02-23 17:55:21 +00005615 if (cur == NULL)
5616 return;
5617 switch (cur->atype) {
5618 case XML_ATTRIBUTE_CDATA:
5619 case XML_ATTRIBUTE_ID:
5620 case XML_ATTRIBUTE_IDREF :
5621 case XML_ATTRIBUTE_IDREFS:
5622 case XML_ATTRIBUTE_NMTOKEN:
5623 case XML_ATTRIBUTE_NMTOKENS:
5624 case XML_ATTRIBUTE_ENUMERATION:
5625 break;
5626 case XML_ATTRIBUTE_ENTITY:
5627 case XML_ATTRIBUTE_ENTITIES:
5628 case XML_ATTRIBUTE_NOTATION:
5629 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005630
5631 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
5632 cur->atype, cur->defaultValue);
5633 if ((ret == 0) && (ctxt->valid == 1))
5634 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005635 }
5636 if (cur->tree != NULL) {
5637 xmlEnumerationPtr tree = cur->tree;
5638 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005639 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00005640 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005641 if ((ret == 0) && (ctxt->valid == 1))
5642 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005643 tree = tree->next;
5644 }
5645 }
5646 }
Daniel Veillard878eab02002-02-19 13:46:09 +00005647 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
5648 doc = cur->doc;
5649 if ((doc == NULL) || (cur->elem == NULL)) {
5650 VERROR(ctxt->userData,
5651 "xmlValidateAttributeCallback(%s): internal error\n",
5652 cur->name);
5653 return;
5654 }
5655 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
5656 if (elem == NULL)
5657 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
5658 if (elem == NULL) {
5659 VERROR(ctxt->userData,
5660 "attribute %s: could not find decl for element %s\n",
5661 cur->name, cur->elem);
5662 return;
5663 }
5664 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
5665 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005666 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00005667 cur->name, cur->elem);
5668 ctxt->valid = 0;
5669 }
5670 }
Owen Taylor3473f882001-02-23 17:55:21 +00005671}
5672
5673/**
5674 * xmlValidateDtdFinal:
5675 * @ctxt: the validation context
5676 * @doc: a document instance
5677 *
5678 * Does the final step for the dtds validation once all the
5679 * subsets have been parsed
5680 *
5681 * basically it does the following checks described by the XML Rec
5682 * - check that ENTITY and ENTITIES type attributes default or
5683 * possible values matches one of the defined entities.
5684 * - check that NOTATION type attributes default or
5685 * possible values matches one of the defined notations.
5686 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005687 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00005688 */
5689
5690int
5691xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00005692 xmlDtdPtr dtd;
5693 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005694 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00005695
5696 if (doc == NULL) return(0);
5697 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5698 return(0);
5699 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005700 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005701 dtd = doc->intSubset;
5702 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5703 table = (xmlAttributeTablePtr) dtd->attributes;
5704 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005705 }
5706 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005707 entities = (xmlEntitiesTablePtr) dtd->entities;
5708 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5709 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005710 }
5711 dtd = doc->extSubset;
5712 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5713 table = (xmlAttributeTablePtr) dtd->attributes;
5714 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005715 }
5716 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005717 entities = (xmlEntitiesTablePtr) dtd->entities;
5718 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5719 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005720 }
5721 return(ctxt->valid);
5722}
5723
5724/**
5725 * xmlValidateDocument:
5726 * @ctxt: the validation context
5727 * @doc: a document instance
5728 *
5729 * Try to validate the document instance
5730 *
5731 * basically it does the all the checks described by the XML Rec
5732 * i.e. validates the internal and external subset (if present)
5733 * and validate the document tree.
5734 *
5735 * returns 1 if valid or 0 otherwise
5736 */
5737
5738int
5739xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5740 int ret;
5741 xmlNodePtr root;
5742
5743 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5744 return(0);
5745 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
5746 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
5747 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
5748 doc->intSubset->SystemID);
5749 if (doc->extSubset == NULL) {
5750 if (doc->intSubset->SystemID != NULL) {
5751 VERROR(ctxt->userData,
5752 "Could not load the external subset \"%s\"\n",
5753 doc->intSubset->SystemID);
5754 } else {
5755 VERROR(ctxt->userData,
5756 "Could not load the external subset \"%s\"\n",
5757 doc->intSubset->ExternalID);
5758 }
5759 return(0);
5760 }
5761 }
5762
5763 if (doc->ids != NULL) {
5764 xmlFreeIDTable(doc->ids);
5765 doc->ids = NULL;
5766 }
5767 if (doc->refs != NULL) {
5768 xmlFreeRefTable(doc->refs);
5769 doc->refs = NULL;
5770 }
5771 ret = xmlValidateDtdFinal(ctxt, doc);
5772 if (!xmlValidateRoot(ctxt, doc)) return(0);
5773
5774 root = xmlDocGetRootElement(doc);
5775 ret &= xmlValidateElement(ctxt, doc, root);
5776 ret &= xmlValidateDocumentFinal(ctxt, doc);
5777 return(ret);
5778}
5779
5780
5781/************************************************************************
5782 * *
5783 * Routines for dynamic validation editing *
5784 * *
5785 ************************************************************************/
5786
5787/**
5788 * xmlValidGetPotentialChildren:
5789 * @ctree: an element content tree
5790 * @list: an array to store the list of child names
5791 * @len: a pointer to the number of element in the list
5792 * @max: the size of the array
5793 *
5794 * Build/extend a list of potential children allowed by the content tree
5795 *
5796 * returns the number of element in the list, or -1 in case of error.
5797 */
5798
5799int
5800xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
5801 int *len, int max) {
5802 int i;
5803
5804 if ((ctree == NULL) || (list == NULL) || (len == NULL))
5805 return(-1);
5806 if (*len >= max) return(*len);
5807
5808 switch (ctree->type) {
5809 case XML_ELEMENT_CONTENT_PCDATA:
5810 for (i = 0; i < *len;i++)
5811 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
5812 list[(*len)++] = BAD_CAST "#PCDATA";
5813 break;
5814 case XML_ELEMENT_CONTENT_ELEMENT:
5815 for (i = 0; i < *len;i++)
5816 if (xmlStrEqual(ctree->name, list[i])) return(*len);
5817 list[(*len)++] = ctree->name;
5818 break;
5819 case XML_ELEMENT_CONTENT_SEQ:
5820 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5821 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5822 break;
5823 case XML_ELEMENT_CONTENT_OR:
5824 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5825 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5826 break;
5827 }
5828
5829 return(*len);
5830}
5831
5832/**
5833 * xmlValidGetValidElements:
5834 * @prev: an element to insert after
5835 * @next: an element to insert next
5836 * @list: an array to store the list of child names
5837 * @max: the size of the array
5838 *
5839 * This function returns the list of authorized children to insert
5840 * within an existing tree while respecting the validity constraints
5841 * forced by the Dtd. The insertion point is defined using @prev and
5842 * @next in the following ways:
5843 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
5844 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
5845 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
5846 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
5847 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
5848 *
5849 * pointers to the element names are inserted at the beginning of the array
5850 * and do not need to be freed.
5851 *
5852 * returns the number of element in the list, or -1 in case of error. If
5853 * the function returns the value @max the caller is invited to grow the
5854 * receiving array and retry.
5855 */
5856
5857int
5858xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
5859 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005860 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005861 int nb_valid_elements = 0;
5862 const xmlChar *elements[256];
5863 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005864 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005865
5866 xmlNode *ref_node;
5867 xmlNode *parent;
5868 xmlNode *test_node;
5869
5870 xmlNode *prev_next;
5871 xmlNode *next_prev;
5872 xmlNode *parent_childs;
5873 xmlNode *parent_last;
5874
5875 xmlElement *element_desc;
5876
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00005877 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005878
Owen Taylor3473f882001-02-23 17:55:21 +00005879 if (prev == NULL && next == NULL)
5880 return(-1);
5881
5882 if (list == NULL) return(-1);
5883 if (max <= 0) return(-1);
5884
5885 nb_valid_elements = 0;
5886 ref_node = prev ? prev : next;
5887 parent = ref_node->parent;
5888
5889 /*
5890 * Retrieves the parent element declaration
5891 */
5892 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5893 parent->name);
5894 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5895 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5896 parent->name);
5897 if (element_desc == NULL) return(-1);
5898
5899 /*
5900 * Do a backup of the current tree structure
5901 */
5902 prev_next = prev ? prev->next : NULL;
5903 next_prev = next ? next->prev : NULL;
5904 parent_childs = parent->children;
5905 parent_last = parent->last;
5906
5907 /*
5908 * Creates a dummy node and insert it into the tree
5909 */
5910 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5911 test_node->doc = ref_node->doc;
5912 test_node->parent = parent;
5913 test_node->prev = prev;
5914 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005915 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005916
5917 if (prev) prev->next = test_node;
5918 else parent->children = test_node;
5919
5920 if (next) next->prev = test_node;
5921 else parent->last = test_node;
5922
5923 /*
5924 * Insert each potential child node and check if the parent is
5925 * still valid
5926 */
5927 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5928 elements, &nb_elements, 256);
5929
5930 for (i = 0;i < nb_elements;i++) {
5931 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005932 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005933 int j;
5934
5935 for (j = 0; j < nb_valid_elements;j++)
5936 if (xmlStrEqual(elements[i], list[j])) break;
5937 list[nb_valid_elements++] = elements[i];
5938 if (nb_valid_elements >= max) break;
5939 }
5940 }
5941
5942 /*
5943 * Restore the tree structure
5944 */
5945 if (prev) prev->next = prev_next;
5946 if (next) next->prev = next_prev;
5947 parent->children = parent_childs;
5948 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005949
5950 /*
5951 * Free up the dummy node
5952 */
5953 test_node->name = name;
5954 xmlFreeNode(test_node);
5955
Owen Taylor3473f882001-02-23 17:55:21 +00005956 return(nb_valid_elements);
5957}