blob: fbdc76654045de6b591ce94e7d02544e911e80bc [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 Veillard23e73572002-09-19 19:56:43 +0000556 if (!xmlRegexpIsDeterminist(elem->contModel)) {
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
Daniel Veillard23e73572002-09-19 19:56:43 +00003852#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003853/**
3854 * xmlValidateElementType:
3855 * @ctxt: the validation context
3856 *
3857 * Try to validate the content model of an element internal function
3858 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003859 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3860 * reference is found and -3 if the validation succeeded but
3861 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003862 */
3863
3864static int
3865xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003866 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003867 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003868
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003869 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003870 if ((NODE == NULL) && (CONT == NULL))
3871 return(1);
3872 if ((NODE == NULL) &&
3873 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3874 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3875 return(1);
3876 }
3877 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003878 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003879 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003880
3881 /*
3882 * We arrive here when more states need to be examined
3883 */
3884cont:
3885
3886 /*
3887 * We just recovered from a rollback generated by a possible
3888 * epsilon transition, go directly to the analysis phase
3889 */
3890 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003891 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003892 DEBUG_VALID_STATE(NODE, CONT)
3893 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003894 goto analyze;
3895 }
3896
3897 DEBUG_VALID_STATE(NODE, CONT)
3898 /*
3899 * we may have to save a backup state here. This is the equivalent
3900 * of handling epsilon transition in NFAs.
3901 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003902 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003903 ((CONT->parent == NULL) ||
3904 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003905 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003906 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003907 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003908 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003909 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
3910 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003911 }
3912
3913
3914 /*
3915 * Check first if the content matches
3916 */
3917 switch (CONT->type) {
3918 case XML_ELEMENT_CONTENT_PCDATA:
3919 if (NODE == NULL) {
3920 DEBUG_VALID_MSG("pcdata failed no node");
3921 ret = 0;
3922 break;
3923 }
3924 if (NODE->type == XML_TEXT_NODE) {
3925 DEBUG_VALID_MSG("pcdata found, skip to next");
3926 /*
3927 * go to next element in the content model
3928 * skipping ignorable elems
3929 */
3930 do {
3931 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003932 NODE = xmlValidateSkipIgnorable(NODE);
3933 if ((NODE != NULL) &&
3934 (NODE->type == XML_ENTITY_REF_NODE))
3935 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003936 } while ((NODE != NULL) &&
3937 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003938 (NODE->type != XML_TEXT_NODE) &&
3939 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003940 ret = 1;
3941 break;
3942 } else {
3943 DEBUG_VALID_MSG("pcdata failed");
3944 ret = 0;
3945 break;
3946 }
3947 break;
3948 case XML_ELEMENT_CONTENT_ELEMENT:
3949 if (NODE == NULL) {
3950 DEBUG_VALID_MSG("element failed no node");
3951 ret = 0;
3952 break;
3953 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003954 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3955 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003956 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003957 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3958 ret = (CONT->prefix == NULL);
3959 } else if (CONT->prefix == NULL) {
3960 ret = 0;
3961 } else {
3962 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3963 }
3964 }
3965 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003966 DEBUG_VALID_MSG("element found, skip to next");
3967 /*
3968 * go to next element in the content model
3969 * skipping ignorable elems
3970 */
3971 do {
3972 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003973 NODE = xmlValidateSkipIgnorable(NODE);
3974 if ((NODE != NULL) &&
3975 (NODE->type == XML_ENTITY_REF_NODE))
3976 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003977 } while ((NODE != NULL) &&
3978 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003979 (NODE->type != XML_TEXT_NODE) &&
3980 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003981 } else {
3982 DEBUG_VALID_MSG("element failed");
3983 ret = 0;
3984 break;
3985 }
3986 break;
3987 case XML_ELEMENT_CONTENT_OR:
3988 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003989 * Small optimization.
3990 */
3991 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3992 if ((NODE == NULL) ||
3993 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3994 DEPTH++;
3995 CONT = CONT->c2;
3996 goto cont;
3997 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003998 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3999 ret = (CONT->c1->prefix == NULL);
4000 } else if (CONT->c1->prefix == NULL) {
4001 ret = 0;
4002 } else {
4003 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4004 }
4005 if (ret == 0) {
4006 DEPTH++;
4007 CONT = CONT->c2;
4008 goto cont;
4009 }
Daniel Veillard85349052001-04-20 13:48:21 +00004010 }
4011
4012 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004013 * save the second branch 'or' branch
4014 */
4015 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00004016 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
4017 OCCURS, ROLLBACK_OR) < 0)
4018 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004019 DEPTH++;
4020 CONT = CONT->c1;
4021 goto cont;
4022 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004023 /*
4024 * Small optimization.
4025 */
4026 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4027 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4028 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4029 if ((NODE == NULL) ||
4030 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4031 DEPTH++;
4032 CONT = CONT->c2;
4033 goto cont;
4034 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004035 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4036 ret = (CONT->c1->prefix == NULL);
4037 } else if (CONT->c1->prefix == NULL) {
4038 ret = 0;
4039 } else {
4040 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4041 }
4042 if (ret == 0) {
4043 DEPTH++;
4044 CONT = CONT->c2;
4045 goto cont;
4046 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004047 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004048 DEPTH++;
4049 CONT = CONT->c1;
4050 goto cont;
4051 }
4052
4053 /*
4054 * At this point handle going up in the tree
4055 */
4056 if (ret == -1) {
4057 DEBUG_VALID_MSG("error found returning");
4058 return(ret);
4059 }
4060analyze:
4061 while (CONT != NULL) {
4062 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004063 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004064 * this level.
4065 */
4066 if (ret == 0) {
4067 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004068 xmlNodePtr cur;
4069
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004070 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004071 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004072 DEBUG_VALID_MSG("Once branch failed, rollback");
4073 if (vstateVPop(ctxt) < 0 ) {
4074 DEBUG_VALID_MSG("exhaustion, failed");
4075 return(0);
4076 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004077 if (cur != ctxt->vstate->node)
4078 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004079 goto cont;
4080 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004081 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004082 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004083 DEBUG_VALID_MSG("Plus branch failed, rollback");
4084 if (vstateVPop(ctxt) < 0 ) {
4085 DEBUG_VALID_MSG("exhaustion, failed");
4086 return(0);
4087 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004088 if (cur != ctxt->vstate->node)
4089 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004090 goto cont;
4091 }
4092 DEBUG_VALID_MSG("Plus branch found");
4093 ret = 1;
4094 break;
4095 case XML_ELEMENT_CONTENT_MULT:
4096#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004097 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004098 DEBUG_VALID_MSG("Mult branch failed");
4099 } else {
4100 DEBUG_VALID_MSG("Mult branch found");
4101 }
4102#endif
4103 ret = 1;
4104 break;
4105 case XML_ELEMENT_CONTENT_OPT:
4106 DEBUG_VALID_MSG("Option branch failed");
4107 ret = 1;
4108 break;
4109 }
4110 } else {
4111 switch (CONT->ocur) {
4112 case XML_ELEMENT_CONTENT_OPT:
4113 DEBUG_VALID_MSG("Option branch succeeded");
4114 ret = 1;
4115 break;
4116 case XML_ELEMENT_CONTENT_ONCE:
4117 DEBUG_VALID_MSG("Once branch succeeded");
4118 ret = 1;
4119 break;
4120 case XML_ELEMENT_CONTENT_PLUS:
4121 if (STATE == ROLLBACK_PARENT) {
4122 DEBUG_VALID_MSG("Plus branch rollback");
4123 ret = 1;
4124 break;
4125 }
4126 if (NODE == NULL) {
4127 DEBUG_VALID_MSG("Plus branch exhausted");
4128 ret = 1;
4129 break;
4130 }
4131 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004132 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004133 goto cont;
4134 case XML_ELEMENT_CONTENT_MULT:
4135 if (STATE == ROLLBACK_PARENT) {
4136 DEBUG_VALID_MSG("Mult branch rollback");
4137 ret = 1;
4138 break;
4139 }
4140 if (NODE == NULL) {
4141 DEBUG_VALID_MSG("Mult branch exhausted");
4142 ret = 1;
4143 break;
4144 }
4145 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004146 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004147 goto cont;
4148 }
4149 }
4150 STATE = 0;
4151
4152 /*
4153 * Then act accordingly at the parent level
4154 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004155 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004156 if (CONT->parent == NULL)
4157 break;
4158
4159 switch (CONT->parent->type) {
4160 case XML_ELEMENT_CONTENT_PCDATA:
4161 DEBUG_VALID_MSG("Error: parent pcdata");
4162 return(-1);
4163 case XML_ELEMENT_CONTENT_ELEMENT:
4164 DEBUG_VALID_MSG("Error: parent element");
4165 return(-1);
4166 case XML_ELEMENT_CONTENT_OR:
4167 if (ret == 1) {
4168 DEBUG_VALID_MSG("Or succeeded");
4169 CONT = CONT->parent;
4170 DEPTH--;
4171 } else {
4172 DEBUG_VALID_MSG("Or failed");
4173 CONT = CONT->parent;
4174 DEPTH--;
4175 }
4176 break;
4177 case XML_ELEMENT_CONTENT_SEQ:
4178 if (ret == 0) {
4179 DEBUG_VALID_MSG("Sequence failed");
4180 CONT = CONT->parent;
4181 DEPTH--;
4182 } else if (CONT == CONT->parent->c1) {
4183 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4184 CONT = CONT->parent->c2;
4185 goto cont;
4186 } else {
4187 DEBUG_VALID_MSG("Sequence succeeded");
4188 CONT = CONT->parent;
4189 DEPTH--;
4190 }
4191 }
4192 }
4193 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004194 xmlNodePtr cur;
4195
4196 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004197 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4198 if (vstateVPop(ctxt) < 0 ) {
4199 DEBUG_VALID_MSG("exhaustion, failed");
4200 return(0);
4201 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004202 if (cur != ctxt->vstate->node)
4203 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004204 goto cont;
4205 }
4206 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004207 xmlNodePtr cur;
4208
4209 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004210 DEBUG_VALID_MSG("Failure, rollback");
4211 if (vstateVPop(ctxt) < 0 ) {
4212 DEBUG_VALID_MSG("exhaustion, failed");
4213 return(0);
4214 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004215 if (cur != ctxt->vstate->node)
4216 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004217 goto cont;
4218 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004219 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004220}
Daniel Veillard23e73572002-09-19 19:56:43 +00004221#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004222
4223/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004224 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004225 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004226 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004227 * @content: An element
4228 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4229 *
4230 * This will dump the list of elements to the buffer
4231 * Intended just for the debug routine
4232 */
4233static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004234xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004235 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004236 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004237
4238 if (node == NULL) return;
4239 if (glob) strcat(buf, "(");
4240 cur = node;
4241 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004242 len = strlen(buf);
4243 if (size - len < 50) {
4244 if ((size - len > 4) && (buf[len - 1] != '.'))
4245 strcat(buf, " ...");
4246 return;
4247 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004248 switch (cur->type) {
4249 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004250 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004251 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004252 if ((size - len > 4) && (buf[len - 1] != '.'))
4253 strcat(buf, " ...");
4254 return;
4255 }
4256 strcat(buf, (char *) cur->ns->prefix);
4257 strcat(buf, ":");
4258 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004259 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004260 if ((size - len > 4) && (buf[len - 1] != '.'))
4261 strcat(buf, " ...");
4262 return;
4263 }
4264 strcat(buf, (char *) cur->name);
4265 if (cur->next != NULL)
4266 strcat(buf, " ");
4267 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004268 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004269 if (xmlIsBlankNode(cur))
4270 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004271 case XML_CDATA_SECTION_NODE:
4272 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004273 strcat(buf, "CDATA");
4274 if (cur->next != NULL)
4275 strcat(buf, " ");
4276 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004277 case XML_ATTRIBUTE_NODE:
4278 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004279#ifdef LIBXML_DOCB_ENABLED
4280 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004281#endif
4282 case XML_HTML_DOCUMENT_NODE:
4283 case XML_DOCUMENT_TYPE_NODE:
4284 case XML_DOCUMENT_FRAG_NODE:
4285 case XML_NOTATION_NODE:
4286 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004287 strcat(buf, "???");
4288 if (cur->next != NULL)
4289 strcat(buf, " ");
4290 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004291 case XML_ENTITY_NODE:
4292 case XML_PI_NODE:
4293 case XML_DTD_NODE:
4294 case XML_COMMENT_NODE:
4295 case XML_ELEMENT_DECL:
4296 case XML_ATTRIBUTE_DECL:
4297 case XML_ENTITY_DECL:
4298 case XML_XINCLUDE_START:
4299 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004300 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004301 }
4302 cur = cur->next;
4303 }
4304 if (glob) strcat(buf, ")");
4305}
4306
4307/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004308 * xmlValidateElementContent:
4309 * @ctxt: the validation context
4310 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004311 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004312 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004313 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004314 *
4315 * Try to validate the content model of an element
4316 *
4317 * returns 1 if valid or 0 if not and -1 in case of error
4318 */
4319
4320static int
4321xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004322 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillard5acfd6b2002-09-18 16:29:02 +00004323 int ret = 1;
Daniel Veillard23e73572002-09-19 19:56:43 +00004324#ifndef LIBXML_REGEXP_ENABLED
4325 xmlNodePtr last = NULL;
4326#endif
4327 xmlNodePtr repl = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004328 xmlElementContentPtr cont;
4329 const xmlChar *name;
4330
4331 if (elemDecl == NULL)
4332 return(-1);
4333 cont = elemDecl->content;
4334 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004335
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004336#ifdef LIBXML_REGEXP_ENABLED
4337 /* Build the regexp associated to the content model */
4338 if (elemDecl->contModel == NULL)
4339 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4340 if (elemDecl->contModel == NULL) {
4341 ret = -1;
4342 } else {
4343 xmlRegExecCtxtPtr exec;
4344
4345 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4346 if (exec != NULL) {
4347 cur = child;
4348 while (cur != NULL) {
4349 switch (cur->type) {
4350 case XML_ENTITY_REF_NODE:
4351 /*
4352 * Push the current node to be able to roll back
4353 * and process within the entity
4354 */
4355 if ((cur->children != NULL) &&
4356 (cur->children->children != NULL)) {
4357 nodeVPush(ctxt, cur);
4358 cur = cur->children->children;
4359 continue;
4360 }
4361 break;
4362 case XML_TEXT_NODE:
4363 if (xmlIsBlankNode(cur))
4364 break;
4365 ret = 0;
4366 goto fail;
4367 case XML_CDATA_SECTION_NODE:
4368 TODO
4369 ret = 0;
4370 goto fail;
4371 case XML_ELEMENT_NODE:
4372 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
4373 xmlChar *QName;
4374 int len;
4375
4376 len = xmlStrlen(cur->name) +
4377 xmlStrlen(cur->ns->prefix) + 2;
4378 QName = xmlMalloc(len);
4379 if (QName == NULL) {
4380 ret = -1;
4381 goto fail;
4382 }
4383 snprintf((char *) QName, len, "%s:%s",
4384 (char *)cur->ns->prefix,
4385 (char *)cur->name);
4386 ret = xmlRegExecPushString(exec, QName, NULL);
4387 xmlFree(QName);
4388 } else {
4389 ret = xmlRegExecPushString(exec, cur->name, NULL);
4390 }
4391 break;
4392 default:
4393 break;
4394 }
4395 /*
4396 * Switch to next element
4397 */
4398 cur = cur->next;
4399 while (cur == NULL) {
4400 cur = nodeVPop(ctxt);
4401 if (cur == NULL)
4402 break;
4403 cur = cur->next;
4404 }
4405 }
4406 ret = xmlRegExecPushString(exec, NULL, NULL);
4407fail:
4408 xmlRegFreeExecCtxt(exec);
4409 }
4410 }
4411#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004412 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004413 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004414 */
4415 ctxt->vstateMax = 8;
4416 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4417 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4418 if (ctxt->vstateTab == NULL) {
4419 xmlGenericError(xmlGenericErrorContext,
4420 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004421 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004422 }
4423 /*
4424 * The first entry in the stack is reserved to the current state
4425 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004426 ctxt->nodeMax = 0;
4427 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004428 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004429 ctxt->vstate = &ctxt->vstateTab[0];
4430 ctxt->vstateNr = 1;
4431 CONT = cont;
4432 NODE = child;
4433 DEPTH = 0;
4434 OCCURS = 0;
4435 STATE = 0;
4436 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004437 if ((ret == -3) && (warn)) {
4438 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004439 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004440 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004441 /*
4442 * An entities reference appeared at this level.
4443 * Buid a minimal representation of this node content
4444 * sufficient to run the validation process on it
4445 */
4446 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004447 cur = child;
4448 while (cur != NULL) {
4449 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004450 case XML_ENTITY_REF_NODE:
4451 /*
4452 * Push the current node to be able to roll back
4453 * and process within the entity
4454 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004455 if ((cur->children != NULL) &&
4456 (cur->children->children != NULL)) {
4457 nodeVPush(ctxt, cur);
4458 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004459 continue;
4460 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004461 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004462 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004463 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004464 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004465 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004466 case XML_CDATA_SECTION_NODE:
4467 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004468 case XML_ELEMENT_NODE:
4469 /*
4470 * Allocate a new node and minimally fills in
4471 * what's required
4472 */
4473 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4474 if (tmp == NULL) {
4475 xmlGenericError(xmlGenericErrorContext,
4476 "xmlValidateElementContent : malloc failed\n");
4477 xmlFreeNodeList(repl);
4478 ret = -1;
4479 goto done;
4480 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004481 tmp->type = cur->type;
4482 tmp->name = cur->name;
4483 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004484 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004485 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004486 if (repl == NULL)
4487 repl = last = tmp;
4488 else {
4489 last->next = tmp;
4490 last = tmp;
4491 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004492 if (cur->type == XML_CDATA_SECTION_NODE) {
4493 /*
4494 * E59 spaces in CDATA does not match the
4495 * nonterminal S
4496 */
4497 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4498 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004499 break;
4500 default:
4501 break;
4502 }
4503 /*
4504 * Switch to next element
4505 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004506 cur = cur->next;
4507 while (cur == NULL) {
4508 cur = nodeVPop(ctxt);
4509 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004510 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004511 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004512 }
4513 }
4514
4515 /*
4516 * Relaunch the validation
4517 */
4518 ctxt->vstate = &ctxt->vstateTab[0];
4519 ctxt->vstateNr = 1;
4520 CONT = cont;
4521 NODE = repl;
4522 DEPTH = 0;
4523 OCCURS = 0;
4524 STATE = 0;
4525 ret = xmlValidateElementType(ctxt);
4526 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004527#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004528 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004529 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4530 char expr[5000];
4531 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004532
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004533 expr[0] = 0;
4534 xmlSnprintfElementContent(expr, 5000, cont, 1);
4535 list[0] = 0;
4536 if (repl != NULL)
4537 xmlSnprintfElements(list, 5000, repl, 1);
4538 else
4539 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004540
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004541 if (name != NULL) {
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 %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004545 name, expr, list);
4546 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004547 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004548 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004549 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004550 expr, list);
4551 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004552 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004553 if (name != NULL) {
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 %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004557 name);
4558 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004559 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004560 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004561 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004562 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004563 }
4564 ret = 0;
4565 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004566 if (ret == -3)
4567 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004568
Daniel Veillard23e73572002-09-19 19:56:43 +00004569#ifndef LIBXML_REGEXP_ENABLED
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004570done:
Daniel Veillard23e73572002-09-19 19:56:43 +00004571#endif
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004572 /*
4573 * Deallocate the copy if done, and free up the validation stack
4574 */
4575 while (repl != NULL) {
4576 tmp = repl->next;
4577 xmlFree(repl);
4578 repl = tmp;
4579 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004580 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004581 if (ctxt->vstateTab != NULL) {
4582 xmlFree(ctxt->vstateTab);
4583 ctxt->vstateTab = NULL;
4584 }
4585 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004586 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004587 if (ctxt->nodeTab != NULL) {
4588 xmlFree(ctxt->nodeTab);
4589 ctxt->nodeTab = NULL;
4590 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004591 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004592
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004593}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004594
Owen Taylor3473f882001-02-23 17:55:21 +00004595/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004596 * xmlValidateCdataElement:
4597 * @ctxt: the validation context
4598 * @doc: a document instance
4599 * @elem: an element instance
4600 *
4601 * Check that an element follows #CDATA
4602 *
4603 * returns 1 if valid or 0 otherwise
4604 */
4605static int
4606xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4607 xmlNodePtr elem) {
4608 int ret = 1;
4609 xmlNodePtr cur, child;
4610
4611 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4612 return(0);
4613
4614 child = elem->children;
4615
4616 cur = child;
4617 while (cur != NULL) {
4618 switch (cur->type) {
4619 case XML_ENTITY_REF_NODE:
4620 /*
4621 * Push the current node to be able to roll back
4622 * and process within the entity
4623 */
4624 if ((cur->children != NULL) &&
4625 (cur->children->children != NULL)) {
4626 nodeVPush(ctxt, cur);
4627 cur = cur->children->children;
4628 continue;
4629 }
4630 break;
4631 case XML_COMMENT_NODE:
4632 case XML_PI_NODE:
4633 case XML_TEXT_NODE:
4634 case XML_CDATA_SECTION_NODE:
4635 break;
4636 default:
4637 ret = 0;
4638 goto done;
4639 }
4640 /*
4641 * Switch to next element
4642 */
4643 cur = cur->next;
4644 while (cur == NULL) {
4645 cur = nodeVPop(ctxt);
4646 if (cur == NULL)
4647 break;
4648 cur = cur->next;
4649 }
4650 }
4651done:
4652 ctxt->nodeMax = 0;
4653 ctxt->nodeNr = 0;
4654 if (ctxt->nodeTab != NULL) {
4655 xmlFree(ctxt->nodeTab);
4656 ctxt->nodeTab = NULL;
4657 }
4658 return(ret);
4659}
4660
4661/**
Owen Taylor3473f882001-02-23 17:55:21 +00004662 * xmlValidateOneElement:
4663 * @ctxt: the validation context
4664 * @doc: a document instance
4665 * @elem: an element instance
4666 *
4667 * Try to validate a single element and it's attributes,
4668 * basically it does the following checks as described by the
4669 * XML-1.0 recommendation:
4670 * - [ VC: Element Valid ]
4671 * - [ VC: Required Attribute ]
4672 * Then call xmlValidateOneAttribute() for each attribute present.
4673 *
4674 * The ID/IDREF checkings are done separately
4675 *
4676 * returns 1 if valid or 0 otherwise
4677 */
4678
4679int
4680xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4681 xmlNodePtr elem) {
4682 xmlElementPtr elemDecl = NULL;
4683 xmlElementContentPtr cont;
4684 xmlAttributePtr attr;
4685 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004686 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004687 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004688 const xmlChar *prefix = NULL;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004689 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004690
4691 CHECK_DTD;
4692
4693 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004694 switch (elem->type) {
4695 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004696 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004697 VERROR(ctxt->userData,
4698 "Attribute element not expected here\n");
4699 return(0);
4700 case XML_TEXT_NODE:
4701 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004702 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004703 VERROR(ctxt->userData, "Text element has childs !\n");
4704 return(0);
4705 }
4706 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004707 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004708 VERROR(ctxt->userData, "Text element has attributes !\n");
4709 return(0);
4710 }
4711 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004712 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004713 VERROR(ctxt->userData, "Text element has namespace !\n");
4714 return(0);
4715 }
4716 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004717 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004718 VERROR(ctxt->userData,
4719 "Text element carries namespace definitions !\n");
4720 return(0);
4721 }
4722 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004723 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004724 VERROR(ctxt->userData,
4725 "Text element has no content !\n");
4726 return(0);
4727 }
4728 return(1);
4729 case XML_XINCLUDE_START:
4730 case XML_XINCLUDE_END:
4731 return(1);
4732 case XML_CDATA_SECTION_NODE:
4733 case XML_ENTITY_REF_NODE:
4734 case XML_PI_NODE:
4735 case XML_COMMENT_NODE:
4736 return(1);
4737 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004738 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004739 VERROR(ctxt->userData,
4740 "Entity element not expected here\n");
4741 return(0);
4742 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004743 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004744 VERROR(ctxt->userData,
4745 "Notation element not expected here\n");
4746 return(0);
4747 case XML_DOCUMENT_NODE:
4748 case XML_DOCUMENT_TYPE_NODE:
4749 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004750 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004751 VERROR(ctxt->userData,
4752 "Document element not expected here\n");
4753 return(0);
4754 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004755 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004756 VERROR(ctxt->userData,
4757 "\n");
4758 return(0);
4759 case XML_ELEMENT_NODE:
4760 break;
4761 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004762 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004763 VERROR(ctxt->userData,
4764 "unknown element type %d\n", elem->type);
4765 return(0);
4766 }
4767 if (elem->name == NULL) return(0);
4768
4769 /*
4770 * Fetch the declaration for the qualified name
4771 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004772 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4773 prefix = elem->ns->prefix;
4774
4775 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004776 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004777 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004778 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004779 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004780 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004781 if (elemDecl != NULL)
4782 extsubset = 1;
4783 }
Owen Taylor3473f882001-02-23 17:55:21 +00004784 }
4785
4786 /*
4787 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004788 * This is "non-strict" validation should be done on the
4789 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004790 */
4791 if (elemDecl == NULL) {
4792 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004793 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004794 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004795 if (elemDecl != NULL)
4796 extsubset = 1;
4797 }
Owen Taylor3473f882001-02-23 17:55:21 +00004798 }
4799 if (elemDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004800 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004801 VERROR(ctxt->userData, "No declaration for element %s\n",
4802 elem->name);
4803 return(0);
4804 }
4805
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004806 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004807 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004808 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004809 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004810 VERROR(ctxt->userData, "No declaration for element %s\n",
4811 elem->name);
4812 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004813 case XML_ELEMENT_TYPE_EMPTY:
4814 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004815 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004816 VERROR(ctxt->userData,
4817 "Element %s was declared EMPTY this one has content\n",
4818 elem->name);
4819 ret = 0;
4820 }
4821 break;
4822 case XML_ELEMENT_TYPE_ANY:
4823 /* I don't think anything is required then */
4824 break;
4825 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004826
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004827 /* simple case of declared as #PCDATA */
4828 if ((elemDecl->content != NULL) &&
4829 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4830 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4831 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004832 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004833 VERROR(ctxt->userData,
4834 "Element %s was declared #PCDATA but contains non text nodes\n",
4835 elem->name);
4836 }
4837 break;
4838 }
Owen Taylor3473f882001-02-23 17:55:21 +00004839 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004840 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004841 while (child != NULL) {
4842 if (child->type == XML_ELEMENT_NODE) {
4843 name = child->name;
4844 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4845 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004846 snprintf((char *) qname, sizeof(qname), "%s:%s",
4847 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004848 qname[sizeof(qname) - 1] = 0;
4849 cont = elemDecl->content;
4850 while (cont != NULL) {
4851 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4852 if (xmlStrEqual(cont->name, qname)) break;
4853 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4854 (cont->c1 != NULL) &&
4855 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4856 if (xmlStrEqual(cont->c1->name, qname)) break;
4857 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4858 (cont->c1 == NULL) ||
4859 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4860 /* Internal error !!! */
4861 xmlGenericError(xmlGenericErrorContext,
4862 "Internal: MIXED struct bad\n");
4863 break;
4864 }
4865 cont = cont->c2;
4866 }
4867 if (cont != NULL)
4868 goto child_ok;
4869 }
4870 cont = elemDecl->content;
4871 while (cont != NULL) {
4872 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4873 if (xmlStrEqual(cont->name, name)) break;
4874 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4875 (cont->c1 != NULL) &&
4876 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4877 if (xmlStrEqual(cont->c1->name, name)) break;
4878 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4879 (cont->c1 == NULL) ||
4880 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4881 /* Internal error !!! */
4882 xmlGenericError(xmlGenericErrorContext,
4883 "Internal: MIXED struct bad\n");
4884 break;
4885 }
4886 cont = cont->c2;
4887 }
4888 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004889 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004890 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004891 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004892 name, elem->name);
4893 ret = 0;
4894 }
4895 }
4896child_ok:
4897 child = child->next;
4898 }
4899 break;
4900 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004901 if ((doc->standalone == 1) && (extsubset == 1)) {
4902 /*
4903 * VC: Standalone Document Declaration
4904 * - element types with element content, if white space
4905 * occurs directly within any instance of those types.
4906 */
4907 child = elem->children;
4908 while (child != NULL) {
4909 if (child->type == XML_TEXT_NODE) {
4910 const xmlChar *content = child->content;
4911
4912 while (IS_BLANK(*content))
4913 content++;
4914 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004915 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004916 VERROR(ctxt->userData,
4917"standalone: %s declared in the external subset contains white spaces nodes\n",
4918 elem->name);
4919 ret = 0;
4920 break;
4921 }
4922 }
4923 child =child->next;
4924 }
4925 }
Owen Taylor3473f882001-02-23 17:55:21 +00004926 child = elem->children;
4927 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004928 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004929 if (tmp <= 0)
4930 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004931 break;
4932 }
4933
4934 /* [ VC: Required Attribute ] */
4935 attr = elemDecl->attributes;
4936 while (attr != NULL) {
4937 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004938 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00004939
Daniel Veillarde4301c82002-02-13 13:32:35 +00004940 if ((attr->prefix == NULL) &&
4941 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4942 xmlNsPtr ns;
4943
4944 ns = elem->nsDef;
4945 while (ns != NULL) {
4946 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00004947 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004948 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00004949 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004950 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4951 xmlNsPtr ns;
4952
4953 ns = elem->nsDef;
4954 while (ns != NULL) {
4955 if (xmlStrEqual(attr->name, ns->prefix))
4956 goto found;
4957 ns = ns->next;
4958 }
4959 } else {
4960 xmlAttrPtr attrib;
4961
4962 attrib = elem->properties;
4963 while (attrib != NULL) {
4964 if (xmlStrEqual(attrib->name, attr->name)) {
4965 if (attr->prefix != NULL) {
4966 xmlNsPtr nameSpace = attrib->ns;
4967
4968 if (nameSpace == NULL)
4969 nameSpace = elem->ns;
4970 /*
4971 * qualified names handling is problematic, having a
4972 * different prefix should be possible but DTDs don't
4973 * allow to define the URI instead of the prefix :-(
4974 */
4975 if (nameSpace == NULL) {
4976 if (qualified < 0)
4977 qualified = 0;
4978 } else if (!xmlStrEqual(nameSpace->prefix,
4979 attr->prefix)) {
4980 if (qualified < 1)
4981 qualified = 1;
4982 } else
4983 goto found;
4984 } else {
4985 /*
4986 * We should allow applications to define namespaces
4987 * for their application even if the DTD doesn't
4988 * carry one, otherwise, basically we would always
4989 * break.
4990 */
4991 goto found;
4992 }
4993 }
4994 attrib = attrib->next;
4995 }
Owen Taylor3473f882001-02-23 17:55:21 +00004996 }
4997 if (qualified == -1) {
4998 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004999 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005000 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005001 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005002 elem->name, attr->name);
5003 ret = 0;
5004 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005005 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00005006 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005007 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005008 elem->name, attr->prefix,attr->name);
5009 ret = 0;
5010 }
5011 } else if (qualified == 0) {
5012 VWARNING(ctxt->userData,
5013 "Element %s required attribute %s:%s has no prefix\n",
5014 elem->name, attr->prefix,attr->name);
5015 } else if (qualified == 1) {
5016 VWARNING(ctxt->userData,
5017 "Element %s required attribute %s:%s has different prefix\n",
5018 elem->name, attr->prefix,attr->name);
5019 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00005020 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
5021 /*
5022 * Special tests checking #FIXED namespace declarations
5023 * have the right value since this is not done as an
5024 * attribute checking
5025 */
5026 if ((attr->prefix == NULL) &&
5027 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5028 xmlNsPtr ns;
5029
5030 ns = elem->nsDef;
5031 while (ns != NULL) {
5032 if (ns->prefix == NULL) {
5033 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005034 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005035 VERROR(ctxt->userData,
5036 "Element %s namespace name for default namespace does not match the DTD\n",
5037 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005038 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005039 }
5040 goto found;
5041 }
5042 ns = ns->next;
5043 }
5044 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5045 xmlNsPtr ns;
5046
5047 ns = elem->nsDef;
5048 while (ns != NULL) {
5049 if (xmlStrEqual(attr->name, ns->prefix)) {
5050 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005051 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005052 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005053 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005054 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005055 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005056 }
5057 goto found;
5058 }
5059 ns = ns->next;
5060 }
5061 }
Owen Taylor3473f882001-02-23 17:55:21 +00005062 }
5063found:
5064 attr = attr->nexth;
5065 }
5066 return(ret);
5067}
5068
5069/**
5070 * xmlValidateRoot:
5071 * @ctxt: the validation context
5072 * @doc: a document instance
5073 *
5074 * Try to validate a the root element
5075 * basically it does the following check as described by the
5076 * XML-1.0 recommendation:
5077 * - [ VC: Root Element Type ]
5078 * it doesn't try to recurse or apply other check to the element
5079 *
5080 * returns 1 if valid or 0 otherwise
5081 */
5082
5083int
5084xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5085 xmlNodePtr root;
5086 if (doc == NULL) return(0);
5087
5088 root = xmlDocGetRootElement(doc);
5089 if ((root == NULL) || (root->name == NULL)) {
5090 VERROR(ctxt->userData, "Not valid: no root element\n");
5091 return(0);
5092 }
5093
5094 /*
5095 * When doing post validation against a separate DTD, those may
5096 * no internal subset has been generated
5097 */
5098 if ((doc->intSubset != NULL) &&
5099 (doc->intSubset->name != NULL)) {
5100 /*
5101 * Check first the document root against the NQName
5102 */
5103 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5104 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
5105 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005106 snprintf((char *) qname, sizeof(qname), "%s:%s",
5107 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005108 qname[sizeof(qname) - 1] = 0;
5109 if (xmlStrEqual(doc->intSubset->name, qname))
5110 goto name_ok;
5111 }
5112 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5113 (xmlStrEqual(root->name, BAD_CAST "html")))
5114 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005115 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005116 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005117 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005118 root->name, doc->intSubset->name);
5119 return(0);
5120
5121 }
5122 }
5123name_ok:
5124 return(1);
5125}
5126
5127
5128/**
5129 * xmlValidateElement:
5130 * @ctxt: the validation context
5131 * @doc: a document instance
5132 * @elem: an element instance
5133 *
5134 * Try to validate the subtree under an element
5135 *
5136 * returns 1 if valid or 0 otherwise
5137 */
5138
5139int
5140xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5141 xmlNodePtr child;
5142 xmlAttrPtr attr;
5143 xmlChar *value;
5144 int ret = 1;
5145
5146 if (elem == NULL) return(0);
5147
5148 /*
5149 * XInclude elements were added after parsing in the infoset,
5150 * they don't really mean anything validation wise.
5151 */
5152 if ((elem->type == XML_XINCLUDE_START) ||
5153 (elem->type == XML_XINCLUDE_END))
5154 return(1);
5155
5156 CHECK_DTD;
5157
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005158 /*
5159 * Entities references have to be handled separately
5160 */
5161 if (elem->type == XML_ENTITY_REF_NODE) {
5162 return(1);
5163 }
5164
Owen Taylor3473f882001-02-23 17:55:21 +00005165 ret &= xmlValidateOneElement(ctxt, doc, elem);
5166 attr = elem->properties;
5167 while(attr != NULL) {
5168 value = xmlNodeListGetString(doc, attr->children, 0);
5169 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5170 if (value != NULL)
5171 xmlFree(value);
5172 attr= attr->next;
5173 }
5174 child = elem->children;
5175 while (child != NULL) {
5176 ret &= xmlValidateElement(ctxt, doc, child);
5177 child = child->next;
5178 }
5179
5180 return(ret);
5181}
5182
Daniel Veillard8730c562001-02-26 10:49:57 +00005183/**
5184 * xmlValidateRef:
5185 * @ref: A reference to be validated
5186 * @ctxt: Validation context
5187 * @name: Name of ID we are searching for
5188 *
5189 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005190static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005191xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005192 const xmlChar *name) {
5193 xmlAttrPtr id;
5194 xmlAttrPtr attr;
5195
5196 if (ref == NULL)
5197 return;
5198 attr = ref->attr;
5199 if (attr == NULL)
5200 return;
5201 if (attr->atype == XML_ATTRIBUTE_IDREF) {
5202 id = xmlGetID(ctxt->doc, name);
5203 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005204 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005205 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005206 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005207 attr->name, name);
5208 ctxt->valid = 0;
5209 }
5210 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5211 xmlChar *dup, *str = NULL, *cur, save;
5212
5213 dup = xmlStrdup(name);
5214 if (dup == NULL) {
5215 ctxt->valid = 0;
5216 return;
5217 }
5218 cur = dup;
5219 while (*cur != 0) {
5220 str = cur;
5221 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5222 save = *cur;
5223 *cur = 0;
5224 id = xmlGetID(ctxt->doc, str);
5225 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005226 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005227 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005228 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005229 attr->name, str);
5230 ctxt->valid = 0;
5231 }
5232 if (save == 0)
5233 break;
5234 *cur = save;
5235 while (IS_BLANK(*cur)) cur++;
5236 }
5237 xmlFree(dup);
5238 }
5239}
5240
5241/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005242 * xmlWalkValidateList:
5243 * @data: Contents of current link
5244 * @user: Value supplied by the user
5245 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005246 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005247 */
5248static int
5249xmlWalkValidateList(const void *data, const void *user)
5250{
5251 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5252 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5253 return 1;
5254}
5255
5256/**
5257 * xmlValidateCheckRefCallback:
5258 * @ref_list: List of references
5259 * @ctxt: Validation context
5260 * @name: Name of ID we are searching for
5261 *
5262 */
5263static void
5264xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5265 const xmlChar *name) {
5266 xmlValidateMemo memo;
5267
5268 if (ref_list == NULL)
5269 return;
5270 memo.ctxt = ctxt;
5271 memo.name = name;
5272
5273 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5274
5275}
5276
5277/**
Owen Taylor3473f882001-02-23 17:55:21 +00005278 * xmlValidateDocumentFinal:
5279 * @ctxt: the validation context
5280 * @doc: a document instance
5281 *
5282 * Does the final step for the document validation once all the
5283 * incremental validation steps have been completed
5284 *
5285 * basically it does the following checks described by the XML Rec
5286 *
5287 *
5288 * returns 1 if valid or 0 otherwise
5289 */
5290
5291int
5292xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5293 xmlRefTablePtr table;
5294
5295 if (doc == NULL) {
5296 xmlGenericError(xmlGenericErrorContext,
5297 "xmlValidateDocumentFinal: doc == NULL\n");
5298 return(0);
5299 }
5300
5301 /*
5302 * Check all the NOTATION/NOTATIONS attributes
5303 */
5304 /*
5305 * Check all the ENTITY/ENTITIES attributes definition for validity
5306 */
5307 /*
5308 * Check all the IDREF/IDREFS attributes definition for validity
5309 */
5310 table = (xmlRefTablePtr) doc->refs;
5311 ctxt->doc = doc;
5312 ctxt->valid = 1;
5313 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
5314 return(ctxt->valid);
5315}
5316
5317/**
5318 * xmlValidateDtd:
5319 * @ctxt: the validation context
5320 * @doc: a document instance
5321 * @dtd: a dtd instance
5322 *
5323 * Try to validate the document against the dtd instance
5324 *
5325 * basically it does check all the definitions in the DtD.
5326 *
5327 * returns 1 if valid or 0 otherwise
5328 */
5329
5330int
5331xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
5332 int ret;
5333 xmlDtdPtr oldExt;
5334 xmlNodePtr root;
5335
5336 if (dtd == NULL) return(0);
5337 if (doc == NULL) return(0);
5338 oldExt = doc->extSubset;
5339 doc->extSubset = dtd;
5340 ret = xmlValidateRoot(ctxt, doc);
5341 if (ret == 0) {
5342 doc->extSubset = oldExt;
5343 return(ret);
5344 }
5345 if (doc->ids != NULL) {
5346 xmlFreeIDTable(doc->ids);
5347 doc->ids = NULL;
5348 }
5349 if (doc->refs != NULL) {
5350 xmlFreeRefTable(doc->refs);
5351 doc->refs = NULL;
5352 }
5353 root = xmlDocGetRootElement(doc);
5354 ret = xmlValidateElement(ctxt, doc, root);
5355 ret &= xmlValidateDocumentFinal(ctxt, doc);
5356 doc->extSubset = oldExt;
5357 return(ret);
5358}
5359
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005360static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005361xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
5362 const xmlChar *name ATTRIBUTE_UNUSED) {
5363 if (cur == NULL)
5364 return;
5365 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
5366 xmlChar *notation = cur->content;
5367
Daniel Veillard878eab02002-02-19 13:46:09 +00005368 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005369 int ret;
5370
5371 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
5372 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00005373 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005374 }
5375 }
5376 }
5377}
5378
5379static void
Owen Taylor3473f882001-02-23 17:55:21 +00005380xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00005381 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005382 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00005383 xmlDocPtr doc;
5384 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005385
Owen Taylor3473f882001-02-23 17:55:21 +00005386 if (cur == NULL)
5387 return;
5388 switch (cur->atype) {
5389 case XML_ATTRIBUTE_CDATA:
5390 case XML_ATTRIBUTE_ID:
5391 case XML_ATTRIBUTE_IDREF :
5392 case XML_ATTRIBUTE_IDREFS:
5393 case XML_ATTRIBUTE_NMTOKEN:
5394 case XML_ATTRIBUTE_NMTOKENS:
5395 case XML_ATTRIBUTE_ENUMERATION:
5396 break;
5397 case XML_ATTRIBUTE_ENTITY:
5398 case XML_ATTRIBUTE_ENTITIES:
5399 case XML_ATTRIBUTE_NOTATION:
5400 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005401
5402 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
5403 cur->atype, cur->defaultValue);
5404 if ((ret == 0) && (ctxt->valid == 1))
5405 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005406 }
5407 if (cur->tree != NULL) {
5408 xmlEnumerationPtr tree = cur->tree;
5409 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005410 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00005411 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005412 if ((ret == 0) && (ctxt->valid == 1))
5413 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005414 tree = tree->next;
5415 }
5416 }
5417 }
Daniel Veillard878eab02002-02-19 13:46:09 +00005418 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
5419 doc = cur->doc;
5420 if ((doc == NULL) || (cur->elem == NULL)) {
5421 VERROR(ctxt->userData,
5422 "xmlValidateAttributeCallback(%s): internal error\n",
5423 cur->name);
5424 return;
5425 }
5426 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
5427 if (elem == NULL)
5428 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
5429 if (elem == NULL) {
5430 VERROR(ctxt->userData,
5431 "attribute %s: could not find decl for element %s\n",
5432 cur->name, cur->elem);
5433 return;
5434 }
5435 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
5436 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005437 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00005438 cur->name, cur->elem);
5439 ctxt->valid = 0;
5440 }
5441 }
Owen Taylor3473f882001-02-23 17:55:21 +00005442}
5443
5444/**
5445 * xmlValidateDtdFinal:
5446 * @ctxt: the validation context
5447 * @doc: a document instance
5448 *
5449 * Does the final step for the dtds validation once all the
5450 * subsets have been parsed
5451 *
5452 * basically it does the following checks described by the XML Rec
5453 * - check that ENTITY and ENTITIES type attributes default or
5454 * possible values matches one of the defined entities.
5455 * - check that NOTATION type attributes default or
5456 * possible values matches one of the defined notations.
5457 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005458 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00005459 */
5460
5461int
5462xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00005463 xmlDtdPtr dtd;
5464 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005465 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00005466
5467 if (doc == NULL) return(0);
5468 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5469 return(0);
5470 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005471 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005472 dtd = doc->intSubset;
5473 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5474 table = (xmlAttributeTablePtr) dtd->attributes;
5475 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005476 }
5477 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005478 entities = (xmlEntitiesTablePtr) dtd->entities;
5479 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5480 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005481 }
5482 dtd = doc->extSubset;
5483 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5484 table = (xmlAttributeTablePtr) dtd->attributes;
5485 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005486 }
5487 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005488 entities = (xmlEntitiesTablePtr) dtd->entities;
5489 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5490 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005491 }
5492 return(ctxt->valid);
5493}
5494
5495/**
5496 * xmlValidateDocument:
5497 * @ctxt: the validation context
5498 * @doc: a document instance
5499 *
5500 * Try to validate the document instance
5501 *
5502 * basically it does the all the checks described by the XML Rec
5503 * i.e. validates the internal and external subset (if present)
5504 * and validate the document tree.
5505 *
5506 * returns 1 if valid or 0 otherwise
5507 */
5508
5509int
5510xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5511 int ret;
5512 xmlNodePtr root;
5513
5514 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5515 return(0);
5516 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
5517 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
5518 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
5519 doc->intSubset->SystemID);
5520 if (doc->extSubset == NULL) {
5521 if (doc->intSubset->SystemID != NULL) {
5522 VERROR(ctxt->userData,
5523 "Could not load the external subset \"%s\"\n",
5524 doc->intSubset->SystemID);
5525 } else {
5526 VERROR(ctxt->userData,
5527 "Could not load the external subset \"%s\"\n",
5528 doc->intSubset->ExternalID);
5529 }
5530 return(0);
5531 }
5532 }
5533
5534 if (doc->ids != NULL) {
5535 xmlFreeIDTable(doc->ids);
5536 doc->ids = NULL;
5537 }
5538 if (doc->refs != NULL) {
5539 xmlFreeRefTable(doc->refs);
5540 doc->refs = NULL;
5541 }
5542 ret = xmlValidateDtdFinal(ctxt, doc);
5543 if (!xmlValidateRoot(ctxt, doc)) return(0);
5544
5545 root = xmlDocGetRootElement(doc);
5546 ret &= xmlValidateElement(ctxt, doc, root);
5547 ret &= xmlValidateDocumentFinal(ctxt, doc);
5548 return(ret);
5549}
5550
5551
5552/************************************************************************
5553 * *
5554 * Routines for dynamic validation editing *
5555 * *
5556 ************************************************************************/
5557
5558/**
5559 * xmlValidGetPotentialChildren:
5560 * @ctree: an element content tree
5561 * @list: an array to store the list of child names
5562 * @len: a pointer to the number of element in the list
5563 * @max: the size of the array
5564 *
5565 * Build/extend a list of potential children allowed by the content tree
5566 *
5567 * returns the number of element in the list, or -1 in case of error.
5568 */
5569
5570int
5571xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
5572 int *len, int max) {
5573 int i;
5574
5575 if ((ctree == NULL) || (list == NULL) || (len == NULL))
5576 return(-1);
5577 if (*len >= max) return(*len);
5578
5579 switch (ctree->type) {
5580 case XML_ELEMENT_CONTENT_PCDATA:
5581 for (i = 0; i < *len;i++)
5582 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
5583 list[(*len)++] = BAD_CAST "#PCDATA";
5584 break;
5585 case XML_ELEMENT_CONTENT_ELEMENT:
5586 for (i = 0; i < *len;i++)
5587 if (xmlStrEqual(ctree->name, list[i])) return(*len);
5588 list[(*len)++] = ctree->name;
5589 break;
5590 case XML_ELEMENT_CONTENT_SEQ:
5591 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5592 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5593 break;
5594 case XML_ELEMENT_CONTENT_OR:
5595 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5596 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5597 break;
5598 }
5599
5600 return(*len);
5601}
5602
5603/**
5604 * xmlValidGetValidElements:
5605 * @prev: an element to insert after
5606 * @next: an element to insert next
5607 * @list: an array to store the list of child names
5608 * @max: the size of the array
5609 *
5610 * This function returns the list of authorized children to insert
5611 * within an existing tree while respecting the validity constraints
5612 * forced by the Dtd. The insertion point is defined using @prev and
5613 * @next in the following ways:
5614 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
5615 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
5616 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
5617 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
5618 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
5619 *
5620 * pointers to the element names are inserted at the beginning of the array
5621 * and do not need to be freed.
5622 *
5623 * returns the number of element in the list, or -1 in case of error. If
5624 * the function returns the value @max the caller is invited to grow the
5625 * receiving array and retry.
5626 */
5627
5628int
5629xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
5630 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005631 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005632 int nb_valid_elements = 0;
5633 const xmlChar *elements[256];
5634 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005635 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005636
5637 xmlNode *ref_node;
5638 xmlNode *parent;
5639 xmlNode *test_node;
5640
5641 xmlNode *prev_next;
5642 xmlNode *next_prev;
5643 xmlNode *parent_childs;
5644 xmlNode *parent_last;
5645
5646 xmlElement *element_desc;
5647
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00005648 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005649
Owen Taylor3473f882001-02-23 17:55:21 +00005650 if (prev == NULL && next == NULL)
5651 return(-1);
5652
5653 if (list == NULL) return(-1);
5654 if (max <= 0) return(-1);
5655
5656 nb_valid_elements = 0;
5657 ref_node = prev ? prev : next;
5658 parent = ref_node->parent;
5659
5660 /*
5661 * Retrieves the parent element declaration
5662 */
5663 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5664 parent->name);
5665 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5666 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5667 parent->name);
5668 if (element_desc == NULL) return(-1);
5669
5670 /*
5671 * Do a backup of the current tree structure
5672 */
5673 prev_next = prev ? prev->next : NULL;
5674 next_prev = next ? next->prev : NULL;
5675 parent_childs = parent->children;
5676 parent_last = parent->last;
5677
5678 /*
5679 * Creates a dummy node and insert it into the tree
5680 */
5681 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5682 test_node->doc = ref_node->doc;
5683 test_node->parent = parent;
5684 test_node->prev = prev;
5685 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005686 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005687
5688 if (prev) prev->next = test_node;
5689 else parent->children = test_node;
5690
5691 if (next) next->prev = test_node;
5692 else parent->last = test_node;
5693
5694 /*
5695 * Insert each potential child node and check if the parent is
5696 * still valid
5697 */
5698 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5699 elements, &nb_elements, 256);
5700
5701 for (i = 0;i < nb_elements;i++) {
5702 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005703 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005704 int j;
5705
5706 for (j = 0; j < nb_valid_elements;j++)
5707 if (xmlStrEqual(elements[i], list[j])) break;
5708 list[nb_valid_elements++] = elements[i];
5709 if (nb_valid_elements >= max) break;
5710 }
5711 }
5712
5713 /*
5714 * Restore the tree structure
5715 */
5716 if (prev) prev->next = prev_next;
5717 if (next) next->prev = next_prev;
5718 parent->children = parent_childs;
5719 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005720
5721 /*
5722 * Free up the dummy node
5723 */
5724 test_node->name = name;
5725 xmlFreeNode(test_node);
5726
Owen Taylor3473f882001-02-23 17:55:21 +00005727 return(nb_valid_elements);
5728}