blob: 5a9a6147cbc0e1899e695055435b03bd9d991b11 [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
113static int
114vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
115 xmlNodePtr node, unsigned char depth, long occurs,
116 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000117 int i = ctxt->vstateNr - 1;
118
Daniel Veillard940492d2002-04-15 10:15:25 +0000119 if (ctxt->vstateNr > MAX_RECURSE) {
120 return(-1);
121 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000122 if (ctxt->vstateNr >= ctxt->vstateMax) {
123 ctxt->vstateMax *= 2;
124 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
125 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
126 if (ctxt->vstateTab == NULL) {
127 xmlGenericError(xmlGenericErrorContext,
128 "realloc failed !n");
Daniel Veillard940492d2002-04-15 10:15:25 +0000129 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000130 }
Daniel Veillard06803992001-04-22 10:35:56 +0000131 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000132 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000133 /*
134 * Don't push on the stack a state already here
135 */
136 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
137 (ctxt->vstateTab[i].node == node) &&
138 (ctxt->vstateTab[i].depth == depth) &&
139 (ctxt->vstateTab[i].occurs == occurs) &&
140 (ctxt->vstateTab[i].state == state))
141 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000142 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
143 ctxt->vstateTab[ctxt->vstateNr].node = node;
144 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
145 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
146 ctxt->vstateTab[ctxt->vstateNr].state = state;
147 return(ctxt->vstateNr++);
148}
149
150static int
151vstateVPop(xmlValidCtxtPtr ctxt) {
152 if (ctxt->vstateNr <= 1) return(-1);
153 ctxt->vstateNr--;
154 ctxt->vstate = &ctxt->vstateTab[0];
155 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
156 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
157 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
158 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
159 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
160 return(ctxt->vstateNr);
161}
162
Owen Taylor3473f882001-02-23 17:55:21 +0000163PUSH_AND_POP(static, xmlNodePtr, node)
164
Owen Taylor3473f882001-02-23 17:55:21 +0000165#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000166static void
167xmlValidPrintNode(xmlNodePtr cur) {
168 if (cur == NULL) {
169 xmlGenericError(xmlGenericErrorContext, "null");
170 return;
171 }
172 switch (cur->type) {
173 case XML_ELEMENT_NODE:
174 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
175 break;
176 case XML_TEXT_NODE:
177 xmlGenericError(xmlGenericErrorContext, "text ");
178 break;
179 case XML_CDATA_SECTION_NODE:
180 xmlGenericError(xmlGenericErrorContext, "cdata ");
181 break;
182 case XML_ENTITY_REF_NODE:
183 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
184 break;
185 case XML_PI_NODE:
186 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
187 break;
188 case XML_COMMENT_NODE:
189 xmlGenericError(xmlGenericErrorContext, "comment ");
190 break;
191 case XML_ATTRIBUTE_NODE:
192 xmlGenericError(xmlGenericErrorContext, "?attr? ");
193 break;
194 case XML_ENTITY_NODE:
195 xmlGenericError(xmlGenericErrorContext, "?ent? ");
196 break;
197 case XML_DOCUMENT_NODE:
198 xmlGenericError(xmlGenericErrorContext, "?doc? ");
199 break;
200 case XML_DOCUMENT_TYPE_NODE:
201 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
202 break;
203 case XML_DOCUMENT_FRAG_NODE:
204 xmlGenericError(xmlGenericErrorContext, "?frag? ");
205 break;
206 case XML_NOTATION_NODE:
207 xmlGenericError(xmlGenericErrorContext, "?nota? ");
208 break;
209 case XML_HTML_DOCUMENT_NODE:
210 xmlGenericError(xmlGenericErrorContext, "?html? ");
211 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000212#ifdef LIBXML_DOCB_ENABLED
213 case XML_DOCB_DOCUMENT_NODE:
214 xmlGenericError(xmlGenericErrorContext, "?docb? ");
215 break;
216#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000217 case XML_DTD_NODE:
218 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
219 break;
220 case XML_ELEMENT_DECL:
221 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
222 break;
223 case XML_ATTRIBUTE_DECL:
224 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
225 break;
226 case XML_ENTITY_DECL:
227 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
228 break;
229 case XML_NAMESPACE_DECL:
230 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
231 break;
232 case XML_XINCLUDE_START:
233 xmlGenericError(xmlGenericErrorContext, "incstart ");
234 break;
235 case XML_XINCLUDE_END:
236 xmlGenericError(xmlGenericErrorContext, "incend ");
237 break;
238 }
239}
240
241static void
242xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000243 if (cur == NULL)
244 xmlGenericError(xmlGenericErrorContext, "null ");
245 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000246 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000247 cur = cur->next;
248 }
249}
250
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000251static void
252xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000253 char expr[1000];
254
255 expr[0] = 0;
256 xmlGenericError(xmlGenericErrorContext, "valid: ");
257 xmlValidPrintNodeList(cur);
258 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000259 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000260 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
261}
262
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000263static void
264xmlValidDebugState(xmlValidStatePtr state) {
265 xmlGenericError(xmlGenericErrorContext, "(");
266 if (state->cont == NULL)
267 xmlGenericError(xmlGenericErrorContext, "null,");
268 else
269 switch (state->cont->type) {
270 case XML_ELEMENT_CONTENT_PCDATA:
271 xmlGenericError(xmlGenericErrorContext, "pcdata,");
272 break;
273 case XML_ELEMENT_CONTENT_ELEMENT:
274 xmlGenericError(xmlGenericErrorContext, "%s,",
275 state->cont->name);
276 break;
277 case XML_ELEMENT_CONTENT_SEQ:
278 xmlGenericError(xmlGenericErrorContext, "seq,");
279 break;
280 case XML_ELEMENT_CONTENT_OR:
281 xmlGenericError(xmlGenericErrorContext, "or,");
282 break;
283 }
284 xmlValidPrintNode(state->node);
285 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
286 state->depth, state->occurs, state->state);
287}
288
289static void
290xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
291 int i, j;
292
293 xmlGenericError(xmlGenericErrorContext, "state: ");
294 xmlValidDebugState(ctxt->vstate);
295 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
296 ctxt->vstateNr - 1);
297 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
298 xmlValidDebugState(&ctxt->vstateTab[j]);
299 xmlGenericError(xmlGenericErrorContext, "\n");
300}
301
302/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000303#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000304 *****/
305
306#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000307#define DEBUG_VALID_MSG(m) \
308 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
309
Owen Taylor3473f882001-02-23 17:55:21 +0000310#else
311#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000312#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000313#endif
314
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000315/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000316
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000317#define VECTXT(ctxt, node) \
318 if ((ctxt != NULL) && (ctxt->error != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000319 (node != NULL)) { \
320 xmlChar *base = xmlNodeGetBase(NULL,node); \
321 if (base != NULL) { \
322 ctxt->error(ctxt->userData, "%s:%d: ", base, \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000323 (int) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000324 xmlFree(base); \
325 } else \
326 ctxt->error(ctxt->userData, ":%d: ", \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000327 (int) node->content); \
328 }
329
330#define VWCTXT(ctxt, node) \
331 if ((ctxt != NULL) && (ctxt->warning != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000332 (node != NULL)) { \
333 xmlChar *base = xmlNodeGetBase(NULL,node); \
334 if (base != NULL) { \
335 ctxt->warning(ctxt->userData, "%s:%d: ", base, \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000336 (int) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000337 xmlFree(base); \
338 } else \
339 ctxt->warning(ctxt->userData, ":%d: ", \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000340 (int) node->content); \
341 }
342
Owen Taylor3473f882001-02-23 17:55:21 +0000343#define VERROR \
344 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
345
346#define VWARNING \
347 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
348
349#define CHECK_DTD \
350 if (doc == NULL) return(0); \
351 else if ((doc->intSubset == NULL) && \
352 (doc->extSubset == NULL)) return(0)
353
Daniel Veillarda10efa82001-04-18 13:09:01 +0000354static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
355 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000356xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
357
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000358#ifdef LIBXML_REGEXP_ENABLED
359
360/************************************************************************
361 * *
362 * Content model validation based on the regexps *
363 * *
364 ************************************************************************/
365
366/**
367 * xmlValidBuildAContentModel:
368 * @content: the content model
369 * @ctxt: the schema parser context
370 * @name: the element name whose content is being built
371 *
372 * Generate the automata sequence needed for that type
373 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000374 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000375 */
376static int
377xmlValidBuildAContentModel(xmlElementContentPtr content,
378 xmlValidCtxtPtr ctxt,
379 const xmlChar *name) {
380 if (content == NULL) {
381 VERROR(ctxt->userData,
382 "Found unexpected type = NULL in %s content model\n", name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000383 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000384 }
385 switch (content->type) {
386 case XML_ELEMENT_CONTENT_PCDATA:
387 VERROR(ctxt->userData, "ContentModel found PCDATA for element %s\n",
388 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000389 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000390 break;
391 case XML_ELEMENT_CONTENT_ELEMENT: {
392 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000393 xmlChar *QName = NULL;
394 const xmlChar *fname = content->name;
395
396 if (content->prefix != NULL) {
397 int len;
398
399 len = xmlStrlen(content->name) +
400 xmlStrlen(content->prefix) + 2;
401 QName = xmlMalloc(len);
402 if (QName == NULL) {
403 VERROR(ctxt->userData,
404 "ContentModel %s : alloc failed\n", name);
405 return(0);
406 }
407 snprintf((char *) QName, len, "%s:%s",
408 (char *)content->prefix,
409 (char *)content->name);
410 fname = QName;
411 }
412
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000413 switch (content->ocur) {
414 case XML_ELEMENT_CONTENT_ONCE:
415 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000416 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000417 break;
418 case XML_ELEMENT_CONTENT_OPT:
419 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000420 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000421 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
422 break;
423 case XML_ELEMENT_CONTENT_PLUS:
424 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000425 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000426 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000427 ctxt->state, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000428 break;
429 case XML_ELEMENT_CONTENT_MULT:
430 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000431 ctxt->state, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000432 break;
433 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000434 if (QName != NULL)
435 xmlFree(QName);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000436 break;
437 }
438 case XML_ELEMENT_CONTENT_SEQ: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000439 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000440 xmlElementContentOccur ocur;
441
442 /*
443 * Simply iterate over the content
444 */
445 oldstate = ctxt->state;
446 ocur = content->ocur;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000447 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000448 xmlValidBuildAContentModel(content->c1, ctxt, name);
449 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000450 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
451 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
452 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000453 oldend = ctxt->state;
454 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000455 switch (ocur) {
456 case XML_ELEMENT_CONTENT_ONCE:
457 break;
458 case XML_ELEMENT_CONTENT_OPT:
459 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
460 break;
461 case XML_ELEMENT_CONTENT_MULT:
462 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000463 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000464 break;
465 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000466 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000467 break;
468 }
469 break;
470 }
471 case XML_ELEMENT_CONTENT_OR: {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000472 xmlAutomataStatePtr oldstate, oldend;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000473 xmlElementContentOccur ocur;
474
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000475 ocur = content->ocur;
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000476 if ((ocur == XML_ELEMENT_CONTENT_PLUS) ||
477 (ocur == XML_ELEMENT_CONTENT_MULT)) {
478 ctxt->state = xmlAutomataNewEpsilon(ctxt->am,
479 ctxt->state, NULL);
480 }
481 oldstate = ctxt->state;
482 oldend = xmlAutomataNewState(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000483
484 /*
485 * iterate over the subtypes and remerge the end with an
486 * epsilon transition
487 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000488 do {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000489 ctxt->state = oldstate;
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000490 xmlValidBuildAContentModel(content->c1, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000491 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000492 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000493 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
494 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000495 ctxt->state = oldstate;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000496 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000497 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend);
498 ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000499 switch (ocur) {
500 case XML_ELEMENT_CONTENT_ONCE:
501 break;
502 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000503 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000504 break;
505 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000506 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
507 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000508 break;
509 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000510 xmlAutomataNewEpsilon(ctxt->am, oldend, oldstate);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000511 break;
512 }
513 break;
514 }
515 default:
516 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
517 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000518 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000519 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000520 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000521}
522/**
523 * xmlValidBuildContentModel:
524 * @ctxt: a validation context
525 * @elem: an element declaration node
526 *
527 * (Re)Build the automata associated to the content model of this
528 * element
529 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000530 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000531 */
532int
533xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
534 xmlAutomataStatePtr start;
535
536 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000537 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000538 if (elem->type != XML_ELEMENT_DECL)
539 return(0);
540 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
541 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000542 /* TODO: should we rebuild in this case ? */
543 if (elem->contModel != NULL)
Daniel Veillard84d70a42002-09-16 10:51:38 +0000544 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000545
546 ctxt->am = xmlNewAutomata();
547 if (ctxt->am == NULL) {
548 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
549 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000550 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000551 }
552 start = ctxt->state = xmlAutomataGetInitState(ctxt->am);
553 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
554 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000555 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000556 if (!xmlAutomataIsDeterminist(ctxt->am)) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000557 char expr[5000];
558 expr[0] = 0;
559 xmlSnprintfElementContent(expr, 5000, elem->content, 1);
560 VERROR(ctxt->userData, "Content model of %s is not determinist: %s\n",
561 elem->name, expr);
562#ifdef DEBUG_REGEXP_ALGO
563 xmlRegexpPrint(stderr, elem->contModel);
564#endif
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000565 ctxt->valid = 0;
566 }
567 ctxt->state = NULL;
568 xmlFreeAutomata(ctxt->am);
569 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000570 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000571}
572
573#endif /* LIBXML_REGEXP_ENABLED */
574
Owen Taylor3473f882001-02-23 17:55:21 +0000575/************************************************************************
576 * *
577 * QName handling helper *
578 * *
579 ************************************************************************/
580
581/**
582 * xmlSplitQName2:
583 * @name: an XML parser context
584 * @prefix: a xmlChar **
585 *
586 * parse an XML qualified name string
587 *
588 * [NS 5] QName ::= (Prefix ':')? LocalPart
589 *
590 * [NS 6] Prefix ::= NCName
591 *
592 * [NS 7] LocalPart ::= NCName
593 *
594 * Returns NULL if not a QName, otherwise the local part, and prefix
595 * is updated to get the Prefix if any.
596 */
597
598xmlChar *
599xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
600 int len = 0;
601 xmlChar *ret = NULL;
602
603 *prefix = NULL;
604
Daniel Veillardf4309d72001-10-02 09:28:58 +0000605#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000606 /* xml: prefix is not really a namespace */
607 if ((name[0] == 'x') && (name[1] == 'm') &&
608 (name[2] == 'l') && (name[3] == ':'))
609 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000610#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000611
612 /* nasty but valid */
613 if (name[0] == ':')
614 return(NULL);
615
616 /*
617 * we are not trying to validate but just to cut, and yes it will
618 * work even if this is as set of UTF-8 encoded chars
619 */
620 while ((name[len] != 0) && (name[len] != ':'))
621 len++;
622
623 if (name[len] == 0)
624 return(NULL);
625
626 *prefix = xmlStrndup(name, len);
627 ret = xmlStrdup(&name[len + 1]);
628
629 return(ret);
630}
631
632/****************************************************************
633 * *
634 * Util functions for data allocation/deallocation *
635 * *
636 ****************************************************************/
637
638/**
639 * xmlNewElementContent:
640 * @name: the subelement name or NULL
641 * @type: the type of element content decl
642 *
643 * Allocate an element content structure.
644 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000645 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000646 */
647xmlElementContentPtr
648xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
649 xmlElementContentPtr ret;
650
651 switch(type) {
652 case XML_ELEMENT_CONTENT_ELEMENT:
653 if (name == NULL) {
654 xmlGenericError(xmlGenericErrorContext,
655 "xmlNewElementContent : name == NULL !\n");
656 }
657 break;
658 case XML_ELEMENT_CONTENT_PCDATA:
659 case XML_ELEMENT_CONTENT_SEQ:
660 case XML_ELEMENT_CONTENT_OR:
661 if (name != NULL) {
662 xmlGenericError(xmlGenericErrorContext,
663 "xmlNewElementContent : name != NULL !\n");
664 }
665 break;
666 default:
667 xmlGenericError(xmlGenericErrorContext,
668 "xmlNewElementContent: unknown type %d\n", type);
669 return(NULL);
670 }
671 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
672 if (ret == NULL) {
673 xmlGenericError(xmlGenericErrorContext,
674 "xmlNewElementContent : out of memory!\n");
675 return(NULL);
676 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000677 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000678 ret->type = type;
679 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000680 if (name != NULL) {
681 xmlChar *prefix = NULL;
682 ret->name = xmlSplitQName2(name, &prefix);
683 if (ret->name == NULL)
684 ret->name = xmlStrdup(name);
685 ret->prefix = prefix;
686 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000687 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000688 ret->prefix = NULL;
689 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000690 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000691 return(ret);
692}
693
694/**
695 * xmlCopyElementContent:
696 * @content: An element content pointer.
697 *
698 * Build a copy of an element content description.
699 *
700 * Returns the new xmlElementContentPtr or NULL in case of error.
701 */
702xmlElementContentPtr
703xmlCopyElementContent(xmlElementContentPtr cur) {
704 xmlElementContentPtr ret;
705
706 if (cur == NULL) return(NULL);
707 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
708 if (ret == NULL) {
709 xmlGenericError(xmlGenericErrorContext,
710 "xmlCopyElementContent : out of memory\n");
711 return(NULL);
712 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000713 if (cur->prefix != NULL)
714 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000715 ret->ocur = cur->ocur;
716 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000717 if (ret->c1 != NULL)
718 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000719 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000720 if (ret->c2 != NULL)
721 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000722 return(ret);
723}
724
725/**
726 * xmlFreeElementContent:
727 * @cur: the element content tree to free
728 *
729 * Free an element content structure. This is a recursive call !
730 */
731void
732xmlFreeElementContent(xmlElementContentPtr cur) {
733 if (cur == NULL) return;
734 switch (cur->type) {
735 case XML_ELEMENT_CONTENT_PCDATA:
736 case XML_ELEMENT_CONTENT_ELEMENT:
737 case XML_ELEMENT_CONTENT_SEQ:
738 case XML_ELEMENT_CONTENT_OR:
739 break;
740 default:
741 xmlGenericError(xmlGenericErrorContext,
742 "xmlFreeElementContent : type %d\n", cur->type);
743 return;
744 }
745 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
746 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
747 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000748 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000749 xmlFree(cur);
750}
751
752/**
753 * xmlDumpElementContent:
754 * @buf: An XML buffer
755 * @content: An element table
756 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
757 *
758 * This will dump the content of the element table as an XML DTD definition
759 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000760static void
Owen Taylor3473f882001-02-23 17:55:21 +0000761xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
762 if (content == NULL) return;
763
764 if (glob) xmlBufferWriteChar(buf, "(");
765 switch (content->type) {
766 case XML_ELEMENT_CONTENT_PCDATA:
767 xmlBufferWriteChar(buf, "#PCDATA");
768 break;
769 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000770 if (content->prefix != NULL) {
771 xmlBufferWriteCHAR(buf, content->prefix);
772 xmlBufferWriteChar(buf, ":");
773 }
Owen Taylor3473f882001-02-23 17:55:21 +0000774 xmlBufferWriteCHAR(buf, content->name);
775 break;
776 case XML_ELEMENT_CONTENT_SEQ:
777 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
778 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
779 xmlDumpElementContent(buf, content->c1, 1);
780 else
781 xmlDumpElementContent(buf, content->c1, 0);
782 xmlBufferWriteChar(buf, " , ");
783 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
784 xmlDumpElementContent(buf, content->c2, 1);
785 else
786 xmlDumpElementContent(buf, content->c2, 0);
787 break;
788 case XML_ELEMENT_CONTENT_OR:
789 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
790 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
791 xmlDumpElementContent(buf, content->c1, 1);
792 else
793 xmlDumpElementContent(buf, content->c1, 0);
794 xmlBufferWriteChar(buf, " | ");
795 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
796 xmlDumpElementContent(buf, content->c2, 1);
797 else
798 xmlDumpElementContent(buf, content->c2, 0);
799 break;
800 default:
801 xmlGenericError(xmlGenericErrorContext,
802 "xmlDumpElementContent: unknown type %d\n",
803 content->type);
804 }
805 if (glob)
806 xmlBufferWriteChar(buf, ")");
807 switch (content->ocur) {
808 case XML_ELEMENT_CONTENT_ONCE:
809 break;
810 case XML_ELEMENT_CONTENT_OPT:
811 xmlBufferWriteChar(buf, "?");
812 break;
813 case XML_ELEMENT_CONTENT_MULT:
814 xmlBufferWriteChar(buf, "*");
815 break;
816 case XML_ELEMENT_CONTENT_PLUS:
817 xmlBufferWriteChar(buf, "+");
818 break;
819 }
820}
821
822/**
823 * xmlSprintfElementContent:
824 * @buf: an output buffer
825 * @content: An element table
826 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
827 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000828 * Deprecated, unsafe, use xmlSnprintfElementContent
829 */
830void
831xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
832 xmlElementContentPtr content ATTRIBUTE_UNUSED,
833 int glob ATTRIBUTE_UNUSED) {
834}
835
836/**
837 * xmlSnprintfElementContent:
838 * @buf: an output buffer
839 * @size: the buffer size
840 * @content: An element table
841 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
842 *
Owen Taylor3473f882001-02-23 17:55:21 +0000843 * This will dump the content of the element content definition
844 * Intended just for the debug routine
845 */
846void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000847xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
848 int len;
849
Owen Taylor3473f882001-02-23 17:55:21 +0000850 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000851 len = strlen(buf);
852 if (size - len < 50) {
853 if ((size - len > 4) && (buf[len - 1] != '.'))
854 strcat(buf, " ...");
855 return;
856 }
Owen Taylor3473f882001-02-23 17:55:21 +0000857 if (glob) strcat(buf, "(");
858 switch (content->type) {
859 case XML_ELEMENT_CONTENT_PCDATA:
860 strcat(buf, "#PCDATA");
861 break;
862 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000863 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000864 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000865 strcat(buf, " ...");
866 return;
867 }
868 strcat(buf, (char *) content->prefix);
869 strcat(buf, ":");
870 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000871 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000872 strcat(buf, " ...");
873 return;
874 }
Owen Taylor3473f882001-02-23 17:55:21 +0000875 strcat(buf, (char *) content->name);
876 break;
877 case XML_ELEMENT_CONTENT_SEQ:
878 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
879 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000880 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000881 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000882 xmlSnprintfElementContent(buf, size, content->c1, 0);
883 len = strlen(buf);
884 if (size - len < 50) {
885 if ((size - len > 4) && (buf[len - 1] != '.'))
886 strcat(buf, " ...");
887 return;
888 }
Owen Taylor3473f882001-02-23 17:55:21 +0000889 strcat(buf, " , ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000890 if (((content->c2->type == XML_ELEMENT_CONTENT_OR) ||
891 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
892 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000893 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000894 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000895 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000896 break;
897 case XML_ELEMENT_CONTENT_OR:
898 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
899 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000900 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000901 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000902 xmlSnprintfElementContent(buf, size, content->c1, 0);
903 len = strlen(buf);
904 if (size - len < 50) {
905 if ((size - len > 4) && (buf[len - 1] != '.'))
906 strcat(buf, " ...");
907 return;
908 }
Owen Taylor3473f882001-02-23 17:55:21 +0000909 strcat(buf, " | ");
Daniel Veillard5acfd6b2002-09-18 16:29:02 +0000910 if (((content->c2->type == XML_ELEMENT_CONTENT_SEQ) ||
911 (content->c2->ocur != XML_ELEMENT_CONTENT_ONCE)) &&
912 (content->c2->type != XML_ELEMENT_CONTENT_ELEMENT))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000913 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000914 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000915 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000916 break;
917 }
918 if (glob)
919 strcat(buf, ")");
920 switch (content->ocur) {
921 case XML_ELEMENT_CONTENT_ONCE:
922 break;
923 case XML_ELEMENT_CONTENT_OPT:
924 strcat(buf, "?");
925 break;
926 case XML_ELEMENT_CONTENT_MULT:
927 strcat(buf, "*");
928 break;
929 case XML_ELEMENT_CONTENT_PLUS:
930 strcat(buf, "+");
931 break;
932 }
933}
934
935/****************************************************************
936 * *
937 * Registration of DTD declarations *
938 * *
939 ****************************************************************/
940
941/**
942 * xmlCreateElementTable:
943 *
944 * create and initialize an empty element hash table.
945 *
946 * Returns the xmlElementTablePtr just created or NULL in case of error.
947 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000948static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000949xmlCreateElementTable(void) {
950 return(xmlHashCreate(0));
951}
952
953/**
954 * xmlFreeElement:
955 * @elem: An element
956 *
957 * Deallocate the memory used by an element definition
958 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000959static void
Owen Taylor3473f882001-02-23 17:55:21 +0000960xmlFreeElement(xmlElementPtr elem) {
961 if (elem == NULL) return;
962 xmlUnlinkNode((xmlNodePtr) elem);
963 xmlFreeElementContent(elem->content);
964 if (elem->name != NULL)
965 xmlFree((xmlChar *) elem->name);
966 if (elem->prefix != NULL)
967 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000968#ifdef LIBXML_REGEXP_ENABLED
969 if (elem->contModel != NULL)
970 xmlRegFreeRegexp(elem->contModel);
971#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000972 xmlFree(elem);
973}
974
975
976/**
977 * xmlAddElementDecl:
978 * @ctxt: the validation context
979 * @dtd: pointer to the DTD
980 * @name: the entity name
981 * @type: the element type
982 * @content: the element content tree or NULL
983 *
984 * Register a new element declaration
985 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000986 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +0000987 */
988xmlElementPtr
989xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
990 xmlElementTypeVal type,
991 xmlElementContentPtr content) {
992 xmlElementPtr ret;
993 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000994 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000995 xmlChar *ns, *uqname;
996
997 if (dtd == NULL) {
998 xmlGenericError(xmlGenericErrorContext,
999 "xmlAddElementDecl: dtd == NULL\n");
1000 return(NULL);
1001 }
1002 if (name == NULL) {
1003 xmlGenericError(xmlGenericErrorContext,
1004 "xmlAddElementDecl: name == NULL\n");
1005 return(NULL);
1006 }
1007 switch (type) {
1008 case XML_ELEMENT_TYPE_EMPTY:
1009 if (content != NULL) {
1010 xmlGenericError(xmlGenericErrorContext,
1011 "xmlAddElementDecl: content != NULL for EMPTY\n");
1012 return(NULL);
1013 }
1014 break;
1015 case XML_ELEMENT_TYPE_ANY:
1016 if (content != NULL) {
1017 xmlGenericError(xmlGenericErrorContext,
1018 "xmlAddElementDecl: content != NULL for ANY\n");
1019 return(NULL);
1020 }
1021 break;
1022 case XML_ELEMENT_TYPE_MIXED:
1023 if (content == NULL) {
1024 xmlGenericError(xmlGenericErrorContext,
1025 "xmlAddElementDecl: content == NULL for MIXED\n");
1026 return(NULL);
1027 }
1028 break;
1029 case XML_ELEMENT_TYPE_ELEMENT:
1030 if (content == NULL) {
1031 xmlGenericError(xmlGenericErrorContext,
1032 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1033 return(NULL);
1034 }
1035 break;
1036 default:
1037 xmlGenericError(xmlGenericErrorContext,
1038 "xmlAddElementDecl: unknown type %d\n", type);
1039 return(NULL);
1040 }
1041
1042 /*
1043 * check if name is a QName
1044 */
1045 uqname = xmlSplitQName2(name, &ns);
1046 if (uqname != NULL)
1047 name = uqname;
1048
1049 /*
1050 * Create the Element table if needed.
1051 */
1052 table = (xmlElementTablePtr) dtd->elements;
1053 if (table == NULL) {
1054 table = xmlCreateElementTable();
1055 dtd->elements = (void *) table;
1056 }
1057 if (table == NULL) {
1058 xmlGenericError(xmlGenericErrorContext,
1059 "xmlAddElementDecl: Table creation failed!\n");
1060 return(NULL);
1061 }
1062
Daniel Veillarda10efa82001-04-18 13:09:01 +00001063 /*
1064 * lookup old attributes inserted on an undefined element in the
1065 * internal subset.
1066 */
1067 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1068 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1069 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1070 oldAttributes = ret->attributes;
1071 ret->attributes = NULL;
1072 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1073 xmlFreeElement(ret);
1074 }
Owen Taylor3473f882001-02-23 17:55:21 +00001075 }
Owen Taylor3473f882001-02-23 17:55:21 +00001076
1077 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001078 * The element may already be present if one of its attribute
1079 * was registered first
1080 */
1081 ret = xmlHashLookup2(table, name, ns);
1082 if (ret != NULL) {
1083 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1084 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001085 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001086 */
1087 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1088 if (uqname != NULL)
1089 xmlFree(uqname);
1090 return(NULL);
1091 }
1092 } else {
1093 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1094 if (ret == NULL) {
1095 xmlGenericError(xmlGenericErrorContext,
1096 "xmlAddElementDecl: out of memory\n");
1097 return(NULL);
1098 }
1099 memset(ret, 0, sizeof(xmlElement));
1100 ret->type = XML_ELEMENT_DECL;
1101
1102 /*
1103 * fill the structure.
1104 */
1105 ret->name = xmlStrdup(name);
1106 ret->prefix = ns;
1107
1108 /*
1109 * Validity Check:
1110 * Insertion must not fail
1111 */
1112 if (xmlHashAddEntry2(table, name, ns, ret)) {
1113 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001114 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001115 */
1116 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1117 xmlFreeElement(ret);
1118 if (uqname != NULL)
1119 xmlFree(uqname);
1120 return(NULL);
1121 }
1122 }
1123
1124 /*
1125 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001126 */
1127 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001128 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001129 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001130
1131 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001132 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001133 */
1134 ret->parent = dtd;
1135 ret->doc = dtd->doc;
1136 if (dtd->last == NULL) {
1137 dtd->children = dtd->last = (xmlNodePtr) ret;
1138 } else {
1139 dtd->last->next = (xmlNodePtr) ret;
1140 ret->prev = dtd->last;
1141 dtd->last = (xmlNodePtr) ret;
1142 }
1143 if (uqname != NULL)
1144 xmlFree(uqname);
1145 return(ret);
1146}
1147
1148/**
1149 * xmlFreeElementTable:
1150 * @table: An element table
1151 *
1152 * Deallocate the memory used by an element hash table.
1153 */
1154void
1155xmlFreeElementTable(xmlElementTablePtr table) {
1156 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1157}
1158
1159/**
1160 * xmlCopyElement:
1161 * @elem: An element
1162 *
1163 * Build a copy of an element.
1164 *
1165 * Returns the new xmlElementPtr or NULL in case of error.
1166 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001167static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001168xmlCopyElement(xmlElementPtr elem) {
1169 xmlElementPtr cur;
1170
1171 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1172 if (cur == NULL) {
1173 xmlGenericError(xmlGenericErrorContext,
1174 "xmlCopyElement: out of memory !\n");
1175 return(NULL);
1176 }
1177 memset(cur, 0, sizeof(xmlElement));
1178 cur->type = XML_ELEMENT_DECL;
1179 cur->etype = elem->etype;
1180 if (elem->name != NULL)
1181 cur->name = xmlStrdup(elem->name);
1182 else
1183 cur->name = NULL;
1184 if (elem->prefix != NULL)
1185 cur->prefix = xmlStrdup(elem->prefix);
1186 else
1187 cur->prefix = NULL;
1188 cur->content = xmlCopyElementContent(elem->content);
1189 /* TODO : rebuild the attribute list on the copy */
1190 cur->attributes = NULL;
1191 return(cur);
1192}
1193
1194/**
1195 * xmlCopyElementTable:
1196 * @table: An element table
1197 *
1198 * Build a copy of an element table.
1199 *
1200 * Returns the new xmlElementTablePtr or NULL in case of error.
1201 */
1202xmlElementTablePtr
1203xmlCopyElementTable(xmlElementTablePtr table) {
1204 return((xmlElementTablePtr) xmlHashCopy(table,
1205 (xmlHashCopier) xmlCopyElement));
1206}
1207
1208/**
1209 * xmlDumpElementDecl:
1210 * @buf: the XML buffer output
1211 * @elem: An element table
1212 *
1213 * This will dump the content of the element declaration as an XML
1214 * DTD definition
1215 */
1216void
1217xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1218 switch (elem->etype) {
1219 case XML_ELEMENT_TYPE_EMPTY:
1220 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001221 if (elem->prefix != NULL) {
1222 xmlBufferWriteCHAR(buf, elem->prefix);
1223 xmlBufferWriteChar(buf, ":");
1224 }
Owen Taylor3473f882001-02-23 17:55:21 +00001225 xmlBufferWriteCHAR(buf, elem->name);
1226 xmlBufferWriteChar(buf, " EMPTY>\n");
1227 break;
1228 case XML_ELEMENT_TYPE_ANY:
1229 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001230 if (elem->prefix != NULL) {
1231 xmlBufferWriteCHAR(buf, elem->prefix);
1232 xmlBufferWriteChar(buf, ":");
1233 }
Owen Taylor3473f882001-02-23 17:55:21 +00001234 xmlBufferWriteCHAR(buf, elem->name);
1235 xmlBufferWriteChar(buf, " ANY>\n");
1236 break;
1237 case XML_ELEMENT_TYPE_MIXED:
1238 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001239 if (elem->prefix != NULL) {
1240 xmlBufferWriteCHAR(buf, elem->prefix);
1241 xmlBufferWriteChar(buf, ":");
1242 }
Owen Taylor3473f882001-02-23 17:55:21 +00001243 xmlBufferWriteCHAR(buf, elem->name);
1244 xmlBufferWriteChar(buf, " ");
1245 xmlDumpElementContent(buf, elem->content, 1);
1246 xmlBufferWriteChar(buf, ">\n");
1247 break;
1248 case XML_ELEMENT_TYPE_ELEMENT:
1249 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001250 if (elem->prefix != NULL) {
1251 xmlBufferWriteCHAR(buf, elem->prefix);
1252 xmlBufferWriteChar(buf, ":");
1253 }
Owen Taylor3473f882001-02-23 17:55:21 +00001254 xmlBufferWriteCHAR(buf, elem->name);
1255 xmlBufferWriteChar(buf, " ");
1256 xmlDumpElementContent(buf, elem->content, 1);
1257 xmlBufferWriteChar(buf, ">\n");
1258 break;
1259 default:
1260 xmlGenericError(xmlGenericErrorContext,
1261 "xmlDumpElementDecl: internal: unknown type %d\n",
1262 elem->etype);
1263 }
1264}
1265
1266/**
1267 * xmlDumpElementTable:
1268 * @buf: the XML buffer output
1269 * @table: An element table
1270 *
1271 * This will dump the content of the element table as an XML DTD definition
1272 */
1273void
1274xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1275 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1276}
1277
1278/**
1279 * xmlCreateEnumeration:
1280 * @name: the enumeration name or NULL
1281 *
1282 * create and initialize an enumeration attribute node.
1283 *
1284 * Returns the xmlEnumerationPtr just created or NULL in case
1285 * of error.
1286 */
1287xmlEnumerationPtr
1288xmlCreateEnumeration(xmlChar *name) {
1289 xmlEnumerationPtr ret;
1290
1291 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1292 if (ret == NULL) {
1293 xmlGenericError(xmlGenericErrorContext,
1294 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1295 (long)sizeof(xmlEnumeration));
1296 return(NULL);
1297 }
1298 memset(ret, 0, sizeof(xmlEnumeration));
1299
1300 if (name != NULL)
1301 ret->name = xmlStrdup(name);
1302 return(ret);
1303}
1304
1305/**
1306 * xmlFreeEnumeration:
1307 * @cur: the tree to free.
1308 *
1309 * free an enumeration attribute node (recursive).
1310 */
1311void
1312xmlFreeEnumeration(xmlEnumerationPtr cur) {
1313 if (cur == NULL) return;
1314
1315 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1316
1317 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001318 xmlFree(cur);
1319}
1320
1321/**
1322 * xmlCopyEnumeration:
1323 * @cur: the tree to copy.
1324 *
1325 * Copy an enumeration attribute node (recursive).
1326 *
1327 * Returns the xmlEnumerationPtr just created or NULL in case
1328 * of error.
1329 */
1330xmlEnumerationPtr
1331xmlCopyEnumeration(xmlEnumerationPtr cur) {
1332 xmlEnumerationPtr ret;
1333
1334 if (cur == NULL) return(NULL);
1335 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1336
1337 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1338 else ret->next = NULL;
1339
1340 return(ret);
1341}
1342
1343/**
1344 * xmlDumpEnumeration:
1345 * @buf: the XML buffer output
1346 * @enum: An enumeration
1347 *
1348 * This will dump the content of the enumeration
1349 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001350static void
Owen Taylor3473f882001-02-23 17:55:21 +00001351xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1352 if (cur == NULL) return;
1353
1354 xmlBufferWriteCHAR(buf, cur->name);
1355 if (cur->next == NULL)
1356 xmlBufferWriteChar(buf, ")");
1357 else {
1358 xmlBufferWriteChar(buf, " | ");
1359 xmlDumpEnumeration(buf, cur->next);
1360 }
1361}
1362
1363/**
1364 * xmlCreateAttributeTable:
1365 *
1366 * create and initialize an empty attribute hash table.
1367 *
1368 * Returns the xmlAttributeTablePtr just created or NULL in case
1369 * of error.
1370 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001371static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001372xmlCreateAttributeTable(void) {
1373 return(xmlHashCreate(0));
1374}
1375
1376/**
1377 * xmlScanAttributeDeclCallback:
1378 * @attr: the attribute decl
1379 * @list: the list to update
1380 *
1381 * Callback called by xmlScanAttributeDecl when a new attribute
1382 * has to be entered in the list.
1383 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001384static void
Owen Taylor3473f882001-02-23 17:55:21 +00001385xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001386 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001387 attr->nexth = *list;
1388 *list = attr;
1389}
1390
1391/**
1392 * xmlScanAttributeDecl:
1393 * @dtd: pointer to the DTD
1394 * @elem: the element name
1395 *
1396 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001397 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001398 *
1399 * Returns the pointer to the first attribute decl in the chain,
1400 * possibly NULL.
1401 */
1402xmlAttributePtr
1403xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1404 xmlAttributePtr ret = NULL;
1405 xmlAttributeTablePtr table;
1406
1407 if (dtd == NULL) {
1408 xmlGenericError(xmlGenericErrorContext,
1409 "xmlScanAttributeDecl: dtd == NULL\n");
1410 return(NULL);
1411 }
1412 if (elem == NULL) {
1413 xmlGenericError(xmlGenericErrorContext,
1414 "xmlScanAttributeDecl: elem == NULL\n");
1415 return(NULL);
1416 }
1417 table = (xmlAttributeTablePtr) dtd->attributes;
1418 if (table == NULL)
1419 return(NULL);
1420
1421 /* WRONG !!! */
1422 xmlHashScan3(table, NULL, NULL, elem,
1423 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1424 return(ret);
1425}
1426
1427/**
1428 * xmlScanIDAttributeDecl:
1429 * @ctxt: the validation context
1430 * @elem: the element name
1431 *
1432 * Verify that the element don't have too many ID attributes
1433 * declared.
1434 *
1435 * Returns the number of ID attributes found.
1436 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001437static int
Owen Taylor3473f882001-02-23 17:55:21 +00001438xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1439 xmlAttributePtr cur;
1440 int ret = 0;
1441
1442 if (elem == NULL) return(0);
1443 cur = elem->attributes;
1444 while (cur != NULL) {
1445 if (cur->atype == XML_ATTRIBUTE_ID) {
1446 ret ++;
1447 if (ret > 1)
1448 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001449 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001450 elem->name, cur->name);
1451 }
1452 cur = cur->nexth;
1453 }
1454 return(ret);
1455}
1456
1457/**
1458 * xmlFreeAttribute:
1459 * @elem: An attribute
1460 *
1461 * Deallocate the memory used by an attribute definition
1462 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001463static void
Owen Taylor3473f882001-02-23 17:55:21 +00001464xmlFreeAttribute(xmlAttributePtr attr) {
1465 if (attr == NULL) return;
1466 xmlUnlinkNode((xmlNodePtr) attr);
1467 if (attr->tree != NULL)
1468 xmlFreeEnumeration(attr->tree);
1469 if (attr->elem != NULL)
1470 xmlFree((xmlChar *) attr->elem);
1471 if (attr->name != NULL)
1472 xmlFree((xmlChar *) attr->name);
1473 if (attr->defaultValue != NULL)
1474 xmlFree((xmlChar *) attr->defaultValue);
1475 if (attr->prefix != NULL)
1476 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001477 xmlFree(attr);
1478}
1479
1480
1481/**
1482 * xmlAddAttributeDecl:
1483 * @ctxt: the validation context
1484 * @dtd: pointer to the DTD
1485 * @elem: the element name
1486 * @name: the attribute name
1487 * @ns: the attribute namespace prefix
1488 * @type: the attribute type
1489 * @def: the attribute default type
1490 * @defaultValue: the attribute default value
1491 * @tree: if it's an enumeration, the associated list
1492 *
1493 * Register a new attribute declaration
1494 * Note that @tree becomes the ownership of the DTD
1495 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001496 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001497 */
1498xmlAttributePtr
1499xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1500 const xmlChar *name, const xmlChar *ns,
1501 xmlAttributeType type, xmlAttributeDefault def,
1502 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1503 xmlAttributePtr ret;
1504 xmlAttributeTablePtr table;
1505 xmlElementPtr elemDef;
1506
1507 if (dtd == NULL) {
1508 xmlGenericError(xmlGenericErrorContext,
1509 "xmlAddAttributeDecl: dtd == NULL\n");
1510 xmlFreeEnumeration(tree);
1511 return(NULL);
1512 }
1513 if (name == NULL) {
1514 xmlGenericError(xmlGenericErrorContext,
1515 "xmlAddAttributeDecl: name == NULL\n");
1516 xmlFreeEnumeration(tree);
1517 return(NULL);
1518 }
1519 if (elem == NULL) {
1520 xmlGenericError(xmlGenericErrorContext,
1521 "xmlAddAttributeDecl: elem == NULL\n");
1522 xmlFreeEnumeration(tree);
1523 return(NULL);
1524 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001525
Owen Taylor3473f882001-02-23 17:55:21 +00001526 /*
1527 * Check the type and possibly the default value.
1528 */
1529 switch (type) {
1530 case XML_ATTRIBUTE_CDATA:
1531 break;
1532 case XML_ATTRIBUTE_ID:
1533 break;
1534 case XML_ATTRIBUTE_IDREF:
1535 break;
1536 case XML_ATTRIBUTE_IDREFS:
1537 break;
1538 case XML_ATTRIBUTE_ENTITY:
1539 break;
1540 case XML_ATTRIBUTE_ENTITIES:
1541 break;
1542 case XML_ATTRIBUTE_NMTOKEN:
1543 break;
1544 case XML_ATTRIBUTE_NMTOKENS:
1545 break;
1546 case XML_ATTRIBUTE_ENUMERATION:
1547 break;
1548 case XML_ATTRIBUTE_NOTATION:
1549 break;
1550 default:
1551 xmlGenericError(xmlGenericErrorContext,
1552 "xmlAddAttributeDecl: unknown type %d\n", type);
1553 xmlFreeEnumeration(tree);
1554 return(NULL);
1555 }
1556 if ((defaultValue != NULL) &&
1557 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001558 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001559 elem, name, defaultValue);
1560 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001561 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001562 }
1563
1564 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001565 * Check first that an attribute defined in the external subset wasn't
1566 * already defined in the internal subset
1567 */
1568 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1569 (dtd->doc->intSubset != NULL) &&
1570 (dtd->doc->intSubset->attributes != NULL)) {
1571 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1572 if (ret != NULL)
1573 return(NULL);
1574 }
1575
1576 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001577 * Create the Attribute table if needed.
1578 */
1579 table = (xmlAttributeTablePtr) dtd->attributes;
1580 if (table == NULL) {
1581 table = xmlCreateAttributeTable();
1582 dtd->attributes = (void *) table;
1583 }
1584 if (table == NULL) {
1585 xmlGenericError(xmlGenericErrorContext,
1586 "xmlAddAttributeDecl: Table creation failed!\n");
1587 return(NULL);
1588 }
1589
1590
1591 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1592 if (ret == NULL) {
1593 xmlGenericError(xmlGenericErrorContext,
1594 "xmlAddAttributeDecl: out of memory\n");
1595 return(NULL);
1596 }
1597 memset(ret, 0, sizeof(xmlAttribute));
1598 ret->type = XML_ATTRIBUTE_DECL;
1599
1600 /*
1601 * fill the structure.
1602 */
1603 ret->atype = type;
1604 ret->name = xmlStrdup(name);
1605 ret->prefix = xmlStrdup(ns);
1606 ret->elem = xmlStrdup(elem);
1607 ret->def = def;
1608 ret->tree = tree;
1609 if (defaultValue != NULL)
1610 ret->defaultValue = xmlStrdup(defaultValue);
1611
1612 /*
1613 * Validity Check:
1614 * Search the DTD for previous declarations of the ATTLIST
1615 */
1616 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1617 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001618 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001619 */
1620 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001621 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001622 name, elem);
1623 xmlFreeAttribute(ret);
1624 return(NULL);
1625 }
1626
1627 /*
1628 * Validity Check:
1629 * Multiple ID per element
1630 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001631 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001632 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001633
Owen Taylor3473f882001-02-23 17:55:21 +00001634 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001635 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001636 VERROR(ctxt->userData,
1637 "Element %s has too may ID attributes defined : %s\n",
1638 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001639 ctxt->valid = 0;
1640 }
1641
Daniel Veillard48da9102001-08-07 01:10:10 +00001642 /*
1643 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001644 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001645 */
1646 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1647 ((ret->prefix != NULL &&
1648 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1649 ret->nexth = elemDef->attributes;
1650 elemDef->attributes = ret;
1651 } else {
1652 xmlAttributePtr tmp = elemDef->attributes;
1653
1654 while ((tmp != NULL) &&
1655 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1656 ((ret->prefix != NULL &&
1657 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1658 if (tmp->nexth == NULL)
1659 break;
1660 tmp = tmp->nexth;
1661 }
1662 if (tmp != NULL) {
1663 ret->nexth = tmp->nexth;
1664 tmp->nexth = ret;
1665 } else {
1666 ret->nexth = elemDef->attributes;
1667 elemDef->attributes = ret;
1668 }
1669 }
Owen Taylor3473f882001-02-23 17:55:21 +00001670 }
1671
1672 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001673 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001674 */
1675 ret->parent = dtd;
1676 ret->doc = dtd->doc;
1677 if (dtd->last == NULL) {
1678 dtd->children = dtd->last = (xmlNodePtr) ret;
1679 } else {
1680 dtd->last->next = (xmlNodePtr) ret;
1681 ret->prev = dtd->last;
1682 dtd->last = (xmlNodePtr) ret;
1683 }
1684 return(ret);
1685}
1686
1687/**
1688 * xmlFreeAttributeTable:
1689 * @table: An attribute table
1690 *
1691 * Deallocate the memory used by an entities hash table.
1692 */
1693void
1694xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1695 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1696}
1697
1698/**
1699 * xmlCopyAttribute:
1700 * @attr: An attribute
1701 *
1702 * Build a copy of an attribute.
1703 *
1704 * Returns the new xmlAttributePtr or NULL in case of error.
1705 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001706static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001707xmlCopyAttribute(xmlAttributePtr attr) {
1708 xmlAttributePtr cur;
1709
1710 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1711 if (cur == NULL) {
1712 xmlGenericError(xmlGenericErrorContext,
1713 "xmlCopyAttribute: out of memory !\n");
1714 return(NULL);
1715 }
1716 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001717 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001718 cur->atype = attr->atype;
1719 cur->def = attr->def;
1720 cur->tree = xmlCopyEnumeration(attr->tree);
1721 if (attr->elem != NULL)
1722 cur->elem = xmlStrdup(attr->elem);
1723 if (attr->name != NULL)
1724 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001725 if (attr->prefix != NULL)
1726 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001727 if (attr->defaultValue != NULL)
1728 cur->defaultValue = xmlStrdup(attr->defaultValue);
1729 return(cur);
1730}
1731
1732/**
1733 * xmlCopyAttributeTable:
1734 * @table: An attribute table
1735 *
1736 * Build a copy of an attribute table.
1737 *
1738 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1739 */
1740xmlAttributeTablePtr
1741xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1742 return((xmlAttributeTablePtr) xmlHashCopy(table,
1743 (xmlHashCopier) xmlCopyAttribute));
1744}
1745
1746/**
1747 * xmlDumpAttributeDecl:
1748 * @buf: the XML buffer output
1749 * @attr: An attribute declaration
1750 *
1751 * This will dump the content of the attribute declaration as an XML
1752 * DTD definition
1753 */
1754void
1755xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1756 xmlBufferWriteChar(buf, "<!ATTLIST ");
1757 xmlBufferWriteCHAR(buf, attr->elem);
1758 xmlBufferWriteChar(buf, " ");
1759 if (attr->prefix != NULL) {
1760 xmlBufferWriteCHAR(buf, attr->prefix);
1761 xmlBufferWriteChar(buf, ":");
1762 }
1763 xmlBufferWriteCHAR(buf, attr->name);
1764 switch (attr->atype) {
1765 case XML_ATTRIBUTE_CDATA:
1766 xmlBufferWriteChar(buf, " CDATA");
1767 break;
1768 case XML_ATTRIBUTE_ID:
1769 xmlBufferWriteChar(buf, " ID");
1770 break;
1771 case XML_ATTRIBUTE_IDREF:
1772 xmlBufferWriteChar(buf, " IDREF");
1773 break;
1774 case XML_ATTRIBUTE_IDREFS:
1775 xmlBufferWriteChar(buf, " IDREFS");
1776 break;
1777 case XML_ATTRIBUTE_ENTITY:
1778 xmlBufferWriteChar(buf, " ENTITY");
1779 break;
1780 case XML_ATTRIBUTE_ENTITIES:
1781 xmlBufferWriteChar(buf, " ENTITIES");
1782 break;
1783 case XML_ATTRIBUTE_NMTOKEN:
1784 xmlBufferWriteChar(buf, " NMTOKEN");
1785 break;
1786 case XML_ATTRIBUTE_NMTOKENS:
1787 xmlBufferWriteChar(buf, " NMTOKENS");
1788 break;
1789 case XML_ATTRIBUTE_ENUMERATION:
1790 xmlBufferWriteChar(buf, " (");
1791 xmlDumpEnumeration(buf, attr->tree);
1792 break;
1793 case XML_ATTRIBUTE_NOTATION:
1794 xmlBufferWriteChar(buf, " NOTATION (");
1795 xmlDumpEnumeration(buf, attr->tree);
1796 break;
1797 default:
1798 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001799 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001800 attr->atype);
1801 }
1802 switch (attr->def) {
1803 case XML_ATTRIBUTE_NONE:
1804 break;
1805 case XML_ATTRIBUTE_REQUIRED:
1806 xmlBufferWriteChar(buf, " #REQUIRED");
1807 break;
1808 case XML_ATTRIBUTE_IMPLIED:
1809 xmlBufferWriteChar(buf, " #IMPLIED");
1810 break;
1811 case XML_ATTRIBUTE_FIXED:
1812 xmlBufferWriteChar(buf, " #FIXED");
1813 break;
1814 default:
1815 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001816 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001817 attr->def);
1818 }
1819 if (attr->defaultValue != NULL) {
1820 xmlBufferWriteChar(buf, " ");
1821 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1822 }
1823 xmlBufferWriteChar(buf, ">\n");
1824}
1825
1826/**
1827 * xmlDumpAttributeTable:
1828 * @buf: the XML buffer output
1829 * @table: An attribute table
1830 *
1831 * This will dump the content of the attribute table as an XML DTD definition
1832 */
1833void
1834xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1835 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1836}
1837
1838/************************************************************************
1839 * *
1840 * NOTATIONs *
1841 * *
1842 ************************************************************************/
1843/**
1844 * xmlCreateNotationTable:
1845 *
1846 * create and initialize an empty notation hash table.
1847 *
1848 * Returns the xmlNotationTablePtr just created or NULL in case
1849 * of error.
1850 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001851static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001852xmlCreateNotationTable(void) {
1853 return(xmlHashCreate(0));
1854}
1855
1856/**
1857 * xmlFreeNotation:
1858 * @not: A notation
1859 *
1860 * Deallocate the memory used by an notation definition
1861 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001862static void
Owen Taylor3473f882001-02-23 17:55:21 +00001863xmlFreeNotation(xmlNotationPtr nota) {
1864 if (nota == NULL) return;
1865 if (nota->name != NULL)
1866 xmlFree((xmlChar *) nota->name);
1867 if (nota->PublicID != NULL)
1868 xmlFree((xmlChar *) nota->PublicID);
1869 if (nota->SystemID != NULL)
1870 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001871 xmlFree(nota);
1872}
1873
1874
1875/**
1876 * xmlAddNotationDecl:
1877 * @dtd: pointer to the DTD
1878 * @ctxt: the validation context
1879 * @name: the entity name
1880 * @PublicID: the public identifier or NULL
1881 * @SystemID: the system identifier or NULL
1882 *
1883 * Register a new notation declaration
1884 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001885 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001886 */
1887xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001888xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001889 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001890 const xmlChar *PublicID, const xmlChar *SystemID) {
1891 xmlNotationPtr ret;
1892 xmlNotationTablePtr table;
1893
1894 if (dtd == NULL) {
1895 xmlGenericError(xmlGenericErrorContext,
1896 "xmlAddNotationDecl: dtd == NULL\n");
1897 return(NULL);
1898 }
1899 if (name == NULL) {
1900 xmlGenericError(xmlGenericErrorContext,
1901 "xmlAddNotationDecl: name == NULL\n");
1902 return(NULL);
1903 }
1904 if ((PublicID == NULL) && (SystemID == NULL)) {
1905 xmlGenericError(xmlGenericErrorContext,
1906 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001907 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001908 }
1909
1910 /*
1911 * Create the Notation table if needed.
1912 */
1913 table = (xmlNotationTablePtr) dtd->notations;
1914 if (table == NULL)
1915 dtd->notations = table = xmlCreateNotationTable();
1916 if (table == NULL) {
1917 xmlGenericError(xmlGenericErrorContext,
1918 "xmlAddNotationDecl: Table creation failed!\n");
1919 return(NULL);
1920 }
1921
1922 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1923 if (ret == NULL) {
1924 xmlGenericError(xmlGenericErrorContext,
1925 "xmlAddNotationDecl: out of memory\n");
1926 return(NULL);
1927 }
1928 memset(ret, 0, sizeof(xmlNotation));
1929
1930 /*
1931 * fill the structure.
1932 */
1933 ret->name = xmlStrdup(name);
1934 if (SystemID != NULL)
1935 ret->SystemID = xmlStrdup(SystemID);
1936 if (PublicID != NULL)
1937 ret->PublicID = xmlStrdup(PublicID);
1938
1939 /*
1940 * Validity Check:
1941 * Check the DTD for previous declarations of the ATTLIST
1942 */
1943 if (xmlHashAddEntry(table, name, ret)) {
1944 xmlGenericError(xmlGenericErrorContext,
1945 "xmlAddNotationDecl: %s already defined\n", name);
1946 xmlFreeNotation(ret);
1947 return(NULL);
1948 }
1949 return(ret);
1950}
1951
1952/**
1953 * xmlFreeNotationTable:
1954 * @table: An notation table
1955 *
1956 * Deallocate the memory used by an entities hash table.
1957 */
1958void
1959xmlFreeNotationTable(xmlNotationTablePtr table) {
1960 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1961}
1962
1963/**
1964 * xmlCopyNotation:
1965 * @nota: A notation
1966 *
1967 * Build a copy of a notation.
1968 *
1969 * Returns the new xmlNotationPtr or NULL in case of error.
1970 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001971static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001972xmlCopyNotation(xmlNotationPtr nota) {
1973 xmlNotationPtr cur;
1974
1975 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1976 if (cur == NULL) {
1977 xmlGenericError(xmlGenericErrorContext,
1978 "xmlCopyNotation: out of memory !\n");
1979 return(NULL);
1980 }
1981 if (nota->name != NULL)
1982 cur->name = xmlStrdup(nota->name);
1983 else
1984 cur->name = NULL;
1985 if (nota->PublicID != NULL)
1986 cur->PublicID = xmlStrdup(nota->PublicID);
1987 else
1988 cur->PublicID = NULL;
1989 if (nota->SystemID != NULL)
1990 cur->SystemID = xmlStrdup(nota->SystemID);
1991 else
1992 cur->SystemID = NULL;
1993 return(cur);
1994}
1995
1996/**
1997 * xmlCopyNotationTable:
1998 * @table: A notation table
1999 *
2000 * Build a copy of a notation table.
2001 *
2002 * Returns the new xmlNotationTablePtr or NULL in case of error.
2003 */
2004xmlNotationTablePtr
2005xmlCopyNotationTable(xmlNotationTablePtr table) {
2006 return((xmlNotationTablePtr) xmlHashCopy(table,
2007 (xmlHashCopier) xmlCopyNotation));
2008}
2009
2010/**
2011 * xmlDumpNotationDecl:
2012 * @buf: the XML buffer output
2013 * @nota: A notation declaration
2014 *
2015 * This will dump the content the notation declaration as an XML DTD definition
2016 */
2017void
2018xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2019 xmlBufferWriteChar(buf, "<!NOTATION ");
2020 xmlBufferWriteCHAR(buf, nota->name);
2021 if (nota->PublicID != NULL) {
2022 xmlBufferWriteChar(buf, " PUBLIC ");
2023 xmlBufferWriteQuotedString(buf, nota->PublicID);
2024 if (nota->SystemID != NULL) {
2025 xmlBufferWriteChar(buf, " ");
2026 xmlBufferWriteCHAR(buf, nota->SystemID);
2027 }
2028 } else {
2029 xmlBufferWriteChar(buf, " SYSTEM ");
2030 xmlBufferWriteCHAR(buf, nota->SystemID);
2031 }
2032 xmlBufferWriteChar(buf, " >\n");
2033}
2034
2035/**
2036 * xmlDumpNotationTable:
2037 * @buf: the XML buffer output
2038 * @table: A notation table
2039 *
2040 * This will dump the content of the notation table as an XML DTD definition
2041 */
2042void
2043xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2044 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2045}
2046
2047/************************************************************************
2048 * *
2049 * IDs *
2050 * *
2051 ************************************************************************/
2052/**
2053 * xmlCreateIDTable:
2054 *
2055 * create and initialize an empty id hash table.
2056 *
2057 * Returns the xmlIDTablePtr just created or NULL in case
2058 * of error.
2059 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002060static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002061xmlCreateIDTable(void) {
2062 return(xmlHashCreate(0));
2063}
2064
2065/**
2066 * xmlFreeID:
2067 * @not: A id
2068 *
2069 * Deallocate the memory used by an id definition
2070 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002071static void
Owen Taylor3473f882001-02-23 17:55:21 +00002072xmlFreeID(xmlIDPtr id) {
2073 if (id == NULL) return;
2074 if (id->value != NULL)
2075 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00002076 xmlFree(id);
2077}
2078
2079/**
2080 * xmlAddID:
2081 * @ctxt: the validation context
2082 * @doc: pointer to the document
2083 * @value: the value name
2084 * @attr: the attribute holding the ID
2085 *
2086 * Register a new id declaration
2087 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002088 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002089 */
2090xmlIDPtr
2091xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2092 xmlAttrPtr attr) {
2093 xmlIDPtr ret;
2094 xmlIDTablePtr table;
2095
2096 if (doc == NULL) {
2097 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002098 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002099 return(NULL);
2100 }
2101 if (value == NULL) {
2102 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002103 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002104 return(NULL);
2105 }
2106 if (attr == NULL) {
2107 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002108 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002109 return(NULL);
2110 }
2111
2112 /*
2113 * Create the ID table if needed.
2114 */
2115 table = (xmlIDTablePtr) doc->ids;
2116 if (table == NULL)
2117 doc->ids = table = xmlCreateIDTable();
2118 if (table == NULL) {
2119 xmlGenericError(xmlGenericErrorContext,
2120 "xmlAddID: Table creation failed!\n");
2121 return(NULL);
2122 }
2123
2124 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2125 if (ret == NULL) {
2126 xmlGenericError(xmlGenericErrorContext,
2127 "xmlAddID: out of memory\n");
2128 return(NULL);
2129 }
2130
2131 /*
2132 * fill the structure.
2133 */
2134 ret->value = xmlStrdup(value);
2135 ret->attr = attr;
2136
2137 if (xmlHashAddEntry(table, value, ret) < 0) {
2138 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002139 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002140 */
Daniel Veillard76575762002-09-05 14:21:15 +00002141 if (ctxt != NULL) {
2142 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002143 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002144 }
Owen Taylor3473f882001-02-23 17:55:21 +00002145 xmlFreeID(ret);
2146 return(NULL);
2147 }
2148 return(ret);
2149}
2150
2151/**
2152 * xmlFreeIDTable:
2153 * @table: An id table
2154 *
2155 * Deallocate the memory used by an ID hash table.
2156 */
2157void
2158xmlFreeIDTable(xmlIDTablePtr table) {
2159 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2160}
2161
2162/**
2163 * xmlIsID:
2164 * @doc: the document
2165 * @elem: the element carrying the attribute
2166 * @attr: the attribute
2167 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002168 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002169 * then this is simple, otherwise we use an heuristic: name ID (upper
2170 * or lowercase).
2171 *
2172 * Returns 0 or 1 depending on the lookup result
2173 */
2174int
2175xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2176 if (doc == NULL) return(0);
2177 if (attr == NULL) return(0);
2178 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2179 return(0);
2180 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2181 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2182 (xmlStrEqual(BAD_CAST "name", attr->name)))
2183 return(1);
2184 return(0);
2185 } else {
2186 xmlAttributePtr attrDecl;
2187
2188 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002189 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2190 /*
2191 * TODO: this sucks ... recomputing this every time is stupid
2192 */
2193 int len = xmlStrlen(elem->name) + xmlStrlen(elem->ns->prefix) + 2;
2194 xmlChar *fullname;
2195
2196 fullname = xmlMalloc(len);
2197 if (fullname == NULL)
2198 return(0);
2199 snprintf((char *) fullname, len, "%s:%s", (char *) elem->ns->prefix,
2200 (char *) elem->name);
2201 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2202 attr->name);
2203 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2204 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2205 attr->name);
2206 xmlFree(fullname);
2207 } else {
2208 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2209 attr->name);
2210 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2211 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2212 attr->name);
2213 }
Owen Taylor3473f882001-02-23 17:55:21 +00002214
2215 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2216 return(1);
2217 }
2218 return(0);
2219}
2220
2221/**
2222 * xmlRemoveID
2223 * @doc: the document
2224 * @attr: the attribute
2225 *
2226 * Remove the given attribute from the ID table maintained internally.
2227 *
2228 * Returns -1 if the lookup failed and 0 otherwise
2229 */
2230int
2231xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2232 xmlAttrPtr cur;
2233 xmlIDTablePtr table;
2234 xmlChar *ID;
2235
2236 if (doc == NULL) return(-1);
2237 if (attr == NULL) return(-1);
2238 table = (xmlIDTablePtr) doc->ids;
2239 if (table == NULL)
2240 return(-1);
2241
2242 if (attr == NULL)
2243 return(-1);
2244 ID = xmlNodeListGetString(doc, attr->children, 1);
2245 if (ID == NULL)
2246 return(-1);
2247 cur = xmlHashLookup(table, ID);
2248 if (cur != attr) {
2249 xmlFree(ID);
2250 return(-1);
2251 }
2252 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2253 xmlFree(ID);
2254 return(0);
2255}
2256
2257/**
2258 * xmlGetID:
2259 * @doc: pointer to the document
2260 * @ID: the ID value
2261 *
2262 * Search the attribute declaring the given ID
2263 *
2264 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2265 */
2266xmlAttrPtr
2267xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2268 xmlIDTablePtr table;
2269 xmlIDPtr id;
2270
2271 if (doc == NULL) {
2272 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2273 return(NULL);
2274 }
2275
2276 if (ID == NULL) {
2277 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2278 return(NULL);
2279 }
2280
2281 table = (xmlIDTablePtr) doc->ids;
2282 if (table == NULL)
2283 return(NULL);
2284
2285 id = xmlHashLookup(table, ID);
2286 if (id == NULL)
2287 return(NULL);
2288 return(id->attr);
2289}
2290
2291/************************************************************************
2292 * *
2293 * Refs *
2294 * *
2295 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002296typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002297{
2298 xmlListPtr l;
2299 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002300} xmlRemoveMemo;
2301
2302typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2303
2304typedef struct xmlValidateMemo_t
2305{
2306 xmlValidCtxtPtr ctxt;
2307 const xmlChar *name;
2308} xmlValidateMemo;
2309
2310typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002311
2312/**
2313 * xmlCreateRefTable:
2314 *
2315 * create and initialize an empty ref hash table.
2316 *
2317 * Returns the xmlRefTablePtr just created or NULL in case
2318 * of error.
2319 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002320static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002321xmlCreateRefTable(void) {
2322 return(xmlHashCreate(0));
2323}
2324
2325/**
2326 * xmlFreeRef:
2327 * @lk: A list link
2328 *
2329 * Deallocate the memory used by a ref definition
2330 */
2331static void
2332xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002333 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2334 if (ref == NULL) return;
2335 if (ref->value != NULL)
2336 xmlFree((xmlChar *)ref->value);
2337 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002338}
2339
2340/**
2341 * xmlFreeRefList:
2342 * @list_ref: A list of references.
2343 *
2344 * Deallocate the memory used by a list of references
2345 */
2346static void
2347xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002348 if (list_ref == NULL) return;
2349 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002350}
2351
2352/**
2353 * xmlWalkRemoveRef:
2354 * @data: Contents of current link
2355 * @user: Value supplied by the user
2356 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002357 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002358 */
2359static int
2360xmlWalkRemoveRef(const void *data, const void *user)
2361{
Daniel Veillard37721922001-05-04 15:21:12 +00002362 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2363 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2364 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002365
Daniel Veillard37721922001-05-04 15:21:12 +00002366 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2367 xmlListRemoveFirst(ref_list, (void *)data);
2368 return 0;
2369 }
2370 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002371}
2372
2373/**
2374 * xmlAddRef:
2375 * @ctxt: the validation context
2376 * @doc: pointer to the document
2377 * @value: the value name
2378 * @attr: the attribute holding the Ref
2379 *
2380 * Register a new ref declaration
2381 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002382 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002383 */
2384xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002385xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002386 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002387 xmlRefPtr ret;
2388 xmlRefTablePtr table;
2389 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002390
Daniel Veillard37721922001-05-04 15:21:12 +00002391 if (doc == NULL) {
2392 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002393 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002394 return(NULL);
2395 }
2396 if (value == NULL) {
2397 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002398 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002399 return(NULL);
2400 }
2401 if (attr == NULL) {
2402 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002403 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002404 return(NULL);
2405 }
Owen Taylor3473f882001-02-23 17:55:21 +00002406
Daniel Veillard37721922001-05-04 15:21:12 +00002407 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002408 * Create the Ref table if needed.
2409 */
Daniel Veillard37721922001-05-04 15:21:12 +00002410 table = (xmlRefTablePtr) doc->refs;
2411 if (table == NULL)
2412 doc->refs = table = xmlCreateRefTable();
2413 if (table == NULL) {
2414 xmlGenericError(xmlGenericErrorContext,
2415 "xmlAddRef: Table creation failed!\n");
2416 return(NULL);
2417 }
Owen Taylor3473f882001-02-23 17:55:21 +00002418
Daniel Veillard37721922001-05-04 15:21:12 +00002419 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2420 if (ret == NULL) {
2421 xmlGenericError(xmlGenericErrorContext,
2422 "xmlAddRef: out of memory\n");
2423 return(NULL);
2424 }
Owen Taylor3473f882001-02-23 17:55:21 +00002425
Daniel Veillard37721922001-05-04 15:21:12 +00002426 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002427 * fill the structure.
2428 */
Daniel Veillard37721922001-05-04 15:21:12 +00002429 ret->value = xmlStrdup(value);
2430 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002431
Daniel Veillard37721922001-05-04 15:21:12 +00002432 /* To add a reference :-
2433 * References are maintained as a list of references,
2434 * Lookup the entry, if no entry create new nodelist
2435 * Add the owning node to the NodeList
2436 * Return the ref
2437 */
Owen Taylor3473f882001-02-23 17:55:21 +00002438
Daniel Veillard37721922001-05-04 15:21:12 +00002439 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2440 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2441 xmlGenericError(xmlGenericErrorContext,
2442 "xmlAddRef: Reference list creation failed!\n");
2443 return(NULL);
2444 }
2445 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2446 xmlListDelete(ref_list);
2447 xmlGenericError(xmlGenericErrorContext,
2448 "xmlAddRef: Reference list insertion failed!\n");
2449 return(NULL);
2450 }
2451 }
2452 xmlListInsert(ref_list, ret);
2453 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002454}
2455
2456/**
2457 * xmlFreeRefTable:
2458 * @table: An ref table
2459 *
2460 * Deallocate the memory used by an Ref hash table.
2461 */
2462void
2463xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002464 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002465}
2466
2467/**
2468 * xmlIsRef:
2469 * @doc: the document
2470 * @elem: the element carrying the attribute
2471 * @attr: the attribute
2472 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002473 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002474 * then this is simple, otherwise we use an heuristic: name Ref (upper
2475 * or lowercase).
2476 *
2477 * Returns 0 or 1 depending on the lookup result
2478 */
2479int
2480xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002481 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2482 return(0);
2483 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2484 /* TODO @@@ */
2485 return(0);
2486 } else {
2487 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002488
Daniel Veillard37721922001-05-04 15:21:12 +00002489 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2490 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2491 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2492 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002493
Daniel Veillard37721922001-05-04 15:21:12 +00002494 if ((attrDecl != NULL) &&
2495 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2496 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2497 return(1);
2498 }
2499 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002500}
2501
2502/**
2503 * xmlRemoveRef
2504 * @doc: the document
2505 * @attr: the attribute
2506 *
2507 * Remove the given attribute from the Ref table maintained internally.
2508 *
2509 * Returns -1 if the lookup failed and 0 otherwise
2510 */
2511int
2512xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002513 xmlListPtr ref_list;
2514 xmlRefTablePtr table;
2515 xmlChar *ID;
2516 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002517
Daniel Veillard37721922001-05-04 15:21:12 +00002518 if (doc == NULL) return(-1);
2519 if (attr == NULL) return(-1);
2520 table = (xmlRefTablePtr) doc->refs;
2521 if (table == NULL)
2522 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002523
Daniel Veillard37721922001-05-04 15:21:12 +00002524 if (attr == NULL)
2525 return(-1);
2526 ID = xmlNodeListGetString(doc, attr->children, 1);
2527 if (ID == NULL)
2528 return(-1);
2529 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002530
Daniel Veillard37721922001-05-04 15:21:12 +00002531 if(ref_list == NULL) {
2532 xmlFree(ID);
2533 return (-1);
2534 }
2535 /* At this point, ref_list refers to a list of references which
2536 * have the same key as the supplied attr. Our list of references
2537 * is ordered by reference address and we don't have that information
2538 * here to use when removing. We'll have to walk the list and
2539 * check for a matching attribute, when we find one stop the walk
2540 * and remove the entry.
2541 * The list is ordered by reference, so that means we don't have the
2542 * key. Passing the list and the reference to the walker means we
2543 * will have enough data to be able to remove the entry.
2544 */
2545 target.l = ref_list;
2546 target.ap = attr;
2547
2548 /* Remove the supplied attr from our list */
2549 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002550
Daniel Veillard37721922001-05-04 15:21:12 +00002551 /*If the list is empty then remove the list entry in the hash */
2552 if (xmlListEmpty(ref_list))
2553 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2554 xmlFreeRefList);
2555 xmlFree(ID);
2556 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002557}
2558
2559/**
2560 * xmlGetRefs:
2561 * @doc: pointer to the document
2562 * @ID: the ID value
2563 *
2564 * Find the set of references for the supplied ID.
2565 *
2566 * Returns NULL if not found, otherwise node set for the ID.
2567 */
2568xmlListPtr
2569xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002570 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002571
Daniel Veillard37721922001-05-04 15:21:12 +00002572 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002573 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002574 return(NULL);
2575 }
Owen Taylor3473f882001-02-23 17:55:21 +00002576
Daniel Veillard37721922001-05-04 15:21:12 +00002577 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002578 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002579 return(NULL);
2580 }
Owen Taylor3473f882001-02-23 17:55:21 +00002581
Daniel Veillard37721922001-05-04 15:21:12 +00002582 table = (xmlRefTablePtr) doc->refs;
2583 if (table == NULL)
2584 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002585
Daniel Veillard37721922001-05-04 15:21:12 +00002586 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002587}
2588
2589/************************************************************************
2590 * *
2591 * Routines for validity checking *
2592 * *
2593 ************************************************************************/
2594
2595/**
2596 * xmlGetDtdElementDesc:
2597 * @dtd: a pointer to the DtD to search
2598 * @name: the element name
2599 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002600 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002601 *
2602 * returns the xmlElementPtr if found or NULL
2603 */
2604
2605xmlElementPtr
2606xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2607 xmlElementTablePtr table;
2608 xmlElementPtr cur;
2609 xmlChar *uqname = NULL, *prefix = NULL;
2610
2611 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002612 if (dtd->elements == NULL)
2613 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002614 table = (xmlElementTablePtr) dtd->elements;
2615
2616 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002617 if (uqname != NULL)
2618 name = uqname;
2619 cur = xmlHashLookup2(table, name, prefix);
2620 if (prefix != NULL) xmlFree(prefix);
2621 if (uqname != NULL) xmlFree(uqname);
2622 return(cur);
2623}
2624/**
2625 * xmlGetDtdElementDesc2:
2626 * @dtd: a pointer to the DtD to search
2627 * @name: the element name
2628 * @create: create an empty description if not found
2629 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002630 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002631 *
2632 * returns the xmlElementPtr if found or NULL
2633 */
2634
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002635static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002636xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2637 xmlElementTablePtr table;
2638 xmlElementPtr cur;
2639 xmlChar *uqname = NULL, *prefix = NULL;
2640
2641 if (dtd == NULL) return(NULL);
2642 if (dtd->elements == NULL) {
2643 if (!create)
2644 return(NULL);
2645 /*
2646 * Create the Element table if needed.
2647 */
2648 table = (xmlElementTablePtr) dtd->elements;
2649 if (table == NULL) {
2650 table = xmlCreateElementTable();
2651 dtd->elements = (void *) table;
2652 }
2653 if (table == NULL) {
2654 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002655 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002656 return(NULL);
2657 }
2658 }
2659 table = (xmlElementTablePtr) dtd->elements;
2660
2661 uqname = xmlSplitQName2(name, &prefix);
2662 if (uqname != NULL)
2663 name = uqname;
2664 cur = xmlHashLookup2(table, name, prefix);
2665 if ((cur == NULL) && (create)) {
2666 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2667 if (cur == NULL) {
2668 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002669 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002670 return(NULL);
2671 }
2672 memset(cur, 0, sizeof(xmlElement));
2673 cur->type = XML_ELEMENT_DECL;
2674
2675 /*
2676 * fill the structure.
2677 */
2678 cur->name = xmlStrdup(name);
2679 cur->prefix = xmlStrdup(prefix);
2680 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2681
2682 xmlHashAddEntry2(table, name, prefix, cur);
2683 }
2684 if (prefix != NULL) xmlFree(prefix);
2685 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002686 return(cur);
2687}
2688
2689/**
2690 * xmlGetDtdQElementDesc:
2691 * @dtd: a pointer to the DtD to search
2692 * @name: the element name
2693 * @prefix: the element namespace prefix
2694 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002695 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002696 *
2697 * returns the xmlElementPtr if found or NULL
2698 */
2699
Daniel Veillard48da9102001-08-07 01:10:10 +00002700xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002701xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2702 const xmlChar *prefix) {
2703 xmlElementTablePtr table;
2704
2705 if (dtd == NULL) return(NULL);
2706 if (dtd->elements == NULL) return(NULL);
2707 table = (xmlElementTablePtr) dtd->elements;
2708
2709 return(xmlHashLookup2(table, name, prefix));
2710}
2711
2712/**
2713 * xmlGetDtdAttrDesc:
2714 * @dtd: a pointer to the DtD to search
2715 * @elem: the element name
2716 * @name: the attribute name
2717 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002718 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002719 * this element.
2720 *
2721 * returns the xmlAttributePtr if found or NULL
2722 */
2723
2724xmlAttributePtr
2725xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2726 xmlAttributeTablePtr table;
2727 xmlAttributePtr cur;
2728 xmlChar *uqname = NULL, *prefix = NULL;
2729
2730 if (dtd == NULL) return(NULL);
2731 if (dtd->attributes == NULL) return(NULL);
2732
2733 table = (xmlAttributeTablePtr) dtd->attributes;
2734 if (table == NULL)
2735 return(NULL);
2736
2737 uqname = xmlSplitQName2(name, &prefix);
2738
2739 if (uqname != NULL) {
2740 cur = xmlHashLookup3(table, uqname, prefix, elem);
2741 if (prefix != NULL) xmlFree(prefix);
2742 if (uqname != NULL) xmlFree(uqname);
2743 } else
2744 cur = xmlHashLookup3(table, name, NULL, elem);
2745 return(cur);
2746}
2747
2748/**
2749 * xmlGetDtdQAttrDesc:
2750 * @dtd: a pointer to the DtD to search
2751 * @elem: the element name
2752 * @name: the attribute name
2753 * @prefix: the attribute namespace prefix
2754 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002755 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002756 * this element.
2757 *
2758 * returns the xmlAttributePtr if found or NULL
2759 */
2760
Daniel Veillard48da9102001-08-07 01:10:10 +00002761xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002762xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2763 const xmlChar *prefix) {
2764 xmlAttributeTablePtr table;
2765
2766 if (dtd == NULL) return(NULL);
2767 if (dtd->attributes == NULL) return(NULL);
2768 table = (xmlAttributeTablePtr) dtd->attributes;
2769
2770 return(xmlHashLookup3(table, name, prefix, elem));
2771}
2772
2773/**
2774 * xmlGetDtdNotationDesc:
2775 * @dtd: a pointer to the DtD to search
2776 * @name: the notation name
2777 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002778 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002779 *
2780 * returns the xmlNotationPtr if found or NULL
2781 */
2782
2783xmlNotationPtr
2784xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2785 xmlNotationTablePtr table;
2786
2787 if (dtd == NULL) return(NULL);
2788 if (dtd->notations == NULL) return(NULL);
2789 table = (xmlNotationTablePtr) dtd->notations;
2790
2791 return(xmlHashLookup(table, name));
2792}
2793
2794/**
2795 * xmlValidateNotationUse:
2796 * @ctxt: the validation context
2797 * @doc: the document
2798 * @notationName: the notation name to check
2799 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002800 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002801 * - [ VC: Notation Declared ]
2802 *
2803 * returns 1 if valid or 0 otherwise
2804 */
2805
2806int
2807xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2808 const xmlChar *notationName) {
2809 xmlNotationPtr notaDecl;
2810 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2811
2812 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2813 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2814 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2815
2816 if (notaDecl == NULL) {
2817 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2818 notationName);
2819 return(0);
2820 }
2821 return(1);
2822}
2823
2824/**
2825 * xmlIsMixedElement
2826 * @doc: the document
2827 * @name: the element name
2828 *
2829 * Search in the DtDs whether an element accept Mixed content (or ANY)
2830 * basically if it is supposed to accept text childs
2831 *
2832 * returns 0 if no, 1 if yes, and -1 if no element description is available
2833 */
2834
2835int
2836xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2837 xmlElementPtr elemDecl;
2838
2839 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2840
2841 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2842 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2843 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2844 if (elemDecl == NULL) return(-1);
2845 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002846 case XML_ELEMENT_TYPE_UNDEFINED:
2847 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002848 case XML_ELEMENT_TYPE_ELEMENT:
2849 return(0);
2850 case XML_ELEMENT_TYPE_EMPTY:
2851 /*
2852 * return 1 for EMPTY since we want VC error to pop up
2853 * on <empty> </empty> for example
2854 */
2855 case XML_ELEMENT_TYPE_ANY:
2856 case XML_ELEMENT_TYPE_MIXED:
2857 return(1);
2858 }
2859 return(1);
2860}
2861
2862/**
2863 * xmlValidateNameValue:
2864 * @value: an Name value
2865 *
2866 * Validate that the given value match Name production
2867 *
2868 * returns 1 if valid or 0 otherwise
2869 */
2870
Daniel Veillard9b731d72002-04-14 12:56:08 +00002871int
Owen Taylor3473f882001-02-23 17:55:21 +00002872xmlValidateNameValue(const xmlChar *value) {
2873 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002874 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002875
2876 if (value == NULL) return(0);
2877 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002878 val = xmlStringCurrentChar(NULL, cur, &len);
2879 cur += len;
2880 if (!IS_LETTER(val) && (val != '_') &&
2881 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002882 return(0);
2883 }
2884
Daniel Veillardd8224e02002-01-13 15:43:22 +00002885 val = xmlStringCurrentChar(NULL, cur, &len);
2886 cur += len;
2887 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2888 (val == '.') || (val == '-') ||
2889 (val == '_') || (val == ':') ||
2890 (IS_COMBINING(val)) ||
2891 (IS_EXTENDER(val))) {
2892 val = xmlStringCurrentChar(NULL, cur, &len);
2893 cur += len;
2894 }
Owen Taylor3473f882001-02-23 17:55:21 +00002895
Daniel Veillardd8224e02002-01-13 15:43:22 +00002896 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002897
2898 return(1);
2899}
2900
2901/**
2902 * xmlValidateNamesValue:
2903 * @value: an Names value
2904 *
2905 * Validate that the given value match Names production
2906 *
2907 * returns 1 if valid or 0 otherwise
2908 */
2909
Daniel Veillard9b731d72002-04-14 12:56:08 +00002910int
Owen Taylor3473f882001-02-23 17:55:21 +00002911xmlValidateNamesValue(const xmlChar *value) {
2912 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002913 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002914
2915 if (value == NULL) return(0);
2916 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002917 val = xmlStringCurrentChar(NULL, cur, &len);
2918 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002919
Daniel Veillardd8224e02002-01-13 15:43:22 +00002920 if (!IS_LETTER(val) && (val != '_') &&
2921 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002922 return(0);
2923 }
2924
Daniel Veillardd8224e02002-01-13 15:43:22 +00002925 val = xmlStringCurrentChar(NULL, cur, &len);
2926 cur += len;
2927 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2928 (val == '.') || (val == '-') ||
2929 (val == '_') || (val == ':') ||
2930 (IS_COMBINING(val)) ||
2931 (IS_EXTENDER(val))) {
2932 val = xmlStringCurrentChar(NULL, cur, &len);
2933 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002934 }
2935
Daniel Veillardd8224e02002-01-13 15:43:22 +00002936 while (IS_BLANK(val)) {
2937 while (IS_BLANK(val)) {
2938 val = xmlStringCurrentChar(NULL, cur, &len);
2939 cur += len;
2940 }
2941
2942 if (!IS_LETTER(val) && (val != '_') &&
2943 (val != ':')) {
2944 return(0);
2945 }
2946 val = xmlStringCurrentChar(NULL, cur, &len);
2947 cur += len;
2948
2949 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2950 (val == '.') || (val == '-') ||
2951 (val == '_') || (val == ':') ||
2952 (IS_COMBINING(val)) ||
2953 (IS_EXTENDER(val))) {
2954 val = xmlStringCurrentChar(NULL, cur, &len);
2955 cur += len;
2956 }
2957 }
2958
2959 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002960
2961 return(1);
2962}
2963
2964/**
2965 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002966 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002967 *
2968 * Validate that the given value match Nmtoken production
2969 *
2970 * [ VC: Name Token ]
2971 *
2972 * returns 1 if valid or 0 otherwise
2973 */
2974
Daniel Veillard9b731d72002-04-14 12:56:08 +00002975int
Owen Taylor3473f882001-02-23 17:55:21 +00002976xmlValidateNmtokenValue(const xmlChar *value) {
2977 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002978 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002979
2980 if (value == NULL) return(0);
2981 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002982 val = xmlStringCurrentChar(NULL, cur, &len);
2983 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002984
Daniel Veillardd8224e02002-01-13 15:43:22 +00002985 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2986 (val != '.') && (val != '-') &&
2987 (val != '_') && (val != ':') &&
2988 (!IS_COMBINING(val)) &&
2989 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002990 return(0);
2991
Daniel Veillardd8224e02002-01-13 15:43:22 +00002992 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2993 (val == '.') || (val == '-') ||
2994 (val == '_') || (val == ':') ||
2995 (IS_COMBINING(val)) ||
2996 (IS_EXTENDER(val))) {
2997 val = xmlStringCurrentChar(NULL, cur, &len);
2998 cur += len;
2999 }
Owen Taylor3473f882001-02-23 17:55:21 +00003000
Daniel Veillardd8224e02002-01-13 15:43:22 +00003001 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003002
3003 return(1);
3004}
3005
3006/**
3007 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003008 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00003009 *
3010 * Validate that the given value match Nmtokens production
3011 *
3012 * [ VC: Name Token ]
3013 *
3014 * returns 1 if valid or 0 otherwise
3015 */
3016
Daniel Veillard9b731d72002-04-14 12:56:08 +00003017int
Owen Taylor3473f882001-02-23 17:55:21 +00003018xmlValidateNmtokensValue(const xmlChar *value) {
3019 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003020 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003021
3022 if (value == NULL) return(0);
3023 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003024 val = xmlStringCurrentChar(NULL, cur, &len);
3025 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003026
Daniel Veillardd8224e02002-01-13 15:43:22 +00003027 while (IS_BLANK(val)) {
3028 val = xmlStringCurrentChar(NULL, cur, &len);
3029 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003030 }
3031
Daniel Veillardd8224e02002-01-13 15:43:22 +00003032 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3033 (val != '.') && (val != '-') &&
3034 (val != '_') && (val != ':') &&
3035 (!IS_COMBINING(val)) &&
3036 (!IS_EXTENDER(val)))
3037 return(0);
3038
3039 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3040 (val == '.') || (val == '-') ||
3041 (val == '_') || (val == ':') ||
3042 (IS_COMBINING(val)) ||
3043 (IS_EXTENDER(val))) {
3044 val = xmlStringCurrentChar(NULL, cur, &len);
3045 cur += len;
3046 }
3047
3048 while (IS_BLANK(val)) {
3049 while (IS_BLANK(val)) {
3050 val = xmlStringCurrentChar(NULL, cur, &len);
3051 cur += len;
3052 }
3053 if (val == 0) return(1);
3054
3055 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3056 (val != '.') && (val != '-') &&
3057 (val != '_') && (val != ':') &&
3058 (!IS_COMBINING(val)) &&
3059 (!IS_EXTENDER(val)))
3060 return(0);
3061
3062 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3063 (val == '.') || (val == '-') ||
3064 (val == '_') || (val == ':') ||
3065 (IS_COMBINING(val)) ||
3066 (IS_EXTENDER(val))) {
3067 val = xmlStringCurrentChar(NULL, cur, &len);
3068 cur += len;
3069 }
3070 }
3071
3072 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003073
3074 return(1);
3075}
3076
3077/**
3078 * xmlValidateNotationDecl:
3079 * @ctxt: the validation context
3080 * @doc: a document instance
3081 * @nota: a notation definition
3082 *
3083 * Try to validate a single notation definition
3084 * basically it does the following checks as described by the
3085 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003086 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003087 * But this function get called anyway ...
3088 *
3089 * returns 1 if valid or 0 otherwise
3090 */
3091
3092int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003093xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3094 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003095 int ret = 1;
3096
3097 return(ret);
3098}
3099
3100/**
3101 * xmlValidateAttributeValue:
3102 * @type: an attribute type
3103 * @value: an attribute value
3104 *
3105 * Validate that the given attribute value match the proper production
3106 *
3107 * [ VC: ID ]
3108 * Values of type ID must match the Name production....
3109 *
3110 * [ VC: IDREF ]
3111 * Values of type IDREF must match the Name production, and values
3112 * of type IDREFS must match Names ...
3113 *
3114 * [ VC: Entity Name ]
3115 * Values of type ENTITY must match the Name production, values
3116 * of type ENTITIES must match Names ...
3117 *
3118 * [ VC: Name Token ]
3119 * Values of type NMTOKEN must match the Nmtoken production; values
3120 * of type NMTOKENS must match Nmtokens.
3121 *
3122 * returns 1 if valid or 0 otherwise
3123 */
3124
3125int
3126xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3127 switch (type) {
3128 case XML_ATTRIBUTE_ENTITIES:
3129 case XML_ATTRIBUTE_IDREFS:
3130 return(xmlValidateNamesValue(value));
3131 case XML_ATTRIBUTE_ENTITY:
3132 case XML_ATTRIBUTE_IDREF:
3133 case XML_ATTRIBUTE_ID:
3134 case XML_ATTRIBUTE_NOTATION:
3135 return(xmlValidateNameValue(value));
3136 case XML_ATTRIBUTE_NMTOKENS:
3137 case XML_ATTRIBUTE_ENUMERATION:
3138 return(xmlValidateNmtokensValue(value));
3139 case XML_ATTRIBUTE_NMTOKEN:
3140 return(xmlValidateNmtokenValue(value));
3141 case XML_ATTRIBUTE_CDATA:
3142 break;
3143 }
3144 return(1);
3145}
3146
3147/**
3148 * xmlValidateAttributeValue2:
3149 * @ctxt: the validation context
3150 * @doc: the document
3151 * @name: the attribute name (used for error reporting only)
3152 * @type: the attribute type
3153 * @value: the attribute value
3154 *
3155 * Validate that the given attribute value match a given type.
3156 * This typically cannot be done before having finished parsing
3157 * the subsets.
3158 *
3159 * [ VC: IDREF ]
3160 * Values of type IDREF must match one of the declared IDs
3161 * Values of type IDREFS must match a sequence of the declared IDs
3162 * each Name must match the value of an ID attribute on some element
3163 * in the XML document; i.e. IDREF values must match the value of
3164 * some ID attribute
3165 *
3166 * [ VC: Entity Name ]
3167 * Values of type ENTITY must match one declared entity
3168 * Values of type ENTITIES must match a sequence of declared entities
3169 *
3170 * [ VC: Notation Attributes ]
3171 * all notation names in the declaration must be declared.
3172 *
3173 * returns 1 if valid or 0 otherwise
3174 */
3175
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003176static int
Owen Taylor3473f882001-02-23 17:55:21 +00003177xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3178 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3179 int ret = 1;
3180 switch (type) {
3181 case XML_ATTRIBUTE_IDREFS:
3182 case XML_ATTRIBUTE_IDREF:
3183 case XML_ATTRIBUTE_ID:
3184 case XML_ATTRIBUTE_NMTOKENS:
3185 case XML_ATTRIBUTE_ENUMERATION:
3186 case XML_ATTRIBUTE_NMTOKEN:
3187 case XML_ATTRIBUTE_CDATA:
3188 break;
3189 case XML_ATTRIBUTE_ENTITY: {
3190 xmlEntityPtr ent;
3191
3192 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003193 if ((ent == NULL) && (doc->standalone == 1)) {
3194 doc->standalone = 0;
3195 ent = xmlGetDocEntity(doc, value);
3196 if (ent != NULL) {
3197 VERROR(ctxt->userData,
3198"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3199 name, value);
3200 /* WAIT to get answer from the Core WG on this
3201 ret = 0;
3202 */
3203 }
3204 }
Owen Taylor3473f882001-02-23 17:55:21 +00003205 if (ent == NULL) {
3206 VERROR(ctxt->userData,
3207 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3208 name, value);
3209 ret = 0;
3210 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3211 VERROR(ctxt->userData,
3212 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3213 name, value);
3214 ret = 0;
3215 }
3216 break;
3217 }
3218 case XML_ATTRIBUTE_ENTITIES: {
3219 xmlChar *dup, *nam = NULL, *cur, save;
3220 xmlEntityPtr ent;
3221
3222 dup = xmlStrdup(value);
3223 if (dup == NULL)
3224 return(0);
3225 cur = dup;
3226 while (*cur != 0) {
3227 nam = cur;
3228 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3229 save = *cur;
3230 *cur = 0;
3231 ent = xmlGetDocEntity(doc, nam);
3232 if (ent == NULL) {
3233 VERROR(ctxt->userData,
3234 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3235 name, nam);
3236 ret = 0;
3237 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3238 VERROR(ctxt->userData,
3239 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3240 name, nam);
3241 ret = 0;
3242 }
3243 if (save == 0)
3244 break;
3245 *cur = save;
3246 while (IS_BLANK(*cur)) cur++;
3247 }
3248 xmlFree(dup);
3249 break;
3250 }
3251 case XML_ATTRIBUTE_NOTATION: {
3252 xmlNotationPtr nota;
3253
3254 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3255 if ((nota == NULL) && (doc->extSubset != NULL))
3256 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3257
3258 if (nota == NULL) {
3259 VERROR(ctxt->userData,
3260 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3261 name, value);
3262 ret = 0;
3263 }
3264 break;
3265 }
3266 }
3267 return(ret);
3268}
3269
3270/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003271 * xmlValidCtxtNormalizeAttributeValue:
3272 * @ctxt: the validation context
3273 * @doc: the document
3274 * @elem: the parent
3275 * @name: the attribute name
3276 * @value: the attribute value
3277 * @ctxt: the validation context or NULL
3278 *
3279 * Does the validation related extra step of the normalization of attribute
3280 * values:
3281 *
3282 * If the declared value is not CDATA, then the XML processor must further
3283 * process the normalized attribute value by discarding any leading and
3284 * trailing space (#x20) characters, and by replacing sequences of space
3285 * (#x20) characters by single space (#x20) character.
3286 *
3287 * Also check VC: Standalone Document Declaration in P32, and update
3288 * ctxt->valid accordingly
3289 *
3290 * returns a new normalized string if normalization is needed, NULL otherwise
3291 * the caller must free the returned value.
3292 */
3293
3294xmlChar *
3295xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3296 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3297 xmlChar *ret, *dst;
3298 const xmlChar *src;
3299 xmlAttributePtr attrDecl = NULL;
3300 int extsubset = 0;
3301
3302 if (doc == NULL) return(NULL);
3303 if (elem == NULL) return(NULL);
3304 if (name == NULL) return(NULL);
3305 if (value == NULL) return(NULL);
3306
3307 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3308 xmlChar qname[500];
3309 snprintf((char *) qname, sizeof(qname), "%s:%s",
3310 elem->ns->prefix, elem->name);
3311 qname[sizeof(qname) - 1] = 0;
3312 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3313 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3314 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3315 if (attrDecl != NULL)
3316 extsubset = 1;
3317 }
3318 }
3319 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3320 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3321 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3322 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3323 if (attrDecl != NULL)
3324 extsubset = 1;
3325 }
3326
3327 if (attrDecl == NULL)
3328 return(NULL);
3329 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3330 return(NULL);
3331
3332 ret = xmlStrdup(value);
3333 if (ret == NULL)
3334 return(NULL);
3335 src = value;
3336 dst = ret;
3337 while (*src == 0x20) src++;
3338 while (*src != 0) {
3339 if (*src == 0x20) {
3340 while (*src == 0x20) src++;
3341 if (*src != 0)
3342 *dst++ = 0x20;
3343 } else {
3344 *dst++ = *src++;
3345 }
3346 }
3347 *dst = 0;
3348 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3349 VERROR(ctxt->userData,
3350"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3351 name, elem->name);
3352 ctxt->valid = 0;
3353 }
3354 return(ret);
3355}
3356
3357/**
Owen Taylor3473f882001-02-23 17:55:21 +00003358 * xmlValidNormalizeAttributeValue:
3359 * @doc: the document
3360 * @elem: the parent
3361 * @name: the attribute name
3362 * @value: the attribute value
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003363 * @ctxt: the validation context or NULL
Owen Taylor3473f882001-02-23 17:55:21 +00003364 *
3365 * Does the validation related extra step of the normalization of attribute
3366 * values:
3367 *
3368 * If the declared value is not CDATA, then the XML processor must further
3369 * process the normalized attribute value by discarding any leading and
3370 * trailing space (#x20) characters, and by replacing sequences of space
3371 * (#x20) characters by single space (#x20) character.
3372 *
3373 * returns a new normalized string if normalization is needed, NULL otherwise
3374 * the caller must free the returned value.
3375 */
3376
3377xmlChar *
3378xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3379 const xmlChar *name, const xmlChar *value) {
3380 xmlChar *ret, *dst;
3381 const xmlChar *src;
3382 xmlAttributePtr attrDecl = NULL;
3383
3384 if (doc == NULL) return(NULL);
3385 if (elem == NULL) return(NULL);
3386 if (name == NULL) return(NULL);
3387 if (value == NULL) return(NULL);
3388
3389 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3390 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003391 snprintf((char *) qname, sizeof(qname), "%s:%s",
3392 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003393 qname[sizeof(qname) - 1] = 0;
3394 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3395 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3396 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3397 }
3398 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3399 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3400 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3401
3402 if (attrDecl == NULL)
3403 return(NULL);
3404 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3405 return(NULL);
3406
3407 ret = xmlStrdup(value);
3408 if (ret == NULL)
3409 return(NULL);
3410 src = value;
3411 dst = ret;
3412 while (*src == 0x20) src++;
3413 while (*src != 0) {
3414 if (*src == 0x20) {
3415 while (*src == 0x20) src++;
3416 if (*src != 0)
3417 *dst++ = 0x20;
3418 } else {
3419 *dst++ = *src++;
3420 }
3421 }
3422 *dst = 0;
3423 return(ret);
3424}
3425
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003426static void
Owen Taylor3473f882001-02-23 17:55:21 +00003427xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003428 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003429 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3430}
3431
3432/**
3433 * xmlValidateAttributeDecl:
3434 * @ctxt: the validation context
3435 * @doc: a document instance
3436 * @attr: an attribute definition
3437 *
3438 * Try to validate a single attribute definition
3439 * basically it does the following checks as described by the
3440 * XML-1.0 recommendation:
3441 * - [ VC: Attribute Default Legal ]
3442 * - [ VC: Enumeration ]
3443 * - [ VC: ID Attribute Default ]
3444 *
3445 * The ID/IDREF uniqueness and matching are done separately
3446 *
3447 * returns 1 if valid or 0 otherwise
3448 */
3449
3450int
3451xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3452 xmlAttributePtr attr) {
3453 int ret = 1;
3454 int val;
3455 CHECK_DTD;
3456 if(attr == NULL) return(1);
3457
3458 /* Attribute Default Legal */
3459 /* Enumeration */
3460 if (attr->defaultValue != NULL) {
3461 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3462 if (val == 0) {
3463 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003464 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003465 attr->name, attr->elem);
3466 }
3467 ret &= val;
3468 }
3469
3470 /* ID Attribute Default */
3471 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3472 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3473 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3474 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003475 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003476 attr->name, attr->elem);
3477 ret = 0;
3478 }
3479
3480 /* One ID per Element Type */
3481 if (attr->atype == XML_ATTRIBUTE_ID) {
3482 int nbId;
3483
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003484 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003485 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3486 attr->elem);
3487 if (elem != NULL) {
3488 nbId = xmlScanIDAttributeDecl(NULL, elem);
3489 } else {
3490 xmlAttributeTablePtr table;
3491
3492 /*
3493 * The attribute may be declared in the internal subset and the
3494 * element in the external subset.
3495 */
3496 nbId = 0;
3497 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3498 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3499 xmlValidateAttributeIdCallback, &nbId);
3500 }
3501 if (nbId > 1) {
3502 VERROR(ctxt->userData,
3503 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3504 attr->elem, nbId, attr->name);
3505 } else if (doc->extSubset != NULL) {
3506 int extId = 0;
3507 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3508 if (elem != NULL) {
3509 extId = xmlScanIDAttributeDecl(NULL, elem);
3510 }
3511 if (extId > 1) {
3512 VERROR(ctxt->userData,
3513 "Element %s has %d ID attribute defined in the external subset : %s\n",
3514 attr->elem, extId, attr->name);
3515 } else if (extId + nbId > 1) {
3516 VERROR(ctxt->userData,
3517"Element %s has ID attributes defined in the internal and external subset : %s\n",
3518 attr->elem, attr->name);
3519 }
3520 }
3521 }
3522
3523 /* Validity Constraint: Enumeration */
3524 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3525 xmlEnumerationPtr tree = attr->tree;
3526 while (tree != NULL) {
3527 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3528 tree = tree->next;
3529 }
3530 if (tree == NULL) {
3531 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003532"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003533 attr->defaultValue, attr->name, attr->elem);
3534 ret = 0;
3535 }
3536 }
3537
3538 return(ret);
3539}
3540
3541/**
3542 * xmlValidateElementDecl:
3543 * @ctxt: the validation context
3544 * @doc: a document instance
3545 * @elem: an element definition
3546 *
3547 * Try to validate a single element definition
3548 * basically it does the following checks as described by the
3549 * XML-1.0 recommendation:
3550 * - [ VC: One ID per Element Type ]
3551 * - [ VC: No Duplicate Types ]
3552 * - [ VC: Unique Element Type Declaration ]
3553 *
3554 * returns 1 if valid or 0 otherwise
3555 */
3556
3557int
3558xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3559 xmlElementPtr elem) {
3560 int ret = 1;
3561 xmlElementPtr tst;
3562
3563 CHECK_DTD;
3564
3565 if (elem == NULL) return(1);
3566
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003567#if 0
Daniel Veillard84d70a42002-09-16 10:51:38 +00003568#ifdef LIBXML_REGEXP_ENABLED
3569 /* Build the regexp associated to the content model */
3570 ret = xmlValidBuildContentModel(ctxt, elem);
3571#endif
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00003572#endif
Daniel Veillard84d70a42002-09-16 10:51:38 +00003573
Owen Taylor3473f882001-02-23 17:55:21 +00003574 /* No Duplicate Types */
3575 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3576 xmlElementContentPtr cur, next;
3577 const xmlChar *name;
3578
3579 cur = elem->content;
3580 while (cur != NULL) {
3581 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3582 if (cur->c1 == NULL) break;
3583 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3584 name = cur->c1->name;
3585 next = cur->c2;
3586 while (next != NULL) {
3587 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3588 if (xmlStrEqual(next->name, name)) {
3589 VERROR(ctxt->userData,
3590 "Definition of %s has duplicate references of %s\n",
3591 elem->name, name);
3592 ret = 0;
3593 }
3594 break;
3595 }
3596 if (next->c1 == NULL) break;
3597 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3598 if (xmlStrEqual(next->c1->name, name)) {
3599 VERROR(ctxt->userData,
3600 "Definition of %s has duplicate references of %s\n",
3601 elem->name, name);
3602 ret = 0;
3603 }
3604 next = next->c2;
3605 }
3606 }
3607 cur = cur->c2;
3608 }
3609 }
3610
3611 /* VC: Unique Element Type Declaration */
3612 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003613 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003614 ((tst->prefix == elem->prefix) ||
3615 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003616 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003617 VERROR(ctxt->userData, "Redefinition of element %s\n",
3618 elem->name);
3619 ret = 0;
3620 }
3621 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003622 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003623 ((tst->prefix == elem->prefix) ||
3624 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003625 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003626 VERROR(ctxt->userData, "Redefinition of element %s\n",
3627 elem->name);
3628 ret = 0;
3629 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003630 /* One ID per Element Type
3631 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003632 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3633 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003634 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003635 return(ret);
3636}
3637
3638/**
3639 * xmlValidateOneAttribute:
3640 * @ctxt: the validation context
3641 * @doc: a document instance
3642 * @elem: an element instance
3643 * @attr: an attribute instance
3644 * @value: the attribute value (without entities processing)
3645 *
3646 * Try to validate a single attribute for an element
3647 * basically it does the following checks as described by the
3648 * XML-1.0 recommendation:
3649 * - [ VC: Attribute Value Type ]
3650 * - [ VC: Fixed Attribute Default ]
3651 * - [ VC: Entity Name ]
3652 * - [ VC: Name Token ]
3653 * - [ VC: ID ]
3654 * - [ VC: IDREF ]
3655 * - [ VC: Entity Name ]
3656 * - [ VC: Notation Attributes ]
3657 *
3658 * The ID/IDREF uniqueness and matching are done separately
3659 *
3660 * returns 1 if valid or 0 otherwise
3661 */
3662
3663int
3664xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3665 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3666 /* xmlElementPtr elemDecl; */
3667 xmlAttributePtr attrDecl = NULL;
3668 int val;
3669 int ret = 1;
3670
3671 CHECK_DTD;
3672 if ((elem == NULL) || (elem->name == NULL)) return(0);
3673 if ((attr == NULL) || (attr->name == NULL)) return(0);
3674
3675 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3676 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003677 snprintf((char *) qname, sizeof(qname), "%s:%s",
3678 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003679 qname[sizeof(qname) - 1] = 0;
3680 if (attr->ns != NULL) {
3681 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3682 attr->name, attr->ns->prefix);
3683 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3684 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3685 attr->name, attr->ns->prefix);
3686 } else {
3687 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3688 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3689 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3690 qname, attr->name);
3691 }
3692 }
3693 if (attrDecl == NULL) {
3694 if (attr->ns != NULL) {
3695 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3696 attr->name, attr->ns->prefix);
3697 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3698 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3699 attr->name, attr->ns->prefix);
3700 } else {
3701 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3702 elem->name, attr->name);
3703 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3704 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3705 elem->name, attr->name);
3706 }
3707 }
3708
3709
3710 /* Validity Constraint: Attribute Value Type */
3711 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003712 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003713 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003714 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003715 attr->name, elem->name);
3716 return(0);
3717 }
3718 attr->atype = attrDecl->atype;
3719
3720 val = xmlValidateAttributeValue(attrDecl->atype, value);
3721 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003722 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003723 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003724 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003725 attr->name, elem->name);
3726 ret = 0;
3727 }
3728
3729 /* Validity constraint: Fixed Attribute Default */
3730 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3731 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003732 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003733 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003734 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003735 attr->name, elem->name, attrDecl->defaultValue);
3736 ret = 0;
3737 }
3738 }
3739
3740 /* Validity Constraint: ID uniqueness */
3741 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3742 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3743 ret = 0;
3744 }
3745
3746 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3747 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3748 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3749 ret = 0;
3750 }
3751
3752 /* Validity Constraint: Notation Attributes */
3753 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3754 xmlEnumerationPtr tree = attrDecl->tree;
3755 xmlNotationPtr nota;
3756
3757 /* First check that the given NOTATION was declared */
3758 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3759 if (nota == NULL)
3760 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3761
3762 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003763 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003764 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003765 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003766 value, attr->name, elem->name);
3767 ret = 0;
3768 }
3769
3770 /* Second, verify that it's among the list */
3771 while (tree != NULL) {
3772 if (xmlStrEqual(tree->name, value)) break;
3773 tree = tree->next;
3774 }
3775 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003776 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003777 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003778"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003779 value, attr->name, elem->name);
3780 ret = 0;
3781 }
3782 }
3783
3784 /* Validity Constraint: Enumeration */
3785 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3786 xmlEnumerationPtr tree = attrDecl->tree;
3787 while (tree != NULL) {
3788 if (xmlStrEqual(tree->name, value)) break;
3789 tree = tree->next;
3790 }
3791 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003792 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003793 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003794 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003795 value, attr->name, elem->name);
3796 ret = 0;
3797 }
3798 }
3799
3800 /* Fixed Attribute Default */
3801 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3802 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003803 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003804 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003805 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003806 attr->name, elem->name, attrDecl->defaultValue);
3807 ret = 0;
3808 }
3809
3810 /* Extra check for the attribute value */
3811 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3812 attrDecl->atype, value);
3813
3814 return(ret);
3815}
3816
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003817/**
3818 * xmlValidateSkipIgnorable:
3819 * @ctxt: the validation context
3820 * @child: the child list
3821 *
3822 * Skip ignorable elements w.r.t. the validation process
3823 *
3824 * returns the first element to consider for validation of the content model
3825 */
3826
3827static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003828xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003829 while (child != NULL) {
3830 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003831 /* These things are ignored (skipped) during validation. */
3832 case XML_PI_NODE:
3833 case XML_COMMENT_NODE:
3834 case XML_XINCLUDE_START:
3835 case XML_XINCLUDE_END:
3836 child = child->next;
3837 break;
3838 case XML_TEXT_NODE:
3839 if (xmlIsBlankNode(child))
3840 child = child->next;
3841 else
3842 return(child);
3843 break;
3844 /* keep current node */
3845 default:
3846 return(child);
3847 }
3848 }
3849 return(child);
3850}
3851
3852/**
3853 * xmlValidateElementType:
3854 * @ctxt: the validation context
3855 *
3856 * Try to validate the content model of an element internal function
3857 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003858 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3859 * reference is found and -3 if the validation succeeded but
3860 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003861 */
3862
3863static int
3864xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003865 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003866 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003867
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003868 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003869 if ((NODE == NULL) && (CONT == NULL))
3870 return(1);
3871 if ((NODE == NULL) &&
3872 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3873 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3874 return(1);
3875 }
3876 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003877 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003878 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003879
3880 /*
3881 * We arrive here when more states need to be examined
3882 */
3883cont:
3884
3885 /*
3886 * We just recovered from a rollback generated by a possible
3887 * epsilon transition, go directly to the analysis phase
3888 */
3889 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003890 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003891 DEBUG_VALID_STATE(NODE, CONT)
3892 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003893 goto analyze;
3894 }
3895
3896 DEBUG_VALID_STATE(NODE, CONT)
3897 /*
3898 * we may have to save a backup state here. This is the equivalent
3899 * of handling epsilon transition in NFAs.
3900 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003901 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003902 ((CONT->parent == NULL) ||
3903 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003904 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003905 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003906 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003907 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003908 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
3909 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003910 }
3911
3912
3913 /*
3914 * Check first if the content matches
3915 */
3916 switch (CONT->type) {
3917 case XML_ELEMENT_CONTENT_PCDATA:
3918 if (NODE == NULL) {
3919 DEBUG_VALID_MSG("pcdata failed no node");
3920 ret = 0;
3921 break;
3922 }
3923 if (NODE->type == XML_TEXT_NODE) {
3924 DEBUG_VALID_MSG("pcdata found, skip to next");
3925 /*
3926 * go to next element in the content model
3927 * skipping ignorable elems
3928 */
3929 do {
3930 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003931 NODE = xmlValidateSkipIgnorable(NODE);
3932 if ((NODE != NULL) &&
3933 (NODE->type == XML_ENTITY_REF_NODE))
3934 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003935 } while ((NODE != NULL) &&
3936 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003937 (NODE->type != XML_TEXT_NODE) &&
3938 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003939 ret = 1;
3940 break;
3941 } else {
3942 DEBUG_VALID_MSG("pcdata failed");
3943 ret = 0;
3944 break;
3945 }
3946 break;
3947 case XML_ELEMENT_CONTENT_ELEMENT:
3948 if (NODE == NULL) {
3949 DEBUG_VALID_MSG("element failed no node");
3950 ret = 0;
3951 break;
3952 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003953 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3954 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003955 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003956 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3957 ret = (CONT->prefix == NULL);
3958 } else if (CONT->prefix == NULL) {
3959 ret = 0;
3960 } else {
3961 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3962 }
3963 }
3964 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003965 DEBUG_VALID_MSG("element found, skip to next");
3966 /*
3967 * go to next element in the content model
3968 * skipping ignorable elems
3969 */
3970 do {
3971 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003972 NODE = xmlValidateSkipIgnorable(NODE);
3973 if ((NODE != NULL) &&
3974 (NODE->type == XML_ENTITY_REF_NODE))
3975 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003976 } while ((NODE != NULL) &&
3977 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003978 (NODE->type != XML_TEXT_NODE) &&
3979 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003980 } else {
3981 DEBUG_VALID_MSG("element failed");
3982 ret = 0;
3983 break;
3984 }
3985 break;
3986 case XML_ELEMENT_CONTENT_OR:
3987 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003988 * Small optimization.
3989 */
3990 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3991 if ((NODE == NULL) ||
3992 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3993 DEPTH++;
3994 CONT = CONT->c2;
3995 goto cont;
3996 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003997 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3998 ret = (CONT->c1->prefix == NULL);
3999 } else if (CONT->c1->prefix == NULL) {
4000 ret = 0;
4001 } else {
4002 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4003 }
4004 if (ret == 0) {
4005 DEPTH++;
4006 CONT = CONT->c2;
4007 goto cont;
4008 }
Daniel Veillard85349052001-04-20 13:48:21 +00004009 }
4010
4011 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004012 * save the second branch 'or' branch
4013 */
4014 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004015 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4016 OCCURS, ROLLBACK_OR) < 0)
4017 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004018 DEPTH++;
4019 CONT = CONT->c1;
4020 goto cont;
4021 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004022 /*
4023 * Small optimization.
4024 */
4025 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4026 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4027 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4028 if ((NODE == NULL) ||
4029 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4030 DEPTH++;
4031 CONT = CONT->c2;
4032 goto cont;
4033 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004034 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4035 ret = (CONT->c1->prefix == NULL);
4036 } else if (CONT->c1->prefix == NULL) {
4037 ret = 0;
4038 } else {
4039 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4040 }
4041 if (ret == 0) {
4042 DEPTH++;
4043 CONT = CONT->c2;
4044 goto cont;
4045 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004046 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004047 DEPTH++;
4048 CONT = CONT->c1;
4049 goto cont;
4050 }
4051
4052 /*
4053 * At this point handle going up in the tree
4054 */
4055 if (ret == -1) {
4056 DEBUG_VALID_MSG("error found returning");
4057 return(ret);
4058 }
4059analyze:
4060 while (CONT != NULL) {
4061 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004062 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004063 * this level.
4064 */
4065 if (ret == 0) {
4066 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004067 xmlNodePtr cur;
4068
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004069 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004070 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004071 DEBUG_VALID_MSG("Once branch failed, rollback");
4072 if (vstateVPop(ctxt) < 0 ) {
4073 DEBUG_VALID_MSG("exhaustion, failed");
4074 return(0);
4075 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004076 if (cur != ctxt->vstate->node)
4077 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004078 goto cont;
4079 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004080 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004081 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004082 DEBUG_VALID_MSG("Plus branch failed, rollback");
4083 if (vstateVPop(ctxt) < 0 ) {
4084 DEBUG_VALID_MSG("exhaustion, failed");
4085 return(0);
4086 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004087 if (cur != ctxt->vstate->node)
4088 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004089 goto cont;
4090 }
4091 DEBUG_VALID_MSG("Plus branch found");
4092 ret = 1;
4093 break;
4094 case XML_ELEMENT_CONTENT_MULT:
4095#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004096 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004097 DEBUG_VALID_MSG("Mult branch failed");
4098 } else {
4099 DEBUG_VALID_MSG("Mult branch found");
4100 }
4101#endif
4102 ret = 1;
4103 break;
4104 case XML_ELEMENT_CONTENT_OPT:
4105 DEBUG_VALID_MSG("Option branch failed");
4106 ret = 1;
4107 break;
4108 }
4109 } else {
4110 switch (CONT->ocur) {
4111 case XML_ELEMENT_CONTENT_OPT:
4112 DEBUG_VALID_MSG("Option branch succeeded");
4113 ret = 1;
4114 break;
4115 case XML_ELEMENT_CONTENT_ONCE:
4116 DEBUG_VALID_MSG("Once branch succeeded");
4117 ret = 1;
4118 break;
4119 case XML_ELEMENT_CONTENT_PLUS:
4120 if (STATE == ROLLBACK_PARENT) {
4121 DEBUG_VALID_MSG("Plus branch rollback");
4122 ret = 1;
4123 break;
4124 }
4125 if (NODE == NULL) {
4126 DEBUG_VALID_MSG("Plus branch exhausted");
4127 ret = 1;
4128 break;
4129 }
4130 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004131 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004132 goto cont;
4133 case XML_ELEMENT_CONTENT_MULT:
4134 if (STATE == ROLLBACK_PARENT) {
4135 DEBUG_VALID_MSG("Mult branch rollback");
4136 ret = 1;
4137 break;
4138 }
4139 if (NODE == NULL) {
4140 DEBUG_VALID_MSG("Mult branch exhausted");
4141 ret = 1;
4142 break;
4143 }
4144 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004145 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004146 goto cont;
4147 }
4148 }
4149 STATE = 0;
4150
4151 /*
4152 * Then act accordingly at the parent level
4153 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004154 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004155 if (CONT->parent == NULL)
4156 break;
4157
4158 switch (CONT->parent->type) {
4159 case XML_ELEMENT_CONTENT_PCDATA:
4160 DEBUG_VALID_MSG("Error: parent pcdata");
4161 return(-1);
4162 case XML_ELEMENT_CONTENT_ELEMENT:
4163 DEBUG_VALID_MSG("Error: parent element");
4164 return(-1);
4165 case XML_ELEMENT_CONTENT_OR:
4166 if (ret == 1) {
4167 DEBUG_VALID_MSG("Or succeeded");
4168 CONT = CONT->parent;
4169 DEPTH--;
4170 } else {
4171 DEBUG_VALID_MSG("Or failed");
4172 CONT = CONT->parent;
4173 DEPTH--;
4174 }
4175 break;
4176 case XML_ELEMENT_CONTENT_SEQ:
4177 if (ret == 0) {
4178 DEBUG_VALID_MSG("Sequence failed");
4179 CONT = CONT->parent;
4180 DEPTH--;
4181 } else if (CONT == CONT->parent->c1) {
4182 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4183 CONT = CONT->parent->c2;
4184 goto cont;
4185 } else {
4186 DEBUG_VALID_MSG("Sequence succeeded");
4187 CONT = CONT->parent;
4188 DEPTH--;
4189 }
4190 }
4191 }
4192 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004193 xmlNodePtr cur;
4194
4195 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004196 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4197 if (vstateVPop(ctxt) < 0 ) {
4198 DEBUG_VALID_MSG("exhaustion, failed");
4199 return(0);
4200 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004201 if (cur != ctxt->vstate->node)
4202 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004203 goto cont;
4204 }
4205 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004206 xmlNodePtr cur;
4207
4208 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004209 DEBUG_VALID_MSG("Failure, rollback");
4210 if (vstateVPop(ctxt) < 0 ) {
4211 DEBUG_VALID_MSG("exhaustion, failed");
4212 return(0);
4213 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004214 if (cur != ctxt->vstate->node)
4215 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004216 goto cont;
4217 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004218 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004219}
4220
4221/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004222 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004223 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004224 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004225 * @content: An element
4226 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4227 *
4228 * This will dump the list of elements to the buffer
4229 * Intended just for the debug routine
4230 */
4231static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004232xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004233 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004234 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004235
4236 if (node == NULL) return;
4237 if (glob) strcat(buf, "(");
4238 cur = node;
4239 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004240 len = strlen(buf);
4241 if (size - len < 50) {
4242 if ((size - len > 4) && (buf[len - 1] != '.'))
4243 strcat(buf, " ...");
4244 return;
4245 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004246 switch (cur->type) {
4247 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004248 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004249 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004250 if ((size - len > 4) && (buf[len - 1] != '.'))
4251 strcat(buf, " ...");
4252 return;
4253 }
4254 strcat(buf, (char *) cur->ns->prefix);
4255 strcat(buf, ":");
4256 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004257 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004258 if ((size - len > 4) && (buf[len - 1] != '.'))
4259 strcat(buf, " ...");
4260 return;
4261 }
4262 strcat(buf, (char *) cur->name);
4263 if (cur->next != NULL)
4264 strcat(buf, " ");
4265 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004266 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004267 if (xmlIsBlankNode(cur))
4268 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004269 case XML_CDATA_SECTION_NODE:
4270 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004271 strcat(buf, "CDATA");
4272 if (cur->next != NULL)
4273 strcat(buf, " ");
4274 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004275 case XML_ATTRIBUTE_NODE:
4276 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004277#ifdef LIBXML_DOCB_ENABLED
4278 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004279#endif
4280 case XML_HTML_DOCUMENT_NODE:
4281 case XML_DOCUMENT_TYPE_NODE:
4282 case XML_DOCUMENT_FRAG_NODE:
4283 case XML_NOTATION_NODE:
4284 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004285 strcat(buf, "???");
4286 if (cur->next != NULL)
4287 strcat(buf, " ");
4288 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004289 case XML_ENTITY_NODE:
4290 case XML_PI_NODE:
4291 case XML_DTD_NODE:
4292 case XML_COMMENT_NODE:
4293 case XML_ELEMENT_DECL:
4294 case XML_ATTRIBUTE_DECL:
4295 case XML_ENTITY_DECL:
4296 case XML_XINCLUDE_START:
4297 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004298 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004299 }
4300 cur = cur->next;
4301 }
4302 if (glob) strcat(buf, ")");
4303}
4304
4305/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004306 * xmlValidateElementContent:
4307 * @ctxt: the validation context
4308 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004309 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004310 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004311 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004312 *
4313 * Try to validate the content model of an element
4314 *
4315 * returns 1 if valid or 0 if not and -1 in case of error
4316 */
4317
4318static int
4319xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004320 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004321 int ret = 1;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004322 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004323 xmlElementContentPtr cont;
4324 const xmlChar *name;
4325
4326 if (elemDecl == NULL)
4327 return(-1);
4328 cont = elemDecl->content;
4329 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004330
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004331#ifdef LIBXML_REGEXP_ENABLED
4332 /* Build the regexp associated to the content model */
4333 if (elemDecl->contModel == NULL)
4334 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4335 if (elemDecl->contModel == NULL) {
4336 ret = -1;
4337 } else {
4338 xmlRegExecCtxtPtr exec;
4339
4340 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4341 if (exec != NULL) {
4342 cur = child;
4343 while (cur != NULL) {
4344 switch (cur->type) {
4345 case XML_ENTITY_REF_NODE:
4346 /*
4347 * Push the current node to be able to roll back
4348 * and process within the entity
4349 */
4350 if ((cur->children != NULL) &&
4351 (cur->children->children != NULL)) {
4352 nodeVPush(ctxt, cur);
4353 cur = cur->children->children;
4354 continue;
4355 }
4356 break;
4357 case XML_TEXT_NODE:
4358 if (xmlIsBlankNode(cur))
4359 break;
4360 ret = 0;
4361 goto fail;
4362 case XML_CDATA_SECTION_NODE:
4363 TODO
4364 ret = 0;
4365 goto fail;
4366 case XML_ELEMENT_NODE:
4367 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
4368 xmlChar *QName;
4369 int len;
4370
4371 len = xmlStrlen(cur->name) +
4372 xmlStrlen(cur->ns->prefix) + 2;
4373 QName = xmlMalloc(len);
4374 if (QName == NULL) {
4375 ret = -1;
4376 goto fail;
4377 }
4378 snprintf((char *) QName, len, "%s:%s",
4379 (char *)cur->ns->prefix,
4380 (char *)cur->name);
4381 ret = xmlRegExecPushString(exec, QName, NULL);
4382 xmlFree(QName);
4383 } else {
4384 ret = xmlRegExecPushString(exec, cur->name, NULL);
4385 }
4386 break;
4387 default:
4388 break;
4389 }
4390 /*
4391 * Switch to next element
4392 */
4393 cur = cur->next;
4394 while (cur == NULL) {
4395 cur = nodeVPop(ctxt);
4396 if (cur == NULL)
4397 break;
4398 cur = cur->next;
4399 }
4400 }
4401 ret = xmlRegExecPushString(exec, NULL, NULL);
4402fail:
4403 xmlRegFreeExecCtxt(exec);
4404 }
4405 }
4406#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004407 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004408 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004409 */
4410 ctxt->vstateMax = 8;
4411 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4412 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4413 if (ctxt->vstateTab == NULL) {
4414 xmlGenericError(xmlGenericErrorContext,
4415 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004416 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004417 }
4418 /*
4419 * The first entry in the stack is reserved to the current state
4420 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004421 ctxt->nodeMax = 0;
4422 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004423 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004424 ctxt->vstate = &ctxt->vstateTab[0];
4425 ctxt->vstateNr = 1;
4426 CONT = cont;
4427 NODE = child;
4428 DEPTH = 0;
4429 OCCURS = 0;
4430 STATE = 0;
4431 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004432 if ((ret == -3) && (warn)) {
4433 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004434 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004435 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004436 /*
4437 * An entities reference appeared at this level.
4438 * Buid a minimal representation of this node content
4439 * sufficient to run the validation process on it
4440 */
4441 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004442 cur = child;
4443 while (cur != NULL) {
4444 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004445 case XML_ENTITY_REF_NODE:
4446 /*
4447 * Push the current node to be able to roll back
4448 * and process within the entity
4449 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004450 if ((cur->children != NULL) &&
4451 (cur->children->children != NULL)) {
4452 nodeVPush(ctxt, cur);
4453 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004454 continue;
4455 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004456 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004457 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004458 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004459 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004460 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004461 case XML_CDATA_SECTION_NODE:
4462 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004463 case XML_ELEMENT_NODE:
4464 /*
4465 * Allocate a new node and minimally fills in
4466 * what's required
4467 */
4468 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4469 if (tmp == NULL) {
4470 xmlGenericError(xmlGenericErrorContext,
4471 "xmlValidateElementContent : malloc failed\n");
4472 xmlFreeNodeList(repl);
4473 ret = -1;
4474 goto done;
4475 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004476 tmp->type = cur->type;
4477 tmp->name = cur->name;
4478 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004479 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004480 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004481 if (repl == NULL)
4482 repl = last = tmp;
4483 else {
4484 last->next = tmp;
4485 last = tmp;
4486 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004487 if (cur->type == XML_CDATA_SECTION_NODE) {
4488 /*
4489 * E59 spaces in CDATA does not match the
4490 * nonterminal S
4491 */
4492 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4493 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004494 break;
4495 default:
4496 break;
4497 }
4498 /*
4499 * Switch to next element
4500 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004501 cur = cur->next;
4502 while (cur == NULL) {
4503 cur = nodeVPop(ctxt);
4504 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004505 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004506 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004507 }
4508 }
4509
4510 /*
4511 * Relaunch the validation
4512 */
4513 ctxt->vstate = &ctxt->vstateTab[0];
4514 ctxt->vstateNr = 1;
4515 CONT = cont;
4516 NODE = repl;
4517 DEPTH = 0;
4518 OCCURS = 0;
4519 STATE = 0;
4520 ret = xmlValidateElementType(ctxt);
4521 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004522#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004523 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004524 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4525 char expr[5000];
4526 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004527
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004528 expr[0] = 0;
4529 xmlSnprintfElementContent(expr, 5000, cont, 1);
4530 list[0] = 0;
4531 if (repl != NULL)
4532 xmlSnprintfElements(list, 5000, repl, 1);
4533 else
4534 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004535
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004536 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004537 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004538 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004539 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004540 name, expr, list);
4541 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004542 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004543 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004544 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004545 expr, list);
4546 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004547 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004548 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004549 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004550 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004551 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004552 name);
4553 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004554 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004555 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004556 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004557 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004558 }
4559 ret = 0;
4560 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004561 if (ret == -3)
4562 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004563
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004564done:
4565 /*
4566 * Deallocate the copy if done, and free up the validation stack
4567 */
4568 while (repl != NULL) {
4569 tmp = repl->next;
4570 xmlFree(repl);
4571 repl = tmp;
4572 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004573 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004574 if (ctxt->vstateTab != NULL) {
4575 xmlFree(ctxt->vstateTab);
4576 ctxt->vstateTab = NULL;
4577 }
4578 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004579 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004580 if (ctxt->nodeTab != NULL) {
4581 xmlFree(ctxt->nodeTab);
4582 ctxt->nodeTab = NULL;
4583 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004584 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004585
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004586}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004587
Owen Taylor3473f882001-02-23 17:55:21 +00004588/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004589 * xmlValidateCdataElement:
4590 * @ctxt: the validation context
4591 * @doc: a document instance
4592 * @elem: an element instance
4593 *
4594 * Check that an element follows #CDATA
4595 *
4596 * returns 1 if valid or 0 otherwise
4597 */
4598static int
4599xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4600 xmlNodePtr elem) {
4601 int ret = 1;
4602 xmlNodePtr cur, child;
4603
4604 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4605 return(0);
4606
4607 child = elem->children;
4608
4609 cur = child;
4610 while (cur != NULL) {
4611 switch (cur->type) {
4612 case XML_ENTITY_REF_NODE:
4613 /*
4614 * Push the current node to be able to roll back
4615 * and process within the entity
4616 */
4617 if ((cur->children != NULL) &&
4618 (cur->children->children != NULL)) {
4619 nodeVPush(ctxt, cur);
4620 cur = cur->children->children;
4621 continue;
4622 }
4623 break;
4624 case XML_COMMENT_NODE:
4625 case XML_PI_NODE:
4626 case XML_TEXT_NODE:
4627 case XML_CDATA_SECTION_NODE:
4628 break;
4629 default:
4630 ret = 0;
4631 goto done;
4632 }
4633 /*
4634 * Switch to next element
4635 */
4636 cur = cur->next;
4637 while (cur == NULL) {
4638 cur = nodeVPop(ctxt);
4639 if (cur == NULL)
4640 break;
4641 cur = cur->next;
4642 }
4643 }
4644done:
4645 ctxt->nodeMax = 0;
4646 ctxt->nodeNr = 0;
4647 if (ctxt->nodeTab != NULL) {
4648 xmlFree(ctxt->nodeTab);
4649 ctxt->nodeTab = NULL;
4650 }
4651 return(ret);
4652}
4653
4654/**
Owen Taylor3473f882001-02-23 17:55:21 +00004655 * xmlValidateOneElement:
4656 * @ctxt: the validation context
4657 * @doc: a document instance
4658 * @elem: an element instance
4659 *
4660 * Try to validate a single element and it's attributes,
4661 * basically it does the following checks as described by the
4662 * XML-1.0 recommendation:
4663 * - [ VC: Element Valid ]
4664 * - [ VC: Required Attribute ]
4665 * Then call xmlValidateOneAttribute() for each attribute present.
4666 *
4667 * The ID/IDREF checkings are done separately
4668 *
4669 * returns 1 if valid or 0 otherwise
4670 */
4671
4672int
4673xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4674 xmlNodePtr elem) {
4675 xmlElementPtr elemDecl = NULL;
4676 xmlElementContentPtr cont;
4677 xmlAttributePtr attr;
4678 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004679 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004680 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004681 const xmlChar *prefix = NULL;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004682 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004683
4684 CHECK_DTD;
4685
4686 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004687 switch (elem->type) {
4688 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004689 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004690 VERROR(ctxt->userData,
4691 "Attribute element not expected here\n");
4692 return(0);
4693 case XML_TEXT_NODE:
4694 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004695 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004696 VERROR(ctxt->userData, "Text element has childs !\n");
4697 return(0);
4698 }
4699 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004700 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004701 VERROR(ctxt->userData, "Text element has attributes !\n");
4702 return(0);
4703 }
4704 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004705 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004706 VERROR(ctxt->userData, "Text element has namespace !\n");
4707 return(0);
4708 }
4709 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004710 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004711 VERROR(ctxt->userData,
4712 "Text element carries namespace definitions !\n");
4713 return(0);
4714 }
4715 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004716 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004717 VERROR(ctxt->userData,
4718 "Text element has no content !\n");
4719 return(0);
4720 }
4721 return(1);
4722 case XML_XINCLUDE_START:
4723 case XML_XINCLUDE_END:
4724 return(1);
4725 case XML_CDATA_SECTION_NODE:
4726 case XML_ENTITY_REF_NODE:
4727 case XML_PI_NODE:
4728 case XML_COMMENT_NODE:
4729 return(1);
4730 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004731 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004732 VERROR(ctxt->userData,
4733 "Entity element not expected here\n");
4734 return(0);
4735 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004736 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004737 VERROR(ctxt->userData,
4738 "Notation element not expected here\n");
4739 return(0);
4740 case XML_DOCUMENT_NODE:
4741 case XML_DOCUMENT_TYPE_NODE:
4742 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004743 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004744 VERROR(ctxt->userData,
4745 "Document element not expected here\n");
4746 return(0);
4747 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004748 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004749 VERROR(ctxt->userData,
4750 "\n");
4751 return(0);
4752 case XML_ELEMENT_NODE:
4753 break;
4754 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004755 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004756 VERROR(ctxt->userData,
4757 "unknown element type %d\n", elem->type);
4758 return(0);
4759 }
4760 if (elem->name == NULL) return(0);
4761
4762 /*
4763 * Fetch the declaration for the qualified name
4764 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004765 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4766 prefix = elem->ns->prefix;
4767
4768 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004769 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004770 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004771 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004772 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004773 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004774 if (elemDecl != NULL)
4775 extsubset = 1;
4776 }
Owen Taylor3473f882001-02-23 17:55:21 +00004777 }
4778
4779 /*
4780 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004781 * This is "non-strict" validation should be done on the
4782 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004783 */
4784 if (elemDecl == NULL) {
4785 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004786 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004787 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004788 if (elemDecl != NULL)
4789 extsubset = 1;
4790 }
Owen Taylor3473f882001-02-23 17:55:21 +00004791 }
4792 if (elemDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004793 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004794 VERROR(ctxt->userData, "No declaration for element %s\n",
4795 elem->name);
4796 return(0);
4797 }
4798
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004799 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004800 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004801 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004802 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004803 VERROR(ctxt->userData, "No declaration for element %s\n",
4804 elem->name);
4805 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004806 case XML_ELEMENT_TYPE_EMPTY:
4807 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004808 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004809 VERROR(ctxt->userData,
4810 "Element %s was declared EMPTY this one has content\n",
4811 elem->name);
4812 ret = 0;
4813 }
4814 break;
4815 case XML_ELEMENT_TYPE_ANY:
4816 /* I don't think anything is required then */
4817 break;
4818 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004819
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004820 /* simple case of declared as #PCDATA */
4821 if ((elemDecl->content != NULL) &&
4822 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4823 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4824 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004825 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004826 VERROR(ctxt->userData,
4827 "Element %s was declared #PCDATA but contains non text nodes\n",
4828 elem->name);
4829 }
4830 break;
4831 }
Owen Taylor3473f882001-02-23 17:55:21 +00004832 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004833 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004834 while (child != NULL) {
4835 if (child->type == XML_ELEMENT_NODE) {
4836 name = child->name;
4837 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4838 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004839 snprintf((char *) qname, sizeof(qname), "%s:%s",
4840 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004841 qname[sizeof(qname) - 1] = 0;
4842 cont = elemDecl->content;
4843 while (cont != NULL) {
4844 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4845 if (xmlStrEqual(cont->name, qname)) break;
4846 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4847 (cont->c1 != NULL) &&
4848 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4849 if (xmlStrEqual(cont->c1->name, qname)) break;
4850 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4851 (cont->c1 == NULL) ||
4852 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4853 /* Internal error !!! */
4854 xmlGenericError(xmlGenericErrorContext,
4855 "Internal: MIXED struct bad\n");
4856 break;
4857 }
4858 cont = cont->c2;
4859 }
4860 if (cont != NULL)
4861 goto child_ok;
4862 }
4863 cont = elemDecl->content;
4864 while (cont != NULL) {
4865 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4866 if (xmlStrEqual(cont->name, name)) break;
4867 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4868 (cont->c1 != NULL) &&
4869 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4870 if (xmlStrEqual(cont->c1->name, name)) break;
4871 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4872 (cont->c1 == NULL) ||
4873 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4874 /* Internal error !!! */
4875 xmlGenericError(xmlGenericErrorContext,
4876 "Internal: MIXED struct bad\n");
4877 break;
4878 }
4879 cont = cont->c2;
4880 }
4881 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004882 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004883 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004884 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004885 name, elem->name);
4886 ret = 0;
4887 }
4888 }
4889child_ok:
4890 child = child->next;
4891 }
4892 break;
4893 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004894 if ((doc->standalone == 1) && (extsubset == 1)) {
4895 /*
4896 * VC: Standalone Document Declaration
4897 * - element types with element content, if white space
4898 * occurs directly within any instance of those types.
4899 */
4900 child = elem->children;
4901 while (child != NULL) {
4902 if (child->type == XML_TEXT_NODE) {
4903 const xmlChar *content = child->content;
4904
4905 while (IS_BLANK(*content))
4906 content++;
4907 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004908 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004909 VERROR(ctxt->userData,
4910"standalone: %s declared in the external subset contains white spaces nodes\n",
4911 elem->name);
4912 ret = 0;
4913 break;
4914 }
4915 }
4916 child =child->next;
4917 }
4918 }
Owen Taylor3473f882001-02-23 17:55:21 +00004919 child = elem->children;
4920 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004921 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004922 if (tmp <= 0)
4923 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004924 break;
4925 }
4926
4927 /* [ VC: Required Attribute ] */
4928 attr = elemDecl->attributes;
4929 while (attr != NULL) {
4930 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004931 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00004932
Daniel Veillarde4301c82002-02-13 13:32:35 +00004933 if ((attr->prefix == NULL) &&
4934 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4935 xmlNsPtr ns;
4936
4937 ns = elem->nsDef;
4938 while (ns != NULL) {
4939 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00004940 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004941 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00004942 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004943 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4944 xmlNsPtr ns;
4945
4946 ns = elem->nsDef;
4947 while (ns != NULL) {
4948 if (xmlStrEqual(attr->name, ns->prefix))
4949 goto found;
4950 ns = ns->next;
4951 }
4952 } else {
4953 xmlAttrPtr attrib;
4954
4955 attrib = elem->properties;
4956 while (attrib != NULL) {
4957 if (xmlStrEqual(attrib->name, attr->name)) {
4958 if (attr->prefix != NULL) {
4959 xmlNsPtr nameSpace = attrib->ns;
4960
4961 if (nameSpace == NULL)
4962 nameSpace = elem->ns;
4963 /*
4964 * qualified names handling is problematic, having a
4965 * different prefix should be possible but DTDs don't
4966 * allow to define the URI instead of the prefix :-(
4967 */
4968 if (nameSpace == NULL) {
4969 if (qualified < 0)
4970 qualified = 0;
4971 } else if (!xmlStrEqual(nameSpace->prefix,
4972 attr->prefix)) {
4973 if (qualified < 1)
4974 qualified = 1;
4975 } else
4976 goto found;
4977 } else {
4978 /*
4979 * We should allow applications to define namespaces
4980 * for their application even if the DTD doesn't
4981 * carry one, otherwise, basically we would always
4982 * break.
4983 */
4984 goto found;
4985 }
4986 }
4987 attrib = attrib->next;
4988 }
Owen Taylor3473f882001-02-23 17:55:21 +00004989 }
4990 if (qualified == -1) {
4991 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004992 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004993 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004994 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004995 elem->name, attr->name);
4996 ret = 0;
4997 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004998 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004999 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005000 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005001 elem->name, attr->prefix,attr->name);
5002 ret = 0;
5003 }
5004 } else if (qualified == 0) {
5005 VWARNING(ctxt->userData,
5006 "Element %s required attribute %s:%s has no prefix\n",
5007 elem->name, attr->prefix,attr->name);
5008 } else if (qualified == 1) {
5009 VWARNING(ctxt->userData,
5010 "Element %s required attribute %s:%s has different prefix\n",
5011 elem->name, attr->prefix,attr->name);
5012 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005013 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5014 /*
5015 * Special tests checking #FIXED namespace declarations
5016 * have the right value since this is not done as an
5017 * attribute checking
5018 */
5019 if ((attr->prefix == NULL) &&
5020 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5021 xmlNsPtr ns;
5022
5023 ns = elem->nsDef;
5024 while (ns != NULL) {
5025 if (ns->prefix == NULL) {
5026 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005027 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005028 VERROR(ctxt->userData,
5029 "Element %s namespace name for default namespace does not match the DTD\n",
5030 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005031 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005032 }
5033 goto found;
5034 }
5035 ns = ns->next;
5036 }
5037 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5038 xmlNsPtr ns;
5039
5040 ns = elem->nsDef;
5041 while (ns != NULL) {
5042 if (xmlStrEqual(attr->name, ns->prefix)) {
5043 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005044 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005045 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005046 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005047 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005048 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005049 }
5050 goto found;
5051 }
5052 ns = ns->next;
5053 }
5054 }
Owen Taylor3473f882001-02-23 17:55:21 +00005055 }
5056found:
5057 attr = attr->nexth;
5058 }
5059 return(ret);
5060}
5061
5062/**
5063 * xmlValidateRoot:
5064 * @ctxt: the validation context
5065 * @doc: a document instance
5066 *
5067 * Try to validate a the root element
5068 * basically it does the following check as described by the
5069 * XML-1.0 recommendation:
5070 * - [ VC: Root Element Type ]
5071 * it doesn't try to recurse or apply other check to the element
5072 *
5073 * returns 1 if valid or 0 otherwise
5074 */
5075
5076int
5077xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5078 xmlNodePtr root;
5079 if (doc == NULL) return(0);
5080
5081 root = xmlDocGetRootElement(doc);
5082 if ((root == NULL) || (root->name == NULL)) {
5083 VERROR(ctxt->userData, "Not valid: no root element\n");
5084 return(0);
5085 }
5086
5087 /*
5088 * When doing post validation against a separate DTD, those may
5089 * no internal subset has been generated
5090 */
5091 if ((doc->intSubset != NULL) &&
5092 (doc->intSubset->name != NULL)) {
5093 /*
5094 * Check first the document root against the NQName
5095 */
5096 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5097 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
5098 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005099 snprintf((char *) qname, sizeof(qname), "%s:%s",
5100 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005101 qname[sizeof(qname) - 1] = 0;
5102 if (xmlStrEqual(doc->intSubset->name, qname))
5103 goto name_ok;
5104 }
5105 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5106 (xmlStrEqual(root->name, BAD_CAST "html")))
5107 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005108 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005109 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005110 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005111 root->name, doc->intSubset->name);
5112 return(0);
5113
5114 }
5115 }
5116name_ok:
5117 return(1);
5118}
5119
5120
5121/**
5122 * xmlValidateElement:
5123 * @ctxt: the validation context
5124 * @doc: a document instance
5125 * @elem: an element instance
5126 *
5127 * Try to validate the subtree under an element
5128 *
5129 * returns 1 if valid or 0 otherwise
5130 */
5131
5132int
5133xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5134 xmlNodePtr child;
5135 xmlAttrPtr attr;
5136 xmlChar *value;
5137 int ret = 1;
5138
5139 if (elem == NULL) return(0);
5140
5141 /*
5142 * XInclude elements were added after parsing in the infoset,
5143 * they don't really mean anything validation wise.
5144 */
5145 if ((elem->type == XML_XINCLUDE_START) ||
5146 (elem->type == XML_XINCLUDE_END))
5147 return(1);
5148
5149 CHECK_DTD;
5150
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005151 /*
5152 * Entities references have to be handled separately
5153 */
5154 if (elem->type == XML_ENTITY_REF_NODE) {
5155 return(1);
5156 }
5157
Owen Taylor3473f882001-02-23 17:55:21 +00005158 ret &= xmlValidateOneElement(ctxt, doc, elem);
5159 attr = elem->properties;
5160 while(attr != NULL) {
5161 value = xmlNodeListGetString(doc, attr->children, 0);
5162 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5163 if (value != NULL)
5164 xmlFree(value);
5165 attr= attr->next;
5166 }
5167 child = elem->children;
5168 while (child != NULL) {
5169 ret &= xmlValidateElement(ctxt, doc, child);
5170 child = child->next;
5171 }
5172
5173 return(ret);
5174}
5175
Daniel Veillard8730c562001-02-26 10:49:57 +00005176/**
5177 * xmlValidateRef:
5178 * @ref: A reference to be validated
5179 * @ctxt: Validation context
5180 * @name: Name of ID we are searching for
5181 *
5182 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005183static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005184xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005185 const xmlChar *name) {
5186 xmlAttrPtr id;
5187 xmlAttrPtr attr;
5188
5189 if (ref == NULL)
5190 return;
5191 attr = ref->attr;
5192 if (attr == NULL)
5193 return;
5194 if (attr->atype == XML_ATTRIBUTE_IDREF) {
5195 id = xmlGetID(ctxt->doc, name);
5196 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005197 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005198 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005199 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005200 attr->name, name);
5201 ctxt->valid = 0;
5202 }
5203 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5204 xmlChar *dup, *str = NULL, *cur, save;
5205
5206 dup = xmlStrdup(name);
5207 if (dup == NULL) {
5208 ctxt->valid = 0;
5209 return;
5210 }
5211 cur = dup;
5212 while (*cur != 0) {
5213 str = cur;
5214 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5215 save = *cur;
5216 *cur = 0;
5217 id = xmlGetID(ctxt->doc, str);
5218 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005219 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005220 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005221 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005222 attr->name, str);
5223 ctxt->valid = 0;
5224 }
5225 if (save == 0)
5226 break;
5227 *cur = save;
5228 while (IS_BLANK(*cur)) cur++;
5229 }
5230 xmlFree(dup);
5231 }
5232}
5233
5234/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005235 * xmlWalkValidateList:
5236 * @data: Contents of current link
5237 * @user: Value supplied by the user
5238 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005239 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005240 */
5241static int
5242xmlWalkValidateList(const void *data, const void *user)
5243{
5244 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5245 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5246 return 1;
5247}
5248
5249/**
5250 * xmlValidateCheckRefCallback:
5251 * @ref_list: List of references
5252 * @ctxt: Validation context
5253 * @name: Name of ID we are searching for
5254 *
5255 */
5256static void
5257xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5258 const xmlChar *name) {
5259 xmlValidateMemo memo;
5260
5261 if (ref_list == NULL)
5262 return;
5263 memo.ctxt = ctxt;
5264 memo.name = name;
5265
5266 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5267
5268}
5269
5270/**
Owen Taylor3473f882001-02-23 17:55:21 +00005271 * xmlValidateDocumentFinal:
5272 * @ctxt: the validation context
5273 * @doc: a document instance
5274 *
5275 * Does the final step for the document validation once all the
5276 * incremental validation steps have been completed
5277 *
5278 * basically it does the following checks described by the XML Rec
5279 *
5280 *
5281 * returns 1 if valid or 0 otherwise
5282 */
5283
5284int
5285xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5286 xmlRefTablePtr table;
5287
5288 if (doc == NULL) {
5289 xmlGenericError(xmlGenericErrorContext,
5290 "xmlValidateDocumentFinal: doc == NULL\n");
5291 return(0);
5292 }
5293
5294 /*
5295 * Check all the NOTATION/NOTATIONS attributes
5296 */
5297 /*
5298 * Check all the ENTITY/ENTITIES attributes definition for validity
5299 */
5300 /*
5301 * Check all the IDREF/IDREFS attributes definition for validity
5302 */
5303 table = (xmlRefTablePtr) doc->refs;
5304 ctxt->doc = doc;
5305 ctxt->valid = 1;
5306 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
5307 return(ctxt->valid);
5308}
5309
5310/**
5311 * xmlValidateDtd:
5312 * @ctxt: the validation context
5313 * @doc: a document instance
5314 * @dtd: a dtd instance
5315 *
5316 * Try to validate the document against the dtd instance
5317 *
5318 * basically it does check all the definitions in the DtD.
5319 *
5320 * returns 1 if valid or 0 otherwise
5321 */
5322
5323int
5324xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
5325 int ret;
5326 xmlDtdPtr oldExt;
5327 xmlNodePtr root;
5328
5329 if (dtd == NULL) return(0);
5330 if (doc == NULL) return(0);
5331 oldExt = doc->extSubset;
5332 doc->extSubset = dtd;
5333 ret = xmlValidateRoot(ctxt, doc);
5334 if (ret == 0) {
5335 doc->extSubset = oldExt;
5336 return(ret);
5337 }
5338 if (doc->ids != NULL) {
5339 xmlFreeIDTable(doc->ids);
5340 doc->ids = NULL;
5341 }
5342 if (doc->refs != NULL) {
5343 xmlFreeRefTable(doc->refs);
5344 doc->refs = NULL;
5345 }
5346 root = xmlDocGetRootElement(doc);
5347 ret = xmlValidateElement(ctxt, doc, root);
5348 ret &= xmlValidateDocumentFinal(ctxt, doc);
5349 doc->extSubset = oldExt;
5350 return(ret);
5351}
5352
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005353static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005354xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
5355 const xmlChar *name ATTRIBUTE_UNUSED) {
5356 if (cur == NULL)
5357 return;
5358 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
5359 xmlChar *notation = cur->content;
5360
Daniel Veillard878eab02002-02-19 13:46:09 +00005361 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005362 int ret;
5363
5364 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
5365 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00005366 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005367 }
5368 }
5369 }
5370}
5371
5372static void
Owen Taylor3473f882001-02-23 17:55:21 +00005373xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00005374 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005375 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00005376 xmlDocPtr doc;
5377 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005378
Owen Taylor3473f882001-02-23 17:55:21 +00005379 if (cur == NULL)
5380 return;
5381 switch (cur->atype) {
5382 case XML_ATTRIBUTE_CDATA:
5383 case XML_ATTRIBUTE_ID:
5384 case XML_ATTRIBUTE_IDREF :
5385 case XML_ATTRIBUTE_IDREFS:
5386 case XML_ATTRIBUTE_NMTOKEN:
5387 case XML_ATTRIBUTE_NMTOKENS:
5388 case XML_ATTRIBUTE_ENUMERATION:
5389 break;
5390 case XML_ATTRIBUTE_ENTITY:
5391 case XML_ATTRIBUTE_ENTITIES:
5392 case XML_ATTRIBUTE_NOTATION:
5393 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005394
5395 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
5396 cur->atype, cur->defaultValue);
5397 if ((ret == 0) && (ctxt->valid == 1))
5398 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005399 }
5400 if (cur->tree != NULL) {
5401 xmlEnumerationPtr tree = cur->tree;
5402 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005403 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00005404 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005405 if ((ret == 0) && (ctxt->valid == 1))
5406 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005407 tree = tree->next;
5408 }
5409 }
5410 }
Daniel Veillard878eab02002-02-19 13:46:09 +00005411 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
5412 doc = cur->doc;
5413 if ((doc == NULL) || (cur->elem == NULL)) {
5414 VERROR(ctxt->userData,
5415 "xmlValidateAttributeCallback(%s): internal error\n",
5416 cur->name);
5417 return;
5418 }
5419 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
5420 if (elem == NULL)
5421 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
5422 if (elem == NULL) {
5423 VERROR(ctxt->userData,
5424 "attribute %s: could not find decl for element %s\n",
5425 cur->name, cur->elem);
5426 return;
5427 }
5428 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
5429 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005430 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00005431 cur->name, cur->elem);
5432 ctxt->valid = 0;
5433 }
5434 }
Owen Taylor3473f882001-02-23 17:55:21 +00005435}
5436
5437/**
5438 * xmlValidateDtdFinal:
5439 * @ctxt: the validation context
5440 * @doc: a document instance
5441 *
5442 * Does the final step for the dtds validation once all the
5443 * subsets have been parsed
5444 *
5445 * basically it does the following checks described by the XML Rec
5446 * - check that ENTITY and ENTITIES type attributes default or
5447 * possible values matches one of the defined entities.
5448 * - check that NOTATION type attributes default or
5449 * possible values matches one of the defined notations.
5450 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005451 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00005452 */
5453
5454int
5455xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00005456 xmlDtdPtr dtd;
5457 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005458 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00005459
5460 if (doc == NULL) return(0);
5461 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5462 return(0);
5463 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005464 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005465 dtd = doc->intSubset;
5466 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5467 table = (xmlAttributeTablePtr) dtd->attributes;
5468 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005469 }
5470 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005471 entities = (xmlEntitiesTablePtr) dtd->entities;
5472 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5473 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005474 }
5475 dtd = doc->extSubset;
5476 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5477 table = (xmlAttributeTablePtr) dtd->attributes;
5478 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005479 }
5480 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005481 entities = (xmlEntitiesTablePtr) dtd->entities;
5482 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5483 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005484 }
5485 return(ctxt->valid);
5486}
5487
5488/**
5489 * xmlValidateDocument:
5490 * @ctxt: the validation context
5491 * @doc: a document instance
5492 *
5493 * Try to validate the document instance
5494 *
5495 * basically it does the all the checks described by the XML Rec
5496 * i.e. validates the internal and external subset (if present)
5497 * and validate the document tree.
5498 *
5499 * returns 1 if valid or 0 otherwise
5500 */
5501
5502int
5503xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5504 int ret;
5505 xmlNodePtr root;
5506
5507 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5508 return(0);
5509 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
5510 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
5511 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
5512 doc->intSubset->SystemID);
5513 if (doc->extSubset == NULL) {
5514 if (doc->intSubset->SystemID != NULL) {
5515 VERROR(ctxt->userData,
5516 "Could not load the external subset \"%s\"\n",
5517 doc->intSubset->SystemID);
5518 } else {
5519 VERROR(ctxt->userData,
5520 "Could not load the external subset \"%s\"\n",
5521 doc->intSubset->ExternalID);
5522 }
5523 return(0);
5524 }
5525 }
5526
5527 if (doc->ids != NULL) {
5528 xmlFreeIDTable(doc->ids);
5529 doc->ids = NULL;
5530 }
5531 if (doc->refs != NULL) {
5532 xmlFreeRefTable(doc->refs);
5533 doc->refs = NULL;
5534 }
5535 ret = xmlValidateDtdFinal(ctxt, doc);
5536 if (!xmlValidateRoot(ctxt, doc)) return(0);
5537
5538 root = xmlDocGetRootElement(doc);
5539 ret &= xmlValidateElement(ctxt, doc, root);
5540 ret &= xmlValidateDocumentFinal(ctxt, doc);
5541 return(ret);
5542}
5543
5544
5545/************************************************************************
5546 * *
5547 * Routines for dynamic validation editing *
5548 * *
5549 ************************************************************************/
5550
5551/**
5552 * xmlValidGetPotentialChildren:
5553 * @ctree: an element content tree
5554 * @list: an array to store the list of child names
5555 * @len: a pointer to the number of element in the list
5556 * @max: the size of the array
5557 *
5558 * Build/extend a list of potential children allowed by the content tree
5559 *
5560 * returns the number of element in the list, or -1 in case of error.
5561 */
5562
5563int
5564xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
5565 int *len, int max) {
5566 int i;
5567
5568 if ((ctree == NULL) || (list == NULL) || (len == NULL))
5569 return(-1);
5570 if (*len >= max) return(*len);
5571
5572 switch (ctree->type) {
5573 case XML_ELEMENT_CONTENT_PCDATA:
5574 for (i = 0; i < *len;i++)
5575 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
5576 list[(*len)++] = BAD_CAST "#PCDATA";
5577 break;
5578 case XML_ELEMENT_CONTENT_ELEMENT:
5579 for (i = 0; i < *len;i++)
5580 if (xmlStrEqual(ctree->name, list[i])) return(*len);
5581 list[(*len)++] = ctree->name;
5582 break;
5583 case XML_ELEMENT_CONTENT_SEQ:
5584 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5585 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5586 break;
5587 case XML_ELEMENT_CONTENT_OR:
5588 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5589 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5590 break;
5591 }
5592
5593 return(*len);
5594}
5595
5596/**
5597 * xmlValidGetValidElements:
5598 * @prev: an element to insert after
5599 * @next: an element to insert next
5600 * @list: an array to store the list of child names
5601 * @max: the size of the array
5602 *
5603 * This function returns the list of authorized children to insert
5604 * within an existing tree while respecting the validity constraints
5605 * forced by the Dtd. The insertion point is defined using @prev and
5606 * @next in the following ways:
5607 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
5608 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
5609 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
5610 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
5611 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
5612 *
5613 * pointers to the element names are inserted at the beginning of the array
5614 * and do not need to be freed.
5615 *
5616 * returns the number of element in the list, or -1 in case of error. If
5617 * the function returns the value @max the caller is invited to grow the
5618 * receiving array and retry.
5619 */
5620
5621int
5622xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
5623 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005624 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005625 int nb_valid_elements = 0;
5626 const xmlChar *elements[256];
5627 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005628 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005629
5630 xmlNode *ref_node;
5631 xmlNode *parent;
5632 xmlNode *test_node;
5633
5634 xmlNode *prev_next;
5635 xmlNode *next_prev;
5636 xmlNode *parent_childs;
5637 xmlNode *parent_last;
5638
5639 xmlElement *element_desc;
5640
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00005641 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005642
Owen Taylor3473f882001-02-23 17:55:21 +00005643 if (prev == NULL && next == NULL)
5644 return(-1);
5645
5646 if (list == NULL) return(-1);
5647 if (max <= 0) return(-1);
5648
5649 nb_valid_elements = 0;
5650 ref_node = prev ? prev : next;
5651 parent = ref_node->parent;
5652
5653 /*
5654 * Retrieves the parent element declaration
5655 */
5656 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5657 parent->name);
5658 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5659 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5660 parent->name);
5661 if (element_desc == NULL) return(-1);
5662
5663 /*
5664 * Do a backup of the current tree structure
5665 */
5666 prev_next = prev ? prev->next : NULL;
5667 next_prev = next ? next->prev : NULL;
5668 parent_childs = parent->children;
5669 parent_last = parent->last;
5670
5671 /*
5672 * Creates a dummy node and insert it into the tree
5673 */
5674 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5675 test_node->doc = ref_node->doc;
5676 test_node->parent = parent;
5677 test_node->prev = prev;
5678 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005679 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005680
5681 if (prev) prev->next = test_node;
5682 else parent->children = test_node;
5683
5684 if (next) next->prev = test_node;
5685 else parent->last = test_node;
5686
5687 /*
5688 * Insert each potential child node and check if the parent is
5689 * still valid
5690 */
5691 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5692 elements, &nb_elements, 256);
5693
5694 for (i = 0;i < nb_elements;i++) {
5695 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005696 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005697 int j;
5698
5699 for (j = 0; j < nb_valid_elements;j++)
5700 if (xmlStrEqual(elements[i], list[j])) break;
5701 list[nb_valid_elements++] = elements[i];
5702 if (nb_valid_elements >= max) break;
5703 }
5704 }
5705
5706 /*
5707 * Restore the tree structure
5708 */
5709 if (prev) prev->next = prev_next;
5710 if (next) next->prev = next_prev;
5711 parent->children = parent_childs;
5712 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005713
5714 /*
5715 * Free up the dummy node
5716 */
5717 test_node->name = name;
5718 xmlFreeNode(test_node);
5719
Owen Taylor3473f882001-02-23 17:55:21 +00005720 return(nb_valid_elements);
5721}