blob: 03b97c4aa242143bea9702353276490dadad3c58 [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 */
29
Daniel Veillarda646cfd2002-09-17 21:50:03 +000030#define TODO \
31 xmlGenericError(xmlGenericErrorContext, \
32 "Unimplemented block at %s:%d\n", \
33 __FILE__, __LINE__);
Owen Taylor3473f882001-02-23 17:55:21 +000034/*
35 * Generic function for accessing stacks in the Validity Context
36 */
37
38#define PUSH_AND_POP(scope, type, name) \
39scope int name##VPush(xmlValidCtxtPtr ctxt, type value) { \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000040 if (ctxt->name##Max <= 0) { \
41 ctxt->name##Max = 4; \
42 ctxt->name##Tab = (type *) xmlMalloc( \
43 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
44 if (ctxt->name##Tab == NULL) { \
45 xmlGenericError(xmlGenericErrorContext, \
46 "malloc failed !\n"); \
Daniel Veillarda9142e72001-06-19 11:07:54 +000047 ctxt->name##Max = 0; \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000048 return(0); \
49 } \
50 } \
Owen Taylor3473f882001-02-23 17:55:21 +000051 if (ctxt->name##Nr >= ctxt->name##Max) { \
52 ctxt->name##Max *= 2; \
53 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
54 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
55 if (ctxt->name##Tab == NULL) { \
56 xmlGenericError(xmlGenericErrorContext, \
57 "realloc failed !\n"); \
58 return(0); \
59 } \
60 } \
61 ctxt->name##Tab[ctxt->name##Nr] = value; \
62 ctxt->name = value; \
63 return(ctxt->name##Nr++); \
64} \
65scope type name##VPop(xmlValidCtxtPtr ctxt) { \
66 type ret; \
67 if (ctxt->name##Nr <= 0) return(0); \
68 ctxt->name##Nr--; \
69 if (ctxt->name##Nr > 0) \
70 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
71 else \
72 ctxt->name = NULL; \
73 ret = ctxt->name##Tab[ctxt->name##Nr]; \
74 ctxt->name##Tab[ctxt->name##Nr] = 0; \
75 return(ret); \
76} \
77
Daniel Veillarddab4cb32001-04-20 13:03:48 +000078/*
Daniel Veillardb44025c2001-10-11 22:55:55 +000079 * I use a home made algorithm less complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +000080 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +000081 * only restriction is on the deepness of the tree limited by the
82 * size of the occurs bitfield
83 *
84 * this is the content of a saved state for rollbacks
85 */
86
87#define ROLLBACK_OR 0
88#define ROLLBACK_PARENT 1
89
Daniel Veillardb44025c2001-10-11 22:55:55 +000090typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +000091 xmlElementContentPtr cont; /* pointer to the content model subtree */
92 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +000093 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +000094 unsigned char depth; /* current depth in the overall tree */
95 unsigned char state; /* ROLLBACK_XXX */
96} _xmlValidState;
97
Daniel Veillardfc57b412002-04-29 15:50:14 +000098#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +000099#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
100#define CONT ctxt->vstate->cont
101#define NODE ctxt->vstate->node
102#define DEPTH ctxt->vstate->depth
103#define OCCURS ctxt->vstate->occurs
104#define STATE ctxt->vstate->state
105
Daniel Veillard5344c602001-12-31 16:37:34 +0000106#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
107#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000108
Daniel Veillard5344c602001-12-31 16:37:34 +0000109#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
110#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000111
112static int
113vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
114 xmlNodePtr node, unsigned char depth, long occurs,
115 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000116 int i = ctxt->vstateNr - 1;
117
Daniel Veillard940492d2002-04-15 10:15:25 +0000118 if (ctxt->vstateNr > MAX_RECURSE) {
119 return(-1);
120 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000121 if (ctxt->vstateNr >= ctxt->vstateMax) {
122 ctxt->vstateMax *= 2;
123 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
124 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
125 if (ctxt->vstateTab == NULL) {
126 xmlGenericError(xmlGenericErrorContext,
127 "realloc failed !n");
Daniel Veillard940492d2002-04-15 10:15:25 +0000128 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000129 }
Daniel Veillard06803992001-04-22 10:35:56 +0000130 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000131 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000132 /*
133 * Don't push on the stack a state already here
134 */
135 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
136 (ctxt->vstateTab[i].node == node) &&
137 (ctxt->vstateTab[i].depth == depth) &&
138 (ctxt->vstateTab[i].occurs == occurs) &&
139 (ctxt->vstateTab[i].state == state))
140 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000141 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
142 ctxt->vstateTab[ctxt->vstateNr].node = node;
143 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
144 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
145 ctxt->vstateTab[ctxt->vstateNr].state = state;
146 return(ctxt->vstateNr++);
147}
148
149static int
150vstateVPop(xmlValidCtxtPtr ctxt) {
151 if (ctxt->vstateNr <= 1) return(-1);
152 ctxt->vstateNr--;
153 ctxt->vstate = &ctxt->vstateTab[0];
154 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
155 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
156 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
157 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
158 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
159 return(ctxt->vstateNr);
160}
161
Owen Taylor3473f882001-02-23 17:55:21 +0000162PUSH_AND_POP(static, xmlNodePtr, node)
163
Owen Taylor3473f882001-02-23 17:55:21 +0000164#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000165static void
166xmlValidPrintNode(xmlNodePtr cur) {
167 if (cur == NULL) {
168 xmlGenericError(xmlGenericErrorContext, "null");
169 return;
170 }
171 switch (cur->type) {
172 case XML_ELEMENT_NODE:
173 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
174 break;
175 case XML_TEXT_NODE:
176 xmlGenericError(xmlGenericErrorContext, "text ");
177 break;
178 case XML_CDATA_SECTION_NODE:
179 xmlGenericError(xmlGenericErrorContext, "cdata ");
180 break;
181 case XML_ENTITY_REF_NODE:
182 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
183 break;
184 case XML_PI_NODE:
185 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
186 break;
187 case XML_COMMENT_NODE:
188 xmlGenericError(xmlGenericErrorContext, "comment ");
189 break;
190 case XML_ATTRIBUTE_NODE:
191 xmlGenericError(xmlGenericErrorContext, "?attr? ");
192 break;
193 case XML_ENTITY_NODE:
194 xmlGenericError(xmlGenericErrorContext, "?ent? ");
195 break;
196 case XML_DOCUMENT_NODE:
197 xmlGenericError(xmlGenericErrorContext, "?doc? ");
198 break;
199 case XML_DOCUMENT_TYPE_NODE:
200 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
201 break;
202 case XML_DOCUMENT_FRAG_NODE:
203 xmlGenericError(xmlGenericErrorContext, "?frag? ");
204 break;
205 case XML_NOTATION_NODE:
206 xmlGenericError(xmlGenericErrorContext, "?nota? ");
207 break;
208 case XML_HTML_DOCUMENT_NODE:
209 xmlGenericError(xmlGenericErrorContext, "?html? ");
210 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000211#ifdef LIBXML_DOCB_ENABLED
212 case XML_DOCB_DOCUMENT_NODE:
213 xmlGenericError(xmlGenericErrorContext, "?docb? ");
214 break;
215#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000216 case XML_DTD_NODE:
217 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
218 break;
219 case XML_ELEMENT_DECL:
220 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
221 break;
222 case XML_ATTRIBUTE_DECL:
223 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
224 break;
225 case XML_ENTITY_DECL:
226 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
227 break;
228 case XML_NAMESPACE_DECL:
229 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
230 break;
231 case XML_XINCLUDE_START:
232 xmlGenericError(xmlGenericErrorContext, "incstart ");
233 break;
234 case XML_XINCLUDE_END:
235 xmlGenericError(xmlGenericErrorContext, "incend ");
236 break;
237 }
238}
239
240static void
241xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000242 if (cur == NULL)
243 xmlGenericError(xmlGenericErrorContext, "null ");
244 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000245 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000246 cur = cur->next;
247 }
248}
249
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000250static void
251xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000252 char expr[1000];
253
254 expr[0] = 0;
255 xmlGenericError(xmlGenericErrorContext, "valid: ");
256 xmlValidPrintNodeList(cur);
257 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000258 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000259 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
260}
261
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000262static void
263xmlValidDebugState(xmlValidStatePtr state) {
264 xmlGenericError(xmlGenericErrorContext, "(");
265 if (state->cont == NULL)
266 xmlGenericError(xmlGenericErrorContext, "null,");
267 else
268 switch (state->cont->type) {
269 case XML_ELEMENT_CONTENT_PCDATA:
270 xmlGenericError(xmlGenericErrorContext, "pcdata,");
271 break;
272 case XML_ELEMENT_CONTENT_ELEMENT:
273 xmlGenericError(xmlGenericErrorContext, "%s,",
274 state->cont->name);
275 break;
276 case XML_ELEMENT_CONTENT_SEQ:
277 xmlGenericError(xmlGenericErrorContext, "seq,");
278 break;
279 case XML_ELEMENT_CONTENT_OR:
280 xmlGenericError(xmlGenericErrorContext, "or,");
281 break;
282 }
283 xmlValidPrintNode(state->node);
284 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
285 state->depth, state->occurs, state->state);
286}
287
288static void
289xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
290 int i, j;
291
292 xmlGenericError(xmlGenericErrorContext, "state: ");
293 xmlValidDebugState(ctxt->vstate);
294 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
295 ctxt->vstateNr - 1);
296 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
297 xmlValidDebugState(&ctxt->vstateTab[j]);
298 xmlGenericError(xmlGenericErrorContext, "\n");
299}
300
301/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000302#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000303 *****/
304
305#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000306#define DEBUG_VALID_MSG(m) \
307 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
308
Owen Taylor3473f882001-02-23 17:55:21 +0000309#else
310#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000311#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000312#endif
313
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000314/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000315
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000316#define VECTXT(ctxt, node) \
317 if ((ctxt != NULL) && (ctxt->error != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000318 (node != NULL)) { \
319 xmlChar *base = xmlNodeGetBase(NULL,node); \
320 if (base != NULL) { \
321 ctxt->error(ctxt->userData, "%s:%d: ", base, \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000322 (int) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000323 xmlFree(base); \
324 } else \
325 ctxt->error(ctxt->userData, ":%d: ", \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000326 (int) node->content); \
327 }
328
329#define VWCTXT(ctxt, node) \
330 if ((ctxt != NULL) && (ctxt->warning != NULL) && \
Daniel Veillard76575762002-09-05 14:21:15 +0000331 (node != NULL)) { \
332 xmlChar *base = xmlNodeGetBase(NULL,node); \
333 if (base != NULL) { \
334 ctxt->warning(ctxt->userData, "%s:%d: ", base, \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000335 (int) node->content); \
Daniel Veillard76575762002-09-05 14:21:15 +0000336 xmlFree(base); \
337 } else \
338 ctxt->warning(ctxt->userData, ":%d: ", \
Daniel Veillardb9cd8b42002-09-05 10:58:49 +0000339 (int) node->content); \
340 }
341
Owen Taylor3473f882001-02-23 17:55:21 +0000342#define VERROR \
343 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
344
345#define VWARNING \
346 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
347
348#define CHECK_DTD \
349 if (doc == NULL) return(0); \
350 else if ((doc->intSubset == NULL) && \
351 (doc->extSubset == NULL)) return(0)
352
Daniel Veillarda10efa82001-04-18 13:09:01 +0000353static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
354 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000355xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
356
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000357#ifdef LIBXML_REGEXP_ENABLED
358
359/************************************************************************
360 * *
361 * Content model validation based on the regexps *
362 * *
363 ************************************************************************/
364
365/**
366 * xmlValidBuildAContentModel:
367 * @content: the content model
368 * @ctxt: the schema parser context
369 * @name: the element name whose content is being built
370 *
371 * Generate the automata sequence needed for that type
372 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000373 * Returns 1 if successful or 0 in case of error.
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000374 */
375static int
376xmlValidBuildAContentModel(xmlElementContentPtr content,
377 xmlValidCtxtPtr ctxt,
378 const xmlChar *name) {
379 if (content == NULL) {
380 VERROR(ctxt->userData,
381 "Found unexpected type = NULL in %s content model\n", name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000382 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000383 }
384 switch (content->type) {
385 case XML_ELEMENT_CONTENT_PCDATA:
386 VERROR(ctxt->userData, "ContentModel found PCDATA for element %s\n",
387 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000388 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000389 break;
390 case XML_ELEMENT_CONTENT_ELEMENT: {
391 xmlAutomataStatePtr oldstate = ctxt->state;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000392 xmlChar *QName = NULL;
393 const xmlChar *fname = content->name;
394
395 if (content->prefix != NULL) {
396 int len;
397
398 len = xmlStrlen(content->name) +
399 xmlStrlen(content->prefix) + 2;
400 QName = xmlMalloc(len);
401 if (QName == NULL) {
402 VERROR(ctxt->userData,
403 "ContentModel %s : alloc failed\n", name);
404 return(0);
405 }
406 snprintf((char *) QName, len, "%s:%s",
407 (char *)content->prefix,
408 (char *)content->name);
409 fname = QName;
410 }
411
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000412 switch (content->ocur) {
413 case XML_ELEMENT_CONTENT_ONCE:
414 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000415 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000416 break;
417 case XML_ELEMENT_CONTENT_OPT:
418 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000419 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000420 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
421 break;
422 case XML_ELEMENT_CONTENT_PLUS:
423 ctxt->state = xmlAutomataNewTransition(ctxt->am,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000424 ctxt->state, NULL, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000425 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000426 ctxt->state, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000427 break;
428 case XML_ELEMENT_CONTENT_MULT:
429 xmlAutomataNewTransition(ctxt->am, ctxt->state,
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000430 ctxt->state, fname, NULL);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000431 break;
432 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000433 if (QName != NULL)
434 xmlFree(QName);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000435 break;
436 }
437 case XML_ELEMENT_CONTENT_SEQ: {
438 xmlAutomataStatePtr oldstate;
439 xmlElementContentOccur ocur;
440
441 /*
442 * Simply iterate over the content
443 */
444 oldstate = ctxt->state;
445 ocur = content->ocur;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000446 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000447 xmlValidBuildAContentModel(content->c1, ctxt, name);
448 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000449 } while ((content->type == XML_ELEMENT_CONTENT_SEQ) &&
450 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
451 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000452 switch (ocur) {
453 case XML_ELEMENT_CONTENT_ONCE:
454 break;
455 case XML_ELEMENT_CONTENT_OPT:
456 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
457 break;
458 case XML_ELEMENT_CONTENT_MULT:
459 xmlAutomataNewEpsilon(ctxt->am, oldstate, ctxt->state);
460 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldstate);
461 break;
462 case XML_ELEMENT_CONTENT_PLUS:
463 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldstate);
464 break;
465 }
466 break;
467 }
468 case XML_ELEMENT_CONTENT_OR: {
469 xmlAutomataStatePtr start, end;
470 xmlElementContentOccur ocur;
471
472 start = ctxt->state;
473 end = xmlAutomataNewState(ctxt->am);
474 ocur = content->ocur;
475
476 /*
477 * iterate over the subtypes and remerge the end with an
478 * epsilon transition
479 */
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000480 do {
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000481 ctxt->state = start;
482 xmlValidBuildAContentModel(content->c1, ctxt, name);
483 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, end);
484 content = content->c2;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000485 } while ((content->type == XML_ELEMENT_CONTENT_OR) &&
486 (content->ocur == XML_ELEMENT_CONTENT_ONCE));
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000487 ctxt->state = start;
Daniel Veillarda646cfd2002-09-17 21:50:03 +0000488 xmlValidBuildAContentModel(content, ctxt, name);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000489 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, end);
490 ctxt->state = end;
491 switch (ocur) {
492 case XML_ELEMENT_CONTENT_ONCE:
493 break;
494 case XML_ELEMENT_CONTENT_OPT:
495 xmlAutomataNewEpsilon(ctxt->am, start, ctxt->state);
496 break;
497 case XML_ELEMENT_CONTENT_MULT:
498 xmlAutomataNewEpsilon(ctxt->am, start, ctxt->state);
499 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, start);
500 break;
501 case XML_ELEMENT_CONTENT_PLUS:
502 xmlAutomataNewEpsilon(ctxt->am, ctxt->state, start);
503 break;
504 }
505 break;
506 }
507 default:
508 VERROR(ctxt->userData, "ContentModel broken for element %s\n",
509 name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000510 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000511 }
Daniel Veillard84d70a42002-09-16 10:51:38 +0000512 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000513}
514/**
515 * xmlValidBuildContentModel:
516 * @ctxt: a validation context
517 * @elem: an element declaration node
518 *
519 * (Re)Build the automata associated to the content model of this
520 * element
521 *
Daniel Veillard84d70a42002-09-16 10:51:38 +0000522 * Returns 1 in case of success, 0 in case of error
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000523 */
524int
525xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
526 xmlAutomataStatePtr start;
527
528 if ((ctxt == NULL) || (elem == NULL))
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000529 return(0);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000530 if (elem->type != XML_ELEMENT_DECL)
531 return(0);
532 if (elem->etype != XML_ELEMENT_TYPE_ELEMENT)
533 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000534 /* TODO: should we rebuild in this case ? */
535 if (elem->contModel != NULL)
Daniel Veillard84d70a42002-09-16 10:51:38 +0000536 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000537
538 ctxt->am = xmlNewAutomata();
539 if (ctxt->am == NULL) {
540 VERROR(ctxt->userData, "Cannot create automata for element %s\n",
541 elem->name);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000542 return(0);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000543 }
544 start = ctxt->state = xmlAutomataGetInitState(ctxt->am);
545 xmlValidBuildAContentModel(elem->content, ctxt, elem->name);
546 xmlAutomataSetFinalState(ctxt->am, ctxt->state);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000547 elem->contModel = xmlAutomataCompile(ctxt->am);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000548 if (!xmlAutomataIsDeterminist(ctxt->am)) {
549 VERROR(ctxt->userData, "Content model of %s is not determinist:\n",
550 elem->name);
551 ctxt->valid = 0;
552 }
553 ctxt->state = NULL;
554 xmlFreeAutomata(ctxt->am);
555 ctxt->am = NULL;
Daniel Veillard84d70a42002-09-16 10:51:38 +0000556 return(1);
Daniel Veillardaeb258a2002-09-13 14:48:12 +0000557}
558
559#endif /* LIBXML_REGEXP_ENABLED */
560
Owen Taylor3473f882001-02-23 17:55:21 +0000561/************************************************************************
562 * *
563 * QName handling helper *
564 * *
565 ************************************************************************/
566
567/**
568 * xmlSplitQName2:
569 * @name: an XML parser context
570 * @prefix: a xmlChar **
571 *
572 * parse an XML qualified name string
573 *
574 * [NS 5] QName ::= (Prefix ':')? LocalPart
575 *
576 * [NS 6] Prefix ::= NCName
577 *
578 * [NS 7] LocalPart ::= NCName
579 *
580 * Returns NULL if not a QName, otherwise the local part, and prefix
581 * is updated to get the Prefix if any.
582 */
583
584xmlChar *
585xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
586 int len = 0;
587 xmlChar *ret = NULL;
588
589 *prefix = NULL;
590
Daniel Veillardf4309d72001-10-02 09:28:58 +0000591#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000592 /* xml: prefix is not really a namespace */
593 if ((name[0] == 'x') && (name[1] == 'm') &&
594 (name[2] == 'l') && (name[3] == ':'))
595 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000596#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000597
598 /* nasty but valid */
599 if (name[0] == ':')
600 return(NULL);
601
602 /*
603 * we are not trying to validate but just to cut, and yes it will
604 * work even if this is as set of UTF-8 encoded chars
605 */
606 while ((name[len] != 0) && (name[len] != ':'))
607 len++;
608
609 if (name[len] == 0)
610 return(NULL);
611
612 *prefix = xmlStrndup(name, len);
613 ret = xmlStrdup(&name[len + 1]);
614
615 return(ret);
616}
617
618/****************************************************************
619 * *
620 * Util functions for data allocation/deallocation *
621 * *
622 ****************************************************************/
623
624/**
625 * xmlNewElementContent:
626 * @name: the subelement name or NULL
627 * @type: the type of element content decl
628 *
629 * Allocate an element content structure.
630 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000631 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000632 */
633xmlElementContentPtr
634xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
635 xmlElementContentPtr ret;
636
637 switch(type) {
638 case XML_ELEMENT_CONTENT_ELEMENT:
639 if (name == NULL) {
640 xmlGenericError(xmlGenericErrorContext,
641 "xmlNewElementContent : name == NULL !\n");
642 }
643 break;
644 case XML_ELEMENT_CONTENT_PCDATA:
645 case XML_ELEMENT_CONTENT_SEQ:
646 case XML_ELEMENT_CONTENT_OR:
647 if (name != NULL) {
648 xmlGenericError(xmlGenericErrorContext,
649 "xmlNewElementContent : name != NULL !\n");
650 }
651 break;
652 default:
653 xmlGenericError(xmlGenericErrorContext,
654 "xmlNewElementContent: unknown type %d\n", type);
655 return(NULL);
656 }
657 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
658 if (ret == NULL) {
659 xmlGenericError(xmlGenericErrorContext,
660 "xmlNewElementContent : out of memory!\n");
661 return(NULL);
662 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000663 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000664 ret->type = type;
665 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000666 if (name != NULL) {
667 xmlChar *prefix = NULL;
668 ret->name = xmlSplitQName2(name, &prefix);
669 if (ret->name == NULL)
670 ret->name = xmlStrdup(name);
671 ret->prefix = prefix;
672 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000673 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000674 ret->prefix = NULL;
675 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000676 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000677 return(ret);
678}
679
680/**
681 * xmlCopyElementContent:
682 * @content: An element content pointer.
683 *
684 * Build a copy of an element content description.
685 *
686 * Returns the new xmlElementContentPtr or NULL in case of error.
687 */
688xmlElementContentPtr
689xmlCopyElementContent(xmlElementContentPtr cur) {
690 xmlElementContentPtr ret;
691
692 if (cur == NULL) return(NULL);
693 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
694 if (ret == NULL) {
695 xmlGenericError(xmlGenericErrorContext,
696 "xmlCopyElementContent : out of memory\n");
697 return(NULL);
698 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000699 if (cur->prefix != NULL)
700 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000701 ret->ocur = cur->ocur;
702 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000703 if (ret->c1 != NULL)
704 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000705 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000706 if (ret->c2 != NULL)
707 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000708 return(ret);
709}
710
711/**
712 * xmlFreeElementContent:
713 * @cur: the element content tree to free
714 *
715 * Free an element content structure. This is a recursive call !
716 */
717void
718xmlFreeElementContent(xmlElementContentPtr cur) {
719 if (cur == NULL) return;
720 switch (cur->type) {
721 case XML_ELEMENT_CONTENT_PCDATA:
722 case XML_ELEMENT_CONTENT_ELEMENT:
723 case XML_ELEMENT_CONTENT_SEQ:
724 case XML_ELEMENT_CONTENT_OR:
725 break;
726 default:
727 xmlGenericError(xmlGenericErrorContext,
728 "xmlFreeElementContent : type %d\n", cur->type);
729 return;
730 }
731 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
732 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
733 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000734 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000735 xmlFree(cur);
736}
737
738/**
739 * xmlDumpElementContent:
740 * @buf: An XML buffer
741 * @content: An element table
742 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
743 *
744 * This will dump the content of the element table as an XML DTD definition
745 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000746static void
Owen Taylor3473f882001-02-23 17:55:21 +0000747xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
748 if (content == NULL) return;
749
750 if (glob) xmlBufferWriteChar(buf, "(");
751 switch (content->type) {
752 case XML_ELEMENT_CONTENT_PCDATA:
753 xmlBufferWriteChar(buf, "#PCDATA");
754 break;
755 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000756 if (content->prefix != NULL) {
757 xmlBufferWriteCHAR(buf, content->prefix);
758 xmlBufferWriteChar(buf, ":");
759 }
Owen Taylor3473f882001-02-23 17:55:21 +0000760 xmlBufferWriteCHAR(buf, content->name);
761 break;
762 case XML_ELEMENT_CONTENT_SEQ:
763 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
764 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
765 xmlDumpElementContent(buf, content->c1, 1);
766 else
767 xmlDumpElementContent(buf, content->c1, 0);
768 xmlBufferWriteChar(buf, " , ");
769 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
770 xmlDumpElementContent(buf, content->c2, 1);
771 else
772 xmlDumpElementContent(buf, content->c2, 0);
773 break;
774 case XML_ELEMENT_CONTENT_OR:
775 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
776 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
777 xmlDumpElementContent(buf, content->c1, 1);
778 else
779 xmlDumpElementContent(buf, content->c1, 0);
780 xmlBufferWriteChar(buf, " | ");
781 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
782 xmlDumpElementContent(buf, content->c2, 1);
783 else
784 xmlDumpElementContent(buf, content->c2, 0);
785 break;
786 default:
787 xmlGenericError(xmlGenericErrorContext,
788 "xmlDumpElementContent: unknown type %d\n",
789 content->type);
790 }
791 if (glob)
792 xmlBufferWriteChar(buf, ")");
793 switch (content->ocur) {
794 case XML_ELEMENT_CONTENT_ONCE:
795 break;
796 case XML_ELEMENT_CONTENT_OPT:
797 xmlBufferWriteChar(buf, "?");
798 break;
799 case XML_ELEMENT_CONTENT_MULT:
800 xmlBufferWriteChar(buf, "*");
801 break;
802 case XML_ELEMENT_CONTENT_PLUS:
803 xmlBufferWriteChar(buf, "+");
804 break;
805 }
806}
807
808/**
809 * xmlSprintfElementContent:
810 * @buf: an output buffer
811 * @content: An element table
812 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
813 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000814 * Deprecated, unsafe, use xmlSnprintfElementContent
815 */
816void
817xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
818 xmlElementContentPtr content ATTRIBUTE_UNUSED,
819 int glob ATTRIBUTE_UNUSED) {
820}
821
822/**
823 * xmlSnprintfElementContent:
824 * @buf: an output buffer
825 * @size: the buffer size
826 * @content: An element table
827 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
828 *
Owen Taylor3473f882001-02-23 17:55:21 +0000829 * This will dump the content of the element content definition
830 * Intended just for the debug routine
831 */
832void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000833xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
834 int len;
835
Owen Taylor3473f882001-02-23 17:55:21 +0000836 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000837 len = strlen(buf);
838 if (size - len < 50) {
839 if ((size - len > 4) && (buf[len - 1] != '.'))
840 strcat(buf, " ...");
841 return;
842 }
Owen Taylor3473f882001-02-23 17:55:21 +0000843 if (glob) strcat(buf, "(");
844 switch (content->type) {
845 case XML_ELEMENT_CONTENT_PCDATA:
846 strcat(buf, "#PCDATA");
847 break;
848 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000849 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000850 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000851 strcat(buf, " ...");
852 return;
853 }
854 strcat(buf, (char *) content->prefix);
855 strcat(buf, ":");
856 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000857 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000858 strcat(buf, " ...");
859 return;
860 }
Owen Taylor3473f882001-02-23 17:55:21 +0000861 strcat(buf, (char *) content->name);
862 break;
863 case XML_ELEMENT_CONTENT_SEQ:
864 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
865 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000866 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000867 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000868 xmlSnprintfElementContent(buf, size, content->c1, 0);
869 len = strlen(buf);
870 if (size - len < 50) {
871 if ((size - len > 4) && (buf[len - 1] != '.'))
872 strcat(buf, " ...");
873 return;
874 }
Owen Taylor3473f882001-02-23 17:55:21 +0000875 strcat(buf, " , ");
876 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000877 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000878 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000879 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000880 break;
881 case XML_ELEMENT_CONTENT_OR:
882 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
883 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000884 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000885 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000886 xmlSnprintfElementContent(buf, size, content->c1, 0);
887 len = strlen(buf);
888 if (size - len < 50) {
889 if ((size - len > 4) && (buf[len - 1] != '.'))
890 strcat(buf, " ...");
891 return;
892 }
Owen Taylor3473f882001-02-23 17:55:21 +0000893 strcat(buf, " | ");
894 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000895 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000896 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000897 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000898 break;
899 }
900 if (glob)
901 strcat(buf, ")");
902 switch (content->ocur) {
903 case XML_ELEMENT_CONTENT_ONCE:
904 break;
905 case XML_ELEMENT_CONTENT_OPT:
906 strcat(buf, "?");
907 break;
908 case XML_ELEMENT_CONTENT_MULT:
909 strcat(buf, "*");
910 break;
911 case XML_ELEMENT_CONTENT_PLUS:
912 strcat(buf, "+");
913 break;
914 }
915}
916
917/****************************************************************
918 * *
919 * Registration of DTD declarations *
920 * *
921 ****************************************************************/
922
923/**
924 * xmlCreateElementTable:
925 *
926 * create and initialize an empty element hash table.
927 *
928 * Returns the xmlElementTablePtr just created or NULL in case of error.
929 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000930static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000931xmlCreateElementTable(void) {
932 return(xmlHashCreate(0));
933}
934
935/**
936 * xmlFreeElement:
937 * @elem: An element
938 *
939 * Deallocate the memory used by an element definition
940 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000941static void
Owen Taylor3473f882001-02-23 17:55:21 +0000942xmlFreeElement(xmlElementPtr elem) {
943 if (elem == NULL) return;
944 xmlUnlinkNode((xmlNodePtr) elem);
945 xmlFreeElementContent(elem->content);
946 if (elem->name != NULL)
947 xmlFree((xmlChar *) elem->name);
948 if (elem->prefix != NULL)
949 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard84d70a42002-09-16 10:51:38 +0000950#ifdef LIBXML_REGEXP_ENABLED
951 if (elem->contModel != NULL)
952 xmlRegFreeRegexp(elem->contModel);
953#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000954 xmlFree(elem);
955}
956
957
958/**
959 * xmlAddElementDecl:
960 * @ctxt: the validation context
961 * @dtd: pointer to the DTD
962 * @name: the entity name
963 * @type: the element type
964 * @content: the element content tree or NULL
965 *
966 * Register a new element declaration
967 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000968 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +0000969 */
970xmlElementPtr
971xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
972 xmlElementTypeVal type,
973 xmlElementContentPtr content) {
974 xmlElementPtr ret;
975 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000976 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000977 xmlChar *ns, *uqname;
978
979 if (dtd == NULL) {
980 xmlGenericError(xmlGenericErrorContext,
981 "xmlAddElementDecl: dtd == NULL\n");
982 return(NULL);
983 }
984 if (name == NULL) {
985 xmlGenericError(xmlGenericErrorContext,
986 "xmlAddElementDecl: name == NULL\n");
987 return(NULL);
988 }
989 switch (type) {
990 case XML_ELEMENT_TYPE_EMPTY:
991 if (content != NULL) {
992 xmlGenericError(xmlGenericErrorContext,
993 "xmlAddElementDecl: content != NULL for EMPTY\n");
994 return(NULL);
995 }
996 break;
997 case XML_ELEMENT_TYPE_ANY:
998 if (content != NULL) {
999 xmlGenericError(xmlGenericErrorContext,
1000 "xmlAddElementDecl: content != NULL for ANY\n");
1001 return(NULL);
1002 }
1003 break;
1004 case XML_ELEMENT_TYPE_MIXED:
1005 if (content == NULL) {
1006 xmlGenericError(xmlGenericErrorContext,
1007 "xmlAddElementDecl: content == NULL for MIXED\n");
1008 return(NULL);
1009 }
1010 break;
1011 case XML_ELEMENT_TYPE_ELEMENT:
1012 if (content == NULL) {
1013 xmlGenericError(xmlGenericErrorContext,
1014 "xmlAddElementDecl: content == NULL for ELEMENT\n");
1015 return(NULL);
1016 }
1017 break;
1018 default:
1019 xmlGenericError(xmlGenericErrorContext,
1020 "xmlAddElementDecl: unknown type %d\n", type);
1021 return(NULL);
1022 }
1023
1024 /*
1025 * check if name is a QName
1026 */
1027 uqname = xmlSplitQName2(name, &ns);
1028 if (uqname != NULL)
1029 name = uqname;
1030
1031 /*
1032 * Create the Element table if needed.
1033 */
1034 table = (xmlElementTablePtr) dtd->elements;
1035 if (table == NULL) {
1036 table = xmlCreateElementTable();
1037 dtd->elements = (void *) table;
1038 }
1039 if (table == NULL) {
1040 xmlGenericError(xmlGenericErrorContext,
1041 "xmlAddElementDecl: Table creation failed!\n");
1042 return(NULL);
1043 }
1044
Daniel Veillarda10efa82001-04-18 13:09:01 +00001045 /*
1046 * lookup old attributes inserted on an undefined element in the
1047 * internal subset.
1048 */
1049 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
1050 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
1051 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
1052 oldAttributes = ret->attributes;
1053 ret->attributes = NULL;
1054 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
1055 xmlFreeElement(ret);
1056 }
Owen Taylor3473f882001-02-23 17:55:21 +00001057 }
Owen Taylor3473f882001-02-23 17:55:21 +00001058
1059 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +00001060 * The element may already be present if one of its attribute
1061 * was registered first
1062 */
1063 ret = xmlHashLookup2(table, name, ns);
1064 if (ret != NULL) {
1065 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
1066 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001067 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001068 */
1069 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1070 if (uqname != NULL)
1071 xmlFree(uqname);
1072 return(NULL);
1073 }
1074 } else {
1075 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1076 if (ret == NULL) {
1077 xmlGenericError(xmlGenericErrorContext,
1078 "xmlAddElementDecl: out of memory\n");
1079 return(NULL);
1080 }
1081 memset(ret, 0, sizeof(xmlElement));
1082 ret->type = XML_ELEMENT_DECL;
1083
1084 /*
1085 * fill the structure.
1086 */
1087 ret->name = xmlStrdup(name);
1088 ret->prefix = ns;
1089
1090 /*
1091 * Validity Check:
1092 * Insertion must not fail
1093 */
1094 if (xmlHashAddEntry2(table, name, ns, ret)) {
1095 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001096 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +00001097 */
1098 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
1099 xmlFreeElement(ret);
1100 if (uqname != NULL)
1101 xmlFree(uqname);
1102 return(NULL);
1103 }
1104 }
1105
1106 /*
1107 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +00001108 */
1109 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +00001110 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +00001111 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +00001112
1113 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001114 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001115 */
1116 ret->parent = dtd;
1117 ret->doc = dtd->doc;
1118 if (dtd->last == NULL) {
1119 dtd->children = dtd->last = (xmlNodePtr) ret;
1120 } else {
1121 dtd->last->next = (xmlNodePtr) ret;
1122 ret->prev = dtd->last;
1123 dtd->last = (xmlNodePtr) ret;
1124 }
1125 if (uqname != NULL)
1126 xmlFree(uqname);
1127 return(ret);
1128}
1129
1130/**
1131 * xmlFreeElementTable:
1132 * @table: An element table
1133 *
1134 * Deallocate the memory used by an element hash table.
1135 */
1136void
1137xmlFreeElementTable(xmlElementTablePtr table) {
1138 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
1139}
1140
1141/**
1142 * xmlCopyElement:
1143 * @elem: An element
1144 *
1145 * Build a copy of an element.
1146 *
1147 * Returns the new xmlElementPtr or NULL in case of error.
1148 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001149static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001150xmlCopyElement(xmlElementPtr elem) {
1151 xmlElementPtr cur;
1152
1153 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
1154 if (cur == NULL) {
1155 xmlGenericError(xmlGenericErrorContext,
1156 "xmlCopyElement: out of memory !\n");
1157 return(NULL);
1158 }
1159 memset(cur, 0, sizeof(xmlElement));
1160 cur->type = XML_ELEMENT_DECL;
1161 cur->etype = elem->etype;
1162 if (elem->name != NULL)
1163 cur->name = xmlStrdup(elem->name);
1164 else
1165 cur->name = NULL;
1166 if (elem->prefix != NULL)
1167 cur->prefix = xmlStrdup(elem->prefix);
1168 else
1169 cur->prefix = NULL;
1170 cur->content = xmlCopyElementContent(elem->content);
1171 /* TODO : rebuild the attribute list on the copy */
1172 cur->attributes = NULL;
1173 return(cur);
1174}
1175
1176/**
1177 * xmlCopyElementTable:
1178 * @table: An element table
1179 *
1180 * Build a copy of an element table.
1181 *
1182 * Returns the new xmlElementTablePtr or NULL in case of error.
1183 */
1184xmlElementTablePtr
1185xmlCopyElementTable(xmlElementTablePtr table) {
1186 return((xmlElementTablePtr) xmlHashCopy(table,
1187 (xmlHashCopier) xmlCopyElement));
1188}
1189
1190/**
1191 * xmlDumpElementDecl:
1192 * @buf: the XML buffer output
1193 * @elem: An element table
1194 *
1195 * This will dump the content of the element declaration as an XML
1196 * DTD definition
1197 */
1198void
1199xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
1200 switch (elem->etype) {
1201 case XML_ELEMENT_TYPE_EMPTY:
1202 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001203 if (elem->prefix != NULL) {
1204 xmlBufferWriteCHAR(buf, elem->prefix);
1205 xmlBufferWriteChar(buf, ":");
1206 }
Owen Taylor3473f882001-02-23 17:55:21 +00001207 xmlBufferWriteCHAR(buf, elem->name);
1208 xmlBufferWriteChar(buf, " EMPTY>\n");
1209 break;
1210 case XML_ELEMENT_TYPE_ANY:
1211 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001212 if (elem->prefix != NULL) {
1213 xmlBufferWriteCHAR(buf, elem->prefix);
1214 xmlBufferWriteChar(buf, ":");
1215 }
Owen Taylor3473f882001-02-23 17:55:21 +00001216 xmlBufferWriteCHAR(buf, elem->name);
1217 xmlBufferWriteChar(buf, " ANY>\n");
1218 break;
1219 case XML_ELEMENT_TYPE_MIXED:
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, " ");
1227 xmlDumpElementContent(buf, elem->content, 1);
1228 xmlBufferWriteChar(buf, ">\n");
1229 break;
1230 case XML_ELEMENT_TYPE_ELEMENT:
1231 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +00001232 if (elem->prefix != NULL) {
1233 xmlBufferWriteCHAR(buf, elem->prefix);
1234 xmlBufferWriteChar(buf, ":");
1235 }
Owen Taylor3473f882001-02-23 17:55:21 +00001236 xmlBufferWriteCHAR(buf, elem->name);
1237 xmlBufferWriteChar(buf, " ");
1238 xmlDumpElementContent(buf, elem->content, 1);
1239 xmlBufferWriteChar(buf, ">\n");
1240 break;
1241 default:
1242 xmlGenericError(xmlGenericErrorContext,
1243 "xmlDumpElementDecl: internal: unknown type %d\n",
1244 elem->etype);
1245 }
1246}
1247
1248/**
1249 * xmlDumpElementTable:
1250 * @buf: the XML buffer output
1251 * @table: An element table
1252 *
1253 * This will dump the content of the element table as an XML DTD definition
1254 */
1255void
1256xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1257 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1258}
1259
1260/**
1261 * xmlCreateEnumeration:
1262 * @name: the enumeration name or NULL
1263 *
1264 * create and initialize an enumeration attribute node.
1265 *
1266 * Returns the xmlEnumerationPtr just created or NULL in case
1267 * of error.
1268 */
1269xmlEnumerationPtr
1270xmlCreateEnumeration(xmlChar *name) {
1271 xmlEnumerationPtr ret;
1272
1273 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1274 if (ret == NULL) {
1275 xmlGenericError(xmlGenericErrorContext,
1276 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1277 (long)sizeof(xmlEnumeration));
1278 return(NULL);
1279 }
1280 memset(ret, 0, sizeof(xmlEnumeration));
1281
1282 if (name != NULL)
1283 ret->name = xmlStrdup(name);
1284 return(ret);
1285}
1286
1287/**
1288 * xmlFreeEnumeration:
1289 * @cur: the tree to free.
1290 *
1291 * free an enumeration attribute node (recursive).
1292 */
1293void
1294xmlFreeEnumeration(xmlEnumerationPtr cur) {
1295 if (cur == NULL) return;
1296
1297 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1298
1299 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001300 xmlFree(cur);
1301}
1302
1303/**
1304 * xmlCopyEnumeration:
1305 * @cur: the tree to copy.
1306 *
1307 * Copy an enumeration attribute node (recursive).
1308 *
1309 * Returns the xmlEnumerationPtr just created or NULL in case
1310 * of error.
1311 */
1312xmlEnumerationPtr
1313xmlCopyEnumeration(xmlEnumerationPtr cur) {
1314 xmlEnumerationPtr ret;
1315
1316 if (cur == NULL) return(NULL);
1317 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1318
1319 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1320 else ret->next = NULL;
1321
1322 return(ret);
1323}
1324
1325/**
1326 * xmlDumpEnumeration:
1327 * @buf: the XML buffer output
1328 * @enum: An enumeration
1329 *
1330 * This will dump the content of the enumeration
1331 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001332static void
Owen Taylor3473f882001-02-23 17:55:21 +00001333xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1334 if (cur == NULL) return;
1335
1336 xmlBufferWriteCHAR(buf, cur->name);
1337 if (cur->next == NULL)
1338 xmlBufferWriteChar(buf, ")");
1339 else {
1340 xmlBufferWriteChar(buf, " | ");
1341 xmlDumpEnumeration(buf, cur->next);
1342 }
1343}
1344
1345/**
1346 * xmlCreateAttributeTable:
1347 *
1348 * create and initialize an empty attribute hash table.
1349 *
1350 * Returns the xmlAttributeTablePtr just created or NULL in case
1351 * of error.
1352 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001353static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001354xmlCreateAttributeTable(void) {
1355 return(xmlHashCreate(0));
1356}
1357
1358/**
1359 * xmlScanAttributeDeclCallback:
1360 * @attr: the attribute decl
1361 * @list: the list to update
1362 *
1363 * Callback called by xmlScanAttributeDecl when a new attribute
1364 * has to be entered in the list.
1365 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001366static void
Owen Taylor3473f882001-02-23 17:55:21 +00001367xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001368 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001369 attr->nexth = *list;
1370 *list = attr;
1371}
1372
1373/**
1374 * xmlScanAttributeDecl:
1375 * @dtd: pointer to the DTD
1376 * @elem: the element name
1377 *
1378 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001379 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001380 *
1381 * Returns the pointer to the first attribute decl in the chain,
1382 * possibly NULL.
1383 */
1384xmlAttributePtr
1385xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1386 xmlAttributePtr ret = NULL;
1387 xmlAttributeTablePtr table;
1388
1389 if (dtd == NULL) {
1390 xmlGenericError(xmlGenericErrorContext,
1391 "xmlScanAttributeDecl: dtd == NULL\n");
1392 return(NULL);
1393 }
1394 if (elem == NULL) {
1395 xmlGenericError(xmlGenericErrorContext,
1396 "xmlScanAttributeDecl: elem == NULL\n");
1397 return(NULL);
1398 }
1399 table = (xmlAttributeTablePtr) dtd->attributes;
1400 if (table == NULL)
1401 return(NULL);
1402
1403 /* WRONG !!! */
1404 xmlHashScan3(table, NULL, NULL, elem,
1405 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1406 return(ret);
1407}
1408
1409/**
1410 * xmlScanIDAttributeDecl:
1411 * @ctxt: the validation context
1412 * @elem: the element name
1413 *
1414 * Verify that the element don't have too many ID attributes
1415 * declared.
1416 *
1417 * Returns the number of ID attributes found.
1418 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001419static int
Owen Taylor3473f882001-02-23 17:55:21 +00001420xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1421 xmlAttributePtr cur;
1422 int ret = 0;
1423
1424 if (elem == NULL) return(0);
1425 cur = elem->attributes;
1426 while (cur != NULL) {
1427 if (cur->atype == XML_ATTRIBUTE_ID) {
1428 ret ++;
1429 if (ret > 1)
1430 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001431 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001432 elem->name, cur->name);
1433 }
1434 cur = cur->nexth;
1435 }
1436 return(ret);
1437}
1438
1439/**
1440 * xmlFreeAttribute:
1441 * @elem: An attribute
1442 *
1443 * Deallocate the memory used by an attribute definition
1444 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001445static void
Owen Taylor3473f882001-02-23 17:55:21 +00001446xmlFreeAttribute(xmlAttributePtr attr) {
1447 if (attr == NULL) return;
1448 xmlUnlinkNode((xmlNodePtr) attr);
1449 if (attr->tree != NULL)
1450 xmlFreeEnumeration(attr->tree);
1451 if (attr->elem != NULL)
1452 xmlFree((xmlChar *) attr->elem);
1453 if (attr->name != NULL)
1454 xmlFree((xmlChar *) attr->name);
1455 if (attr->defaultValue != NULL)
1456 xmlFree((xmlChar *) attr->defaultValue);
1457 if (attr->prefix != NULL)
1458 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001459 xmlFree(attr);
1460}
1461
1462
1463/**
1464 * xmlAddAttributeDecl:
1465 * @ctxt: the validation context
1466 * @dtd: pointer to the DTD
1467 * @elem: the element name
1468 * @name: the attribute name
1469 * @ns: the attribute namespace prefix
1470 * @type: the attribute type
1471 * @def: the attribute default type
1472 * @defaultValue: the attribute default value
1473 * @tree: if it's an enumeration, the associated list
1474 *
1475 * Register a new attribute declaration
1476 * Note that @tree becomes the ownership of the DTD
1477 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001478 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001479 */
1480xmlAttributePtr
1481xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1482 const xmlChar *name, const xmlChar *ns,
1483 xmlAttributeType type, xmlAttributeDefault def,
1484 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1485 xmlAttributePtr ret;
1486 xmlAttributeTablePtr table;
1487 xmlElementPtr elemDef;
1488
1489 if (dtd == NULL) {
1490 xmlGenericError(xmlGenericErrorContext,
1491 "xmlAddAttributeDecl: dtd == NULL\n");
1492 xmlFreeEnumeration(tree);
1493 return(NULL);
1494 }
1495 if (name == NULL) {
1496 xmlGenericError(xmlGenericErrorContext,
1497 "xmlAddAttributeDecl: name == NULL\n");
1498 xmlFreeEnumeration(tree);
1499 return(NULL);
1500 }
1501 if (elem == NULL) {
1502 xmlGenericError(xmlGenericErrorContext,
1503 "xmlAddAttributeDecl: elem == NULL\n");
1504 xmlFreeEnumeration(tree);
1505 return(NULL);
1506 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001507
Owen Taylor3473f882001-02-23 17:55:21 +00001508 /*
1509 * Check the type and possibly the default value.
1510 */
1511 switch (type) {
1512 case XML_ATTRIBUTE_CDATA:
1513 break;
1514 case XML_ATTRIBUTE_ID:
1515 break;
1516 case XML_ATTRIBUTE_IDREF:
1517 break;
1518 case XML_ATTRIBUTE_IDREFS:
1519 break;
1520 case XML_ATTRIBUTE_ENTITY:
1521 break;
1522 case XML_ATTRIBUTE_ENTITIES:
1523 break;
1524 case XML_ATTRIBUTE_NMTOKEN:
1525 break;
1526 case XML_ATTRIBUTE_NMTOKENS:
1527 break;
1528 case XML_ATTRIBUTE_ENUMERATION:
1529 break;
1530 case XML_ATTRIBUTE_NOTATION:
1531 break;
1532 default:
1533 xmlGenericError(xmlGenericErrorContext,
1534 "xmlAddAttributeDecl: unknown type %d\n", type);
1535 xmlFreeEnumeration(tree);
1536 return(NULL);
1537 }
1538 if ((defaultValue != NULL) &&
1539 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001540 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001541 elem, name, defaultValue);
1542 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001543 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001544 }
1545
1546 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001547 * Check first that an attribute defined in the external subset wasn't
1548 * already defined in the internal subset
1549 */
1550 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1551 (dtd->doc->intSubset != NULL) &&
1552 (dtd->doc->intSubset->attributes != NULL)) {
1553 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1554 if (ret != NULL)
1555 return(NULL);
1556 }
1557
1558 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001559 * Create the Attribute table if needed.
1560 */
1561 table = (xmlAttributeTablePtr) dtd->attributes;
1562 if (table == NULL) {
1563 table = xmlCreateAttributeTable();
1564 dtd->attributes = (void *) table;
1565 }
1566 if (table == NULL) {
1567 xmlGenericError(xmlGenericErrorContext,
1568 "xmlAddAttributeDecl: Table creation failed!\n");
1569 return(NULL);
1570 }
1571
1572
1573 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1574 if (ret == NULL) {
1575 xmlGenericError(xmlGenericErrorContext,
1576 "xmlAddAttributeDecl: out of memory\n");
1577 return(NULL);
1578 }
1579 memset(ret, 0, sizeof(xmlAttribute));
1580 ret->type = XML_ATTRIBUTE_DECL;
1581
1582 /*
1583 * fill the structure.
1584 */
1585 ret->atype = type;
1586 ret->name = xmlStrdup(name);
1587 ret->prefix = xmlStrdup(ns);
1588 ret->elem = xmlStrdup(elem);
1589 ret->def = def;
1590 ret->tree = tree;
1591 if (defaultValue != NULL)
1592 ret->defaultValue = xmlStrdup(defaultValue);
1593
1594 /*
1595 * Validity Check:
1596 * Search the DTD for previous declarations of the ATTLIST
1597 */
1598 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1599 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001600 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001601 */
1602 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001603 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001604 name, elem);
1605 xmlFreeAttribute(ret);
1606 return(NULL);
1607 }
1608
1609 /*
1610 * Validity Check:
1611 * Multiple ID per element
1612 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001613 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001614 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001615
Owen Taylor3473f882001-02-23 17:55:21 +00001616 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001617 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001618 VERROR(ctxt->userData,
1619 "Element %s has too may ID attributes defined : %s\n",
1620 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001621 ctxt->valid = 0;
1622 }
1623
Daniel Veillard48da9102001-08-07 01:10:10 +00001624 /*
1625 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001626 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001627 */
1628 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1629 ((ret->prefix != NULL &&
1630 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1631 ret->nexth = elemDef->attributes;
1632 elemDef->attributes = ret;
1633 } else {
1634 xmlAttributePtr tmp = elemDef->attributes;
1635
1636 while ((tmp != NULL) &&
1637 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1638 ((ret->prefix != NULL &&
1639 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1640 if (tmp->nexth == NULL)
1641 break;
1642 tmp = tmp->nexth;
1643 }
1644 if (tmp != NULL) {
1645 ret->nexth = tmp->nexth;
1646 tmp->nexth = ret;
1647 } else {
1648 ret->nexth = elemDef->attributes;
1649 elemDef->attributes = ret;
1650 }
1651 }
Owen Taylor3473f882001-02-23 17:55:21 +00001652 }
1653
1654 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001655 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001656 */
1657 ret->parent = dtd;
1658 ret->doc = dtd->doc;
1659 if (dtd->last == NULL) {
1660 dtd->children = dtd->last = (xmlNodePtr) ret;
1661 } else {
1662 dtd->last->next = (xmlNodePtr) ret;
1663 ret->prev = dtd->last;
1664 dtd->last = (xmlNodePtr) ret;
1665 }
1666 return(ret);
1667}
1668
1669/**
1670 * xmlFreeAttributeTable:
1671 * @table: An attribute table
1672 *
1673 * Deallocate the memory used by an entities hash table.
1674 */
1675void
1676xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1677 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1678}
1679
1680/**
1681 * xmlCopyAttribute:
1682 * @attr: An attribute
1683 *
1684 * Build a copy of an attribute.
1685 *
1686 * Returns the new xmlAttributePtr or NULL in case of error.
1687 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001688static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001689xmlCopyAttribute(xmlAttributePtr attr) {
1690 xmlAttributePtr cur;
1691
1692 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1693 if (cur == NULL) {
1694 xmlGenericError(xmlGenericErrorContext,
1695 "xmlCopyAttribute: out of memory !\n");
1696 return(NULL);
1697 }
1698 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001699 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001700 cur->atype = attr->atype;
1701 cur->def = attr->def;
1702 cur->tree = xmlCopyEnumeration(attr->tree);
1703 if (attr->elem != NULL)
1704 cur->elem = xmlStrdup(attr->elem);
1705 if (attr->name != NULL)
1706 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001707 if (attr->prefix != NULL)
1708 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001709 if (attr->defaultValue != NULL)
1710 cur->defaultValue = xmlStrdup(attr->defaultValue);
1711 return(cur);
1712}
1713
1714/**
1715 * xmlCopyAttributeTable:
1716 * @table: An attribute table
1717 *
1718 * Build a copy of an attribute table.
1719 *
1720 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1721 */
1722xmlAttributeTablePtr
1723xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1724 return((xmlAttributeTablePtr) xmlHashCopy(table,
1725 (xmlHashCopier) xmlCopyAttribute));
1726}
1727
1728/**
1729 * xmlDumpAttributeDecl:
1730 * @buf: the XML buffer output
1731 * @attr: An attribute declaration
1732 *
1733 * This will dump the content of the attribute declaration as an XML
1734 * DTD definition
1735 */
1736void
1737xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1738 xmlBufferWriteChar(buf, "<!ATTLIST ");
1739 xmlBufferWriteCHAR(buf, attr->elem);
1740 xmlBufferWriteChar(buf, " ");
1741 if (attr->prefix != NULL) {
1742 xmlBufferWriteCHAR(buf, attr->prefix);
1743 xmlBufferWriteChar(buf, ":");
1744 }
1745 xmlBufferWriteCHAR(buf, attr->name);
1746 switch (attr->atype) {
1747 case XML_ATTRIBUTE_CDATA:
1748 xmlBufferWriteChar(buf, " CDATA");
1749 break;
1750 case XML_ATTRIBUTE_ID:
1751 xmlBufferWriteChar(buf, " ID");
1752 break;
1753 case XML_ATTRIBUTE_IDREF:
1754 xmlBufferWriteChar(buf, " IDREF");
1755 break;
1756 case XML_ATTRIBUTE_IDREFS:
1757 xmlBufferWriteChar(buf, " IDREFS");
1758 break;
1759 case XML_ATTRIBUTE_ENTITY:
1760 xmlBufferWriteChar(buf, " ENTITY");
1761 break;
1762 case XML_ATTRIBUTE_ENTITIES:
1763 xmlBufferWriteChar(buf, " ENTITIES");
1764 break;
1765 case XML_ATTRIBUTE_NMTOKEN:
1766 xmlBufferWriteChar(buf, " NMTOKEN");
1767 break;
1768 case XML_ATTRIBUTE_NMTOKENS:
1769 xmlBufferWriteChar(buf, " NMTOKENS");
1770 break;
1771 case XML_ATTRIBUTE_ENUMERATION:
1772 xmlBufferWriteChar(buf, " (");
1773 xmlDumpEnumeration(buf, attr->tree);
1774 break;
1775 case XML_ATTRIBUTE_NOTATION:
1776 xmlBufferWriteChar(buf, " NOTATION (");
1777 xmlDumpEnumeration(buf, attr->tree);
1778 break;
1779 default:
1780 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001781 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001782 attr->atype);
1783 }
1784 switch (attr->def) {
1785 case XML_ATTRIBUTE_NONE:
1786 break;
1787 case XML_ATTRIBUTE_REQUIRED:
1788 xmlBufferWriteChar(buf, " #REQUIRED");
1789 break;
1790 case XML_ATTRIBUTE_IMPLIED:
1791 xmlBufferWriteChar(buf, " #IMPLIED");
1792 break;
1793 case XML_ATTRIBUTE_FIXED:
1794 xmlBufferWriteChar(buf, " #FIXED");
1795 break;
1796 default:
1797 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001798 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001799 attr->def);
1800 }
1801 if (attr->defaultValue != NULL) {
1802 xmlBufferWriteChar(buf, " ");
1803 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1804 }
1805 xmlBufferWriteChar(buf, ">\n");
1806}
1807
1808/**
1809 * xmlDumpAttributeTable:
1810 * @buf: the XML buffer output
1811 * @table: An attribute table
1812 *
1813 * This will dump the content of the attribute table as an XML DTD definition
1814 */
1815void
1816xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1817 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1818}
1819
1820/************************************************************************
1821 * *
1822 * NOTATIONs *
1823 * *
1824 ************************************************************************/
1825/**
1826 * xmlCreateNotationTable:
1827 *
1828 * create and initialize an empty notation hash table.
1829 *
1830 * Returns the xmlNotationTablePtr just created or NULL in case
1831 * of error.
1832 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001833static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001834xmlCreateNotationTable(void) {
1835 return(xmlHashCreate(0));
1836}
1837
1838/**
1839 * xmlFreeNotation:
1840 * @not: A notation
1841 *
1842 * Deallocate the memory used by an notation definition
1843 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001844static void
Owen Taylor3473f882001-02-23 17:55:21 +00001845xmlFreeNotation(xmlNotationPtr nota) {
1846 if (nota == NULL) return;
1847 if (nota->name != NULL)
1848 xmlFree((xmlChar *) nota->name);
1849 if (nota->PublicID != NULL)
1850 xmlFree((xmlChar *) nota->PublicID);
1851 if (nota->SystemID != NULL)
1852 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001853 xmlFree(nota);
1854}
1855
1856
1857/**
1858 * xmlAddNotationDecl:
1859 * @dtd: pointer to the DTD
1860 * @ctxt: the validation context
1861 * @name: the entity name
1862 * @PublicID: the public identifier or NULL
1863 * @SystemID: the system identifier or NULL
1864 *
1865 * Register a new notation declaration
1866 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001867 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001868 */
1869xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001870xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001871 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001872 const xmlChar *PublicID, const xmlChar *SystemID) {
1873 xmlNotationPtr ret;
1874 xmlNotationTablePtr table;
1875
1876 if (dtd == NULL) {
1877 xmlGenericError(xmlGenericErrorContext,
1878 "xmlAddNotationDecl: dtd == NULL\n");
1879 return(NULL);
1880 }
1881 if (name == NULL) {
1882 xmlGenericError(xmlGenericErrorContext,
1883 "xmlAddNotationDecl: name == NULL\n");
1884 return(NULL);
1885 }
1886 if ((PublicID == NULL) && (SystemID == NULL)) {
1887 xmlGenericError(xmlGenericErrorContext,
1888 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001889 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001890 }
1891
1892 /*
1893 * Create the Notation table if needed.
1894 */
1895 table = (xmlNotationTablePtr) dtd->notations;
1896 if (table == NULL)
1897 dtd->notations = table = xmlCreateNotationTable();
1898 if (table == NULL) {
1899 xmlGenericError(xmlGenericErrorContext,
1900 "xmlAddNotationDecl: Table creation failed!\n");
1901 return(NULL);
1902 }
1903
1904 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1905 if (ret == NULL) {
1906 xmlGenericError(xmlGenericErrorContext,
1907 "xmlAddNotationDecl: out of memory\n");
1908 return(NULL);
1909 }
1910 memset(ret, 0, sizeof(xmlNotation));
1911
1912 /*
1913 * fill the structure.
1914 */
1915 ret->name = xmlStrdup(name);
1916 if (SystemID != NULL)
1917 ret->SystemID = xmlStrdup(SystemID);
1918 if (PublicID != NULL)
1919 ret->PublicID = xmlStrdup(PublicID);
1920
1921 /*
1922 * Validity Check:
1923 * Check the DTD for previous declarations of the ATTLIST
1924 */
1925 if (xmlHashAddEntry(table, name, ret)) {
1926 xmlGenericError(xmlGenericErrorContext,
1927 "xmlAddNotationDecl: %s already defined\n", name);
1928 xmlFreeNotation(ret);
1929 return(NULL);
1930 }
1931 return(ret);
1932}
1933
1934/**
1935 * xmlFreeNotationTable:
1936 * @table: An notation table
1937 *
1938 * Deallocate the memory used by an entities hash table.
1939 */
1940void
1941xmlFreeNotationTable(xmlNotationTablePtr table) {
1942 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1943}
1944
1945/**
1946 * xmlCopyNotation:
1947 * @nota: A notation
1948 *
1949 * Build a copy of a notation.
1950 *
1951 * Returns the new xmlNotationPtr or NULL in case of error.
1952 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001953static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001954xmlCopyNotation(xmlNotationPtr nota) {
1955 xmlNotationPtr cur;
1956
1957 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1958 if (cur == NULL) {
1959 xmlGenericError(xmlGenericErrorContext,
1960 "xmlCopyNotation: out of memory !\n");
1961 return(NULL);
1962 }
1963 if (nota->name != NULL)
1964 cur->name = xmlStrdup(nota->name);
1965 else
1966 cur->name = NULL;
1967 if (nota->PublicID != NULL)
1968 cur->PublicID = xmlStrdup(nota->PublicID);
1969 else
1970 cur->PublicID = NULL;
1971 if (nota->SystemID != NULL)
1972 cur->SystemID = xmlStrdup(nota->SystemID);
1973 else
1974 cur->SystemID = NULL;
1975 return(cur);
1976}
1977
1978/**
1979 * xmlCopyNotationTable:
1980 * @table: A notation table
1981 *
1982 * Build a copy of a notation table.
1983 *
1984 * Returns the new xmlNotationTablePtr or NULL in case of error.
1985 */
1986xmlNotationTablePtr
1987xmlCopyNotationTable(xmlNotationTablePtr table) {
1988 return((xmlNotationTablePtr) xmlHashCopy(table,
1989 (xmlHashCopier) xmlCopyNotation));
1990}
1991
1992/**
1993 * xmlDumpNotationDecl:
1994 * @buf: the XML buffer output
1995 * @nota: A notation declaration
1996 *
1997 * This will dump the content the notation declaration as an XML DTD definition
1998 */
1999void
2000xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
2001 xmlBufferWriteChar(buf, "<!NOTATION ");
2002 xmlBufferWriteCHAR(buf, nota->name);
2003 if (nota->PublicID != NULL) {
2004 xmlBufferWriteChar(buf, " PUBLIC ");
2005 xmlBufferWriteQuotedString(buf, nota->PublicID);
2006 if (nota->SystemID != NULL) {
2007 xmlBufferWriteChar(buf, " ");
2008 xmlBufferWriteCHAR(buf, nota->SystemID);
2009 }
2010 } else {
2011 xmlBufferWriteChar(buf, " SYSTEM ");
2012 xmlBufferWriteCHAR(buf, nota->SystemID);
2013 }
2014 xmlBufferWriteChar(buf, " >\n");
2015}
2016
2017/**
2018 * xmlDumpNotationTable:
2019 * @buf: the XML buffer output
2020 * @table: A notation table
2021 *
2022 * This will dump the content of the notation table as an XML DTD definition
2023 */
2024void
2025xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
2026 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
2027}
2028
2029/************************************************************************
2030 * *
2031 * IDs *
2032 * *
2033 ************************************************************************/
2034/**
2035 * xmlCreateIDTable:
2036 *
2037 * create and initialize an empty id hash table.
2038 *
2039 * Returns the xmlIDTablePtr just created or NULL in case
2040 * of error.
2041 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002042static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002043xmlCreateIDTable(void) {
2044 return(xmlHashCreate(0));
2045}
2046
2047/**
2048 * xmlFreeID:
2049 * @not: A id
2050 *
2051 * Deallocate the memory used by an id definition
2052 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002053static void
Owen Taylor3473f882001-02-23 17:55:21 +00002054xmlFreeID(xmlIDPtr id) {
2055 if (id == NULL) return;
2056 if (id->value != NULL)
2057 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00002058 xmlFree(id);
2059}
2060
2061/**
2062 * xmlAddID:
2063 * @ctxt: the validation context
2064 * @doc: pointer to the document
2065 * @value: the value name
2066 * @attr: the attribute holding the ID
2067 *
2068 * Register a new id declaration
2069 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002070 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002071 */
2072xmlIDPtr
2073xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
2074 xmlAttrPtr attr) {
2075 xmlIDPtr ret;
2076 xmlIDTablePtr table;
2077
2078 if (doc == NULL) {
2079 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002080 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002081 return(NULL);
2082 }
2083 if (value == NULL) {
2084 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002085 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002086 return(NULL);
2087 }
2088 if (attr == NULL) {
2089 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002090 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00002091 return(NULL);
2092 }
2093
2094 /*
2095 * Create the ID table if needed.
2096 */
2097 table = (xmlIDTablePtr) doc->ids;
2098 if (table == NULL)
2099 doc->ids = table = xmlCreateIDTable();
2100 if (table == NULL) {
2101 xmlGenericError(xmlGenericErrorContext,
2102 "xmlAddID: Table creation failed!\n");
2103 return(NULL);
2104 }
2105
2106 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
2107 if (ret == NULL) {
2108 xmlGenericError(xmlGenericErrorContext,
2109 "xmlAddID: out of memory\n");
2110 return(NULL);
2111 }
2112
2113 /*
2114 * fill the structure.
2115 */
2116 ret->value = xmlStrdup(value);
2117 ret->attr = attr;
2118
2119 if (xmlHashAddEntry(table, value, ret) < 0) {
2120 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002121 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00002122 */
Daniel Veillard76575762002-09-05 14:21:15 +00002123 if (ctxt != NULL) {
2124 VECTXT(ctxt, attr->parent);
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00002125 VERROR(ctxt->userData, "ID %s already defined\n", value);
Daniel Veillard76575762002-09-05 14:21:15 +00002126 }
Owen Taylor3473f882001-02-23 17:55:21 +00002127 xmlFreeID(ret);
2128 return(NULL);
2129 }
2130 return(ret);
2131}
2132
2133/**
2134 * xmlFreeIDTable:
2135 * @table: An id table
2136 *
2137 * Deallocate the memory used by an ID hash table.
2138 */
2139void
2140xmlFreeIDTable(xmlIDTablePtr table) {
2141 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
2142}
2143
2144/**
2145 * xmlIsID:
2146 * @doc: the document
2147 * @elem: the element carrying the attribute
2148 * @attr: the attribute
2149 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002150 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002151 * then this is simple, otherwise we use an heuristic: name ID (upper
2152 * or lowercase).
2153 *
2154 * Returns 0 or 1 depending on the lookup result
2155 */
2156int
2157xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2158 if (doc == NULL) return(0);
2159 if (attr == NULL) return(0);
2160 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2161 return(0);
2162 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2163 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
2164 (xmlStrEqual(BAD_CAST "name", attr->name)))
2165 return(1);
2166 return(0);
2167 } else {
2168 xmlAttributePtr attrDecl;
2169
2170 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00002171 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2172 /*
2173 * TODO: this sucks ... recomputing this every time is stupid
2174 */
2175 int len = xmlStrlen(elem->name) + xmlStrlen(elem->ns->prefix) + 2;
2176 xmlChar *fullname;
2177
2178 fullname = xmlMalloc(len);
2179 if (fullname == NULL)
2180 return(0);
2181 snprintf((char *) fullname, len, "%s:%s", (char *) elem->ns->prefix,
2182 (char *) elem->name);
2183 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
2184 attr->name);
2185 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2186 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
2187 attr->name);
2188 xmlFree(fullname);
2189 } else {
2190 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
2191 attr->name);
2192 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2193 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2194 attr->name);
2195 }
Owen Taylor3473f882001-02-23 17:55:21 +00002196
2197 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
2198 return(1);
2199 }
2200 return(0);
2201}
2202
2203/**
2204 * xmlRemoveID
2205 * @doc: the document
2206 * @attr: the attribute
2207 *
2208 * Remove the given attribute from the ID table maintained internally.
2209 *
2210 * Returns -1 if the lookup failed and 0 otherwise
2211 */
2212int
2213xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
2214 xmlAttrPtr cur;
2215 xmlIDTablePtr table;
2216 xmlChar *ID;
2217
2218 if (doc == NULL) return(-1);
2219 if (attr == NULL) return(-1);
2220 table = (xmlIDTablePtr) doc->ids;
2221 if (table == NULL)
2222 return(-1);
2223
2224 if (attr == NULL)
2225 return(-1);
2226 ID = xmlNodeListGetString(doc, attr->children, 1);
2227 if (ID == NULL)
2228 return(-1);
2229 cur = xmlHashLookup(table, ID);
2230 if (cur != attr) {
2231 xmlFree(ID);
2232 return(-1);
2233 }
2234 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
2235 xmlFree(ID);
2236 return(0);
2237}
2238
2239/**
2240 * xmlGetID:
2241 * @doc: pointer to the document
2242 * @ID: the ID value
2243 *
2244 * Search the attribute declaring the given ID
2245 *
2246 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2247 */
2248xmlAttrPtr
2249xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2250 xmlIDTablePtr table;
2251 xmlIDPtr id;
2252
2253 if (doc == NULL) {
2254 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2255 return(NULL);
2256 }
2257
2258 if (ID == NULL) {
2259 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2260 return(NULL);
2261 }
2262
2263 table = (xmlIDTablePtr) doc->ids;
2264 if (table == NULL)
2265 return(NULL);
2266
2267 id = xmlHashLookup(table, ID);
2268 if (id == NULL)
2269 return(NULL);
2270 return(id->attr);
2271}
2272
2273/************************************************************************
2274 * *
2275 * Refs *
2276 * *
2277 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002278typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002279{
2280 xmlListPtr l;
2281 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002282} xmlRemoveMemo;
2283
2284typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2285
2286typedef struct xmlValidateMemo_t
2287{
2288 xmlValidCtxtPtr ctxt;
2289 const xmlChar *name;
2290} xmlValidateMemo;
2291
2292typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002293
2294/**
2295 * xmlCreateRefTable:
2296 *
2297 * create and initialize an empty ref hash table.
2298 *
2299 * Returns the xmlRefTablePtr just created or NULL in case
2300 * of error.
2301 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002302static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002303xmlCreateRefTable(void) {
2304 return(xmlHashCreate(0));
2305}
2306
2307/**
2308 * xmlFreeRef:
2309 * @lk: A list link
2310 *
2311 * Deallocate the memory used by a ref definition
2312 */
2313static void
2314xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002315 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2316 if (ref == NULL) return;
2317 if (ref->value != NULL)
2318 xmlFree((xmlChar *)ref->value);
2319 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002320}
2321
2322/**
2323 * xmlFreeRefList:
2324 * @list_ref: A list of references.
2325 *
2326 * Deallocate the memory used by a list of references
2327 */
2328static void
2329xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002330 if (list_ref == NULL) return;
2331 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002332}
2333
2334/**
2335 * xmlWalkRemoveRef:
2336 * @data: Contents of current link
2337 * @user: Value supplied by the user
2338 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002339 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002340 */
2341static int
2342xmlWalkRemoveRef(const void *data, const void *user)
2343{
Daniel Veillard37721922001-05-04 15:21:12 +00002344 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2345 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2346 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002347
Daniel Veillard37721922001-05-04 15:21:12 +00002348 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2349 xmlListRemoveFirst(ref_list, (void *)data);
2350 return 0;
2351 }
2352 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002353}
2354
2355/**
2356 * xmlAddRef:
2357 * @ctxt: the validation context
2358 * @doc: pointer to the document
2359 * @value: the value name
2360 * @attr: the attribute holding the Ref
2361 *
2362 * Register a new ref declaration
2363 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002364 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002365 */
2366xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002367xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002368 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002369 xmlRefPtr ret;
2370 xmlRefTablePtr table;
2371 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002372
Daniel Veillard37721922001-05-04 15:21:12 +00002373 if (doc == NULL) {
2374 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002375 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002376 return(NULL);
2377 }
2378 if (value == NULL) {
2379 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002380 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002381 return(NULL);
2382 }
2383 if (attr == NULL) {
2384 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002385 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002386 return(NULL);
2387 }
Owen Taylor3473f882001-02-23 17:55:21 +00002388
Daniel Veillard37721922001-05-04 15:21:12 +00002389 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002390 * Create the Ref table if needed.
2391 */
Daniel Veillard37721922001-05-04 15:21:12 +00002392 table = (xmlRefTablePtr) doc->refs;
2393 if (table == NULL)
2394 doc->refs = table = xmlCreateRefTable();
2395 if (table == NULL) {
2396 xmlGenericError(xmlGenericErrorContext,
2397 "xmlAddRef: Table creation failed!\n");
2398 return(NULL);
2399 }
Owen Taylor3473f882001-02-23 17:55:21 +00002400
Daniel Veillard37721922001-05-04 15:21:12 +00002401 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2402 if (ret == NULL) {
2403 xmlGenericError(xmlGenericErrorContext,
2404 "xmlAddRef: out of memory\n");
2405 return(NULL);
2406 }
Owen Taylor3473f882001-02-23 17:55:21 +00002407
Daniel Veillard37721922001-05-04 15:21:12 +00002408 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002409 * fill the structure.
2410 */
Daniel Veillard37721922001-05-04 15:21:12 +00002411 ret->value = xmlStrdup(value);
2412 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002413
Daniel Veillard37721922001-05-04 15:21:12 +00002414 /* To add a reference :-
2415 * References are maintained as a list of references,
2416 * Lookup the entry, if no entry create new nodelist
2417 * Add the owning node to the NodeList
2418 * Return the ref
2419 */
Owen Taylor3473f882001-02-23 17:55:21 +00002420
Daniel Veillard37721922001-05-04 15:21:12 +00002421 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2422 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2423 xmlGenericError(xmlGenericErrorContext,
2424 "xmlAddRef: Reference list creation failed!\n");
2425 return(NULL);
2426 }
2427 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2428 xmlListDelete(ref_list);
2429 xmlGenericError(xmlGenericErrorContext,
2430 "xmlAddRef: Reference list insertion failed!\n");
2431 return(NULL);
2432 }
2433 }
2434 xmlListInsert(ref_list, ret);
2435 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002436}
2437
2438/**
2439 * xmlFreeRefTable:
2440 * @table: An ref table
2441 *
2442 * Deallocate the memory used by an Ref hash table.
2443 */
2444void
2445xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002446 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002447}
2448
2449/**
2450 * xmlIsRef:
2451 * @doc: the document
2452 * @elem: the element carrying the attribute
2453 * @attr: the attribute
2454 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002455 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002456 * then this is simple, otherwise we use an heuristic: name Ref (upper
2457 * or lowercase).
2458 *
2459 * Returns 0 or 1 depending on the lookup result
2460 */
2461int
2462xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002463 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2464 return(0);
2465 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2466 /* TODO @@@ */
2467 return(0);
2468 } else {
2469 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002470
Daniel Veillard37721922001-05-04 15:21:12 +00002471 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2472 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2473 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2474 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002475
Daniel Veillard37721922001-05-04 15:21:12 +00002476 if ((attrDecl != NULL) &&
2477 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2478 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2479 return(1);
2480 }
2481 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002482}
2483
2484/**
2485 * xmlRemoveRef
2486 * @doc: the document
2487 * @attr: the attribute
2488 *
2489 * Remove the given attribute from the Ref table maintained internally.
2490 *
2491 * Returns -1 if the lookup failed and 0 otherwise
2492 */
2493int
2494xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002495 xmlListPtr ref_list;
2496 xmlRefTablePtr table;
2497 xmlChar *ID;
2498 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002499
Daniel Veillard37721922001-05-04 15:21:12 +00002500 if (doc == NULL) return(-1);
2501 if (attr == NULL) return(-1);
2502 table = (xmlRefTablePtr) doc->refs;
2503 if (table == NULL)
2504 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002505
Daniel Veillard37721922001-05-04 15:21:12 +00002506 if (attr == NULL)
2507 return(-1);
2508 ID = xmlNodeListGetString(doc, attr->children, 1);
2509 if (ID == NULL)
2510 return(-1);
2511 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002512
Daniel Veillard37721922001-05-04 15:21:12 +00002513 if(ref_list == NULL) {
2514 xmlFree(ID);
2515 return (-1);
2516 }
2517 /* At this point, ref_list refers to a list of references which
2518 * have the same key as the supplied attr. Our list of references
2519 * is ordered by reference address and we don't have that information
2520 * here to use when removing. We'll have to walk the list and
2521 * check for a matching attribute, when we find one stop the walk
2522 * and remove the entry.
2523 * The list is ordered by reference, so that means we don't have the
2524 * key. Passing the list and the reference to the walker means we
2525 * will have enough data to be able to remove the entry.
2526 */
2527 target.l = ref_list;
2528 target.ap = attr;
2529
2530 /* Remove the supplied attr from our list */
2531 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002532
Daniel Veillard37721922001-05-04 15:21:12 +00002533 /*If the list is empty then remove the list entry in the hash */
2534 if (xmlListEmpty(ref_list))
2535 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2536 xmlFreeRefList);
2537 xmlFree(ID);
2538 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002539}
2540
2541/**
2542 * xmlGetRefs:
2543 * @doc: pointer to the document
2544 * @ID: the ID value
2545 *
2546 * Find the set of references for the supplied ID.
2547 *
2548 * Returns NULL if not found, otherwise node set for the ID.
2549 */
2550xmlListPtr
2551xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002552 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002553
Daniel Veillard37721922001-05-04 15:21:12 +00002554 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002555 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002556 return(NULL);
2557 }
Owen Taylor3473f882001-02-23 17:55:21 +00002558
Daniel Veillard37721922001-05-04 15:21:12 +00002559 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002560 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002561 return(NULL);
2562 }
Owen Taylor3473f882001-02-23 17:55:21 +00002563
Daniel Veillard37721922001-05-04 15:21:12 +00002564 table = (xmlRefTablePtr) doc->refs;
2565 if (table == NULL)
2566 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002567
Daniel Veillard37721922001-05-04 15:21:12 +00002568 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002569}
2570
2571/************************************************************************
2572 * *
2573 * Routines for validity checking *
2574 * *
2575 ************************************************************************/
2576
2577/**
2578 * xmlGetDtdElementDesc:
2579 * @dtd: a pointer to the DtD to search
2580 * @name: the element name
2581 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002582 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002583 *
2584 * returns the xmlElementPtr if found or NULL
2585 */
2586
2587xmlElementPtr
2588xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2589 xmlElementTablePtr table;
2590 xmlElementPtr cur;
2591 xmlChar *uqname = NULL, *prefix = NULL;
2592
2593 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002594 if (dtd->elements == NULL)
2595 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002596 table = (xmlElementTablePtr) dtd->elements;
2597
2598 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002599 if (uqname != NULL)
2600 name = uqname;
2601 cur = xmlHashLookup2(table, name, prefix);
2602 if (prefix != NULL) xmlFree(prefix);
2603 if (uqname != NULL) xmlFree(uqname);
2604 return(cur);
2605}
2606/**
2607 * xmlGetDtdElementDesc2:
2608 * @dtd: a pointer to the DtD to search
2609 * @name: the element name
2610 * @create: create an empty description if not found
2611 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002612 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002613 *
2614 * returns the xmlElementPtr if found or NULL
2615 */
2616
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002617static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002618xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2619 xmlElementTablePtr table;
2620 xmlElementPtr cur;
2621 xmlChar *uqname = NULL, *prefix = NULL;
2622
2623 if (dtd == NULL) return(NULL);
2624 if (dtd->elements == NULL) {
2625 if (!create)
2626 return(NULL);
2627 /*
2628 * Create the Element table if needed.
2629 */
2630 table = (xmlElementTablePtr) dtd->elements;
2631 if (table == NULL) {
2632 table = xmlCreateElementTable();
2633 dtd->elements = (void *) table;
2634 }
2635 if (table == NULL) {
2636 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002637 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002638 return(NULL);
2639 }
2640 }
2641 table = (xmlElementTablePtr) dtd->elements;
2642
2643 uqname = xmlSplitQName2(name, &prefix);
2644 if (uqname != NULL)
2645 name = uqname;
2646 cur = xmlHashLookup2(table, name, prefix);
2647 if ((cur == NULL) && (create)) {
2648 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2649 if (cur == NULL) {
2650 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002651 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002652 return(NULL);
2653 }
2654 memset(cur, 0, sizeof(xmlElement));
2655 cur->type = XML_ELEMENT_DECL;
2656
2657 /*
2658 * fill the structure.
2659 */
2660 cur->name = xmlStrdup(name);
2661 cur->prefix = xmlStrdup(prefix);
2662 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2663
2664 xmlHashAddEntry2(table, name, prefix, cur);
2665 }
2666 if (prefix != NULL) xmlFree(prefix);
2667 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002668 return(cur);
2669}
2670
2671/**
2672 * xmlGetDtdQElementDesc:
2673 * @dtd: a pointer to the DtD to search
2674 * @name: the element name
2675 * @prefix: the element namespace prefix
2676 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002677 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002678 *
2679 * returns the xmlElementPtr if found or NULL
2680 */
2681
Daniel Veillard48da9102001-08-07 01:10:10 +00002682xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002683xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2684 const xmlChar *prefix) {
2685 xmlElementTablePtr table;
2686
2687 if (dtd == NULL) return(NULL);
2688 if (dtd->elements == NULL) return(NULL);
2689 table = (xmlElementTablePtr) dtd->elements;
2690
2691 return(xmlHashLookup2(table, name, prefix));
2692}
2693
2694/**
2695 * xmlGetDtdAttrDesc:
2696 * @dtd: a pointer to the DtD to search
2697 * @elem: the element name
2698 * @name: the attribute name
2699 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002700 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002701 * this element.
2702 *
2703 * returns the xmlAttributePtr if found or NULL
2704 */
2705
2706xmlAttributePtr
2707xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2708 xmlAttributeTablePtr table;
2709 xmlAttributePtr cur;
2710 xmlChar *uqname = NULL, *prefix = NULL;
2711
2712 if (dtd == NULL) return(NULL);
2713 if (dtd->attributes == NULL) return(NULL);
2714
2715 table = (xmlAttributeTablePtr) dtd->attributes;
2716 if (table == NULL)
2717 return(NULL);
2718
2719 uqname = xmlSplitQName2(name, &prefix);
2720
2721 if (uqname != NULL) {
2722 cur = xmlHashLookup3(table, uqname, prefix, elem);
2723 if (prefix != NULL) xmlFree(prefix);
2724 if (uqname != NULL) xmlFree(uqname);
2725 } else
2726 cur = xmlHashLookup3(table, name, NULL, elem);
2727 return(cur);
2728}
2729
2730/**
2731 * xmlGetDtdQAttrDesc:
2732 * @dtd: a pointer to the DtD to search
2733 * @elem: the element name
2734 * @name: the attribute name
2735 * @prefix: the attribute namespace prefix
2736 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002737 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002738 * this element.
2739 *
2740 * returns the xmlAttributePtr if found or NULL
2741 */
2742
Daniel Veillard48da9102001-08-07 01:10:10 +00002743xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002744xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2745 const xmlChar *prefix) {
2746 xmlAttributeTablePtr table;
2747
2748 if (dtd == NULL) return(NULL);
2749 if (dtd->attributes == NULL) return(NULL);
2750 table = (xmlAttributeTablePtr) dtd->attributes;
2751
2752 return(xmlHashLookup3(table, name, prefix, elem));
2753}
2754
2755/**
2756 * xmlGetDtdNotationDesc:
2757 * @dtd: a pointer to the DtD to search
2758 * @name: the notation name
2759 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002760 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002761 *
2762 * returns the xmlNotationPtr if found or NULL
2763 */
2764
2765xmlNotationPtr
2766xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2767 xmlNotationTablePtr table;
2768
2769 if (dtd == NULL) return(NULL);
2770 if (dtd->notations == NULL) return(NULL);
2771 table = (xmlNotationTablePtr) dtd->notations;
2772
2773 return(xmlHashLookup(table, name));
2774}
2775
2776/**
2777 * xmlValidateNotationUse:
2778 * @ctxt: the validation context
2779 * @doc: the document
2780 * @notationName: the notation name to check
2781 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002782 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002783 * - [ VC: Notation Declared ]
2784 *
2785 * returns 1 if valid or 0 otherwise
2786 */
2787
2788int
2789xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2790 const xmlChar *notationName) {
2791 xmlNotationPtr notaDecl;
2792 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2793
2794 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2795 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2796 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2797
2798 if (notaDecl == NULL) {
2799 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2800 notationName);
2801 return(0);
2802 }
2803 return(1);
2804}
2805
2806/**
2807 * xmlIsMixedElement
2808 * @doc: the document
2809 * @name: the element name
2810 *
2811 * Search in the DtDs whether an element accept Mixed content (or ANY)
2812 * basically if it is supposed to accept text childs
2813 *
2814 * returns 0 if no, 1 if yes, and -1 if no element description is available
2815 */
2816
2817int
2818xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2819 xmlElementPtr elemDecl;
2820
2821 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2822
2823 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2824 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2825 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2826 if (elemDecl == NULL) return(-1);
2827 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002828 case XML_ELEMENT_TYPE_UNDEFINED:
2829 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002830 case XML_ELEMENT_TYPE_ELEMENT:
2831 return(0);
2832 case XML_ELEMENT_TYPE_EMPTY:
2833 /*
2834 * return 1 for EMPTY since we want VC error to pop up
2835 * on <empty> </empty> for example
2836 */
2837 case XML_ELEMENT_TYPE_ANY:
2838 case XML_ELEMENT_TYPE_MIXED:
2839 return(1);
2840 }
2841 return(1);
2842}
2843
2844/**
2845 * xmlValidateNameValue:
2846 * @value: an Name value
2847 *
2848 * Validate that the given value match Name production
2849 *
2850 * returns 1 if valid or 0 otherwise
2851 */
2852
Daniel Veillard9b731d72002-04-14 12:56:08 +00002853int
Owen Taylor3473f882001-02-23 17:55:21 +00002854xmlValidateNameValue(const xmlChar *value) {
2855 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002856 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002857
2858 if (value == NULL) return(0);
2859 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002860 val = xmlStringCurrentChar(NULL, cur, &len);
2861 cur += len;
2862 if (!IS_LETTER(val) && (val != '_') &&
2863 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002864 return(0);
2865 }
2866
Daniel Veillardd8224e02002-01-13 15:43:22 +00002867 val = xmlStringCurrentChar(NULL, cur, &len);
2868 cur += len;
2869 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2870 (val == '.') || (val == '-') ||
2871 (val == '_') || (val == ':') ||
2872 (IS_COMBINING(val)) ||
2873 (IS_EXTENDER(val))) {
2874 val = xmlStringCurrentChar(NULL, cur, &len);
2875 cur += len;
2876 }
Owen Taylor3473f882001-02-23 17:55:21 +00002877
Daniel Veillardd8224e02002-01-13 15:43:22 +00002878 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002879
2880 return(1);
2881}
2882
2883/**
2884 * xmlValidateNamesValue:
2885 * @value: an Names value
2886 *
2887 * Validate that the given value match Names production
2888 *
2889 * returns 1 if valid or 0 otherwise
2890 */
2891
Daniel Veillard9b731d72002-04-14 12:56:08 +00002892int
Owen Taylor3473f882001-02-23 17:55:21 +00002893xmlValidateNamesValue(const xmlChar *value) {
2894 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002895 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002896
2897 if (value == NULL) return(0);
2898 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002899 val = xmlStringCurrentChar(NULL, cur, &len);
2900 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002901
Daniel Veillardd8224e02002-01-13 15:43:22 +00002902 if (!IS_LETTER(val) && (val != '_') &&
2903 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002904 return(0);
2905 }
2906
Daniel Veillardd8224e02002-01-13 15:43:22 +00002907 val = xmlStringCurrentChar(NULL, cur, &len);
2908 cur += len;
2909 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2910 (val == '.') || (val == '-') ||
2911 (val == '_') || (val == ':') ||
2912 (IS_COMBINING(val)) ||
2913 (IS_EXTENDER(val))) {
2914 val = xmlStringCurrentChar(NULL, cur, &len);
2915 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002916 }
2917
Daniel Veillardd8224e02002-01-13 15:43:22 +00002918 while (IS_BLANK(val)) {
2919 while (IS_BLANK(val)) {
2920 val = xmlStringCurrentChar(NULL, cur, &len);
2921 cur += len;
2922 }
2923
2924 if (!IS_LETTER(val) && (val != '_') &&
2925 (val != ':')) {
2926 return(0);
2927 }
2928 val = xmlStringCurrentChar(NULL, cur, &len);
2929 cur += len;
2930
2931 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2932 (val == '.') || (val == '-') ||
2933 (val == '_') || (val == ':') ||
2934 (IS_COMBINING(val)) ||
2935 (IS_EXTENDER(val))) {
2936 val = xmlStringCurrentChar(NULL, cur, &len);
2937 cur += len;
2938 }
2939 }
2940
2941 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002942
2943 return(1);
2944}
2945
2946/**
2947 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002948 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002949 *
2950 * Validate that the given value match Nmtoken production
2951 *
2952 * [ VC: Name Token ]
2953 *
2954 * returns 1 if valid or 0 otherwise
2955 */
2956
Daniel Veillard9b731d72002-04-14 12:56:08 +00002957int
Owen Taylor3473f882001-02-23 17:55:21 +00002958xmlValidateNmtokenValue(const xmlChar *value) {
2959 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002960 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002961
2962 if (value == NULL) return(0);
2963 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002964 val = xmlStringCurrentChar(NULL, cur, &len);
2965 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002966
Daniel Veillardd8224e02002-01-13 15:43:22 +00002967 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2968 (val != '.') && (val != '-') &&
2969 (val != '_') && (val != ':') &&
2970 (!IS_COMBINING(val)) &&
2971 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002972 return(0);
2973
Daniel Veillardd8224e02002-01-13 15:43:22 +00002974 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2975 (val == '.') || (val == '-') ||
2976 (val == '_') || (val == ':') ||
2977 (IS_COMBINING(val)) ||
2978 (IS_EXTENDER(val))) {
2979 val = xmlStringCurrentChar(NULL, cur, &len);
2980 cur += len;
2981 }
Owen Taylor3473f882001-02-23 17:55:21 +00002982
Daniel Veillardd8224e02002-01-13 15:43:22 +00002983 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002984
2985 return(1);
2986}
2987
2988/**
2989 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002990 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00002991 *
2992 * Validate that the given value match Nmtokens production
2993 *
2994 * [ VC: Name Token ]
2995 *
2996 * returns 1 if valid or 0 otherwise
2997 */
2998
Daniel Veillard9b731d72002-04-14 12:56:08 +00002999int
Owen Taylor3473f882001-02-23 17:55:21 +00003000xmlValidateNmtokensValue(const xmlChar *value) {
3001 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003002 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00003003
3004 if (value == NULL) return(0);
3005 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00003006 val = xmlStringCurrentChar(NULL, cur, &len);
3007 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003008
Daniel Veillardd8224e02002-01-13 15:43:22 +00003009 while (IS_BLANK(val)) {
3010 val = xmlStringCurrentChar(NULL, cur, &len);
3011 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00003012 }
3013
Daniel Veillardd8224e02002-01-13 15:43:22 +00003014 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3015 (val != '.') && (val != '-') &&
3016 (val != '_') && (val != ':') &&
3017 (!IS_COMBINING(val)) &&
3018 (!IS_EXTENDER(val)))
3019 return(0);
3020
3021 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3022 (val == '.') || (val == '-') ||
3023 (val == '_') || (val == ':') ||
3024 (IS_COMBINING(val)) ||
3025 (IS_EXTENDER(val))) {
3026 val = xmlStringCurrentChar(NULL, cur, &len);
3027 cur += len;
3028 }
3029
3030 while (IS_BLANK(val)) {
3031 while (IS_BLANK(val)) {
3032 val = xmlStringCurrentChar(NULL, cur, &len);
3033 cur += len;
3034 }
3035 if (val == 0) return(1);
3036
3037 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
3038 (val != '.') && (val != '-') &&
3039 (val != '_') && (val != ':') &&
3040 (!IS_COMBINING(val)) &&
3041 (!IS_EXTENDER(val)))
3042 return(0);
3043
3044 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
3045 (val == '.') || (val == '-') ||
3046 (val == '_') || (val == ':') ||
3047 (IS_COMBINING(val)) ||
3048 (IS_EXTENDER(val))) {
3049 val = xmlStringCurrentChar(NULL, cur, &len);
3050 cur += len;
3051 }
3052 }
3053
3054 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003055
3056 return(1);
3057}
3058
3059/**
3060 * xmlValidateNotationDecl:
3061 * @ctxt: the validation context
3062 * @doc: a document instance
3063 * @nota: a notation definition
3064 *
3065 * Try to validate a single notation definition
3066 * basically it does the following checks as described by the
3067 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003068 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00003069 * But this function get called anyway ...
3070 *
3071 * returns 1 if valid or 0 otherwise
3072 */
3073
3074int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003075xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
3076 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003077 int ret = 1;
3078
3079 return(ret);
3080}
3081
3082/**
3083 * xmlValidateAttributeValue:
3084 * @type: an attribute type
3085 * @value: an attribute value
3086 *
3087 * Validate that the given attribute value match the proper production
3088 *
3089 * [ VC: ID ]
3090 * Values of type ID must match the Name production....
3091 *
3092 * [ VC: IDREF ]
3093 * Values of type IDREF must match the Name production, and values
3094 * of type IDREFS must match Names ...
3095 *
3096 * [ VC: Entity Name ]
3097 * Values of type ENTITY must match the Name production, values
3098 * of type ENTITIES must match Names ...
3099 *
3100 * [ VC: Name Token ]
3101 * Values of type NMTOKEN must match the Nmtoken production; values
3102 * of type NMTOKENS must match Nmtokens.
3103 *
3104 * returns 1 if valid or 0 otherwise
3105 */
3106
3107int
3108xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
3109 switch (type) {
3110 case XML_ATTRIBUTE_ENTITIES:
3111 case XML_ATTRIBUTE_IDREFS:
3112 return(xmlValidateNamesValue(value));
3113 case XML_ATTRIBUTE_ENTITY:
3114 case XML_ATTRIBUTE_IDREF:
3115 case XML_ATTRIBUTE_ID:
3116 case XML_ATTRIBUTE_NOTATION:
3117 return(xmlValidateNameValue(value));
3118 case XML_ATTRIBUTE_NMTOKENS:
3119 case XML_ATTRIBUTE_ENUMERATION:
3120 return(xmlValidateNmtokensValue(value));
3121 case XML_ATTRIBUTE_NMTOKEN:
3122 return(xmlValidateNmtokenValue(value));
3123 case XML_ATTRIBUTE_CDATA:
3124 break;
3125 }
3126 return(1);
3127}
3128
3129/**
3130 * xmlValidateAttributeValue2:
3131 * @ctxt: the validation context
3132 * @doc: the document
3133 * @name: the attribute name (used for error reporting only)
3134 * @type: the attribute type
3135 * @value: the attribute value
3136 *
3137 * Validate that the given attribute value match a given type.
3138 * This typically cannot be done before having finished parsing
3139 * the subsets.
3140 *
3141 * [ VC: IDREF ]
3142 * Values of type IDREF must match one of the declared IDs
3143 * Values of type IDREFS must match a sequence of the declared IDs
3144 * each Name must match the value of an ID attribute on some element
3145 * in the XML document; i.e. IDREF values must match the value of
3146 * some ID attribute
3147 *
3148 * [ VC: Entity Name ]
3149 * Values of type ENTITY must match one declared entity
3150 * Values of type ENTITIES must match a sequence of declared entities
3151 *
3152 * [ VC: Notation Attributes ]
3153 * all notation names in the declaration must be declared.
3154 *
3155 * returns 1 if valid or 0 otherwise
3156 */
3157
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003158static int
Owen Taylor3473f882001-02-23 17:55:21 +00003159xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3160 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
3161 int ret = 1;
3162 switch (type) {
3163 case XML_ATTRIBUTE_IDREFS:
3164 case XML_ATTRIBUTE_IDREF:
3165 case XML_ATTRIBUTE_ID:
3166 case XML_ATTRIBUTE_NMTOKENS:
3167 case XML_ATTRIBUTE_ENUMERATION:
3168 case XML_ATTRIBUTE_NMTOKEN:
3169 case XML_ATTRIBUTE_CDATA:
3170 break;
3171 case XML_ATTRIBUTE_ENTITY: {
3172 xmlEntityPtr ent;
3173
3174 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00003175 if ((ent == NULL) && (doc->standalone == 1)) {
3176 doc->standalone = 0;
3177 ent = xmlGetDocEntity(doc, value);
3178 if (ent != NULL) {
3179 VERROR(ctxt->userData,
3180"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
3181 name, value);
3182 /* WAIT to get answer from the Core WG on this
3183 ret = 0;
3184 */
3185 }
3186 }
Owen Taylor3473f882001-02-23 17:55:21 +00003187 if (ent == NULL) {
3188 VERROR(ctxt->userData,
3189 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
3190 name, value);
3191 ret = 0;
3192 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3193 VERROR(ctxt->userData,
3194 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
3195 name, value);
3196 ret = 0;
3197 }
3198 break;
3199 }
3200 case XML_ATTRIBUTE_ENTITIES: {
3201 xmlChar *dup, *nam = NULL, *cur, save;
3202 xmlEntityPtr ent;
3203
3204 dup = xmlStrdup(value);
3205 if (dup == NULL)
3206 return(0);
3207 cur = dup;
3208 while (*cur != 0) {
3209 nam = cur;
3210 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3211 save = *cur;
3212 *cur = 0;
3213 ent = xmlGetDocEntity(doc, nam);
3214 if (ent == NULL) {
3215 VERROR(ctxt->userData,
3216 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
3217 name, nam);
3218 ret = 0;
3219 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
3220 VERROR(ctxt->userData,
3221 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
3222 name, nam);
3223 ret = 0;
3224 }
3225 if (save == 0)
3226 break;
3227 *cur = save;
3228 while (IS_BLANK(*cur)) cur++;
3229 }
3230 xmlFree(dup);
3231 break;
3232 }
3233 case XML_ATTRIBUTE_NOTATION: {
3234 xmlNotationPtr nota;
3235
3236 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3237 if ((nota == NULL) && (doc->extSubset != NULL))
3238 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3239
3240 if (nota == NULL) {
3241 VERROR(ctxt->userData,
3242 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3243 name, value);
3244 ret = 0;
3245 }
3246 break;
3247 }
3248 }
3249 return(ret);
3250}
3251
3252/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003253 * xmlValidCtxtNormalizeAttributeValue:
3254 * @ctxt: the validation context
3255 * @doc: the document
3256 * @elem: the parent
3257 * @name: the attribute name
3258 * @value: the attribute value
3259 * @ctxt: the validation context or NULL
3260 *
3261 * Does the validation related extra step of the normalization of attribute
3262 * values:
3263 *
3264 * If the declared value is not CDATA, then the XML processor must further
3265 * process the normalized attribute value by discarding any leading and
3266 * trailing space (#x20) characters, and by replacing sequences of space
3267 * (#x20) characters by single space (#x20) character.
3268 *
3269 * Also check VC: Standalone Document Declaration in P32, and update
3270 * ctxt->valid accordingly
3271 *
3272 * returns a new normalized string if normalization is needed, NULL otherwise
3273 * the caller must free the returned value.
3274 */
3275
3276xmlChar *
3277xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3278 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3279 xmlChar *ret, *dst;
3280 const xmlChar *src;
3281 xmlAttributePtr attrDecl = NULL;
3282 int extsubset = 0;
3283
3284 if (doc == NULL) return(NULL);
3285 if (elem == NULL) return(NULL);
3286 if (name == NULL) return(NULL);
3287 if (value == NULL) return(NULL);
3288
3289 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3290 xmlChar qname[500];
3291 snprintf((char *) qname, sizeof(qname), "%s:%s",
3292 elem->ns->prefix, elem->name);
3293 qname[sizeof(qname) - 1] = 0;
3294 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3295 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3296 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3297 if (attrDecl != NULL)
3298 extsubset = 1;
3299 }
3300 }
3301 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3302 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3303 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3304 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3305 if (attrDecl != NULL)
3306 extsubset = 1;
3307 }
3308
3309 if (attrDecl == NULL)
3310 return(NULL);
3311 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3312 return(NULL);
3313
3314 ret = xmlStrdup(value);
3315 if (ret == NULL)
3316 return(NULL);
3317 src = value;
3318 dst = ret;
3319 while (*src == 0x20) src++;
3320 while (*src != 0) {
3321 if (*src == 0x20) {
3322 while (*src == 0x20) src++;
3323 if (*src != 0)
3324 *dst++ = 0x20;
3325 } else {
3326 *dst++ = *src++;
3327 }
3328 }
3329 *dst = 0;
3330 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3331 VERROR(ctxt->userData,
3332"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3333 name, elem->name);
3334 ctxt->valid = 0;
3335 }
3336 return(ret);
3337}
3338
3339/**
Owen Taylor3473f882001-02-23 17:55:21 +00003340 * xmlValidNormalizeAttributeValue:
3341 * @doc: the document
3342 * @elem: the parent
3343 * @name: the attribute name
3344 * @value: the attribute value
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003345 * @ctxt: the validation context or NULL
Owen Taylor3473f882001-02-23 17:55:21 +00003346 *
3347 * Does the validation related extra step of the normalization of attribute
3348 * values:
3349 *
3350 * If the declared value is not CDATA, then the XML processor must further
3351 * process the normalized attribute value by discarding any leading and
3352 * trailing space (#x20) characters, and by replacing sequences of space
3353 * (#x20) characters by single space (#x20) character.
3354 *
3355 * returns a new normalized string if normalization is needed, NULL otherwise
3356 * the caller must free the returned value.
3357 */
3358
3359xmlChar *
3360xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3361 const xmlChar *name, const xmlChar *value) {
3362 xmlChar *ret, *dst;
3363 const xmlChar *src;
3364 xmlAttributePtr attrDecl = NULL;
3365
3366 if (doc == NULL) return(NULL);
3367 if (elem == NULL) return(NULL);
3368 if (name == NULL) return(NULL);
3369 if (value == NULL) return(NULL);
3370
3371 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3372 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003373 snprintf((char *) qname, sizeof(qname), "%s:%s",
3374 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003375 qname[sizeof(qname) - 1] = 0;
3376 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3377 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3378 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3379 }
3380 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3381 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3382 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3383
3384 if (attrDecl == NULL)
3385 return(NULL);
3386 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3387 return(NULL);
3388
3389 ret = xmlStrdup(value);
3390 if (ret == NULL)
3391 return(NULL);
3392 src = value;
3393 dst = ret;
3394 while (*src == 0x20) src++;
3395 while (*src != 0) {
3396 if (*src == 0x20) {
3397 while (*src == 0x20) src++;
3398 if (*src != 0)
3399 *dst++ = 0x20;
3400 } else {
3401 *dst++ = *src++;
3402 }
3403 }
3404 *dst = 0;
3405 return(ret);
3406}
3407
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003408static void
Owen Taylor3473f882001-02-23 17:55:21 +00003409xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003410 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003411 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3412}
3413
3414/**
3415 * xmlValidateAttributeDecl:
3416 * @ctxt: the validation context
3417 * @doc: a document instance
3418 * @attr: an attribute definition
3419 *
3420 * Try to validate a single attribute definition
3421 * basically it does the following checks as described by the
3422 * XML-1.0 recommendation:
3423 * - [ VC: Attribute Default Legal ]
3424 * - [ VC: Enumeration ]
3425 * - [ VC: ID Attribute Default ]
3426 *
3427 * The ID/IDREF uniqueness and matching are done separately
3428 *
3429 * returns 1 if valid or 0 otherwise
3430 */
3431
3432int
3433xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3434 xmlAttributePtr attr) {
3435 int ret = 1;
3436 int val;
3437 CHECK_DTD;
3438 if(attr == NULL) return(1);
3439
3440 /* Attribute Default Legal */
3441 /* Enumeration */
3442 if (attr->defaultValue != NULL) {
3443 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3444 if (val == 0) {
3445 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003446 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003447 attr->name, attr->elem);
3448 }
3449 ret &= val;
3450 }
3451
3452 /* ID Attribute Default */
3453 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3454 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3455 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3456 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003457 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003458 attr->name, attr->elem);
3459 ret = 0;
3460 }
3461
3462 /* One ID per Element Type */
3463 if (attr->atype == XML_ATTRIBUTE_ID) {
3464 int nbId;
3465
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003466 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003467 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3468 attr->elem);
3469 if (elem != NULL) {
3470 nbId = xmlScanIDAttributeDecl(NULL, elem);
3471 } else {
3472 xmlAttributeTablePtr table;
3473
3474 /*
3475 * The attribute may be declared in the internal subset and the
3476 * element in the external subset.
3477 */
3478 nbId = 0;
3479 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3480 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3481 xmlValidateAttributeIdCallback, &nbId);
3482 }
3483 if (nbId > 1) {
3484 VERROR(ctxt->userData,
3485 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3486 attr->elem, nbId, attr->name);
3487 } else if (doc->extSubset != NULL) {
3488 int extId = 0;
3489 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3490 if (elem != NULL) {
3491 extId = xmlScanIDAttributeDecl(NULL, elem);
3492 }
3493 if (extId > 1) {
3494 VERROR(ctxt->userData,
3495 "Element %s has %d ID attribute defined in the external subset : %s\n",
3496 attr->elem, extId, attr->name);
3497 } else if (extId + nbId > 1) {
3498 VERROR(ctxt->userData,
3499"Element %s has ID attributes defined in the internal and external subset : %s\n",
3500 attr->elem, attr->name);
3501 }
3502 }
3503 }
3504
3505 /* Validity Constraint: Enumeration */
3506 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3507 xmlEnumerationPtr tree = attr->tree;
3508 while (tree != NULL) {
3509 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3510 tree = tree->next;
3511 }
3512 if (tree == NULL) {
3513 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003514"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003515 attr->defaultValue, attr->name, attr->elem);
3516 ret = 0;
3517 }
3518 }
3519
3520 return(ret);
3521}
3522
3523/**
3524 * xmlValidateElementDecl:
3525 * @ctxt: the validation context
3526 * @doc: a document instance
3527 * @elem: an element definition
3528 *
3529 * Try to validate a single element definition
3530 * basically it does the following checks as described by the
3531 * XML-1.0 recommendation:
3532 * - [ VC: One ID per Element Type ]
3533 * - [ VC: No Duplicate Types ]
3534 * - [ VC: Unique Element Type Declaration ]
3535 *
3536 * returns 1 if valid or 0 otherwise
3537 */
3538
3539int
3540xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3541 xmlElementPtr elem) {
3542 int ret = 1;
3543 xmlElementPtr tst;
3544
3545 CHECK_DTD;
3546
3547 if (elem == NULL) return(1);
3548
Daniel Veillard84d70a42002-09-16 10:51:38 +00003549#ifdef LIBXML_REGEXP_ENABLED
3550 /* Build the regexp associated to the content model */
3551 ret = xmlValidBuildContentModel(ctxt, elem);
3552#endif
3553
Owen Taylor3473f882001-02-23 17:55:21 +00003554 /* No Duplicate Types */
3555 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3556 xmlElementContentPtr cur, next;
3557 const xmlChar *name;
3558
3559 cur = elem->content;
3560 while (cur != NULL) {
3561 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3562 if (cur->c1 == NULL) break;
3563 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3564 name = cur->c1->name;
3565 next = cur->c2;
3566 while (next != NULL) {
3567 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3568 if (xmlStrEqual(next->name, name)) {
3569 VERROR(ctxt->userData,
3570 "Definition of %s has duplicate references of %s\n",
3571 elem->name, name);
3572 ret = 0;
3573 }
3574 break;
3575 }
3576 if (next->c1 == NULL) break;
3577 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3578 if (xmlStrEqual(next->c1->name, name)) {
3579 VERROR(ctxt->userData,
3580 "Definition of %s has duplicate references of %s\n",
3581 elem->name, name);
3582 ret = 0;
3583 }
3584 next = next->c2;
3585 }
3586 }
3587 cur = cur->c2;
3588 }
3589 }
3590
3591 /* VC: Unique Element Type Declaration */
3592 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003593 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003594 ((tst->prefix == elem->prefix) ||
3595 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003596 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003597 VERROR(ctxt->userData, "Redefinition of element %s\n",
3598 elem->name);
3599 ret = 0;
3600 }
3601 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003602 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003603 ((tst->prefix == elem->prefix) ||
3604 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003605 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003606 VERROR(ctxt->userData, "Redefinition of element %s\n",
3607 elem->name);
3608 ret = 0;
3609 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003610 /* One ID per Element Type
3611 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003612 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3613 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003614 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003615 return(ret);
3616}
3617
3618/**
3619 * xmlValidateOneAttribute:
3620 * @ctxt: the validation context
3621 * @doc: a document instance
3622 * @elem: an element instance
3623 * @attr: an attribute instance
3624 * @value: the attribute value (without entities processing)
3625 *
3626 * Try to validate a single attribute for an element
3627 * basically it does the following checks as described by the
3628 * XML-1.0 recommendation:
3629 * - [ VC: Attribute Value Type ]
3630 * - [ VC: Fixed Attribute Default ]
3631 * - [ VC: Entity Name ]
3632 * - [ VC: Name Token ]
3633 * - [ VC: ID ]
3634 * - [ VC: IDREF ]
3635 * - [ VC: Entity Name ]
3636 * - [ VC: Notation Attributes ]
3637 *
3638 * The ID/IDREF uniqueness and matching are done separately
3639 *
3640 * returns 1 if valid or 0 otherwise
3641 */
3642
3643int
3644xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3645 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3646 /* xmlElementPtr elemDecl; */
3647 xmlAttributePtr attrDecl = NULL;
3648 int val;
3649 int ret = 1;
3650
3651 CHECK_DTD;
3652 if ((elem == NULL) || (elem->name == NULL)) return(0);
3653 if ((attr == NULL) || (attr->name == NULL)) return(0);
3654
3655 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3656 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003657 snprintf((char *) qname, sizeof(qname), "%s:%s",
3658 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003659 qname[sizeof(qname) - 1] = 0;
3660 if (attr->ns != NULL) {
3661 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3662 attr->name, attr->ns->prefix);
3663 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3664 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3665 attr->name, attr->ns->prefix);
3666 } else {
3667 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3668 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3669 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3670 qname, attr->name);
3671 }
3672 }
3673 if (attrDecl == NULL) {
3674 if (attr->ns != NULL) {
3675 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3676 attr->name, attr->ns->prefix);
3677 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3678 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3679 attr->name, attr->ns->prefix);
3680 } else {
3681 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3682 elem->name, attr->name);
3683 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3684 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3685 elem->name, attr->name);
3686 }
3687 }
3688
3689
3690 /* Validity Constraint: Attribute Value Type */
3691 if (attrDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003692 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003693 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003694 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003695 attr->name, elem->name);
3696 return(0);
3697 }
3698 attr->atype = attrDecl->atype;
3699
3700 val = xmlValidateAttributeValue(attrDecl->atype, value);
3701 if (val == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003702 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003703 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003704 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003705 attr->name, elem->name);
3706 ret = 0;
3707 }
3708
3709 /* Validity constraint: Fixed Attribute Default */
3710 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3711 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
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 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003715 attr->name, elem->name, attrDecl->defaultValue);
3716 ret = 0;
3717 }
3718 }
3719
3720 /* Validity Constraint: ID uniqueness */
3721 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3722 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3723 ret = 0;
3724 }
3725
3726 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3727 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3728 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3729 ret = 0;
3730 }
3731
3732 /* Validity Constraint: Notation Attributes */
3733 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3734 xmlEnumerationPtr tree = attrDecl->tree;
3735 xmlNotationPtr nota;
3736
3737 /* First check that the given NOTATION was declared */
3738 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3739 if (nota == NULL)
3740 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3741
3742 if (nota == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003743 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003744 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003745 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003746 value, attr->name, elem->name);
3747 ret = 0;
3748 }
3749
3750 /* Second, verify that it's among the list */
3751 while (tree != NULL) {
3752 if (xmlStrEqual(tree->name, value)) break;
3753 tree = tree->next;
3754 }
3755 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003756 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003757 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003758"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003759 value, attr->name, elem->name);
3760 ret = 0;
3761 }
3762 }
3763
3764 /* Validity Constraint: Enumeration */
3765 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3766 xmlEnumerationPtr tree = attrDecl->tree;
3767 while (tree != NULL) {
3768 if (xmlStrEqual(tree->name, value)) break;
3769 tree = tree->next;
3770 }
3771 if (tree == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003772 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003773 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003774 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003775 value, attr->name, elem->name);
3776 ret = 0;
3777 }
3778 }
3779
3780 /* Fixed Attribute Default */
3781 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3782 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00003783 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00003784 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003785 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003786 attr->name, elem->name, attrDecl->defaultValue);
3787 ret = 0;
3788 }
3789
3790 /* Extra check for the attribute value */
3791 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3792 attrDecl->atype, value);
3793
3794 return(ret);
3795}
3796
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003797/**
3798 * xmlValidateSkipIgnorable:
3799 * @ctxt: the validation context
3800 * @child: the child list
3801 *
3802 * Skip ignorable elements w.r.t. the validation process
3803 *
3804 * returns the first element to consider for validation of the content model
3805 */
3806
3807static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003808xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003809 while (child != NULL) {
3810 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003811 /* These things are ignored (skipped) during validation. */
3812 case XML_PI_NODE:
3813 case XML_COMMENT_NODE:
3814 case XML_XINCLUDE_START:
3815 case XML_XINCLUDE_END:
3816 child = child->next;
3817 break;
3818 case XML_TEXT_NODE:
3819 if (xmlIsBlankNode(child))
3820 child = child->next;
3821 else
3822 return(child);
3823 break;
3824 /* keep current node */
3825 default:
3826 return(child);
3827 }
3828 }
3829 return(child);
3830}
3831
3832/**
3833 * xmlValidateElementType:
3834 * @ctxt: the validation context
3835 *
3836 * Try to validate the content model of an element internal function
3837 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003838 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3839 * reference is found and -3 if the validation succeeded but
3840 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003841 */
3842
3843static int
3844xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003845 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003846 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003847
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003848 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003849 if ((NODE == NULL) && (CONT == NULL))
3850 return(1);
3851 if ((NODE == NULL) &&
3852 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3853 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3854 return(1);
3855 }
3856 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003857 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003858 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003859
3860 /*
3861 * We arrive here when more states need to be examined
3862 */
3863cont:
3864
3865 /*
3866 * We just recovered from a rollback generated by a possible
3867 * epsilon transition, go directly to the analysis phase
3868 */
3869 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003870 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003871 DEBUG_VALID_STATE(NODE, CONT)
3872 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003873 goto analyze;
3874 }
3875
3876 DEBUG_VALID_STATE(NODE, CONT)
3877 /*
3878 * we may have to save a backup state here. This is the equivalent
3879 * of handling epsilon transition in NFAs.
3880 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003881 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003882 ((CONT->parent == NULL) ||
3883 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003884 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003885 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003886 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003887 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003888 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
3889 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003890 }
3891
3892
3893 /*
3894 * Check first if the content matches
3895 */
3896 switch (CONT->type) {
3897 case XML_ELEMENT_CONTENT_PCDATA:
3898 if (NODE == NULL) {
3899 DEBUG_VALID_MSG("pcdata failed no node");
3900 ret = 0;
3901 break;
3902 }
3903 if (NODE->type == XML_TEXT_NODE) {
3904 DEBUG_VALID_MSG("pcdata found, skip to next");
3905 /*
3906 * go to next element in the content model
3907 * skipping ignorable elems
3908 */
3909 do {
3910 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003911 NODE = xmlValidateSkipIgnorable(NODE);
3912 if ((NODE != NULL) &&
3913 (NODE->type == XML_ENTITY_REF_NODE))
3914 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003915 } while ((NODE != NULL) &&
3916 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003917 (NODE->type != XML_TEXT_NODE) &&
3918 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003919 ret = 1;
3920 break;
3921 } else {
3922 DEBUG_VALID_MSG("pcdata failed");
3923 ret = 0;
3924 break;
3925 }
3926 break;
3927 case XML_ELEMENT_CONTENT_ELEMENT:
3928 if (NODE == NULL) {
3929 DEBUG_VALID_MSG("element failed no node");
3930 ret = 0;
3931 break;
3932 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003933 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3934 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003935 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003936 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3937 ret = (CONT->prefix == NULL);
3938 } else if (CONT->prefix == NULL) {
3939 ret = 0;
3940 } else {
3941 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3942 }
3943 }
3944 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003945 DEBUG_VALID_MSG("element found, skip to next");
3946 /*
3947 * go to next element in the content model
3948 * skipping ignorable elems
3949 */
3950 do {
3951 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003952 NODE = xmlValidateSkipIgnorable(NODE);
3953 if ((NODE != NULL) &&
3954 (NODE->type == XML_ENTITY_REF_NODE))
3955 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003956 } while ((NODE != NULL) &&
3957 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003958 (NODE->type != XML_TEXT_NODE) &&
3959 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003960 } else {
3961 DEBUG_VALID_MSG("element failed");
3962 ret = 0;
3963 break;
3964 }
3965 break;
3966 case XML_ELEMENT_CONTENT_OR:
3967 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003968 * Small optimization.
3969 */
3970 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3971 if ((NODE == NULL) ||
3972 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3973 DEPTH++;
3974 CONT = CONT->c2;
3975 goto cont;
3976 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003977 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3978 ret = (CONT->c1->prefix == NULL);
3979 } else if (CONT->c1->prefix == NULL) {
3980 ret = 0;
3981 } else {
3982 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3983 }
3984 if (ret == 0) {
3985 DEPTH++;
3986 CONT = CONT->c2;
3987 goto cont;
3988 }
Daniel Veillard85349052001-04-20 13:48:21 +00003989 }
3990
3991 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003992 * save the second branch 'or' branch
3993 */
3994 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003995 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3996 OCCURS, ROLLBACK_OR) < 0)
3997 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003998 DEPTH++;
3999 CONT = CONT->c1;
4000 goto cont;
4001 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00004002 /*
4003 * Small optimization.
4004 */
4005 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
4006 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
4007 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
4008 if ((NODE == NULL) ||
4009 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
4010 DEPTH++;
4011 CONT = CONT->c2;
4012 goto cont;
4013 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004014 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
4015 ret = (CONT->c1->prefix == NULL);
4016 } else if (CONT->c1->prefix == NULL) {
4017 ret = 0;
4018 } else {
4019 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
4020 }
4021 if (ret == 0) {
4022 DEPTH++;
4023 CONT = CONT->c2;
4024 goto cont;
4025 }
Daniel Veillard1d047672001-06-09 16:41:01 +00004026 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004027 DEPTH++;
4028 CONT = CONT->c1;
4029 goto cont;
4030 }
4031
4032 /*
4033 * At this point handle going up in the tree
4034 */
4035 if (ret == -1) {
4036 DEBUG_VALID_MSG("error found returning");
4037 return(ret);
4038 }
4039analyze:
4040 while (CONT != NULL) {
4041 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004042 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004043 * this level.
4044 */
4045 if (ret == 0) {
4046 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004047 xmlNodePtr cur;
4048
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004049 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004050 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004051 DEBUG_VALID_MSG("Once branch failed, rollback");
4052 if (vstateVPop(ctxt) < 0 ) {
4053 DEBUG_VALID_MSG("exhaustion, failed");
4054 return(0);
4055 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004056 if (cur != ctxt->vstate->node)
4057 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004058 goto cont;
4059 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00004060 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004061 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004062 DEBUG_VALID_MSG("Plus branch failed, rollback");
4063 if (vstateVPop(ctxt) < 0 ) {
4064 DEBUG_VALID_MSG("exhaustion, failed");
4065 return(0);
4066 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004067 if (cur != ctxt->vstate->node)
4068 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004069 goto cont;
4070 }
4071 DEBUG_VALID_MSG("Plus branch found");
4072 ret = 1;
4073 break;
4074 case XML_ELEMENT_CONTENT_MULT:
4075#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00004076 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004077 DEBUG_VALID_MSG("Mult branch failed");
4078 } else {
4079 DEBUG_VALID_MSG("Mult branch found");
4080 }
4081#endif
4082 ret = 1;
4083 break;
4084 case XML_ELEMENT_CONTENT_OPT:
4085 DEBUG_VALID_MSG("Option branch failed");
4086 ret = 1;
4087 break;
4088 }
4089 } else {
4090 switch (CONT->ocur) {
4091 case XML_ELEMENT_CONTENT_OPT:
4092 DEBUG_VALID_MSG("Option branch succeeded");
4093 ret = 1;
4094 break;
4095 case XML_ELEMENT_CONTENT_ONCE:
4096 DEBUG_VALID_MSG("Once branch succeeded");
4097 ret = 1;
4098 break;
4099 case XML_ELEMENT_CONTENT_PLUS:
4100 if (STATE == ROLLBACK_PARENT) {
4101 DEBUG_VALID_MSG("Plus branch rollback");
4102 ret = 1;
4103 break;
4104 }
4105 if (NODE == NULL) {
4106 DEBUG_VALID_MSG("Plus branch exhausted");
4107 ret = 1;
4108 break;
4109 }
4110 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004111 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004112 goto cont;
4113 case XML_ELEMENT_CONTENT_MULT:
4114 if (STATE == ROLLBACK_PARENT) {
4115 DEBUG_VALID_MSG("Mult branch rollback");
4116 ret = 1;
4117 break;
4118 }
4119 if (NODE == NULL) {
4120 DEBUG_VALID_MSG("Mult branch exhausted");
4121 ret = 1;
4122 break;
4123 }
4124 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00004125 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004126 goto cont;
4127 }
4128 }
4129 STATE = 0;
4130
4131 /*
4132 * Then act accordingly at the parent level
4133 */
Daniel Veillard5344c602001-12-31 16:37:34 +00004134 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004135 if (CONT->parent == NULL)
4136 break;
4137
4138 switch (CONT->parent->type) {
4139 case XML_ELEMENT_CONTENT_PCDATA:
4140 DEBUG_VALID_MSG("Error: parent pcdata");
4141 return(-1);
4142 case XML_ELEMENT_CONTENT_ELEMENT:
4143 DEBUG_VALID_MSG("Error: parent element");
4144 return(-1);
4145 case XML_ELEMENT_CONTENT_OR:
4146 if (ret == 1) {
4147 DEBUG_VALID_MSG("Or succeeded");
4148 CONT = CONT->parent;
4149 DEPTH--;
4150 } else {
4151 DEBUG_VALID_MSG("Or failed");
4152 CONT = CONT->parent;
4153 DEPTH--;
4154 }
4155 break;
4156 case XML_ELEMENT_CONTENT_SEQ:
4157 if (ret == 0) {
4158 DEBUG_VALID_MSG("Sequence failed");
4159 CONT = CONT->parent;
4160 DEPTH--;
4161 } else if (CONT == CONT->parent->c1) {
4162 DEBUG_VALID_MSG("Sequence testing 2nd branch");
4163 CONT = CONT->parent->c2;
4164 goto cont;
4165 } else {
4166 DEBUG_VALID_MSG("Sequence succeeded");
4167 CONT = CONT->parent;
4168 DEPTH--;
4169 }
4170 }
4171 }
4172 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004173 xmlNodePtr cur;
4174
4175 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004176 DEBUG_VALID_MSG("Failed, remaining input, rollback");
4177 if (vstateVPop(ctxt) < 0 ) {
4178 DEBUG_VALID_MSG("exhaustion, failed");
4179 return(0);
4180 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004181 if (cur != ctxt->vstate->node)
4182 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004183 goto cont;
4184 }
4185 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004186 xmlNodePtr cur;
4187
4188 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004189 DEBUG_VALID_MSG("Failure, rollback");
4190 if (vstateVPop(ctxt) < 0 ) {
4191 DEBUG_VALID_MSG("exhaustion, failed");
4192 return(0);
4193 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004194 if (cur != ctxt->vstate->node)
4195 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004196 goto cont;
4197 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004198 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004199}
4200
4201/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00004202 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004203 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00004204 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004205 * @content: An element
4206 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
4207 *
4208 * This will dump the list of elements to the buffer
4209 * Intended just for the debug routine
4210 */
4211static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00004212xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004213 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00004214 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004215
4216 if (node == NULL) return;
4217 if (glob) strcat(buf, "(");
4218 cur = node;
4219 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004220 len = strlen(buf);
4221 if (size - len < 50) {
4222 if ((size - len > 4) && (buf[len - 1] != '.'))
4223 strcat(buf, " ...");
4224 return;
4225 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004226 switch (cur->type) {
4227 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004228 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004229 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004230 if ((size - len > 4) && (buf[len - 1] != '.'))
4231 strcat(buf, " ...");
4232 return;
4233 }
4234 strcat(buf, (char *) cur->ns->prefix);
4235 strcat(buf, ":");
4236 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00004237 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00004238 if ((size - len > 4) && (buf[len - 1] != '.'))
4239 strcat(buf, " ...");
4240 return;
4241 }
4242 strcat(buf, (char *) cur->name);
4243 if (cur->next != NULL)
4244 strcat(buf, " ");
4245 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004246 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004247 if (xmlIsBlankNode(cur))
4248 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004249 case XML_CDATA_SECTION_NODE:
4250 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004251 strcat(buf, "CDATA");
4252 if (cur->next != NULL)
4253 strcat(buf, " ");
4254 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004255 case XML_ATTRIBUTE_NODE:
4256 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004257#ifdef LIBXML_DOCB_ENABLED
4258 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004259#endif
4260 case XML_HTML_DOCUMENT_NODE:
4261 case XML_DOCUMENT_TYPE_NODE:
4262 case XML_DOCUMENT_FRAG_NODE:
4263 case XML_NOTATION_NODE:
4264 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004265 strcat(buf, "???");
4266 if (cur->next != NULL)
4267 strcat(buf, " ");
4268 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004269 case XML_ENTITY_NODE:
4270 case XML_PI_NODE:
4271 case XML_DTD_NODE:
4272 case XML_COMMENT_NODE:
4273 case XML_ELEMENT_DECL:
4274 case XML_ATTRIBUTE_DECL:
4275 case XML_ENTITY_DECL:
4276 case XML_XINCLUDE_START:
4277 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004278 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004279 }
4280 cur = cur->next;
4281 }
4282 if (glob) strcat(buf, ")");
4283}
4284
4285/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004286 * xmlValidateElementContent:
4287 * @ctxt: the validation context
4288 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004289 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004290 * @warn: emit the error message
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004291 * @parent: the parent element (for error reporting)
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004292 *
4293 * Try to validate the content model of an element
4294 *
4295 * returns 1 if valid or 0 if not and -1 in case of error
4296 */
4297
4298static int
4299xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004300 xmlElementPtr elemDecl, int warn, xmlNodePtr parent) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004301 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004302 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004303 xmlElementContentPtr cont;
4304 const xmlChar *name;
4305
4306 if (elemDecl == NULL)
4307 return(-1);
4308 cont = elemDecl->content;
4309 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004310
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004311#ifdef LIBXML_REGEXP_ENABLED
4312 /* Build the regexp associated to the content model */
4313 if (elemDecl->contModel == NULL)
4314 ret = xmlValidBuildContentModel(ctxt, elemDecl);
4315 if (elemDecl->contModel == NULL) {
4316 ret = -1;
4317 } else {
4318 xmlRegExecCtxtPtr exec;
4319
4320 exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL);
4321 if (exec != NULL) {
4322 cur = child;
4323 while (cur != NULL) {
4324 switch (cur->type) {
4325 case XML_ENTITY_REF_NODE:
4326 /*
4327 * Push the current node to be able to roll back
4328 * and process within the entity
4329 */
4330 if ((cur->children != NULL) &&
4331 (cur->children->children != NULL)) {
4332 nodeVPush(ctxt, cur);
4333 cur = cur->children->children;
4334 continue;
4335 }
4336 break;
4337 case XML_TEXT_NODE:
4338 if (xmlIsBlankNode(cur))
4339 break;
4340 ret = 0;
4341 goto fail;
4342 case XML_CDATA_SECTION_NODE:
4343 TODO
4344 ret = 0;
4345 goto fail;
4346 case XML_ELEMENT_NODE:
4347 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
4348 xmlChar *QName;
4349 int len;
4350
4351 len = xmlStrlen(cur->name) +
4352 xmlStrlen(cur->ns->prefix) + 2;
4353 QName = xmlMalloc(len);
4354 if (QName == NULL) {
4355 ret = -1;
4356 goto fail;
4357 }
4358 snprintf((char *) QName, len, "%s:%s",
4359 (char *)cur->ns->prefix,
4360 (char *)cur->name);
4361 ret = xmlRegExecPushString(exec, QName, NULL);
4362 xmlFree(QName);
4363 } else {
4364 ret = xmlRegExecPushString(exec, cur->name, NULL);
4365 }
4366 break;
4367 default:
4368 break;
4369 }
4370 /*
4371 * Switch to next element
4372 */
4373 cur = cur->next;
4374 while (cur == NULL) {
4375 cur = nodeVPop(ctxt);
4376 if (cur == NULL)
4377 break;
4378 cur = cur->next;
4379 }
4380 }
4381 ret = xmlRegExecPushString(exec, NULL, NULL);
4382fail:
4383 xmlRegFreeExecCtxt(exec);
4384 }
4385 }
4386#else /* LIBXML_REGEXP_ENABLED */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004387 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004388 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004389 */
4390 ctxt->vstateMax = 8;
4391 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4392 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4393 if (ctxt->vstateTab == NULL) {
4394 xmlGenericError(xmlGenericErrorContext,
4395 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004396 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004397 }
4398 /*
4399 * The first entry in the stack is reserved to the current state
4400 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004401 ctxt->nodeMax = 0;
4402 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004403 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004404 ctxt->vstate = &ctxt->vstateTab[0];
4405 ctxt->vstateNr = 1;
4406 CONT = cont;
4407 NODE = child;
4408 DEPTH = 0;
4409 OCCURS = 0;
4410 STATE = 0;
4411 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004412 if ((ret == -3) && (warn)) {
4413 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004414 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004415 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004416 /*
4417 * An entities reference appeared at this level.
4418 * Buid a minimal representation of this node content
4419 * sufficient to run the validation process on it
4420 */
4421 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004422 cur = child;
4423 while (cur != NULL) {
4424 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004425 case XML_ENTITY_REF_NODE:
4426 /*
4427 * Push the current node to be able to roll back
4428 * and process within the entity
4429 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004430 if ((cur->children != NULL) &&
4431 (cur->children->children != NULL)) {
4432 nodeVPush(ctxt, cur);
4433 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004434 continue;
4435 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004436 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004437 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004438 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004439 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004440 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004441 case XML_CDATA_SECTION_NODE:
4442 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004443 case XML_ELEMENT_NODE:
4444 /*
4445 * Allocate a new node and minimally fills in
4446 * what's required
4447 */
4448 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4449 if (tmp == NULL) {
4450 xmlGenericError(xmlGenericErrorContext,
4451 "xmlValidateElementContent : malloc failed\n");
4452 xmlFreeNodeList(repl);
4453 ret = -1;
4454 goto done;
4455 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004456 tmp->type = cur->type;
4457 tmp->name = cur->name;
4458 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004459 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004460 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004461 if (repl == NULL)
4462 repl = last = tmp;
4463 else {
4464 last->next = tmp;
4465 last = tmp;
4466 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004467 if (cur->type == XML_CDATA_SECTION_NODE) {
4468 /*
4469 * E59 spaces in CDATA does not match the
4470 * nonterminal S
4471 */
4472 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4473 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004474 break;
4475 default:
4476 break;
4477 }
4478 /*
4479 * Switch to next element
4480 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004481 cur = cur->next;
4482 while (cur == NULL) {
4483 cur = nodeVPop(ctxt);
4484 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004485 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004486 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004487 }
4488 }
4489
4490 /*
4491 * Relaunch the validation
4492 */
4493 ctxt->vstate = &ctxt->vstateTab[0];
4494 ctxt->vstateNr = 1;
4495 CONT = cont;
4496 NODE = repl;
4497 DEPTH = 0;
4498 OCCURS = 0;
4499 STATE = 0;
4500 ret = xmlValidateElementType(ctxt);
4501 }
Daniel Veillarda646cfd2002-09-17 21:50:03 +00004502#endif /* LIBXML_REGEXP_ENABLED */
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004503 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004504 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4505 char expr[5000];
4506 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004507
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004508 expr[0] = 0;
4509 xmlSnprintfElementContent(expr, 5000, cont, 1);
4510 list[0] = 0;
4511 if (repl != NULL)
4512 xmlSnprintfElements(list, 5000, repl, 1);
4513 else
4514 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004515
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004516 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004517 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004518 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004519 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004520 name, expr, list);
4521 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004522 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004523 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004524 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004525 expr, list);
4526 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004527 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004528 if (name != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004529 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004530 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004531 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004532 name);
4533 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004534 if (parent != NULL) VECTXT(ctxt, parent);
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004535 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004536 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004537 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004538 }
4539 ret = 0;
4540 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004541 if (ret == -3)
4542 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004543
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004544done:
4545 /*
4546 * Deallocate the copy if done, and free up the validation stack
4547 */
4548 while (repl != NULL) {
4549 tmp = repl->next;
4550 xmlFree(repl);
4551 repl = tmp;
4552 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004553 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004554 if (ctxt->vstateTab != NULL) {
4555 xmlFree(ctxt->vstateTab);
4556 ctxt->vstateTab = NULL;
4557 }
4558 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004559 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004560 if (ctxt->nodeTab != NULL) {
4561 xmlFree(ctxt->nodeTab);
4562 ctxt->nodeTab = NULL;
4563 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004564 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004565
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004566}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004567
Owen Taylor3473f882001-02-23 17:55:21 +00004568/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004569 * xmlValidateCdataElement:
4570 * @ctxt: the validation context
4571 * @doc: a document instance
4572 * @elem: an element instance
4573 *
4574 * Check that an element follows #CDATA
4575 *
4576 * returns 1 if valid or 0 otherwise
4577 */
4578static int
4579xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4580 xmlNodePtr elem) {
4581 int ret = 1;
4582 xmlNodePtr cur, child;
4583
4584 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4585 return(0);
4586
4587 child = elem->children;
4588
4589 cur = child;
4590 while (cur != NULL) {
4591 switch (cur->type) {
4592 case XML_ENTITY_REF_NODE:
4593 /*
4594 * Push the current node to be able to roll back
4595 * and process within the entity
4596 */
4597 if ((cur->children != NULL) &&
4598 (cur->children->children != NULL)) {
4599 nodeVPush(ctxt, cur);
4600 cur = cur->children->children;
4601 continue;
4602 }
4603 break;
4604 case XML_COMMENT_NODE:
4605 case XML_PI_NODE:
4606 case XML_TEXT_NODE:
4607 case XML_CDATA_SECTION_NODE:
4608 break;
4609 default:
4610 ret = 0;
4611 goto done;
4612 }
4613 /*
4614 * Switch to next element
4615 */
4616 cur = cur->next;
4617 while (cur == NULL) {
4618 cur = nodeVPop(ctxt);
4619 if (cur == NULL)
4620 break;
4621 cur = cur->next;
4622 }
4623 }
4624done:
4625 ctxt->nodeMax = 0;
4626 ctxt->nodeNr = 0;
4627 if (ctxt->nodeTab != NULL) {
4628 xmlFree(ctxt->nodeTab);
4629 ctxt->nodeTab = NULL;
4630 }
4631 return(ret);
4632}
4633
4634/**
Owen Taylor3473f882001-02-23 17:55:21 +00004635 * xmlValidateOneElement:
4636 * @ctxt: the validation context
4637 * @doc: a document instance
4638 * @elem: an element instance
4639 *
4640 * Try to validate a single element and it's attributes,
4641 * basically it does the following checks as described by the
4642 * XML-1.0 recommendation:
4643 * - [ VC: Element Valid ]
4644 * - [ VC: Required Attribute ]
4645 * Then call xmlValidateOneAttribute() for each attribute present.
4646 *
4647 * The ID/IDREF checkings are done separately
4648 *
4649 * returns 1 if valid or 0 otherwise
4650 */
4651
4652int
4653xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4654 xmlNodePtr elem) {
4655 xmlElementPtr elemDecl = NULL;
4656 xmlElementContentPtr cont;
4657 xmlAttributePtr attr;
4658 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004659 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004660 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004661 const xmlChar *prefix = NULL;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004662 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004663
4664 CHECK_DTD;
4665
4666 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004667 switch (elem->type) {
4668 case XML_ATTRIBUTE_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004669 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004670 VERROR(ctxt->userData,
4671 "Attribute element not expected here\n");
4672 return(0);
4673 case XML_TEXT_NODE:
4674 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004675 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004676 VERROR(ctxt->userData, "Text element has childs !\n");
4677 return(0);
4678 }
4679 if (elem->properties != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004680 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004681 VERROR(ctxt->userData, "Text element has attributes !\n");
4682 return(0);
4683 }
4684 if (elem->ns != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004685 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004686 VERROR(ctxt->userData, "Text element has namespace !\n");
4687 return(0);
4688 }
4689 if (elem->nsDef != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004690 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004691 VERROR(ctxt->userData,
4692 "Text element carries namespace definitions !\n");
4693 return(0);
4694 }
4695 if (elem->content == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004696 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004697 VERROR(ctxt->userData,
4698 "Text element has no content !\n");
4699 return(0);
4700 }
4701 return(1);
4702 case XML_XINCLUDE_START:
4703 case XML_XINCLUDE_END:
4704 return(1);
4705 case XML_CDATA_SECTION_NODE:
4706 case XML_ENTITY_REF_NODE:
4707 case XML_PI_NODE:
4708 case XML_COMMENT_NODE:
4709 return(1);
4710 case XML_ENTITY_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004711 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004712 VERROR(ctxt->userData,
4713 "Entity element not expected here\n");
4714 return(0);
4715 case XML_NOTATION_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004716 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004717 VERROR(ctxt->userData,
4718 "Notation element not expected here\n");
4719 return(0);
4720 case XML_DOCUMENT_NODE:
4721 case XML_DOCUMENT_TYPE_NODE:
4722 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004723 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004724 VERROR(ctxt->userData,
4725 "Document element not expected here\n");
4726 return(0);
4727 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004728 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004729 VERROR(ctxt->userData,
4730 "\n");
4731 return(0);
4732 case XML_ELEMENT_NODE:
4733 break;
4734 default:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004735 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004736 VERROR(ctxt->userData,
4737 "unknown element type %d\n", elem->type);
4738 return(0);
4739 }
4740 if (elem->name == NULL) return(0);
4741
4742 /*
4743 * Fetch the declaration for the qualified name
4744 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004745 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4746 prefix = elem->ns->prefix;
4747
4748 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004749 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004750 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004751 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004752 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004753 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004754 if (elemDecl != NULL)
4755 extsubset = 1;
4756 }
Owen Taylor3473f882001-02-23 17:55:21 +00004757 }
4758
4759 /*
4760 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004761 * This is "non-strict" validation should be done on the
4762 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004763 */
4764 if (elemDecl == NULL) {
4765 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004766 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004767 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004768 if (elemDecl != NULL)
4769 extsubset = 1;
4770 }
Owen Taylor3473f882001-02-23 17:55:21 +00004771 }
4772 if (elemDecl == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004773 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004774 VERROR(ctxt->userData, "No declaration for element %s\n",
4775 elem->name);
4776 return(0);
4777 }
4778
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004779 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004780 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004781 case XML_ELEMENT_TYPE_UNDEFINED:
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004782 VECTXT(ctxt, elem);
Daniel Veillarda10efa82001-04-18 13:09:01 +00004783 VERROR(ctxt->userData, "No declaration for element %s\n",
4784 elem->name);
4785 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004786 case XML_ELEMENT_TYPE_EMPTY:
4787 if (elem->children != NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004788 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004789 VERROR(ctxt->userData,
4790 "Element %s was declared EMPTY this one has content\n",
4791 elem->name);
4792 ret = 0;
4793 }
4794 break;
4795 case XML_ELEMENT_TYPE_ANY:
4796 /* I don't think anything is required then */
4797 break;
4798 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004799
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004800 /* simple case of declared as #PCDATA */
4801 if ((elemDecl->content != NULL) &&
4802 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4803 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4804 if (!ret) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004805 VECTXT(ctxt, elem);
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004806 VERROR(ctxt->userData,
4807 "Element %s was declared #PCDATA but contains non text nodes\n",
4808 elem->name);
4809 }
4810 break;
4811 }
Owen Taylor3473f882001-02-23 17:55:21 +00004812 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004813 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004814 while (child != NULL) {
4815 if (child->type == XML_ELEMENT_NODE) {
4816 name = child->name;
4817 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4818 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004819 snprintf((char *) qname, sizeof(qname), "%s:%s",
4820 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004821 qname[sizeof(qname) - 1] = 0;
4822 cont = elemDecl->content;
4823 while (cont != NULL) {
4824 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4825 if (xmlStrEqual(cont->name, qname)) break;
4826 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4827 (cont->c1 != NULL) &&
4828 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4829 if (xmlStrEqual(cont->c1->name, qname)) break;
4830 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4831 (cont->c1 == NULL) ||
4832 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4833 /* Internal error !!! */
4834 xmlGenericError(xmlGenericErrorContext,
4835 "Internal: MIXED struct bad\n");
4836 break;
4837 }
4838 cont = cont->c2;
4839 }
4840 if (cont != NULL)
4841 goto child_ok;
4842 }
4843 cont = elemDecl->content;
4844 while (cont != NULL) {
4845 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4846 if (xmlStrEqual(cont->name, name)) break;
4847 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4848 (cont->c1 != NULL) &&
4849 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4850 if (xmlStrEqual(cont->c1->name, name)) break;
4851 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4852 (cont->c1 == NULL) ||
4853 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4854 /* Internal error !!! */
4855 xmlGenericError(xmlGenericErrorContext,
4856 "Internal: MIXED struct bad\n");
4857 break;
4858 }
4859 cont = cont->c2;
4860 }
4861 if (cont == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004862 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004863 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004864 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004865 name, elem->name);
4866 ret = 0;
4867 }
4868 }
4869child_ok:
4870 child = child->next;
4871 }
4872 break;
4873 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004874 if ((doc->standalone == 1) && (extsubset == 1)) {
4875 /*
4876 * VC: Standalone Document Declaration
4877 * - element types with element content, if white space
4878 * occurs directly within any instance of those types.
4879 */
4880 child = elem->children;
4881 while (child != NULL) {
4882 if (child->type == XML_TEXT_NODE) {
4883 const xmlChar *content = child->content;
4884
4885 while (IS_BLANK(*content))
4886 content++;
4887 if (*content == 0) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004888 VECTXT(ctxt, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004889 VERROR(ctxt->userData,
4890"standalone: %s declared in the external subset contains white spaces nodes\n",
4891 elem->name);
4892 ret = 0;
4893 break;
4894 }
4895 }
4896 child =child->next;
4897 }
4898 }
Owen Taylor3473f882001-02-23 17:55:21 +00004899 child = elem->children;
4900 cont = elemDecl->content;
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004901 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004902 if (tmp <= 0)
4903 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004904 break;
4905 }
4906
4907 /* [ VC: Required Attribute ] */
4908 attr = elemDecl->attributes;
4909 while (attr != NULL) {
4910 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004911 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00004912
Daniel Veillarde4301c82002-02-13 13:32:35 +00004913 if ((attr->prefix == NULL) &&
4914 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4915 xmlNsPtr ns;
4916
4917 ns = elem->nsDef;
4918 while (ns != NULL) {
4919 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00004920 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004921 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00004922 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004923 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4924 xmlNsPtr ns;
4925
4926 ns = elem->nsDef;
4927 while (ns != NULL) {
4928 if (xmlStrEqual(attr->name, ns->prefix))
4929 goto found;
4930 ns = ns->next;
4931 }
4932 } else {
4933 xmlAttrPtr attrib;
4934
4935 attrib = elem->properties;
4936 while (attrib != NULL) {
4937 if (xmlStrEqual(attrib->name, attr->name)) {
4938 if (attr->prefix != NULL) {
4939 xmlNsPtr nameSpace = attrib->ns;
4940
4941 if (nameSpace == NULL)
4942 nameSpace = elem->ns;
4943 /*
4944 * qualified names handling is problematic, having a
4945 * different prefix should be possible but DTDs don't
4946 * allow to define the URI instead of the prefix :-(
4947 */
4948 if (nameSpace == NULL) {
4949 if (qualified < 0)
4950 qualified = 0;
4951 } else if (!xmlStrEqual(nameSpace->prefix,
4952 attr->prefix)) {
4953 if (qualified < 1)
4954 qualified = 1;
4955 } else
4956 goto found;
4957 } else {
4958 /*
4959 * We should allow applications to define namespaces
4960 * for their application even if the DTD doesn't
4961 * carry one, otherwise, basically we would always
4962 * break.
4963 */
4964 goto found;
4965 }
4966 }
4967 attrib = attrib->next;
4968 }
Owen Taylor3473f882001-02-23 17:55:21 +00004969 }
4970 if (qualified == -1) {
4971 if (attr->prefix == NULL) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004972 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004973 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004974 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004975 elem->name, attr->name);
4976 ret = 0;
4977 } else {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00004978 VECTXT(ctxt, elem);
Owen Taylor3473f882001-02-23 17:55:21 +00004979 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004980 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004981 elem->name, attr->prefix,attr->name);
4982 ret = 0;
4983 }
4984 } else if (qualified == 0) {
4985 VWARNING(ctxt->userData,
4986 "Element %s required attribute %s:%s has no prefix\n",
4987 elem->name, attr->prefix,attr->name);
4988 } else if (qualified == 1) {
4989 VWARNING(ctxt->userData,
4990 "Element %s required attribute %s:%s has different prefix\n",
4991 elem->name, attr->prefix,attr->name);
4992 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004993 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
4994 /*
4995 * Special tests checking #FIXED namespace declarations
4996 * have the right value since this is not done as an
4997 * attribute checking
4998 */
4999 if ((attr->prefix == NULL) &&
5000 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
5001 xmlNsPtr ns;
5002
5003 ns = elem->nsDef;
5004 while (ns != NULL) {
5005 if (ns->prefix == NULL) {
5006 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005007 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005008 VERROR(ctxt->userData,
5009 "Element %s namespace name for default namespace does not match the DTD\n",
5010 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00005011 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005012 }
5013 goto found;
5014 }
5015 ns = ns->next;
5016 }
5017 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
5018 xmlNsPtr ns;
5019
5020 ns = elem->nsDef;
5021 while (ns != NULL) {
5022 if (xmlStrEqual(attr->name, ns->prefix)) {
5023 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
Daniel Veillardb9cd8b42002-09-05 10:58:49 +00005024 VECTXT(ctxt, elem);
Daniel Veillarde4301c82002-02-13 13:32:35 +00005025 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005026 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00005027 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00005028 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00005029 }
5030 goto found;
5031 }
5032 ns = ns->next;
5033 }
5034 }
Owen Taylor3473f882001-02-23 17:55:21 +00005035 }
5036found:
5037 attr = attr->nexth;
5038 }
5039 return(ret);
5040}
5041
5042/**
5043 * xmlValidateRoot:
5044 * @ctxt: the validation context
5045 * @doc: a document instance
5046 *
5047 * Try to validate a the root element
5048 * basically it does the following check as described by the
5049 * XML-1.0 recommendation:
5050 * - [ VC: Root Element Type ]
5051 * it doesn't try to recurse or apply other check to the element
5052 *
5053 * returns 1 if valid or 0 otherwise
5054 */
5055
5056int
5057xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5058 xmlNodePtr root;
5059 if (doc == NULL) return(0);
5060
5061 root = xmlDocGetRootElement(doc);
5062 if ((root == NULL) || (root->name == NULL)) {
5063 VERROR(ctxt->userData, "Not valid: no root element\n");
5064 return(0);
5065 }
5066
5067 /*
5068 * When doing post validation against a separate DTD, those may
5069 * no internal subset has been generated
5070 */
5071 if ((doc->intSubset != NULL) &&
5072 (doc->intSubset->name != NULL)) {
5073 /*
5074 * Check first the document root against the NQName
5075 */
5076 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
5077 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
5078 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00005079 snprintf((char *) qname, sizeof(qname), "%s:%s",
5080 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00005081 qname[sizeof(qname) - 1] = 0;
5082 if (xmlStrEqual(doc->intSubset->name, qname))
5083 goto name_ok;
5084 }
5085 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
5086 (xmlStrEqual(root->name, BAD_CAST "html")))
5087 goto name_ok;
Daniel Veillard76575762002-09-05 14:21:15 +00005088 VECTXT(ctxt, root);
Owen Taylor3473f882001-02-23 17:55:21 +00005089 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005090 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005091 root->name, doc->intSubset->name);
5092 return(0);
5093
5094 }
5095 }
5096name_ok:
5097 return(1);
5098}
5099
5100
5101/**
5102 * xmlValidateElement:
5103 * @ctxt: the validation context
5104 * @doc: a document instance
5105 * @elem: an element instance
5106 *
5107 * Try to validate the subtree under an element
5108 *
5109 * returns 1 if valid or 0 otherwise
5110 */
5111
5112int
5113xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
5114 xmlNodePtr child;
5115 xmlAttrPtr attr;
5116 xmlChar *value;
5117 int ret = 1;
5118
5119 if (elem == NULL) return(0);
5120
5121 /*
5122 * XInclude elements were added after parsing in the infoset,
5123 * they don't really mean anything validation wise.
5124 */
5125 if ((elem->type == XML_XINCLUDE_START) ||
5126 (elem->type == XML_XINCLUDE_END))
5127 return(1);
5128
5129 CHECK_DTD;
5130
Daniel Veillard10ea86c2001-06-20 13:55:33 +00005131 /*
5132 * Entities references have to be handled separately
5133 */
5134 if (elem->type == XML_ENTITY_REF_NODE) {
5135 return(1);
5136 }
5137
Owen Taylor3473f882001-02-23 17:55:21 +00005138 ret &= xmlValidateOneElement(ctxt, doc, elem);
5139 attr = elem->properties;
5140 while(attr != NULL) {
5141 value = xmlNodeListGetString(doc, attr->children, 0);
5142 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
5143 if (value != NULL)
5144 xmlFree(value);
5145 attr= attr->next;
5146 }
5147 child = elem->children;
5148 while (child != NULL) {
5149 ret &= xmlValidateElement(ctxt, doc, child);
5150 child = child->next;
5151 }
5152
5153 return(ret);
5154}
5155
Daniel Veillard8730c562001-02-26 10:49:57 +00005156/**
5157 * xmlValidateRef:
5158 * @ref: A reference to be validated
5159 * @ctxt: Validation context
5160 * @name: Name of ID we are searching for
5161 *
5162 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005163static void
Daniel Veillard8730c562001-02-26 10:49:57 +00005164xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00005165 const xmlChar *name) {
5166 xmlAttrPtr id;
5167 xmlAttrPtr attr;
5168
5169 if (ref == NULL)
5170 return;
5171 attr = ref->attr;
5172 if (attr == NULL)
5173 return;
5174 if (attr->atype == XML_ATTRIBUTE_IDREF) {
5175 id = xmlGetID(ctxt->doc, name);
5176 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005177 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005178 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005179 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005180 attr->name, name);
5181 ctxt->valid = 0;
5182 }
5183 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
5184 xmlChar *dup, *str = NULL, *cur, save;
5185
5186 dup = xmlStrdup(name);
5187 if (dup == NULL) {
5188 ctxt->valid = 0;
5189 return;
5190 }
5191 cur = dup;
5192 while (*cur != 0) {
5193 str = cur;
5194 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
5195 save = *cur;
5196 *cur = 0;
5197 id = xmlGetID(ctxt->doc, str);
5198 if (id == NULL) {
Daniel Veillard76575762002-09-05 14:21:15 +00005199 VECTXT(ctxt, attr->parent);
Owen Taylor3473f882001-02-23 17:55:21 +00005200 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005201 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00005202 attr->name, str);
5203 ctxt->valid = 0;
5204 }
5205 if (save == 0)
5206 break;
5207 *cur = save;
5208 while (IS_BLANK(*cur)) cur++;
5209 }
5210 xmlFree(dup);
5211 }
5212}
5213
5214/**
Daniel Veillard8730c562001-02-26 10:49:57 +00005215 * xmlWalkValidateList:
5216 * @data: Contents of current link
5217 * @user: Value supplied by the user
5218 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00005219 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00005220 */
5221static int
5222xmlWalkValidateList(const void *data, const void *user)
5223{
5224 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
5225 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
5226 return 1;
5227}
5228
5229/**
5230 * xmlValidateCheckRefCallback:
5231 * @ref_list: List of references
5232 * @ctxt: Validation context
5233 * @name: Name of ID we are searching for
5234 *
5235 */
5236static void
5237xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
5238 const xmlChar *name) {
5239 xmlValidateMemo memo;
5240
5241 if (ref_list == NULL)
5242 return;
5243 memo.ctxt = ctxt;
5244 memo.name = name;
5245
5246 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
5247
5248}
5249
5250/**
Owen Taylor3473f882001-02-23 17:55:21 +00005251 * xmlValidateDocumentFinal:
5252 * @ctxt: the validation context
5253 * @doc: a document instance
5254 *
5255 * Does the final step for the document validation once all the
5256 * incremental validation steps have been completed
5257 *
5258 * basically it does the following checks described by the XML Rec
5259 *
5260 *
5261 * returns 1 if valid or 0 otherwise
5262 */
5263
5264int
5265xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5266 xmlRefTablePtr table;
5267
5268 if (doc == NULL) {
5269 xmlGenericError(xmlGenericErrorContext,
5270 "xmlValidateDocumentFinal: doc == NULL\n");
5271 return(0);
5272 }
5273
5274 /*
5275 * Check all the NOTATION/NOTATIONS attributes
5276 */
5277 /*
5278 * Check all the ENTITY/ENTITIES attributes definition for validity
5279 */
5280 /*
5281 * Check all the IDREF/IDREFS attributes definition for validity
5282 */
5283 table = (xmlRefTablePtr) doc->refs;
5284 ctxt->doc = doc;
5285 ctxt->valid = 1;
5286 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
5287 return(ctxt->valid);
5288}
5289
5290/**
5291 * xmlValidateDtd:
5292 * @ctxt: the validation context
5293 * @doc: a document instance
5294 * @dtd: a dtd instance
5295 *
5296 * Try to validate the document against the dtd instance
5297 *
5298 * basically it does check all the definitions in the DtD.
5299 *
5300 * returns 1 if valid or 0 otherwise
5301 */
5302
5303int
5304xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
5305 int ret;
5306 xmlDtdPtr oldExt;
5307 xmlNodePtr root;
5308
5309 if (dtd == NULL) return(0);
5310 if (doc == NULL) return(0);
5311 oldExt = doc->extSubset;
5312 doc->extSubset = dtd;
5313 ret = xmlValidateRoot(ctxt, doc);
5314 if (ret == 0) {
5315 doc->extSubset = oldExt;
5316 return(ret);
5317 }
5318 if (doc->ids != NULL) {
5319 xmlFreeIDTable(doc->ids);
5320 doc->ids = NULL;
5321 }
5322 if (doc->refs != NULL) {
5323 xmlFreeRefTable(doc->refs);
5324 doc->refs = NULL;
5325 }
5326 root = xmlDocGetRootElement(doc);
5327 ret = xmlValidateElement(ctxt, doc, root);
5328 ret &= xmlValidateDocumentFinal(ctxt, doc);
5329 doc->extSubset = oldExt;
5330 return(ret);
5331}
5332
Daniel Veillard56a4cb82001-03-24 17:00:36 +00005333static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005334xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
5335 const xmlChar *name ATTRIBUTE_UNUSED) {
5336 if (cur == NULL)
5337 return;
5338 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
5339 xmlChar *notation = cur->content;
5340
Daniel Veillard878eab02002-02-19 13:46:09 +00005341 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005342 int ret;
5343
5344 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
5345 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00005346 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005347 }
5348 }
5349 }
5350}
5351
5352static void
Owen Taylor3473f882001-02-23 17:55:21 +00005353xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00005354 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005355 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00005356 xmlDocPtr doc;
5357 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005358
Owen Taylor3473f882001-02-23 17:55:21 +00005359 if (cur == NULL)
5360 return;
5361 switch (cur->atype) {
5362 case XML_ATTRIBUTE_CDATA:
5363 case XML_ATTRIBUTE_ID:
5364 case XML_ATTRIBUTE_IDREF :
5365 case XML_ATTRIBUTE_IDREFS:
5366 case XML_ATTRIBUTE_NMTOKEN:
5367 case XML_ATTRIBUTE_NMTOKENS:
5368 case XML_ATTRIBUTE_ENUMERATION:
5369 break;
5370 case XML_ATTRIBUTE_ENTITY:
5371 case XML_ATTRIBUTE_ENTITIES:
5372 case XML_ATTRIBUTE_NOTATION:
5373 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005374
5375 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
5376 cur->atype, cur->defaultValue);
5377 if ((ret == 0) && (ctxt->valid == 1))
5378 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005379 }
5380 if (cur->tree != NULL) {
5381 xmlEnumerationPtr tree = cur->tree;
5382 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005383 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00005384 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005385 if ((ret == 0) && (ctxt->valid == 1))
5386 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005387 tree = tree->next;
5388 }
5389 }
5390 }
Daniel Veillard878eab02002-02-19 13:46:09 +00005391 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
5392 doc = cur->doc;
5393 if ((doc == NULL) || (cur->elem == NULL)) {
5394 VERROR(ctxt->userData,
5395 "xmlValidateAttributeCallback(%s): internal error\n",
5396 cur->name);
5397 return;
5398 }
5399 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
5400 if (elem == NULL)
5401 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
5402 if (elem == NULL) {
5403 VERROR(ctxt->userData,
5404 "attribute %s: could not find decl for element %s\n",
5405 cur->name, cur->elem);
5406 return;
5407 }
5408 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
5409 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005410 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00005411 cur->name, cur->elem);
5412 ctxt->valid = 0;
5413 }
5414 }
Owen Taylor3473f882001-02-23 17:55:21 +00005415}
5416
5417/**
5418 * xmlValidateDtdFinal:
5419 * @ctxt: the validation context
5420 * @doc: a document instance
5421 *
5422 * Does the final step for the dtds validation once all the
5423 * subsets have been parsed
5424 *
5425 * basically it does the following checks described by the XML Rec
5426 * - check that ENTITY and ENTITIES type attributes default or
5427 * possible values matches one of the defined entities.
5428 * - check that NOTATION type attributes default or
5429 * possible values matches one of the defined notations.
5430 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005431 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00005432 */
5433
5434int
5435xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00005436 xmlDtdPtr dtd;
5437 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005438 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00005439
5440 if (doc == NULL) return(0);
5441 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5442 return(0);
5443 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005444 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005445 dtd = doc->intSubset;
5446 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5447 table = (xmlAttributeTablePtr) dtd->attributes;
5448 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005449 }
5450 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005451 entities = (xmlEntitiesTablePtr) dtd->entities;
5452 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5453 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005454 }
5455 dtd = doc->extSubset;
5456 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5457 table = (xmlAttributeTablePtr) dtd->attributes;
5458 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005459 }
5460 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005461 entities = (xmlEntitiesTablePtr) dtd->entities;
5462 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5463 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005464 }
5465 return(ctxt->valid);
5466}
5467
5468/**
5469 * xmlValidateDocument:
5470 * @ctxt: the validation context
5471 * @doc: a document instance
5472 *
5473 * Try to validate the document instance
5474 *
5475 * basically it does the all the checks described by the XML Rec
5476 * i.e. validates the internal and external subset (if present)
5477 * and validate the document tree.
5478 *
5479 * returns 1 if valid or 0 otherwise
5480 */
5481
5482int
5483xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5484 int ret;
5485 xmlNodePtr root;
5486
5487 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5488 return(0);
5489 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
5490 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
5491 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
5492 doc->intSubset->SystemID);
5493 if (doc->extSubset == NULL) {
5494 if (doc->intSubset->SystemID != NULL) {
5495 VERROR(ctxt->userData,
5496 "Could not load the external subset \"%s\"\n",
5497 doc->intSubset->SystemID);
5498 } else {
5499 VERROR(ctxt->userData,
5500 "Could not load the external subset \"%s\"\n",
5501 doc->intSubset->ExternalID);
5502 }
5503 return(0);
5504 }
5505 }
5506
5507 if (doc->ids != NULL) {
5508 xmlFreeIDTable(doc->ids);
5509 doc->ids = NULL;
5510 }
5511 if (doc->refs != NULL) {
5512 xmlFreeRefTable(doc->refs);
5513 doc->refs = NULL;
5514 }
5515 ret = xmlValidateDtdFinal(ctxt, doc);
5516 if (!xmlValidateRoot(ctxt, doc)) return(0);
5517
5518 root = xmlDocGetRootElement(doc);
5519 ret &= xmlValidateElement(ctxt, doc, root);
5520 ret &= xmlValidateDocumentFinal(ctxt, doc);
5521 return(ret);
5522}
5523
5524
5525/************************************************************************
5526 * *
5527 * Routines for dynamic validation editing *
5528 * *
5529 ************************************************************************/
5530
5531/**
5532 * xmlValidGetPotentialChildren:
5533 * @ctree: an element content tree
5534 * @list: an array to store the list of child names
5535 * @len: a pointer to the number of element in the list
5536 * @max: the size of the array
5537 *
5538 * Build/extend a list of potential children allowed by the content tree
5539 *
5540 * returns the number of element in the list, or -1 in case of error.
5541 */
5542
5543int
5544xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
5545 int *len, int max) {
5546 int i;
5547
5548 if ((ctree == NULL) || (list == NULL) || (len == NULL))
5549 return(-1);
5550 if (*len >= max) return(*len);
5551
5552 switch (ctree->type) {
5553 case XML_ELEMENT_CONTENT_PCDATA:
5554 for (i = 0; i < *len;i++)
5555 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
5556 list[(*len)++] = BAD_CAST "#PCDATA";
5557 break;
5558 case XML_ELEMENT_CONTENT_ELEMENT:
5559 for (i = 0; i < *len;i++)
5560 if (xmlStrEqual(ctree->name, list[i])) return(*len);
5561 list[(*len)++] = ctree->name;
5562 break;
5563 case XML_ELEMENT_CONTENT_SEQ:
5564 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5565 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5566 break;
5567 case XML_ELEMENT_CONTENT_OR:
5568 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5569 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5570 break;
5571 }
5572
5573 return(*len);
5574}
5575
5576/**
5577 * xmlValidGetValidElements:
5578 * @prev: an element to insert after
5579 * @next: an element to insert next
5580 * @list: an array to store the list of child names
5581 * @max: the size of the array
5582 *
5583 * This function returns the list of authorized children to insert
5584 * within an existing tree while respecting the validity constraints
5585 * forced by the Dtd. The insertion point is defined using @prev and
5586 * @next in the following ways:
5587 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
5588 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
5589 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
5590 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
5591 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
5592 *
5593 * pointers to the element names are inserted at the beginning of the array
5594 * and do not need to be freed.
5595 *
5596 * returns the number of element in the list, or -1 in case of error. If
5597 * the function returns the value @max the caller is invited to grow the
5598 * receiving array and retry.
5599 */
5600
5601int
5602xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
5603 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005604 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005605 int nb_valid_elements = 0;
5606 const xmlChar *elements[256];
5607 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005608 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005609
5610 xmlNode *ref_node;
5611 xmlNode *parent;
5612 xmlNode *test_node;
5613
5614 xmlNode *prev_next;
5615 xmlNode *next_prev;
5616 xmlNode *parent_childs;
5617 xmlNode *parent_last;
5618
5619 xmlElement *element_desc;
5620
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00005621 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005622
Owen Taylor3473f882001-02-23 17:55:21 +00005623 if (prev == NULL && next == NULL)
5624 return(-1);
5625
5626 if (list == NULL) return(-1);
5627 if (max <= 0) return(-1);
5628
5629 nb_valid_elements = 0;
5630 ref_node = prev ? prev : next;
5631 parent = ref_node->parent;
5632
5633 /*
5634 * Retrieves the parent element declaration
5635 */
5636 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5637 parent->name);
5638 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5639 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5640 parent->name);
5641 if (element_desc == NULL) return(-1);
5642
5643 /*
5644 * Do a backup of the current tree structure
5645 */
5646 prev_next = prev ? prev->next : NULL;
5647 next_prev = next ? next->prev : NULL;
5648 parent_childs = parent->children;
5649 parent_last = parent->last;
5650
5651 /*
5652 * Creates a dummy node and insert it into the tree
5653 */
5654 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5655 test_node->doc = ref_node->doc;
5656 test_node->parent = parent;
5657 test_node->prev = prev;
5658 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005659 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005660
5661 if (prev) prev->next = test_node;
5662 else parent->children = test_node;
5663
5664 if (next) next->prev = test_node;
5665 else parent->last = test_node;
5666
5667 /*
5668 * Insert each potential child node and check if the parent is
5669 * still valid
5670 */
5671 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5672 elements, &nb_elements, 256);
5673
5674 for (i = 0;i < nb_elements;i++) {
5675 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005676 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005677 int j;
5678
5679 for (j = 0; j < nb_valid_elements;j++)
5680 if (xmlStrEqual(elements[i], list[j])) break;
5681 list[nb_valid_elements++] = elements[i];
5682 if (nb_valid_elements >= max) break;
5683 }
5684 }
5685
5686 /*
5687 * Restore the tree structure
5688 */
5689 if (prev) prev->next = prev_next;
5690 if (next) next->prev = next_prev;
5691 parent->children = parent_childs;
5692 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005693
5694 /*
5695 * Free up the dummy node
5696 */
5697 test_node->name = name;
5698 xmlFreeNode(test_node);
5699
Owen Taylor3473f882001-02-23 17:55:21 +00005700 return(nb_valid_elements);
5701}