blob: 331f9fe60affa481b39516ed863305825716033b [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
Owen Taylor3473f882001-02-23 17:55:21 +000030/*
31 * Generic function for accessing stacks in the Validity Context
32 */
33
34#define PUSH_AND_POP(scope, type, name) \
35scope int name##VPush(xmlValidCtxtPtr ctxt, type value) { \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000036 if (ctxt->name##Max <= 0) { \
37 ctxt->name##Max = 4; \
38 ctxt->name##Tab = (type *) xmlMalloc( \
39 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
40 if (ctxt->name##Tab == NULL) { \
41 xmlGenericError(xmlGenericErrorContext, \
42 "malloc failed !\n"); \
Daniel Veillarda9142e72001-06-19 11:07:54 +000043 ctxt->name##Max = 0; \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000044 return(0); \
45 } \
46 } \
Owen Taylor3473f882001-02-23 17:55:21 +000047 if (ctxt->name##Nr >= ctxt->name##Max) { \
48 ctxt->name##Max *= 2; \
49 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
50 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
51 if (ctxt->name##Tab == NULL) { \
52 xmlGenericError(xmlGenericErrorContext, \
53 "realloc failed !\n"); \
54 return(0); \
55 } \
56 } \
57 ctxt->name##Tab[ctxt->name##Nr] = value; \
58 ctxt->name = value; \
59 return(ctxt->name##Nr++); \
60} \
61scope type name##VPop(xmlValidCtxtPtr ctxt) { \
62 type ret; \
63 if (ctxt->name##Nr <= 0) return(0); \
64 ctxt->name##Nr--; \
65 if (ctxt->name##Nr > 0) \
66 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
67 else \
68 ctxt->name = NULL; \
69 ret = ctxt->name##Tab[ctxt->name##Nr]; \
70 ctxt->name##Tab[ctxt->name##Nr] = 0; \
71 return(ret); \
72} \
73
Daniel Veillarddab4cb32001-04-20 13:03:48 +000074/*
Daniel Veillardb44025c2001-10-11 22:55:55 +000075 * I use a home made algorithm less complex and easier to
Daniel Veillardcbaf3992001-12-31 16:16:02 +000076 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +000077 * only restriction is on the deepness of the tree limited by the
78 * size of the occurs bitfield
79 *
80 * this is the content of a saved state for rollbacks
81 */
82
83#define ROLLBACK_OR 0
84#define ROLLBACK_PARENT 1
85
Daniel Veillardb44025c2001-10-11 22:55:55 +000086typedef struct _xmlValidState {
Daniel Veillarddab4cb32001-04-20 13:03:48 +000087 xmlElementContentPtr cont; /* pointer to the content model subtree */
88 xmlNodePtr node; /* pointer to the current node in the list */
Daniel Veillardcbaf3992001-12-31 16:16:02 +000089 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +000090 unsigned char depth; /* current depth in the overall tree */
91 unsigned char state; /* ROLLBACK_XXX */
92} _xmlValidState;
93
Daniel Veillardfc57b412002-04-29 15:50:14 +000094#define MAX_RECURSE 25000
Daniel Veillarddab4cb32001-04-20 13:03:48 +000095#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
96#define CONT ctxt->vstate->cont
97#define NODE ctxt->vstate->node
98#define DEPTH ctxt->vstate->depth
99#define OCCURS ctxt->vstate->occurs
100#define STATE ctxt->vstate->state
101
Daniel Veillard5344c602001-12-31 16:37:34 +0000102#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
103#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000104
Daniel Veillard5344c602001-12-31 16:37:34 +0000105#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
106#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000107
108static int
109vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
110 xmlNodePtr node, unsigned char depth, long occurs,
111 unsigned char state) {
Daniel Veillardbed7b052001-05-19 14:59:49 +0000112 int i = ctxt->vstateNr - 1;
113
Daniel Veillard940492d2002-04-15 10:15:25 +0000114 if (ctxt->vstateNr > MAX_RECURSE) {
115 return(-1);
116 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000117 if (ctxt->vstateNr >= ctxt->vstateMax) {
118 ctxt->vstateMax *= 2;
119 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
120 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
121 if (ctxt->vstateTab == NULL) {
122 xmlGenericError(xmlGenericErrorContext,
123 "realloc failed !n");
Daniel Veillard940492d2002-04-15 10:15:25 +0000124 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000125 }
Daniel Veillard06803992001-04-22 10:35:56 +0000126 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000127 }
Daniel Veillardbed7b052001-05-19 14:59:49 +0000128 /*
129 * Don't push on the stack a state already here
130 */
131 if ((i >= 0) && (ctxt->vstateTab[i].cont == cont) &&
132 (ctxt->vstateTab[i].node == node) &&
133 (ctxt->vstateTab[i].depth == depth) &&
134 (ctxt->vstateTab[i].occurs == occurs) &&
135 (ctxt->vstateTab[i].state == state))
136 return(ctxt->vstateNr);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000137 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
138 ctxt->vstateTab[ctxt->vstateNr].node = node;
139 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
140 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
141 ctxt->vstateTab[ctxt->vstateNr].state = state;
142 return(ctxt->vstateNr++);
143}
144
145static int
146vstateVPop(xmlValidCtxtPtr ctxt) {
147 if (ctxt->vstateNr <= 1) return(-1);
148 ctxt->vstateNr--;
149 ctxt->vstate = &ctxt->vstateTab[0];
150 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
151 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
152 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
153 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
154 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
155 return(ctxt->vstateNr);
156}
157
Owen Taylor3473f882001-02-23 17:55:21 +0000158PUSH_AND_POP(static, xmlNodePtr, node)
159
Owen Taylor3473f882001-02-23 17:55:21 +0000160#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000161static void
162xmlValidPrintNode(xmlNodePtr cur) {
163 if (cur == NULL) {
164 xmlGenericError(xmlGenericErrorContext, "null");
165 return;
166 }
167 switch (cur->type) {
168 case XML_ELEMENT_NODE:
169 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
170 break;
171 case XML_TEXT_NODE:
172 xmlGenericError(xmlGenericErrorContext, "text ");
173 break;
174 case XML_CDATA_SECTION_NODE:
175 xmlGenericError(xmlGenericErrorContext, "cdata ");
176 break;
177 case XML_ENTITY_REF_NODE:
178 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
179 break;
180 case XML_PI_NODE:
181 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
182 break;
183 case XML_COMMENT_NODE:
184 xmlGenericError(xmlGenericErrorContext, "comment ");
185 break;
186 case XML_ATTRIBUTE_NODE:
187 xmlGenericError(xmlGenericErrorContext, "?attr? ");
188 break;
189 case XML_ENTITY_NODE:
190 xmlGenericError(xmlGenericErrorContext, "?ent? ");
191 break;
192 case XML_DOCUMENT_NODE:
193 xmlGenericError(xmlGenericErrorContext, "?doc? ");
194 break;
195 case XML_DOCUMENT_TYPE_NODE:
196 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
197 break;
198 case XML_DOCUMENT_FRAG_NODE:
199 xmlGenericError(xmlGenericErrorContext, "?frag? ");
200 break;
201 case XML_NOTATION_NODE:
202 xmlGenericError(xmlGenericErrorContext, "?nota? ");
203 break;
204 case XML_HTML_DOCUMENT_NODE:
205 xmlGenericError(xmlGenericErrorContext, "?html? ");
206 break;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000207#ifdef LIBXML_DOCB_ENABLED
208 case XML_DOCB_DOCUMENT_NODE:
209 xmlGenericError(xmlGenericErrorContext, "?docb? ");
210 break;
211#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000212 case XML_DTD_NODE:
213 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
214 break;
215 case XML_ELEMENT_DECL:
216 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
217 break;
218 case XML_ATTRIBUTE_DECL:
219 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
220 break;
221 case XML_ENTITY_DECL:
222 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
223 break;
224 case XML_NAMESPACE_DECL:
225 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
226 break;
227 case XML_XINCLUDE_START:
228 xmlGenericError(xmlGenericErrorContext, "incstart ");
229 break;
230 case XML_XINCLUDE_END:
231 xmlGenericError(xmlGenericErrorContext, "incend ");
232 break;
233 }
234}
235
236static void
237xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000238 if (cur == NULL)
239 xmlGenericError(xmlGenericErrorContext, "null ");
240 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000241 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000242 cur = cur->next;
243 }
244}
245
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000246static void
247xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000248 char expr[1000];
249
250 expr[0] = 0;
251 xmlGenericError(xmlGenericErrorContext, "valid: ");
252 xmlValidPrintNodeList(cur);
253 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000254 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000255 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
256}
257
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000258static void
259xmlValidDebugState(xmlValidStatePtr state) {
260 xmlGenericError(xmlGenericErrorContext, "(");
261 if (state->cont == NULL)
262 xmlGenericError(xmlGenericErrorContext, "null,");
263 else
264 switch (state->cont->type) {
265 case XML_ELEMENT_CONTENT_PCDATA:
266 xmlGenericError(xmlGenericErrorContext, "pcdata,");
267 break;
268 case XML_ELEMENT_CONTENT_ELEMENT:
269 xmlGenericError(xmlGenericErrorContext, "%s,",
270 state->cont->name);
271 break;
272 case XML_ELEMENT_CONTENT_SEQ:
273 xmlGenericError(xmlGenericErrorContext, "seq,");
274 break;
275 case XML_ELEMENT_CONTENT_OR:
276 xmlGenericError(xmlGenericErrorContext, "or,");
277 break;
278 }
279 xmlValidPrintNode(state->node);
280 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
281 state->depth, state->occurs, state->state);
282}
283
284static void
285xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
286 int i, j;
287
288 xmlGenericError(xmlGenericErrorContext, "state: ");
289 xmlValidDebugState(ctxt->vstate);
290 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
291 ctxt->vstateNr - 1);
292 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
293 xmlValidDebugState(&ctxt->vstateTab[j]);
294 xmlGenericError(xmlGenericErrorContext, "\n");
295}
296
297/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000298#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000299 *****/
300
301#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000302#define DEBUG_VALID_MSG(m) \
303 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
304
Owen Taylor3473f882001-02-23 17:55:21 +0000305#else
306#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000307#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000308#endif
309
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000310/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000311
312#define VERROR \
313 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
314
315#define VWARNING \
316 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
317
318#define CHECK_DTD \
319 if (doc == NULL) return(0); \
320 else if ((doc->intSubset == NULL) && \
321 (doc->extSubset == NULL)) return(0)
322
Daniel Veillarda10efa82001-04-18 13:09:01 +0000323static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
324 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000325xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
326
327/************************************************************************
328 * *
329 * QName handling helper *
330 * *
331 ************************************************************************/
332
333/**
334 * xmlSplitQName2:
335 * @name: an XML parser context
336 * @prefix: a xmlChar **
337 *
338 * parse an XML qualified name string
339 *
340 * [NS 5] QName ::= (Prefix ':')? LocalPart
341 *
342 * [NS 6] Prefix ::= NCName
343 *
344 * [NS 7] LocalPart ::= NCName
345 *
346 * Returns NULL if not a QName, otherwise the local part, and prefix
347 * is updated to get the Prefix if any.
348 */
349
350xmlChar *
351xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
352 int len = 0;
353 xmlChar *ret = NULL;
354
355 *prefix = NULL;
356
Daniel Veillardf4309d72001-10-02 09:28:58 +0000357#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000358 /* xml: prefix is not really a namespace */
359 if ((name[0] == 'x') && (name[1] == 'm') &&
360 (name[2] == 'l') && (name[3] == ':'))
361 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000362#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000363
364 /* nasty but valid */
365 if (name[0] == ':')
366 return(NULL);
367
368 /*
369 * we are not trying to validate but just to cut, and yes it will
370 * work even if this is as set of UTF-8 encoded chars
371 */
372 while ((name[len] != 0) && (name[len] != ':'))
373 len++;
374
375 if (name[len] == 0)
376 return(NULL);
377
378 *prefix = xmlStrndup(name, len);
379 ret = xmlStrdup(&name[len + 1]);
380
381 return(ret);
382}
383
384/****************************************************************
385 * *
386 * Util functions for data allocation/deallocation *
387 * *
388 ****************************************************************/
389
390/**
391 * xmlNewElementContent:
392 * @name: the subelement name or NULL
393 * @type: the type of element content decl
394 *
395 * Allocate an element content structure.
396 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000397 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000398 */
399xmlElementContentPtr
400xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
401 xmlElementContentPtr ret;
402
403 switch(type) {
404 case XML_ELEMENT_CONTENT_ELEMENT:
405 if (name == NULL) {
406 xmlGenericError(xmlGenericErrorContext,
407 "xmlNewElementContent : name == NULL !\n");
408 }
409 break;
410 case XML_ELEMENT_CONTENT_PCDATA:
411 case XML_ELEMENT_CONTENT_SEQ:
412 case XML_ELEMENT_CONTENT_OR:
413 if (name != NULL) {
414 xmlGenericError(xmlGenericErrorContext,
415 "xmlNewElementContent : name != NULL !\n");
416 }
417 break;
418 default:
419 xmlGenericError(xmlGenericErrorContext,
420 "xmlNewElementContent: unknown type %d\n", type);
421 return(NULL);
422 }
423 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
424 if (ret == NULL) {
425 xmlGenericError(xmlGenericErrorContext,
426 "xmlNewElementContent : out of memory!\n");
427 return(NULL);
428 }
Daniel Veillardd54fa3e2002-02-20 16:48:52 +0000429 memset(ret, 0, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000430 ret->type = type;
431 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000432 if (name != NULL) {
433 xmlChar *prefix = NULL;
434 ret->name = xmlSplitQName2(name, &prefix);
435 if (ret->name == NULL)
436 ret->name = xmlStrdup(name);
437 ret->prefix = prefix;
438 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000439 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000440 ret->prefix = NULL;
441 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000442 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000443 return(ret);
444}
445
446/**
447 * xmlCopyElementContent:
448 * @content: An element content pointer.
449 *
450 * Build a copy of an element content description.
451 *
452 * Returns the new xmlElementContentPtr or NULL in case of error.
453 */
454xmlElementContentPtr
455xmlCopyElementContent(xmlElementContentPtr cur) {
456 xmlElementContentPtr ret;
457
458 if (cur == NULL) return(NULL);
459 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
460 if (ret == NULL) {
461 xmlGenericError(xmlGenericErrorContext,
462 "xmlCopyElementContent : out of memory\n");
463 return(NULL);
464 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000465 if (cur->prefix != NULL)
466 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000467 ret->ocur = cur->ocur;
468 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000469 if (ret->c1 != NULL)
470 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000471 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000472 if (ret->c2 != NULL)
473 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000474 return(ret);
475}
476
477/**
478 * xmlFreeElementContent:
479 * @cur: the element content tree to free
480 *
481 * Free an element content structure. This is a recursive call !
482 */
483void
484xmlFreeElementContent(xmlElementContentPtr cur) {
485 if (cur == NULL) return;
486 switch (cur->type) {
487 case XML_ELEMENT_CONTENT_PCDATA:
488 case XML_ELEMENT_CONTENT_ELEMENT:
489 case XML_ELEMENT_CONTENT_SEQ:
490 case XML_ELEMENT_CONTENT_OR:
491 break;
492 default:
493 xmlGenericError(xmlGenericErrorContext,
494 "xmlFreeElementContent : type %d\n", cur->type);
495 return;
496 }
497 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
498 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
499 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000500 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000501 xmlFree(cur);
502}
503
504/**
505 * xmlDumpElementContent:
506 * @buf: An XML buffer
507 * @content: An element table
508 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
509 *
510 * This will dump the content of the element table as an XML DTD definition
511 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000512static void
Owen Taylor3473f882001-02-23 17:55:21 +0000513xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
514 if (content == NULL) return;
515
516 if (glob) xmlBufferWriteChar(buf, "(");
517 switch (content->type) {
518 case XML_ELEMENT_CONTENT_PCDATA:
519 xmlBufferWriteChar(buf, "#PCDATA");
520 break;
521 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000522 if (content->prefix != NULL) {
523 xmlBufferWriteCHAR(buf, content->prefix);
524 xmlBufferWriteChar(buf, ":");
525 }
Owen Taylor3473f882001-02-23 17:55:21 +0000526 xmlBufferWriteCHAR(buf, content->name);
527 break;
528 case XML_ELEMENT_CONTENT_SEQ:
529 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
530 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
531 xmlDumpElementContent(buf, content->c1, 1);
532 else
533 xmlDumpElementContent(buf, content->c1, 0);
534 xmlBufferWriteChar(buf, " , ");
535 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
536 xmlDumpElementContent(buf, content->c2, 1);
537 else
538 xmlDumpElementContent(buf, content->c2, 0);
539 break;
540 case XML_ELEMENT_CONTENT_OR:
541 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
542 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
543 xmlDumpElementContent(buf, content->c1, 1);
544 else
545 xmlDumpElementContent(buf, content->c1, 0);
546 xmlBufferWriteChar(buf, " | ");
547 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
548 xmlDumpElementContent(buf, content->c2, 1);
549 else
550 xmlDumpElementContent(buf, content->c2, 0);
551 break;
552 default:
553 xmlGenericError(xmlGenericErrorContext,
554 "xmlDumpElementContent: unknown type %d\n",
555 content->type);
556 }
557 if (glob)
558 xmlBufferWriteChar(buf, ")");
559 switch (content->ocur) {
560 case XML_ELEMENT_CONTENT_ONCE:
561 break;
562 case XML_ELEMENT_CONTENT_OPT:
563 xmlBufferWriteChar(buf, "?");
564 break;
565 case XML_ELEMENT_CONTENT_MULT:
566 xmlBufferWriteChar(buf, "*");
567 break;
568 case XML_ELEMENT_CONTENT_PLUS:
569 xmlBufferWriteChar(buf, "+");
570 break;
571 }
572}
573
574/**
575 * xmlSprintfElementContent:
576 * @buf: an output buffer
577 * @content: An element table
578 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
579 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000580 * Deprecated, unsafe, use xmlSnprintfElementContent
581 */
582void
583xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
584 xmlElementContentPtr content ATTRIBUTE_UNUSED,
585 int glob ATTRIBUTE_UNUSED) {
586}
587
588/**
589 * xmlSnprintfElementContent:
590 * @buf: an output buffer
591 * @size: the buffer size
592 * @content: An element table
593 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
594 *
Owen Taylor3473f882001-02-23 17:55:21 +0000595 * This will dump the content of the element content definition
596 * Intended just for the debug routine
597 */
598void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000599xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
600 int len;
601
Owen Taylor3473f882001-02-23 17:55:21 +0000602 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000603 len = strlen(buf);
604 if (size - len < 50) {
605 if ((size - len > 4) && (buf[len - 1] != '.'))
606 strcat(buf, " ...");
607 return;
608 }
Owen Taylor3473f882001-02-23 17:55:21 +0000609 if (glob) strcat(buf, "(");
610 switch (content->type) {
611 case XML_ELEMENT_CONTENT_PCDATA:
612 strcat(buf, "#PCDATA");
613 break;
614 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000615 if (content->prefix != NULL) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000616 if (size - len < xmlStrlen(content->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000617 strcat(buf, " ...");
618 return;
619 }
620 strcat(buf, (char *) content->prefix);
621 strcat(buf, ":");
622 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +0000623 if (size - len < xmlStrlen(content->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +0000624 strcat(buf, " ...");
625 return;
626 }
Owen Taylor3473f882001-02-23 17:55:21 +0000627 strcat(buf, (char *) content->name);
628 break;
629 case XML_ELEMENT_CONTENT_SEQ:
630 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
631 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000632 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000633 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000634 xmlSnprintfElementContent(buf, size, content->c1, 0);
635 len = strlen(buf);
636 if (size - len < 50) {
637 if ((size - len > 4) && (buf[len - 1] != '.'))
638 strcat(buf, " ...");
639 return;
640 }
Owen Taylor3473f882001-02-23 17:55:21 +0000641 strcat(buf, " , ");
642 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000643 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000644 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000645 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000646 break;
647 case XML_ELEMENT_CONTENT_OR:
648 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
649 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000650 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000651 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000652 xmlSnprintfElementContent(buf, size, content->c1, 0);
653 len = strlen(buf);
654 if (size - len < 50) {
655 if ((size - len > 4) && (buf[len - 1] != '.'))
656 strcat(buf, " ...");
657 return;
658 }
Owen Taylor3473f882001-02-23 17:55:21 +0000659 strcat(buf, " | ");
660 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000661 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000662 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000663 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000664 break;
665 }
666 if (glob)
667 strcat(buf, ")");
668 switch (content->ocur) {
669 case XML_ELEMENT_CONTENT_ONCE:
670 break;
671 case XML_ELEMENT_CONTENT_OPT:
672 strcat(buf, "?");
673 break;
674 case XML_ELEMENT_CONTENT_MULT:
675 strcat(buf, "*");
676 break;
677 case XML_ELEMENT_CONTENT_PLUS:
678 strcat(buf, "+");
679 break;
680 }
681}
682
683/****************************************************************
684 * *
685 * Registration of DTD declarations *
686 * *
687 ****************************************************************/
688
689/**
690 * xmlCreateElementTable:
691 *
692 * create and initialize an empty element hash table.
693 *
694 * Returns the xmlElementTablePtr just created or NULL in case of error.
695 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000696static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000697xmlCreateElementTable(void) {
698 return(xmlHashCreate(0));
699}
700
701/**
702 * xmlFreeElement:
703 * @elem: An element
704 *
705 * Deallocate the memory used by an element definition
706 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000707static void
Owen Taylor3473f882001-02-23 17:55:21 +0000708xmlFreeElement(xmlElementPtr elem) {
709 if (elem == NULL) return;
710 xmlUnlinkNode((xmlNodePtr) elem);
711 xmlFreeElementContent(elem->content);
712 if (elem->name != NULL)
713 xmlFree((xmlChar *) elem->name);
714 if (elem->prefix != NULL)
715 xmlFree((xmlChar *) elem->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000716 xmlFree(elem);
717}
718
719
720/**
721 * xmlAddElementDecl:
722 * @ctxt: the validation context
723 * @dtd: pointer to the DTD
724 * @name: the entity name
725 * @type: the element type
726 * @content: the element content tree or NULL
727 *
728 * Register a new element declaration
729 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000730 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +0000731 */
732xmlElementPtr
733xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
734 xmlElementTypeVal type,
735 xmlElementContentPtr content) {
736 xmlElementPtr ret;
737 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000738 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000739 xmlChar *ns, *uqname;
740
741 if (dtd == NULL) {
742 xmlGenericError(xmlGenericErrorContext,
743 "xmlAddElementDecl: dtd == NULL\n");
744 return(NULL);
745 }
746 if (name == NULL) {
747 xmlGenericError(xmlGenericErrorContext,
748 "xmlAddElementDecl: name == NULL\n");
749 return(NULL);
750 }
751 switch (type) {
752 case XML_ELEMENT_TYPE_EMPTY:
753 if (content != NULL) {
754 xmlGenericError(xmlGenericErrorContext,
755 "xmlAddElementDecl: content != NULL for EMPTY\n");
756 return(NULL);
757 }
758 break;
759 case XML_ELEMENT_TYPE_ANY:
760 if (content != NULL) {
761 xmlGenericError(xmlGenericErrorContext,
762 "xmlAddElementDecl: content != NULL for ANY\n");
763 return(NULL);
764 }
765 break;
766 case XML_ELEMENT_TYPE_MIXED:
767 if (content == NULL) {
768 xmlGenericError(xmlGenericErrorContext,
769 "xmlAddElementDecl: content == NULL for MIXED\n");
770 return(NULL);
771 }
772 break;
773 case XML_ELEMENT_TYPE_ELEMENT:
774 if (content == NULL) {
775 xmlGenericError(xmlGenericErrorContext,
776 "xmlAddElementDecl: content == NULL for ELEMENT\n");
777 return(NULL);
778 }
779 break;
780 default:
781 xmlGenericError(xmlGenericErrorContext,
782 "xmlAddElementDecl: unknown type %d\n", type);
783 return(NULL);
784 }
785
786 /*
787 * check if name is a QName
788 */
789 uqname = xmlSplitQName2(name, &ns);
790 if (uqname != NULL)
791 name = uqname;
792
793 /*
794 * Create the Element table if needed.
795 */
796 table = (xmlElementTablePtr) dtd->elements;
797 if (table == NULL) {
798 table = xmlCreateElementTable();
799 dtd->elements = (void *) table;
800 }
801 if (table == NULL) {
802 xmlGenericError(xmlGenericErrorContext,
803 "xmlAddElementDecl: Table creation failed!\n");
804 return(NULL);
805 }
806
Daniel Veillarda10efa82001-04-18 13:09:01 +0000807 /*
808 * lookup old attributes inserted on an undefined element in the
809 * internal subset.
810 */
811 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
812 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
813 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
814 oldAttributes = ret->attributes;
815 ret->attributes = NULL;
816 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
817 xmlFreeElement(ret);
818 }
Owen Taylor3473f882001-02-23 17:55:21 +0000819 }
Owen Taylor3473f882001-02-23 17:55:21 +0000820
821 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +0000822 * The element may already be present if one of its attribute
823 * was registered first
824 */
825 ret = xmlHashLookup2(table, name, ns);
826 if (ret != NULL) {
827 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
828 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000829 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +0000830 */
831 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
832 if (uqname != NULL)
833 xmlFree(uqname);
834 return(NULL);
835 }
836 } else {
837 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
838 if (ret == NULL) {
839 xmlGenericError(xmlGenericErrorContext,
840 "xmlAddElementDecl: out of memory\n");
841 return(NULL);
842 }
843 memset(ret, 0, sizeof(xmlElement));
844 ret->type = XML_ELEMENT_DECL;
845
846 /*
847 * fill the structure.
848 */
849 ret->name = xmlStrdup(name);
850 ret->prefix = ns;
851
852 /*
853 * Validity Check:
854 * Insertion must not fail
855 */
856 if (xmlHashAddEntry2(table, name, ns, ret)) {
857 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000858 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +0000859 */
860 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
861 xmlFreeElement(ret);
862 if (uqname != NULL)
863 xmlFree(uqname);
864 return(NULL);
865 }
866 }
867
868 /*
869 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +0000870 */
871 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +0000872 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000873 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +0000874
875 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000876 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +0000877 */
878 ret->parent = dtd;
879 ret->doc = dtd->doc;
880 if (dtd->last == NULL) {
881 dtd->children = dtd->last = (xmlNodePtr) ret;
882 } else {
883 dtd->last->next = (xmlNodePtr) ret;
884 ret->prev = dtd->last;
885 dtd->last = (xmlNodePtr) ret;
886 }
887 if (uqname != NULL)
888 xmlFree(uqname);
889 return(ret);
890}
891
892/**
893 * xmlFreeElementTable:
894 * @table: An element table
895 *
896 * Deallocate the memory used by an element hash table.
897 */
898void
899xmlFreeElementTable(xmlElementTablePtr table) {
900 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
901}
902
903/**
904 * xmlCopyElement:
905 * @elem: An element
906 *
907 * Build a copy of an element.
908 *
909 * Returns the new xmlElementPtr or NULL in case of error.
910 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000911static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000912xmlCopyElement(xmlElementPtr elem) {
913 xmlElementPtr cur;
914
915 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
916 if (cur == NULL) {
917 xmlGenericError(xmlGenericErrorContext,
918 "xmlCopyElement: out of memory !\n");
919 return(NULL);
920 }
921 memset(cur, 0, sizeof(xmlElement));
922 cur->type = XML_ELEMENT_DECL;
923 cur->etype = elem->etype;
924 if (elem->name != NULL)
925 cur->name = xmlStrdup(elem->name);
926 else
927 cur->name = NULL;
928 if (elem->prefix != NULL)
929 cur->prefix = xmlStrdup(elem->prefix);
930 else
931 cur->prefix = NULL;
932 cur->content = xmlCopyElementContent(elem->content);
933 /* TODO : rebuild the attribute list on the copy */
934 cur->attributes = NULL;
935 return(cur);
936}
937
938/**
939 * xmlCopyElementTable:
940 * @table: An element table
941 *
942 * Build a copy of an element table.
943 *
944 * Returns the new xmlElementTablePtr or NULL in case of error.
945 */
946xmlElementTablePtr
947xmlCopyElementTable(xmlElementTablePtr table) {
948 return((xmlElementTablePtr) xmlHashCopy(table,
949 (xmlHashCopier) xmlCopyElement));
950}
951
952/**
953 * xmlDumpElementDecl:
954 * @buf: the XML buffer output
955 * @elem: An element table
956 *
957 * This will dump the content of the element declaration as an XML
958 * DTD definition
959 */
960void
961xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
962 switch (elem->etype) {
963 case XML_ELEMENT_TYPE_EMPTY:
964 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000965 if (elem->prefix != NULL) {
966 xmlBufferWriteCHAR(buf, elem->prefix);
967 xmlBufferWriteChar(buf, ":");
968 }
Owen Taylor3473f882001-02-23 17:55:21 +0000969 xmlBufferWriteCHAR(buf, elem->name);
970 xmlBufferWriteChar(buf, " EMPTY>\n");
971 break;
972 case XML_ELEMENT_TYPE_ANY:
973 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000974 if (elem->prefix != NULL) {
975 xmlBufferWriteCHAR(buf, elem->prefix);
976 xmlBufferWriteChar(buf, ":");
977 }
Owen Taylor3473f882001-02-23 17:55:21 +0000978 xmlBufferWriteCHAR(buf, elem->name);
979 xmlBufferWriteChar(buf, " ANY>\n");
980 break;
981 case XML_ELEMENT_TYPE_MIXED:
982 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000983 if (elem->prefix != NULL) {
984 xmlBufferWriteCHAR(buf, elem->prefix);
985 xmlBufferWriteChar(buf, ":");
986 }
Owen Taylor3473f882001-02-23 17:55:21 +0000987 xmlBufferWriteCHAR(buf, elem->name);
988 xmlBufferWriteChar(buf, " ");
989 xmlDumpElementContent(buf, elem->content, 1);
990 xmlBufferWriteChar(buf, ">\n");
991 break;
992 case XML_ELEMENT_TYPE_ELEMENT:
993 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000994 if (elem->prefix != NULL) {
995 xmlBufferWriteCHAR(buf, elem->prefix);
996 xmlBufferWriteChar(buf, ":");
997 }
Owen Taylor3473f882001-02-23 17:55:21 +0000998 xmlBufferWriteCHAR(buf, elem->name);
999 xmlBufferWriteChar(buf, " ");
1000 xmlDumpElementContent(buf, elem->content, 1);
1001 xmlBufferWriteChar(buf, ">\n");
1002 break;
1003 default:
1004 xmlGenericError(xmlGenericErrorContext,
1005 "xmlDumpElementDecl: internal: unknown type %d\n",
1006 elem->etype);
1007 }
1008}
1009
1010/**
1011 * xmlDumpElementTable:
1012 * @buf: the XML buffer output
1013 * @table: An element table
1014 *
1015 * This will dump the content of the element table as an XML DTD definition
1016 */
1017void
1018xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1019 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1020}
1021
1022/**
1023 * xmlCreateEnumeration:
1024 * @name: the enumeration name or NULL
1025 *
1026 * create and initialize an enumeration attribute node.
1027 *
1028 * Returns the xmlEnumerationPtr just created or NULL in case
1029 * of error.
1030 */
1031xmlEnumerationPtr
1032xmlCreateEnumeration(xmlChar *name) {
1033 xmlEnumerationPtr ret;
1034
1035 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1036 if (ret == NULL) {
1037 xmlGenericError(xmlGenericErrorContext,
1038 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1039 (long)sizeof(xmlEnumeration));
1040 return(NULL);
1041 }
1042 memset(ret, 0, sizeof(xmlEnumeration));
1043
1044 if (name != NULL)
1045 ret->name = xmlStrdup(name);
1046 return(ret);
1047}
1048
1049/**
1050 * xmlFreeEnumeration:
1051 * @cur: the tree to free.
1052 *
1053 * free an enumeration attribute node (recursive).
1054 */
1055void
1056xmlFreeEnumeration(xmlEnumerationPtr cur) {
1057 if (cur == NULL) return;
1058
1059 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1060
1061 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001062 xmlFree(cur);
1063}
1064
1065/**
1066 * xmlCopyEnumeration:
1067 * @cur: the tree to copy.
1068 *
1069 * Copy an enumeration attribute node (recursive).
1070 *
1071 * Returns the xmlEnumerationPtr just created or NULL in case
1072 * of error.
1073 */
1074xmlEnumerationPtr
1075xmlCopyEnumeration(xmlEnumerationPtr cur) {
1076 xmlEnumerationPtr ret;
1077
1078 if (cur == NULL) return(NULL);
1079 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1080
1081 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1082 else ret->next = NULL;
1083
1084 return(ret);
1085}
1086
1087/**
1088 * xmlDumpEnumeration:
1089 * @buf: the XML buffer output
1090 * @enum: An enumeration
1091 *
1092 * This will dump the content of the enumeration
1093 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001094static void
Owen Taylor3473f882001-02-23 17:55:21 +00001095xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1096 if (cur == NULL) return;
1097
1098 xmlBufferWriteCHAR(buf, cur->name);
1099 if (cur->next == NULL)
1100 xmlBufferWriteChar(buf, ")");
1101 else {
1102 xmlBufferWriteChar(buf, " | ");
1103 xmlDumpEnumeration(buf, cur->next);
1104 }
1105}
1106
1107/**
1108 * xmlCreateAttributeTable:
1109 *
1110 * create and initialize an empty attribute hash table.
1111 *
1112 * Returns the xmlAttributeTablePtr just created or NULL in case
1113 * of error.
1114 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001115static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001116xmlCreateAttributeTable(void) {
1117 return(xmlHashCreate(0));
1118}
1119
1120/**
1121 * xmlScanAttributeDeclCallback:
1122 * @attr: the attribute decl
1123 * @list: the list to update
1124 *
1125 * Callback called by xmlScanAttributeDecl when a new attribute
1126 * has to be entered in the list.
1127 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001128static void
Owen Taylor3473f882001-02-23 17:55:21 +00001129xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001130 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001131 attr->nexth = *list;
1132 *list = attr;
1133}
1134
1135/**
1136 * xmlScanAttributeDecl:
1137 * @dtd: pointer to the DTD
1138 * @elem: the element name
1139 *
1140 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001141 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001142 *
1143 * Returns the pointer to the first attribute decl in the chain,
1144 * possibly NULL.
1145 */
1146xmlAttributePtr
1147xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1148 xmlAttributePtr ret = NULL;
1149 xmlAttributeTablePtr table;
1150
1151 if (dtd == NULL) {
1152 xmlGenericError(xmlGenericErrorContext,
1153 "xmlScanAttributeDecl: dtd == NULL\n");
1154 return(NULL);
1155 }
1156 if (elem == NULL) {
1157 xmlGenericError(xmlGenericErrorContext,
1158 "xmlScanAttributeDecl: elem == NULL\n");
1159 return(NULL);
1160 }
1161 table = (xmlAttributeTablePtr) dtd->attributes;
1162 if (table == NULL)
1163 return(NULL);
1164
1165 /* WRONG !!! */
1166 xmlHashScan3(table, NULL, NULL, elem,
1167 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1168 return(ret);
1169}
1170
1171/**
1172 * xmlScanIDAttributeDecl:
1173 * @ctxt: the validation context
1174 * @elem: the element name
1175 *
1176 * Verify that the element don't have too many ID attributes
1177 * declared.
1178 *
1179 * Returns the number of ID attributes found.
1180 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001181static int
Owen Taylor3473f882001-02-23 17:55:21 +00001182xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1183 xmlAttributePtr cur;
1184 int ret = 0;
1185
1186 if (elem == NULL) return(0);
1187 cur = elem->attributes;
1188 while (cur != NULL) {
1189 if (cur->atype == XML_ATTRIBUTE_ID) {
1190 ret ++;
1191 if (ret > 1)
1192 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001193 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001194 elem->name, cur->name);
1195 }
1196 cur = cur->nexth;
1197 }
1198 return(ret);
1199}
1200
1201/**
1202 * xmlFreeAttribute:
1203 * @elem: An attribute
1204 *
1205 * Deallocate the memory used by an attribute definition
1206 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001207static void
Owen Taylor3473f882001-02-23 17:55:21 +00001208xmlFreeAttribute(xmlAttributePtr attr) {
1209 if (attr == NULL) return;
1210 xmlUnlinkNode((xmlNodePtr) attr);
1211 if (attr->tree != NULL)
1212 xmlFreeEnumeration(attr->tree);
1213 if (attr->elem != NULL)
1214 xmlFree((xmlChar *) attr->elem);
1215 if (attr->name != NULL)
1216 xmlFree((xmlChar *) attr->name);
1217 if (attr->defaultValue != NULL)
1218 xmlFree((xmlChar *) attr->defaultValue);
1219 if (attr->prefix != NULL)
1220 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001221 xmlFree(attr);
1222}
1223
1224
1225/**
1226 * xmlAddAttributeDecl:
1227 * @ctxt: the validation context
1228 * @dtd: pointer to the DTD
1229 * @elem: the element name
1230 * @name: the attribute name
1231 * @ns: the attribute namespace prefix
1232 * @type: the attribute type
1233 * @def: the attribute default type
1234 * @defaultValue: the attribute default value
1235 * @tree: if it's an enumeration, the associated list
1236 *
1237 * Register a new attribute declaration
1238 * Note that @tree becomes the ownership of the DTD
1239 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001240 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001241 */
1242xmlAttributePtr
1243xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1244 const xmlChar *name, const xmlChar *ns,
1245 xmlAttributeType type, xmlAttributeDefault def,
1246 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1247 xmlAttributePtr ret;
1248 xmlAttributeTablePtr table;
1249 xmlElementPtr elemDef;
1250
1251 if (dtd == NULL) {
1252 xmlGenericError(xmlGenericErrorContext,
1253 "xmlAddAttributeDecl: dtd == NULL\n");
1254 xmlFreeEnumeration(tree);
1255 return(NULL);
1256 }
1257 if (name == NULL) {
1258 xmlGenericError(xmlGenericErrorContext,
1259 "xmlAddAttributeDecl: name == NULL\n");
1260 xmlFreeEnumeration(tree);
1261 return(NULL);
1262 }
1263 if (elem == NULL) {
1264 xmlGenericError(xmlGenericErrorContext,
1265 "xmlAddAttributeDecl: elem == NULL\n");
1266 xmlFreeEnumeration(tree);
1267 return(NULL);
1268 }
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001269
Owen Taylor3473f882001-02-23 17:55:21 +00001270 /*
1271 * Check the type and possibly the default value.
1272 */
1273 switch (type) {
1274 case XML_ATTRIBUTE_CDATA:
1275 break;
1276 case XML_ATTRIBUTE_ID:
1277 break;
1278 case XML_ATTRIBUTE_IDREF:
1279 break;
1280 case XML_ATTRIBUTE_IDREFS:
1281 break;
1282 case XML_ATTRIBUTE_ENTITY:
1283 break;
1284 case XML_ATTRIBUTE_ENTITIES:
1285 break;
1286 case XML_ATTRIBUTE_NMTOKEN:
1287 break;
1288 case XML_ATTRIBUTE_NMTOKENS:
1289 break;
1290 case XML_ATTRIBUTE_ENUMERATION:
1291 break;
1292 case XML_ATTRIBUTE_NOTATION:
1293 break;
1294 default:
1295 xmlGenericError(xmlGenericErrorContext,
1296 "xmlAddAttributeDecl: unknown type %d\n", type);
1297 xmlFreeEnumeration(tree);
1298 return(NULL);
1299 }
1300 if ((defaultValue != NULL) &&
1301 (!xmlValidateAttributeValue(type, defaultValue))) {
Daniel Veillard58e44c92002-08-02 22:19:49 +00001302 VERROR(ctxt->userData, "Attribute %s of %s: invalid default value\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001303 elem, name, defaultValue);
1304 defaultValue = NULL;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001305 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001306 }
1307
1308 /*
Daniel Veillardd85f4f42002-03-25 10:48:46 +00001309 * Check first that an attribute defined in the external subset wasn't
1310 * already defined in the internal subset
1311 */
1312 if ((dtd->doc != NULL) && (dtd->doc->extSubset == dtd) &&
1313 (dtd->doc->intSubset != NULL) &&
1314 (dtd->doc->intSubset->attributes != NULL)) {
1315 ret = xmlHashLookup3(dtd->doc->intSubset->attributes, name, ns, elem);
1316 if (ret != NULL)
1317 return(NULL);
1318 }
1319
1320 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001321 * Create the Attribute table if needed.
1322 */
1323 table = (xmlAttributeTablePtr) dtd->attributes;
1324 if (table == NULL) {
1325 table = xmlCreateAttributeTable();
1326 dtd->attributes = (void *) table;
1327 }
1328 if (table == NULL) {
1329 xmlGenericError(xmlGenericErrorContext,
1330 "xmlAddAttributeDecl: Table creation failed!\n");
1331 return(NULL);
1332 }
1333
1334
1335 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1336 if (ret == NULL) {
1337 xmlGenericError(xmlGenericErrorContext,
1338 "xmlAddAttributeDecl: out of memory\n");
1339 return(NULL);
1340 }
1341 memset(ret, 0, sizeof(xmlAttribute));
1342 ret->type = XML_ATTRIBUTE_DECL;
1343
1344 /*
1345 * fill the structure.
1346 */
1347 ret->atype = type;
1348 ret->name = xmlStrdup(name);
1349 ret->prefix = xmlStrdup(ns);
1350 ret->elem = xmlStrdup(elem);
1351 ret->def = def;
1352 ret->tree = tree;
1353 if (defaultValue != NULL)
1354 ret->defaultValue = xmlStrdup(defaultValue);
1355
1356 /*
1357 * Validity Check:
1358 * Search the DTD for previous declarations of the ATTLIST
1359 */
1360 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1361 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001362 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001363 */
1364 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00001365 "Attribute %s of element %s: already defined\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001366 name, elem);
1367 xmlFreeAttribute(ret);
1368 return(NULL);
1369 }
1370
1371 /*
1372 * Validity Check:
1373 * Multiple ID per element
1374 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001375 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001376 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001377
Owen Taylor3473f882001-02-23 17:55:21 +00001378 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001379 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001380 VERROR(ctxt->userData,
1381 "Element %s has too may ID attributes defined : %s\n",
1382 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001383 ctxt->valid = 0;
1384 }
1385
Daniel Veillard48da9102001-08-07 01:10:10 +00001386 /*
1387 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001388 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001389 */
1390 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1391 ((ret->prefix != NULL &&
1392 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1393 ret->nexth = elemDef->attributes;
1394 elemDef->attributes = ret;
1395 } else {
1396 xmlAttributePtr tmp = elemDef->attributes;
1397
1398 while ((tmp != NULL) &&
1399 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1400 ((ret->prefix != NULL &&
1401 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1402 if (tmp->nexth == NULL)
1403 break;
1404 tmp = tmp->nexth;
1405 }
1406 if (tmp != NULL) {
1407 ret->nexth = tmp->nexth;
1408 tmp->nexth = ret;
1409 } else {
1410 ret->nexth = elemDef->attributes;
1411 elemDef->attributes = ret;
1412 }
1413 }
Owen Taylor3473f882001-02-23 17:55:21 +00001414 }
1415
1416 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001417 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001418 */
1419 ret->parent = dtd;
1420 ret->doc = dtd->doc;
1421 if (dtd->last == NULL) {
1422 dtd->children = dtd->last = (xmlNodePtr) ret;
1423 } else {
1424 dtd->last->next = (xmlNodePtr) ret;
1425 ret->prev = dtd->last;
1426 dtd->last = (xmlNodePtr) ret;
1427 }
1428 return(ret);
1429}
1430
1431/**
1432 * xmlFreeAttributeTable:
1433 * @table: An attribute table
1434 *
1435 * Deallocate the memory used by an entities hash table.
1436 */
1437void
1438xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1439 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1440}
1441
1442/**
1443 * xmlCopyAttribute:
1444 * @attr: An attribute
1445 *
1446 * Build a copy of an attribute.
1447 *
1448 * Returns the new xmlAttributePtr or NULL in case of error.
1449 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001450static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001451xmlCopyAttribute(xmlAttributePtr attr) {
1452 xmlAttributePtr cur;
1453
1454 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1455 if (cur == NULL) {
1456 xmlGenericError(xmlGenericErrorContext,
1457 "xmlCopyAttribute: out of memory !\n");
1458 return(NULL);
1459 }
1460 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001461 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001462 cur->atype = attr->atype;
1463 cur->def = attr->def;
1464 cur->tree = xmlCopyEnumeration(attr->tree);
1465 if (attr->elem != NULL)
1466 cur->elem = xmlStrdup(attr->elem);
1467 if (attr->name != NULL)
1468 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001469 if (attr->prefix != NULL)
1470 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001471 if (attr->defaultValue != NULL)
1472 cur->defaultValue = xmlStrdup(attr->defaultValue);
1473 return(cur);
1474}
1475
1476/**
1477 * xmlCopyAttributeTable:
1478 * @table: An attribute table
1479 *
1480 * Build a copy of an attribute table.
1481 *
1482 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1483 */
1484xmlAttributeTablePtr
1485xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1486 return((xmlAttributeTablePtr) xmlHashCopy(table,
1487 (xmlHashCopier) xmlCopyAttribute));
1488}
1489
1490/**
1491 * xmlDumpAttributeDecl:
1492 * @buf: the XML buffer output
1493 * @attr: An attribute declaration
1494 *
1495 * This will dump the content of the attribute declaration as an XML
1496 * DTD definition
1497 */
1498void
1499xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1500 xmlBufferWriteChar(buf, "<!ATTLIST ");
1501 xmlBufferWriteCHAR(buf, attr->elem);
1502 xmlBufferWriteChar(buf, " ");
1503 if (attr->prefix != NULL) {
1504 xmlBufferWriteCHAR(buf, attr->prefix);
1505 xmlBufferWriteChar(buf, ":");
1506 }
1507 xmlBufferWriteCHAR(buf, attr->name);
1508 switch (attr->atype) {
1509 case XML_ATTRIBUTE_CDATA:
1510 xmlBufferWriteChar(buf, " CDATA");
1511 break;
1512 case XML_ATTRIBUTE_ID:
1513 xmlBufferWriteChar(buf, " ID");
1514 break;
1515 case XML_ATTRIBUTE_IDREF:
1516 xmlBufferWriteChar(buf, " IDREF");
1517 break;
1518 case XML_ATTRIBUTE_IDREFS:
1519 xmlBufferWriteChar(buf, " IDREFS");
1520 break;
1521 case XML_ATTRIBUTE_ENTITY:
1522 xmlBufferWriteChar(buf, " ENTITY");
1523 break;
1524 case XML_ATTRIBUTE_ENTITIES:
1525 xmlBufferWriteChar(buf, " ENTITIES");
1526 break;
1527 case XML_ATTRIBUTE_NMTOKEN:
1528 xmlBufferWriteChar(buf, " NMTOKEN");
1529 break;
1530 case XML_ATTRIBUTE_NMTOKENS:
1531 xmlBufferWriteChar(buf, " NMTOKENS");
1532 break;
1533 case XML_ATTRIBUTE_ENUMERATION:
1534 xmlBufferWriteChar(buf, " (");
1535 xmlDumpEnumeration(buf, attr->tree);
1536 break;
1537 case XML_ATTRIBUTE_NOTATION:
1538 xmlBufferWriteChar(buf, " NOTATION (");
1539 xmlDumpEnumeration(buf, attr->tree);
1540 break;
1541 default:
1542 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001543 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001544 attr->atype);
1545 }
1546 switch (attr->def) {
1547 case XML_ATTRIBUTE_NONE:
1548 break;
1549 case XML_ATTRIBUTE_REQUIRED:
1550 xmlBufferWriteChar(buf, " #REQUIRED");
1551 break;
1552 case XML_ATTRIBUTE_IMPLIED:
1553 xmlBufferWriteChar(buf, " #IMPLIED");
1554 break;
1555 case XML_ATTRIBUTE_FIXED:
1556 xmlBufferWriteChar(buf, " #FIXED");
1557 break;
1558 default:
1559 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001560 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001561 attr->def);
1562 }
1563 if (attr->defaultValue != NULL) {
1564 xmlBufferWriteChar(buf, " ");
1565 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1566 }
1567 xmlBufferWriteChar(buf, ">\n");
1568}
1569
1570/**
1571 * xmlDumpAttributeTable:
1572 * @buf: the XML buffer output
1573 * @table: An attribute table
1574 *
1575 * This will dump the content of the attribute table as an XML DTD definition
1576 */
1577void
1578xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1579 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1580}
1581
1582/************************************************************************
1583 * *
1584 * NOTATIONs *
1585 * *
1586 ************************************************************************/
1587/**
1588 * xmlCreateNotationTable:
1589 *
1590 * create and initialize an empty notation hash table.
1591 *
1592 * Returns the xmlNotationTablePtr just created or NULL in case
1593 * of error.
1594 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001595static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001596xmlCreateNotationTable(void) {
1597 return(xmlHashCreate(0));
1598}
1599
1600/**
1601 * xmlFreeNotation:
1602 * @not: A notation
1603 *
1604 * Deallocate the memory used by an notation definition
1605 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001606static void
Owen Taylor3473f882001-02-23 17:55:21 +00001607xmlFreeNotation(xmlNotationPtr nota) {
1608 if (nota == NULL) return;
1609 if (nota->name != NULL)
1610 xmlFree((xmlChar *) nota->name);
1611 if (nota->PublicID != NULL)
1612 xmlFree((xmlChar *) nota->PublicID);
1613 if (nota->SystemID != NULL)
1614 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001615 xmlFree(nota);
1616}
1617
1618
1619/**
1620 * xmlAddNotationDecl:
1621 * @dtd: pointer to the DTD
1622 * @ctxt: the validation context
1623 * @name: the entity name
1624 * @PublicID: the public identifier or NULL
1625 * @SystemID: the system identifier or NULL
1626 *
1627 * Register a new notation declaration
1628 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001629 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001630 */
1631xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001632xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001633 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001634 const xmlChar *PublicID, const xmlChar *SystemID) {
1635 xmlNotationPtr ret;
1636 xmlNotationTablePtr table;
1637
1638 if (dtd == NULL) {
1639 xmlGenericError(xmlGenericErrorContext,
1640 "xmlAddNotationDecl: dtd == NULL\n");
1641 return(NULL);
1642 }
1643 if (name == NULL) {
1644 xmlGenericError(xmlGenericErrorContext,
1645 "xmlAddNotationDecl: name == NULL\n");
1646 return(NULL);
1647 }
1648 if ((PublicID == NULL) && (SystemID == NULL)) {
1649 xmlGenericError(xmlGenericErrorContext,
1650 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001651 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001652 }
1653
1654 /*
1655 * Create the Notation table if needed.
1656 */
1657 table = (xmlNotationTablePtr) dtd->notations;
1658 if (table == NULL)
1659 dtd->notations = table = xmlCreateNotationTable();
1660 if (table == NULL) {
1661 xmlGenericError(xmlGenericErrorContext,
1662 "xmlAddNotationDecl: Table creation failed!\n");
1663 return(NULL);
1664 }
1665
1666 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1667 if (ret == NULL) {
1668 xmlGenericError(xmlGenericErrorContext,
1669 "xmlAddNotationDecl: out of memory\n");
1670 return(NULL);
1671 }
1672 memset(ret, 0, sizeof(xmlNotation));
1673
1674 /*
1675 * fill the structure.
1676 */
1677 ret->name = xmlStrdup(name);
1678 if (SystemID != NULL)
1679 ret->SystemID = xmlStrdup(SystemID);
1680 if (PublicID != NULL)
1681 ret->PublicID = xmlStrdup(PublicID);
1682
1683 /*
1684 * Validity Check:
1685 * Check the DTD for previous declarations of the ATTLIST
1686 */
1687 if (xmlHashAddEntry(table, name, ret)) {
1688 xmlGenericError(xmlGenericErrorContext,
1689 "xmlAddNotationDecl: %s already defined\n", name);
1690 xmlFreeNotation(ret);
1691 return(NULL);
1692 }
1693 return(ret);
1694}
1695
1696/**
1697 * xmlFreeNotationTable:
1698 * @table: An notation table
1699 *
1700 * Deallocate the memory used by an entities hash table.
1701 */
1702void
1703xmlFreeNotationTable(xmlNotationTablePtr table) {
1704 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1705}
1706
1707/**
1708 * xmlCopyNotation:
1709 * @nota: A notation
1710 *
1711 * Build a copy of a notation.
1712 *
1713 * Returns the new xmlNotationPtr or NULL in case of error.
1714 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001715static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001716xmlCopyNotation(xmlNotationPtr nota) {
1717 xmlNotationPtr cur;
1718
1719 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1720 if (cur == NULL) {
1721 xmlGenericError(xmlGenericErrorContext,
1722 "xmlCopyNotation: out of memory !\n");
1723 return(NULL);
1724 }
1725 if (nota->name != NULL)
1726 cur->name = xmlStrdup(nota->name);
1727 else
1728 cur->name = NULL;
1729 if (nota->PublicID != NULL)
1730 cur->PublicID = xmlStrdup(nota->PublicID);
1731 else
1732 cur->PublicID = NULL;
1733 if (nota->SystemID != NULL)
1734 cur->SystemID = xmlStrdup(nota->SystemID);
1735 else
1736 cur->SystemID = NULL;
1737 return(cur);
1738}
1739
1740/**
1741 * xmlCopyNotationTable:
1742 * @table: A notation table
1743 *
1744 * Build a copy of a notation table.
1745 *
1746 * Returns the new xmlNotationTablePtr or NULL in case of error.
1747 */
1748xmlNotationTablePtr
1749xmlCopyNotationTable(xmlNotationTablePtr table) {
1750 return((xmlNotationTablePtr) xmlHashCopy(table,
1751 (xmlHashCopier) xmlCopyNotation));
1752}
1753
1754/**
1755 * xmlDumpNotationDecl:
1756 * @buf: the XML buffer output
1757 * @nota: A notation declaration
1758 *
1759 * This will dump the content the notation declaration as an XML DTD definition
1760 */
1761void
1762xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1763 xmlBufferWriteChar(buf, "<!NOTATION ");
1764 xmlBufferWriteCHAR(buf, nota->name);
1765 if (nota->PublicID != NULL) {
1766 xmlBufferWriteChar(buf, " PUBLIC ");
1767 xmlBufferWriteQuotedString(buf, nota->PublicID);
1768 if (nota->SystemID != NULL) {
1769 xmlBufferWriteChar(buf, " ");
1770 xmlBufferWriteCHAR(buf, nota->SystemID);
1771 }
1772 } else {
1773 xmlBufferWriteChar(buf, " SYSTEM ");
1774 xmlBufferWriteCHAR(buf, nota->SystemID);
1775 }
1776 xmlBufferWriteChar(buf, " >\n");
1777}
1778
1779/**
1780 * xmlDumpNotationTable:
1781 * @buf: the XML buffer output
1782 * @table: A notation table
1783 *
1784 * This will dump the content of the notation table as an XML DTD definition
1785 */
1786void
1787xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1788 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1789}
1790
1791/************************************************************************
1792 * *
1793 * IDs *
1794 * *
1795 ************************************************************************/
1796/**
1797 * xmlCreateIDTable:
1798 *
1799 * create and initialize an empty id hash table.
1800 *
1801 * Returns the xmlIDTablePtr just created or NULL in case
1802 * of error.
1803 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001804static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001805xmlCreateIDTable(void) {
1806 return(xmlHashCreate(0));
1807}
1808
1809/**
1810 * xmlFreeID:
1811 * @not: A id
1812 *
1813 * Deallocate the memory used by an id definition
1814 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001815static void
Owen Taylor3473f882001-02-23 17:55:21 +00001816xmlFreeID(xmlIDPtr id) {
1817 if (id == NULL) return;
1818 if (id->value != NULL)
1819 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001820 xmlFree(id);
1821}
1822
1823/**
1824 * xmlAddID:
1825 * @ctxt: the validation context
1826 * @doc: pointer to the document
1827 * @value: the value name
1828 * @attr: the attribute holding the ID
1829 *
1830 * Register a new id declaration
1831 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001832 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001833 */
1834xmlIDPtr
1835xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1836 xmlAttrPtr attr) {
1837 xmlIDPtr ret;
1838 xmlIDTablePtr table;
1839
1840 if (doc == NULL) {
1841 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001842 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001843 return(NULL);
1844 }
1845 if (value == NULL) {
1846 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001847 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001848 return(NULL);
1849 }
1850 if (attr == NULL) {
1851 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001852 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001853 return(NULL);
1854 }
1855
1856 /*
1857 * Create the ID table if needed.
1858 */
1859 table = (xmlIDTablePtr) doc->ids;
1860 if (table == NULL)
1861 doc->ids = table = xmlCreateIDTable();
1862 if (table == NULL) {
1863 xmlGenericError(xmlGenericErrorContext,
1864 "xmlAddID: Table creation failed!\n");
1865 return(NULL);
1866 }
1867
1868 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1869 if (ret == NULL) {
1870 xmlGenericError(xmlGenericErrorContext,
1871 "xmlAddID: out of memory\n");
1872 return(NULL);
1873 }
1874
1875 /*
1876 * fill the structure.
1877 */
1878 ret->value = xmlStrdup(value);
1879 ret->attr = attr;
1880
1881 if (xmlHashAddEntry(table, value, ret) < 0) {
1882 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001883 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001884 */
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00001885 if (ctxt != NULL)
1886 VERROR(ctxt->userData, "ID %s already defined\n", value);
Owen Taylor3473f882001-02-23 17:55:21 +00001887 xmlFreeID(ret);
1888 return(NULL);
1889 }
1890 return(ret);
1891}
1892
1893/**
1894 * xmlFreeIDTable:
1895 * @table: An id table
1896 *
1897 * Deallocate the memory used by an ID hash table.
1898 */
1899void
1900xmlFreeIDTable(xmlIDTablePtr table) {
1901 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1902}
1903
1904/**
1905 * xmlIsID:
1906 * @doc: the document
1907 * @elem: the element carrying the attribute
1908 * @attr: the attribute
1909 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001910 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00001911 * then this is simple, otherwise we use an heuristic: name ID (upper
1912 * or lowercase).
1913 *
1914 * Returns 0 or 1 depending on the lookup result
1915 */
1916int
1917xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1918 if (doc == NULL) return(0);
1919 if (attr == NULL) return(0);
1920 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1921 return(0);
1922 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1923 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1924 (xmlStrEqual(BAD_CAST "name", attr->name)))
1925 return(1);
1926 return(0);
1927 } else {
1928 xmlAttributePtr attrDecl;
1929
1930 if (elem == NULL) return(0);
Daniel Veillard37f961d2002-07-06 17:53:56 +00001931 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
1932 /*
1933 * TODO: this sucks ... recomputing this every time is stupid
1934 */
1935 int len = xmlStrlen(elem->name) + xmlStrlen(elem->ns->prefix) + 2;
1936 xmlChar *fullname;
1937
1938 fullname = xmlMalloc(len);
1939 if (fullname == NULL)
1940 return(0);
1941 snprintf((char *) fullname, len, "%s:%s", (char *) elem->ns->prefix,
1942 (char *) elem->name);
1943 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname,
1944 attr->name);
1945 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1946 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname,
1947 attr->name);
1948 xmlFree(fullname);
1949 } else {
1950 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name,
1951 attr->name);
1952 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1953 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1954 attr->name);
1955 }
Owen Taylor3473f882001-02-23 17:55:21 +00001956
1957 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1958 return(1);
1959 }
1960 return(0);
1961}
1962
1963/**
1964 * xmlRemoveID
1965 * @doc: the document
1966 * @attr: the attribute
1967 *
1968 * Remove the given attribute from the ID table maintained internally.
1969 *
1970 * Returns -1 if the lookup failed and 0 otherwise
1971 */
1972int
1973xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1974 xmlAttrPtr cur;
1975 xmlIDTablePtr table;
1976 xmlChar *ID;
1977
1978 if (doc == NULL) return(-1);
1979 if (attr == NULL) return(-1);
1980 table = (xmlIDTablePtr) doc->ids;
1981 if (table == NULL)
1982 return(-1);
1983
1984 if (attr == NULL)
1985 return(-1);
1986 ID = xmlNodeListGetString(doc, attr->children, 1);
1987 if (ID == NULL)
1988 return(-1);
1989 cur = xmlHashLookup(table, ID);
1990 if (cur != attr) {
1991 xmlFree(ID);
1992 return(-1);
1993 }
1994 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1995 xmlFree(ID);
1996 return(0);
1997}
1998
1999/**
2000 * xmlGetID:
2001 * @doc: pointer to the document
2002 * @ID: the ID value
2003 *
2004 * Search the attribute declaring the given ID
2005 *
2006 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
2007 */
2008xmlAttrPtr
2009xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
2010 xmlIDTablePtr table;
2011 xmlIDPtr id;
2012
2013 if (doc == NULL) {
2014 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
2015 return(NULL);
2016 }
2017
2018 if (ID == NULL) {
2019 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
2020 return(NULL);
2021 }
2022
2023 table = (xmlIDTablePtr) doc->ids;
2024 if (table == NULL)
2025 return(NULL);
2026
2027 id = xmlHashLookup(table, ID);
2028 if (id == NULL)
2029 return(NULL);
2030 return(id->attr);
2031}
2032
2033/************************************************************************
2034 * *
2035 * Refs *
2036 * *
2037 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002038typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002039{
2040 xmlListPtr l;
2041 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002042} xmlRemoveMemo;
2043
2044typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2045
2046typedef struct xmlValidateMemo_t
2047{
2048 xmlValidCtxtPtr ctxt;
2049 const xmlChar *name;
2050} xmlValidateMemo;
2051
2052typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002053
2054/**
2055 * xmlCreateRefTable:
2056 *
2057 * create and initialize an empty ref hash table.
2058 *
2059 * Returns the xmlRefTablePtr just created or NULL in case
2060 * of error.
2061 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002062static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002063xmlCreateRefTable(void) {
2064 return(xmlHashCreate(0));
2065}
2066
2067/**
2068 * xmlFreeRef:
2069 * @lk: A list link
2070 *
2071 * Deallocate the memory used by a ref definition
2072 */
2073static void
2074xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002075 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2076 if (ref == NULL) return;
2077 if (ref->value != NULL)
2078 xmlFree((xmlChar *)ref->value);
2079 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002080}
2081
2082/**
2083 * xmlFreeRefList:
2084 * @list_ref: A list of references.
2085 *
2086 * Deallocate the memory used by a list of references
2087 */
2088static void
2089xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002090 if (list_ref == NULL) return;
2091 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002092}
2093
2094/**
2095 * xmlWalkRemoveRef:
2096 * @data: Contents of current link
2097 * @user: Value supplied by the user
2098 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002099 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002100 */
2101static int
2102xmlWalkRemoveRef(const void *data, const void *user)
2103{
Daniel Veillard37721922001-05-04 15:21:12 +00002104 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2105 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2106 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002107
Daniel Veillard37721922001-05-04 15:21:12 +00002108 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2109 xmlListRemoveFirst(ref_list, (void *)data);
2110 return 0;
2111 }
2112 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002113}
2114
2115/**
2116 * xmlAddRef:
2117 * @ctxt: the validation context
2118 * @doc: pointer to the document
2119 * @value: the value name
2120 * @attr: the attribute holding the Ref
2121 *
2122 * Register a new ref declaration
2123 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002124 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002125 */
2126xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002127xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002128 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002129 xmlRefPtr ret;
2130 xmlRefTablePtr table;
2131 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002132
Daniel Veillard37721922001-05-04 15:21:12 +00002133 if (doc == NULL) {
2134 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002135 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002136 return(NULL);
2137 }
2138 if (value == NULL) {
2139 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002140 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002141 return(NULL);
2142 }
2143 if (attr == NULL) {
2144 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002145 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002146 return(NULL);
2147 }
Owen Taylor3473f882001-02-23 17:55:21 +00002148
Daniel Veillard37721922001-05-04 15:21:12 +00002149 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002150 * Create the Ref table if needed.
2151 */
Daniel Veillard37721922001-05-04 15:21:12 +00002152 table = (xmlRefTablePtr) doc->refs;
2153 if (table == NULL)
2154 doc->refs = table = xmlCreateRefTable();
2155 if (table == NULL) {
2156 xmlGenericError(xmlGenericErrorContext,
2157 "xmlAddRef: Table creation failed!\n");
2158 return(NULL);
2159 }
Owen Taylor3473f882001-02-23 17:55:21 +00002160
Daniel Veillard37721922001-05-04 15:21:12 +00002161 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2162 if (ret == NULL) {
2163 xmlGenericError(xmlGenericErrorContext,
2164 "xmlAddRef: out of memory\n");
2165 return(NULL);
2166 }
Owen Taylor3473f882001-02-23 17:55:21 +00002167
Daniel Veillard37721922001-05-04 15:21:12 +00002168 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002169 * fill the structure.
2170 */
Daniel Veillard37721922001-05-04 15:21:12 +00002171 ret->value = xmlStrdup(value);
2172 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002173
Daniel Veillard37721922001-05-04 15:21:12 +00002174 /* To add a reference :-
2175 * References are maintained as a list of references,
2176 * Lookup the entry, if no entry create new nodelist
2177 * Add the owning node to the NodeList
2178 * Return the ref
2179 */
Owen Taylor3473f882001-02-23 17:55:21 +00002180
Daniel Veillard37721922001-05-04 15:21:12 +00002181 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2182 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2183 xmlGenericError(xmlGenericErrorContext,
2184 "xmlAddRef: Reference list creation failed!\n");
2185 return(NULL);
2186 }
2187 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2188 xmlListDelete(ref_list);
2189 xmlGenericError(xmlGenericErrorContext,
2190 "xmlAddRef: Reference list insertion failed!\n");
2191 return(NULL);
2192 }
2193 }
2194 xmlListInsert(ref_list, ret);
2195 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002196}
2197
2198/**
2199 * xmlFreeRefTable:
2200 * @table: An ref table
2201 *
2202 * Deallocate the memory used by an Ref hash table.
2203 */
2204void
2205xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002206 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002207}
2208
2209/**
2210 * xmlIsRef:
2211 * @doc: the document
2212 * @elem: the element carrying the attribute
2213 * @attr: the attribute
2214 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002215 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002216 * then this is simple, otherwise we use an heuristic: name Ref (upper
2217 * or lowercase).
2218 *
2219 * Returns 0 or 1 depending on the lookup result
2220 */
2221int
2222xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002223 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2224 return(0);
2225 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2226 /* TODO @@@ */
2227 return(0);
2228 } else {
2229 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002230
Daniel Veillard37721922001-05-04 15:21:12 +00002231 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2232 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2233 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2234 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002235
Daniel Veillard37721922001-05-04 15:21:12 +00002236 if ((attrDecl != NULL) &&
2237 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2238 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2239 return(1);
2240 }
2241 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002242}
2243
2244/**
2245 * xmlRemoveRef
2246 * @doc: the document
2247 * @attr: the attribute
2248 *
2249 * Remove the given attribute from the Ref table maintained internally.
2250 *
2251 * Returns -1 if the lookup failed and 0 otherwise
2252 */
2253int
2254xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002255 xmlListPtr ref_list;
2256 xmlRefTablePtr table;
2257 xmlChar *ID;
2258 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002259
Daniel Veillard37721922001-05-04 15:21:12 +00002260 if (doc == NULL) return(-1);
2261 if (attr == NULL) return(-1);
2262 table = (xmlRefTablePtr) doc->refs;
2263 if (table == NULL)
2264 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002265
Daniel Veillard37721922001-05-04 15:21:12 +00002266 if (attr == NULL)
2267 return(-1);
2268 ID = xmlNodeListGetString(doc, attr->children, 1);
2269 if (ID == NULL)
2270 return(-1);
2271 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002272
Daniel Veillard37721922001-05-04 15:21:12 +00002273 if(ref_list == NULL) {
2274 xmlFree(ID);
2275 return (-1);
2276 }
2277 /* At this point, ref_list refers to a list of references which
2278 * have the same key as the supplied attr. Our list of references
2279 * is ordered by reference address and we don't have that information
2280 * here to use when removing. We'll have to walk the list and
2281 * check for a matching attribute, when we find one stop the walk
2282 * and remove the entry.
2283 * The list is ordered by reference, so that means we don't have the
2284 * key. Passing the list and the reference to the walker means we
2285 * will have enough data to be able to remove the entry.
2286 */
2287 target.l = ref_list;
2288 target.ap = attr;
2289
2290 /* Remove the supplied attr from our list */
2291 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002292
Daniel Veillard37721922001-05-04 15:21:12 +00002293 /*If the list is empty then remove the list entry in the hash */
2294 if (xmlListEmpty(ref_list))
2295 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2296 xmlFreeRefList);
2297 xmlFree(ID);
2298 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002299}
2300
2301/**
2302 * xmlGetRefs:
2303 * @doc: pointer to the document
2304 * @ID: the ID value
2305 *
2306 * Find the set of references for the supplied ID.
2307 *
2308 * Returns NULL if not found, otherwise node set for the ID.
2309 */
2310xmlListPtr
2311xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002312 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002313
Daniel Veillard37721922001-05-04 15:21:12 +00002314 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002315 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002316 return(NULL);
2317 }
Owen Taylor3473f882001-02-23 17:55:21 +00002318
Daniel Veillard37721922001-05-04 15:21:12 +00002319 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002320 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002321 return(NULL);
2322 }
Owen Taylor3473f882001-02-23 17:55:21 +00002323
Daniel Veillard37721922001-05-04 15:21:12 +00002324 table = (xmlRefTablePtr) doc->refs;
2325 if (table == NULL)
2326 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002327
Daniel Veillard37721922001-05-04 15:21:12 +00002328 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002329}
2330
2331/************************************************************************
2332 * *
2333 * Routines for validity checking *
2334 * *
2335 ************************************************************************/
2336
2337/**
2338 * xmlGetDtdElementDesc:
2339 * @dtd: a pointer to the DtD to search
2340 * @name: the element name
2341 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002342 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002343 *
2344 * returns the xmlElementPtr if found or NULL
2345 */
2346
2347xmlElementPtr
2348xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2349 xmlElementTablePtr table;
2350 xmlElementPtr cur;
2351 xmlChar *uqname = NULL, *prefix = NULL;
2352
2353 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002354 if (dtd->elements == NULL)
2355 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002356 table = (xmlElementTablePtr) dtd->elements;
2357
2358 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002359 if (uqname != NULL)
2360 name = uqname;
2361 cur = xmlHashLookup2(table, name, prefix);
2362 if (prefix != NULL) xmlFree(prefix);
2363 if (uqname != NULL) xmlFree(uqname);
2364 return(cur);
2365}
2366/**
2367 * xmlGetDtdElementDesc2:
2368 * @dtd: a pointer to the DtD to search
2369 * @name: the element name
2370 * @create: create an empty description if not found
2371 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002372 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002373 *
2374 * returns the xmlElementPtr if found or NULL
2375 */
2376
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002377static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002378xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2379 xmlElementTablePtr table;
2380 xmlElementPtr cur;
2381 xmlChar *uqname = NULL, *prefix = NULL;
2382
2383 if (dtd == NULL) return(NULL);
2384 if (dtd->elements == NULL) {
2385 if (!create)
2386 return(NULL);
2387 /*
2388 * Create the Element table if needed.
2389 */
2390 table = (xmlElementTablePtr) dtd->elements;
2391 if (table == NULL) {
2392 table = xmlCreateElementTable();
2393 dtd->elements = (void *) table;
2394 }
2395 if (table == NULL) {
2396 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002397 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002398 return(NULL);
2399 }
2400 }
2401 table = (xmlElementTablePtr) dtd->elements;
2402
2403 uqname = xmlSplitQName2(name, &prefix);
2404 if (uqname != NULL)
2405 name = uqname;
2406 cur = xmlHashLookup2(table, name, prefix);
2407 if ((cur == NULL) && (create)) {
2408 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2409 if (cur == NULL) {
2410 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002411 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002412 return(NULL);
2413 }
2414 memset(cur, 0, sizeof(xmlElement));
2415 cur->type = XML_ELEMENT_DECL;
2416
2417 /*
2418 * fill the structure.
2419 */
2420 cur->name = xmlStrdup(name);
2421 cur->prefix = xmlStrdup(prefix);
2422 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2423
2424 xmlHashAddEntry2(table, name, prefix, cur);
2425 }
2426 if (prefix != NULL) xmlFree(prefix);
2427 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002428 return(cur);
2429}
2430
2431/**
2432 * xmlGetDtdQElementDesc:
2433 * @dtd: a pointer to the DtD to search
2434 * @name: the element name
2435 * @prefix: the element namespace prefix
2436 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002437 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002438 *
2439 * returns the xmlElementPtr if found or NULL
2440 */
2441
Daniel Veillard48da9102001-08-07 01:10:10 +00002442xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002443xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2444 const xmlChar *prefix) {
2445 xmlElementTablePtr table;
2446
2447 if (dtd == NULL) return(NULL);
2448 if (dtd->elements == NULL) return(NULL);
2449 table = (xmlElementTablePtr) dtd->elements;
2450
2451 return(xmlHashLookup2(table, name, prefix));
2452}
2453
2454/**
2455 * xmlGetDtdAttrDesc:
2456 * @dtd: a pointer to the DtD to search
2457 * @elem: the element name
2458 * @name: the attribute name
2459 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002460 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002461 * this element.
2462 *
2463 * returns the xmlAttributePtr if found or NULL
2464 */
2465
2466xmlAttributePtr
2467xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2468 xmlAttributeTablePtr table;
2469 xmlAttributePtr cur;
2470 xmlChar *uqname = NULL, *prefix = NULL;
2471
2472 if (dtd == NULL) return(NULL);
2473 if (dtd->attributes == NULL) return(NULL);
2474
2475 table = (xmlAttributeTablePtr) dtd->attributes;
2476 if (table == NULL)
2477 return(NULL);
2478
2479 uqname = xmlSplitQName2(name, &prefix);
2480
2481 if (uqname != NULL) {
2482 cur = xmlHashLookup3(table, uqname, prefix, elem);
2483 if (prefix != NULL) xmlFree(prefix);
2484 if (uqname != NULL) xmlFree(uqname);
2485 } else
2486 cur = xmlHashLookup3(table, name, NULL, elem);
2487 return(cur);
2488}
2489
2490/**
2491 * xmlGetDtdQAttrDesc:
2492 * @dtd: a pointer to the DtD to search
2493 * @elem: the element name
2494 * @name: the attribute name
2495 * @prefix: the attribute namespace prefix
2496 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002497 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002498 * this element.
2499 *
2500 * returns the xmlAttributePtr if found or NULL
2501 */
2502
Daniel Veillard48da9102001-08-07 01:10:10 +00002503xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002504xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2505 const xmlChar *prefix) {
2506 xmlAttributeTablePtr table;
2507
2508 if (dtd == NULL) return(NULL);
2509 if (dtd->attributes == NULL) return(NULL);
2510 table = (xmlAttributeTablePtr) dtd->attributes;
2511
2512 return(xmlHashLookup3(table, name, prefix, elem));
2513}
2514
2515/**
2516 * xmlGetDtdNotationDesc:
2517 * @dtd: a pointer to the DtD to search
2518 * @name: the notation name
2519 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002520 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002521 *
2522 * returns the xmlNotationPtr if found or NULL
2523 */
2524
2525xmlNotationPtr
2526xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2527 xmlNotationTablePtr table;
2528
2529 if (dtd == NULL) return(NULL);
2530 if (dtd->notations == NULL) return(NULL);
2531 table = (xmlNotationTablePtr) dtd->notations;
2532
2533 return(xmlHashLookup(table, name));
2534}
2535
2536/**
2537 * xmlValidateNotationUse:
2538 * @ctxt: the validation context
2539 * @doc: the document
2540 * @notationName: the notation name to check
2541 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002542 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002543 * - [ VC: Notation Declared ]
2544 *
2545 * returns 1 if valid or 0 otherwise
2546 */
2547
2548int
2549xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2550 const xmlChar *notationName) {
2551 xmlNotationPtr notaDecl;
2552 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2553
2554 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2555 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2556 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2557
2558 if (notaDecl == NULL) {
2559 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2560 notationName);
2561 return(0);
2562 }
2563 return(1);
2564}
2565
2566/**
2567 * xmlIsMixedElement
2568 * @doc: the document
2569 * @name: the element name
2570 *
2571 * Search in the DtDs whether an element accept Mixed content (or ANY)
2572 * basically if it is supposed to accept text childs
2573 *
2574 * returns 0 if no, 1 if yes, and -1 if no element description is available
2575 */
2576
2577int
2578xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2579 xmlElementPtr elemDecl;
2580
2581 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2582
2583 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2584 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2585 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2586 if (elemDecl == NULL) return(-1);
2587 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002588 case XML_ELEMENT_TYPE_UNDEFINED:
2589 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002590 case XML_ELEMENT_TYPE_ELEMENT:
2591 return(0);
2592 case XML_ELEMENT_TYPE_EMPTY:
2593 /*
2594 * return 1 for EMPTY since we want VC error to pop up
2595 * on <empty> </empty> for example
2596 */
2597 case XML_ELEMENT_TYPE_ANY:
2598 case XML_ELEMENT_TYPE_MIXED:
2599 return(1);
2600 }
2601 return(1);
2602}
2603
2604/**
2605 * xmlValidateNameValue:
2606 * @value: an Name value
2607 *
2608 * Validate that the given value match Name production
2609 *
2610 * returns 1 if valid or 0 otherwise
2611 */
2612
Daniel Veillard9b731d72002-04-14 12:56:08 +00002613int
Owen Taylor3473f882001-02-23 17:55:21 +00002614xmlValidateNameValue(const xmlChar *value) {
2615 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002616 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002617
2618 if (value == NULL) return(0);
2619 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002620 val = xmlStringCurrentChar(NULL, cur, &len);
2621 cur += len;
2622 if (!IS_LETTER(val) && (val != '_') &&
2623 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002624 return(0);
2625 }
2626
Daniel Veillardd8224e02002-01-13 15:43:22 +00002627 val = xmlStringCurrentChar(NULL, cur, &len);
2628 cur += len;
2629 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2630 (val == '.') || (val == '-') ||
2631 (val == '_') || (val == ':') ||
2632 (IS_COMBINING(val)) ||
2633 (IS_EXTENDER(val))) {
2634 val = xmlStringCurrentChar(NULL, cur, &len);
2635 cur += len;
2636 }
Owen Taylor3473f882001-02-23 17:55:21 +00002637
Daniel Veillardd8224e02002-01-13 15:43:22 +00002638 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002639
2640 return(1);
2641}
2642
2643/**
2644 * xmlValidateNamesValue:
2645 * @value: an Names value
2646 *
2647 * Validate that the given value match Names production
2648 *
2649 * returns 1 if valid or 0 otherwise
2650 */
2651
Daniel Veillard9b731d72002-04-14 12:56:08 +00002652int
Owen Taylor3473f882001-02-23 17:55:21 +00002653xmlValidateNamesValue(const xmlChar *value) {
2654 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002655 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002656
2657 if (value == NULL) return(0);
2658 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002659 val = xmlStringCurrentChar(NULL, cur, &len);
2660 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002661
Daniel Veillardd8224e02002-01-13 15:43:22 +00002662 if (!IS_LETTER(val) && (val != '_') &&
2663 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002664 return(0);
2665 }
2666
Daniel Veillardd8224e02002-01-13 15:43:22 +00002667 val = xmlStringCurrentChar(NULL, cur, &len);
2668 cur += len;
2669 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2670 (val == '.') || (val == '-') ||
2671 (val == '_') || (val == ':') ||
2672 (IS_COMBINING(val)) ||
2673 (IS_EXTENDER(val))) {
2674 val = xmlStringCurrentChar(NULL, cur, &len);
2675 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002676 }
2677
Daniel Veillardd8224e02002-01-13 15:43:22 +00002678 while (IS_BLANK(val)) {
2679 while (IS_BLANK(val)) {
2680 val = xmlStringCurrentChar(NULL, cur, &len);
2681 cur += len;
2682 }
2683
2684 if (!IS_LETTER(val) && (val != '_') &&
2685 (val != ':')) {
2686 return(0);
2687 }
2688 val = xmlStringCurrentChar(NULL, cur, &len);
2689 cur += len;
2690
2691 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2692 (val == '.') || (val == '-') ||
2693 (val == '_') || (val == ':') ||
2694 (IS_COMBINING(val)) ||
2695 (IS_EXTENDER(val))) {
2696 val = xmlStringCurrentChar(NULL, cur, &len);
2697 cur += len;
2698 }
2699 }
2700
2701 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002702
2703 return(1);
2704}
2705
2706/**
2707 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002708 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002709 *
2710 * Validate that the given value match Nmtoken production
2711 *
2712 * [ VC: Name Token ]
2713 *
2714 * returns 1 if valid or 0 otherwise
2715 */
2716
Daniel Veillard9b731d72002-04-14 12:56:08 +00002717int
Owen Taylor3473f882001-02-23 17:55:21 +00002718xmlValidateNmtokenValue(const xmlChar *value) {
2719 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002720 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002721
2722 if (value == NULL) return(0);
2723 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002724 val = xmlStringCurrentChar(NULL, cur, &len);
2725 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002726
Daniel Veillardd8224e02002-01-13 15:43:22 +00002727 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2728 (val != '.') && (val != '-') &&
2729 (val != '_') && (val != ':') &&
2730 (!IS_COMBINING(val)) &&
2731 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002732 return(0);
2733
Daniel Veillardd8224e02002-01-13 15:43:22 +00002734 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2735 (val == '.') || (val == '-') ||
2736 (val == '_') || (val == ':') ||
2737 (IS_COMBINING(val)) ||
2738 (IS_EXTENDER(val))) {
2739 val = xmlStringCurrentChar(NULL, cur, &len);
2740 cur += len;
2741 }
Owen Taylor3473f882001-02-23 17:55:21 +00002742
Daniel Veillardd8224e02002-01-13 15:43:22 +00002743 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002744
2745 return(1);
2746}
2747
2748/**
2749 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002750 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00002751 *
2752 * Validate that the given value match Nmtokens production
2753 *
2754 * [ VC: Name Token ]
2755 *
2756 * returns 1 if valid or 0 otherwise
2757 */
2758
Daniel Veillard9b731d72002-04-14 12:56:08 +00002759int
Owen Taylor3473f882001-02-23 17:55:21 +00002760xmlValidateNmtokensValue(const xmlChar *value) {
2761 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002762 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002763
2764 if (value == NULL) return(0);
2765 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002766 val = xmlStringCurrentChar(NULL, cur, &len);
2767 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002768
Daniel Veillardd8224e02002-01-13 15:43:22 +00002769 while (IS_BLANK(val)) {
2770 val = xmlStringCurrentChar(NULL, cur, &len);
2771 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002772 }
2773
Daniel Veillardd8224e02002-01-13 15:43:22 +00002774 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2775 (val != '.') && (val != '-') &&
2776 (val != '_') && (val != ':') &&
2777 (!IS_COMBINING(val)) &&
2778 (!IS_EXTENDER(val)))
2779 return(0);
2780
2781 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2782 (val == '.') || (val == '-') ||
2783 (val == '_') || (val == ':') ||
2784 (IS_COMBINING(val)) ||
2785 (IS_EXTENDER(val))) {
2786 val = xmlStringCurrentChar(NULL, cur, &len);
2787 cur += len;
2788 }
2789
2790 while (IS_BLANK(val)) {
2791 while (IS_BLANK(val)) {
2792 val = xmlStringCurrentChar(NULL, cur, &len);
2793 cur += len;
2794 }
2795 if (val == 0) return(1);
2796
2797 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2798 (val != '.') && (val != '-') &&
2799 (val != '_') && (val != ':') &&
2800 (!IS_COMBINING(val)) &&
2801 (!IS_EXTENDER(val)))
2802 return(0);
2803
2804 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2805 (val == '.') || (val == '-') ||
2806 (val == '_') || (val == ':') ||
2807 (IS_COMBINING(val)) ||
2808 (IS_EXTENDER(val))) {
2809 val = xmlStringCurrentChar(NULL, cur, &len);
2810 cur += len;
2811 }
2812 }
2813
2814 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002815
2816 return(1);
2817}
2818
2819/**
2820 * xmlValidateNotationDecl:
2821 * @ctxt: the validation context
2822 * @doc: a document instance
2823 * @nota: a notation definition
2824 *
2825 * Try to validate a single notation definition
2826 * basically it does the following checks as described by the
2827 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002828 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00002829 * But this function get called anyway ...
2830 *
2831 * returns 1 if valid or 0 otherwise
2832 */
2833
2834int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002835xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2836 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002837 int ret = 1;
2838
2839 return(ret);
2840}
2841
2842/**
2843 * xmlValidateAttributeValue:
2844 * @type: an attribute type
2845 * @value: an attribute value
2846 *
2847 * Validate that the given attribute value match the proper production
2848 *
2849 * [ VC: ID ]
2850 * Values of type ID must match the Name production....
2851 *
2852 * [ VC: IDREF ]
2853 * Values of type IDREF must match the Name production, and values
2854 * of type IDREFS must match Names ...
2855 *
2856 * [ VC: Entity Name ]
2857 * Values of type ENTITY must match the Name production, values
2858 * of type ENTITIES must match Names ...
2859 *
2860 * [ VC: Name Token ]
2861 * Values of type NMTOKEN must match the Nmtoken production; values
2862 * of type NMTOKENS must match Nmtokens.
2863 *
2864 * returns 1 if valid or 0 otherwise
2865 */
2866
2867int
2868xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2869 switch (type) {
2870 case XML_ATTRIBUTE_ENTITIES:
2871 case XML_ATTRIBUTE_IDREFS:
2872 return(xmlValidateNamesValue(value));
2873 case XML_ATTRIBUTE_ENTITY:
2874 case XML_ATTRIBUTE_IDREF:
2875 case XML_ATTRIBUTE_ID:
2876 case XML_ATTRIBUTE_NOTATION:
2877 return(xmlValidateNameValue(value));
2878 case XML_ATTRIBUTE_NMTOKENS:
2879 case XML_ATTRIBUTE_ENUMERATION:
2880 return(xmlValidateNmtokensValue(value));
2881 case XML_ATTRIBUTE_NMTOKEN:
2882 return(xmlValidateNmtokenValue(value));
2883 case XML_ATTRIBUTE_CDATA:
2884 break;
2885 }
2886 return(1);
2887}
2888
2889/**
2890 * xmlValidateAttributeValue2:
2891 * @ctxt: the validation context
2892 * @doc: the document
2893 * @name: the attribute name (used for error reporting only)
2894 * @type: the attribute type
2895 * @value: the attribute value
2896 *
2897 * Validate that the given attribute value match a given type.
2898 * This typically cannot be done before having finished parsing
2899 * the subsets.
2900 *
2901 * [ VC: IDREF ]
2902 * Values of type IDREF must match one of the declared IDs
2903 * Values of type IDREFS must match a sequence of the declared IDs
2904 * each Name must match the value of an ID attribute on some element
2905 * in the XML document; i.e. IDREF values must match the value of
2906 * some ID attribute
2907 *
2908 * [ VC: Entity Name ]
2909 * Values of type ENTITY must match one declared entity
2910 * Values of type ENTITIES must match a sequence of declared entities
2911 *
2912 * [ VC: Notation Attributes ]
2913 * all notation names in the declaration must be declared.
2914 *
2915 * returns 1 if valid or 0 otherwise
2916 */
2917
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002918static int
Owen Taylor3473f882001-02-23 17:55:21 +00002919xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2920 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2921 int ret = 1;
2922 switch (type) {
2923 case XML_ATTRIBUTE_IDREFS:
2924 case XML_ATTRIBUTE_IDREF:
2925 case XML_ATTRIBUTE_ID:
2926 case XML_ATTRIBUTE_NMTOKENS:
2927 case XML_ATTRIBUTE_ENUMERATION:
2928 case XML_ATTRIBUTE_NMTOKEN:
2929 case XML_ATTRIBUTE_CDATA:
2930 break;
2931 case XML_ATTRIBUTE_ENTITY: {
2932 xmlEntityPtr ent;
2933
2934 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00002935 if ((ent == NULL) && (doc->standalone == 1)) {
2936 doc->standalone = 0;
2937 ent = xmlGetDocEntity(doc, value);
2938 if (ent != NULL) {
2939 VERROR(ctxt->userData,
2940"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
2941 name, value);
2942 /* WAIT to get answer from the Core WG on this
2943 ret = 0;
2944 */
2945 }
2946 }
Owen Taylor3473f882001-02-23 17:55:21 +00002947 if (ent == NULL) {
2948 VERROR(ctxt->userData,
2949 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2950 name, value);
2951 ret = 0;
2952 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2953 VERROR(ctxt->userData,
2954 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2955 name, value);
2956 ret = 0;
2957 }
2958 break;
2959 }
2960 case XML_ATTRIBUTE_ENTITIES: {
2961 xmlChar *dup, *nam = NULL, *cur, save;
2962 xmlEntityPtr ent;
2963
2964 dup = xmlStrdup(value);
2965 if (dup == NULL)
2966 return(0);
2967 cur = dup;
2968 while (*cur != 0) {
2969 nam = cur;
2970 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2971 save = *cur;
2972 *cur = 0;
2973 ent = xmlGetDocEntity(doc, nam);
2974 if (ent == NULL) {
2975 VERROR(ctxt->userData,
2976 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2977 name, nam);
2978 ret = 0;
2979 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2980 VERROR(ctxt->userData,
2981 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2982 name, nam);
2983 ret = 0;
2984 }
2985 if (save == 0)
2986 break;
2987 *cur = save;
2988 while (IS_BLANK(*cur)) cur++;
2989 }
2990 xmlFree(dup);
2991 break;
2992 }
2993 case XML_ATTRIBUTE_NOTATION: {
2994 xmlNotationPtr nota;
2995
2996 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2997 if ((nota == NULL) && (doc->extSubset != NULL))
2998 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2999
3000 if (nota == NULL) {
3001 VERROR(ctxt->userData,
3002 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
3003 name, value);
3004 ret = 0;
3005 }
3006 break;
3007 }
3008 }
3009 return(ret);
3010}
3011
3012/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003013 * xmlValidCtxtNormalizeAttributeValue:
3014 * @ctxt: the validation context
3015 * @doc: the document
3016 * @elem: the parent
3017 * @name: the attribute name
3018 * @value: the attribute value
3019 * @ctxt: the validation context or NULL
3020 *
3021 * Does the validation related extra step of the normalization of attribute
3022 * values:
3023 *
3024 * If the declared value is not CDATA, then the XML processor must further
3025 * process the normalized attribute value by discarding any leading and
3026 * trailing space (#x20) characters, and by replacing sequences of space
3027 * (#x20) characters by single space (#x20) character.
3028 *
3029 * Also check VC: Standalone Document Declaration in P32, and update
3030 * ctxt->valid accordingly
3031 *
3032 * returns a new normalized string if normalization is needed, NULL otherwise
3033 * the caller must free the returned value.
3034 */
3035
3036xmlChar *
3037xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3038 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3039 xmlChar *ret, *dst;
3040 const xmlChar *src;
3041 xmlAttributePtr attrDecl = NULL;
3042 int extsubset = 0;
3043
3044 if (doc == NULL) return(NULL);
3045 if (elem == NULL) return(NULL);
3046 if (name == NULL) return(NULL);
3047 if (value == NULL) return(NULL);
3048
3049 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3050 xmlChar qname[500];
3051 snprintf((char *) qname, sizeof(qname), "%s:%s",
3052 elem->ns->prefix, elem->name);
3053 qname[sizeof(qname) - 1] = 0;
3054 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3055 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3056 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3057 if (attrDecl != NULL)
3058 extsubset = 1;
3059 }
3060 }
3061 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3062 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3063 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3064 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3065 if (attrDecl != NULL)
3066 extsubset = 1;
3067 }
3068
3069 if (attrDecl == NULL)
3070 return(NULL);
3071 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3072 return(NULL);
3073
3074 ret = xmlStrdup(value);
3075 if (ret == NULL)
3076 return(NULL);
3077 src = value;
3078 dst = ret;
3079 while (*src == 0x20) src++;
3080 while (*src != 0) {
3081 if (*src == 0x20) {
3082 while (*src == 0x20) src++;
3083 if (*src != 0)
3084 *dst++ = 0x20;
3085 } else {
3086 *dst++ = *src++;
3087 }
3088 }
3089 *dst = 0;
3090 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3091 VERROR(ctxt->userData,
3092"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3093 name, elem->name);
3094 ctxt->valid = 0;
3095 }
3096 return(ret);
3097}
3098
3099/**
Owen Taylor3473f882001-02-23 17:55:21 +00003100 * xmlValidNormalizeAttributeValue:
3101 * @doc: the document
3102 * @elem: the parent
3103 * @name: the attribute name
3104 * @value: the attribute value
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003105 * @ctxt: the validation context or NULL
Owen Taylor3473f882001-02-23 17:55:21 +00003106 *
3107 * Does the validation related extra step of the normalization of attribute
3108 * values:
3109 *
3110 * If the declared value is not CDATA, then the XML processor must further
3111 * process the normalized attribute value by discarding any leading and
3112 * trailing space (#x20) characters, and by replacing sequences of space
3113 * (#x20) characters by single space (#x20) character.
3114 *
3115 * returns a new normalized string if normalization is needed, NULL otherwise
3116 * the caller must free the returned value.
3117 */
3118
3119xmlChar *
3120xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3121 const xmlChar *name, const xmlChar *value) {
3122 xmlChar *ret, *dst;
3123 const xmlChar *src;
3124 xmlAttributePtr attrDecl = NULL;
3125
3126 if (doc == NULL) return(NULL);
3127 if (elem == NULL) return(NULL);
3128 if (name == NULL) return(NULL);
3129 if (value == NULL) return(NULL);
3130
3131 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3132 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003133 snprintf((char *) qname, sizeof(qname), "%s:%s",
3134 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003135 qname[sizeof(qname) - 1] = 0;
3136 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3137 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3138 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3139 }
3140 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3141 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3142 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3143
3144 if (attrDecl == NULL)
3145 return(NULL);
3146 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3147 return(NULL);
3148
3149 ret = xmlStrdup(value);
3150 if (ret == NULL)
3151 return(NULL);
3152 src = value;
3153 dst = ret;
3154 while (*src == 0x20) src++;
3155 while (*src != 0) {
3156 if (*src == 0x20) {
3157 while (*src == 0x20) src++;
3158 if (*src != 0)
3159 *dst++ = 0x20;
3160 } else {
3161 *dst++ = *src++;
3162 }
3163 }
3164 *dst = 0;
3165 return(ret);
3166}
3167
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003168static void
Owen Taylor3473f882001-02-23 17:55:21 +00003169xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003170 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003171 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3172}
3173
3174/**
3175 * xmlValidateAttributeDecl:
3176 * @ctxt: the validation context
3177 * @doc: a document instance
3178 * @attr: an attribute definition
3179 *
3180 * Try to validate a single attribute definition
3181 * basically it does the following checks as described by the
3182 * XML-1.0 recommendation:
3183 * - [ VC: Attribute Default Legal ]
3184 * - [ VC: Enumeration ]
3185 * - [ VC: ID Attribute Default ]
3186 *
3187 * The ID/IDREF uniqueness and matching are done separately
3188 *
3189 * returns 1 if valid or 0 otherwise
3190 */
3191
3192int
3193xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3194 xmlAttributePtr attr) {
3195 int ret = 1;
3196 int val;
3197 CHECK_DTD;
3198 if(attr == NULL) return(1);
3199
3200 /* Attribute Default Legal */
3201 /* Enumeration */
3202 if (attr->defaultValue != NULL) {
3203 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3204 if (val == 0) {
3205 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003206 "Syntax of default value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003207 attr->name, attr->elem);
3208 }
3209 ret &= val;
3210 }
3211
3212 /* ID Attribute Default */
3213 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3214 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3215 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3216 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003217 "ID attribute %s of %s is not valid must be #IMPLIED or #REQUIRED\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003218 attr->name, attr->elem);
3219 ret = 0;
3220 }
3221
3222 /* One ID per Element Type */
3223 if (attr->atype == XML_ATTRIBUTE_ID) {
3224 int nbId;
3225
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003226 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003227 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3228 attr->elem);
3229 if (elem != NULL) {
3230 nbId = xmlScanIDAttributeDecl(NULL, elem);
3231 } else {
3232 xmlAttributeTablePtr table;
3233
3234 /*
3235 * The attribute may be declared in the internal subset and the
3236 * element in the external subset.
3237 */
3238 nbId = 0;
3239 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3240 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3241 xmlValidateAttributeIdCallback, &nbId);
3242 }
3243 if (nbId > 1) {
3244 VERROR(ctxt->userData,
3245 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3246 attr->elem, nbId, attr->name);
3247 } else if (doc->extSubset != NULL) {
3248 int extId = 0;
3249 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3250 if (elem != NULL) {
3251 extId = xmlScanIDAttributeDecl(NULL, elem);
3252 }
3253 if (extId > 1) {
3254 VERROR(ctxt->userData,
3255 "Element %s has %d ID attribute defined in the external subset : %s\n",
3256 attr->elem, extId, attr->name);
3257 } else if (extId + nbId > 1) {
3258 VERROR(ctxt->userData,
3259"Element %s has ID attributes defined in the internal and external subset : %s\n",
3260 attr->elem, attr->name);
3261 }
3262 }
3263 }
3264
3265 /* Validity Constraint: Enumeration */
3266 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3267 xmlEnumerationPtr tree = attr->tree;
3268 while (tree != NULL) {
3269 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3270 tree = tree->next;
3271 }
3272 if (tree == NULL) {
3273 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003274"Default value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003275 attr->defaultValue, attr->name, attr->elem);
3276 ret = 0;
3277 }
3278 }
3279
3280 return(ret);
3281}
3282
3283/**
3284 * xmlValidateElementDecl:
3285 * @ctxt: the validation context
3286 * @doc: a document instance
3287 * @elem: an element definition
3288 *
3289 * Try to validate a single element definition
3290 * basically it does the following checks as described by the
3291 * XML-1.0 recommendation:
3292 * - [ VC: One ID per Element Type ]
3293 * - [ VC: No Duplicate Types ]
3294 * - [ VC: Unique Element Type Declaration ]
3295 *
3296 * returns 1 if valid or 0 otherwise
3297 */
3298
3299int
3300xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3301 xmlElementPtr elem) {
3302 int ret = 1;
3303 xmlElementPtr tst;
3304
3305 CHECK_DTD;
3306
3307 if (elem == NULL) return(1);
3308
3309 /* No Duplicate Types */
3310 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3311 xmlElementContentPtr cur, next;
3312 const xmlChar *name;
3313
3314 cur = elem->content;
3315 while (cur != NULL) {
3316 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3317 if (cur->c1 == NULL) break;
3318 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3319 name = cur->c1->name;
3320 next = cur->c2;
3321 while (next != NULL) {
3322 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3323 if (xmlStrEqual(next->name, name)) {
3324 VERROR(ctxt->userData,
3325 "Definition of %s has duplicate references of %s\n",
3326 elem->name, name);
3327 ret = 0;
3328 }
3329 break;
3330 }
3331 if (next->c1 == NULL) break;
3332 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3333 if (xmlStrEqual(next->c1->name, name)) {
3334 VERROR(ctxt->userData,
3335 "Definition of %s has duplicate references of %s\n",
3336 elem->name, name);
3337 ret = 0;
3338 }
3339 next = next->c2;
3340 }
3341 }
3342 cur = cur->c2;
3343 }
3344 }
3345
3346 /* VC: Unique Element Type Declaration */
3347 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003348 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003349 ((tst->prefix == elem->prefix) ||
3350 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003351 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003352 VERROR(ctxt->userData, "Redefinition of element %s\n",
3353 elem->name);
3354 ret = 0;
3355 }
3356 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003357 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003358 ((tst->prefix == elem->prefix) ||
3359 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003360 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003361 VERROR(ctxt->userData, "Redefinition of element %s\n",
3362 elem->name);
3363 ret = 0;
3364 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003365 /* One ID per Element Type
3366 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003367 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3368 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003369 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003370 return(ret);
3371}
3372
3373/**
3374 * xmlValidateOneAttribute:
3375 * @ctxt: the validation context
3376 * @doc: a document instance
3377 * @elem: an element instance
3378 * @attr: an attribute instance
3379 * @value: the attribute value (without entities processing)
3380 *
3381 * Try to validate a single attribute for an element
3382 * basically it does the following checks as described by the
3383 * XML-1.0 recommendation:
3384 * - [ VC: Attribute Value Type ]
3385 * - [ VC: Fixed Attribute Default ]
3386 * - [ VC: Entity Name ]
3387 * - [ VC: Name Token ]
3388 * - [ VC: ID ]
3389 * - [ VC: IDREF ]
3390 * - [ VC: Entity Name ]
3391 * - [ VC: Notation Attributes ]
3392 *
3393 * The ID/IDREF uniqueness and matching are done separately
3394 *
3395 * returns 1 if valid or 0 otherwise
3396 */
3397
3398int
3399xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3400 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3401 /* xmlElementPtr elemDecl; */
3402 xmlAttributePtr attrDecl = NULL;
3403 int val;
3404 int ret = 1;
3405
3406 CHECK_DTD;
3407 if ((elem == NULL) || (elem->name == NULL)) return(0);
3408 if ((attr == NULL) || (attr->name == NULL)) return(0);
3409
3410 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3411 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003412 snprintf((char *) qname, sizeof(qname), "%s:%s",
3413 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003414 qname[sizeof(qname) - 1] = 0;
3415 if (attr->ns != NULL) {
3416 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3417 attr->name, attr->ns->prefix);
3418 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3419 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3420 attr->name, attr->ns->prefix);
3421 } else {
3422 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3423 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3424 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3425 qname, attr->name);
3426 }
3427 }
3428 if (attrDecl == NULL) {
3429 if (attr->ns != NULL) {
3430 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3431 attr->name, attr->ns->prefix);
3432 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3433 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3434 attr->name, attr->ns->prefix);
3435 } else {
3436 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3437 elem->name, attr->name);
3438 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3439 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3440 elem->name, attr->name);
3441 }
3442 }
3443
3444
3445 /* Validity Constraint: Attribute Value Type */
3446 if (attrDecl == NULL) {
3447 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003448 "No declaration for attribute %s of element %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003449 attr->name, elem->name);
3450 return(0);
3451 }
3452 attr->atype = attrDecl->atype;
3453
3454 val = xmlValidateAttributeValue(attrDecl->atype, value);
3455 if (val == 0) {
3456 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003457 "Syntax of value for attribute %s of %s is not valid\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003458 attr->name, elem->name);
3459 ret = 0;
3460 }
3461
3462 /* Validity constraint: Fixed Attribute Default */
3463 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3464 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3465 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003466 "Value for attribute %s of %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003467 attr->name, elem->name, attrDecl->defaultValue);
3468 ret = 0;
3469 }
3470 }
3471
3472 /* Validity Constraint: ID uniqueness */
3473 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3474 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3475 ret = 0;
3476 }
3477
3478 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3479 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3480 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3481 ret = 0;
3482 }
3483
3484 /* Validity Constraint: Notation Attributes */
3485 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3486 xmlEnumerationPtr tree = attrDecl->tree;
3487 xmlNotationPtr nota;
3488
3489 /* First check that the given NOTATION was declared */
3490 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3491 if (nota == NULL)
3492 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3493
3494 if (nota == NULL) {
3495 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003496 "Value \"%s\" for attribute %s of %s is not a declared Notation\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003497 value, attr->name, elem->name);
3498 ret = 0;
3499 }
3500
3501 /* Second, verify that it's among the list */
3502 while (tree != NULL) {
3503 if (xmlStrEqual(tree->name, value)) break;
3504 tree = tree->next;
3505 }
3506 if (tree == NULL) {
3507 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003508"Value \"%s\" for attribute %s of %s is not among the enumerated notations\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003509 value, attr->name, elem->name);
3510 ret = 0;
3511 }
3512 }
3513
3514 /* Validity Constraint: Enumeration */
3515 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3516 xmlEnumerationPtr tree = attrDecl->tree;
3517 while (tree != NULL) {
3518 if (xmlStrEqual(tree->name, value)) break;
3519 tree = tree->next;
3520 }
3521 if (tree == NULL) {
3522 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003523 "Value \"%s\" for attribute %s of %s is not among the enumerated set\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003524 value, attr->name, elem->name);
3525 ret = 0;
3526 }
3527 }
3528
3529 /* Fixed Attribute Default */
3530 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3531 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3532 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00003533 "Value for attribute %s of %s must be \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003534 attr->name, elem->name, attrDecl->defaultValue);
3535 ret = 0;
3536 }
3537
3538 /* Extra check for the attribute value */
3539 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3540 attrDecl->atype, value);
3541
3542 return(ret);
3543}
3544
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003545/**
3546 * xmlValidateSkipIgnorable:
3547 * @ctxt: the validation context
3548 * @child: the child list
3549 *
3550 * Skip ignorable elements w.r.t. the validation process
3551 *
3552 * returns the first element to consider for validation of the content model
3553 */
3554
3555static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003556xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003557 while (child != NULL) {
3558 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003559 /* These things are ignored (skipped) during validation. */
3560 case XML_PI_NODE:
3561 case XML_COMMENT_NODE:
3562 case XML_XINCLUDE_START:
3563 case XML_XINCLUDE_END:
3564 child = child->next;
3565 break;
3566 case XML_TEXT_NODE:
3567 if (xmlIsBlankNode(child))
3568 child = child->next;
3569 else
3570 return(child);
3571 break;
3572 /* keep current node */
3573 default:
3574 return(child);
3575 }
3576 }
3577 return(child);
3578}
3579
3580/**
3581 * xmlValidateElementType:
3582 * @ctxt: the validation context
3583 *
3584 * Try to validate the content model of an element internal function
3585 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003586 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3587 * reference is found and -3 if the validation succeeded but
3588 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003589 */
3590
3591static int
3592xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003593 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003594 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003595
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003596 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003597 if ((NODE == NULL) && (CONT == NULL))
3598 return(1);
3599 if ((NODE == NULL) &&
3600 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3601 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3602 return(1);
3603 }
3604 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003605 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003606 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003607
3608 /*
3609 * We arrive here when more states need to be examined
3610 */
3611cont:
3612
3613 /*
3614 * We just recovered from a rollback generated by a possible
3615 * epsilon transition, go directly to the analysis phase
3616 */
3617 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003618 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003619 DEBUG_VALID_STATE(NODE, CONT)
3620 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003621 goto analyze;
3622 }
3623
3624 DEBUG_VALID_STATE(NODE, CONT)
3625 /*
3626 * we may have to save a backup state here. This is the equivalent
3627 * of handling epsilon transition in NFAs.
3628 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003629 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003630 ((CONT->parent == NULL) ||
3631 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003632 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003633 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003634 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003635 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003636 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
3637 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003638 }
3639
3640
3641 /*
3642 * Check first if the content matches
3643 */
3644 switch (CONT->type) {
3645 case XML_ELEMENT_CONTENT_PCDATA:
3646 if (NODE == NULL) {
3647 DEBUG_VALID_MSG("pcdata failed no node");
3648 ret = 0;
3649 break;
3650 }
3651 if (NODE->type == XML_TEXT_NODE) {
3652 DEBUG_VALID_MSG("pcdata found, skip to next");
3653 /*
3654 * go to next element in the content model
3655 * skipping ignorable elems
3656 */
3657 do {
3658 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003659 NODE = xmlValidateSkipIgnorable(NODE);
3660 if ((NODE != NULL) &&
3661 (NODE->type == XML_ENTITY_REF_NODE))
3662 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003663 } while ((NODE != NULL) &&
3664 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003665 (NODE->type != XML_TEXT_NODE) &&
3666 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003667 ret = 1;
3668 break;
3669 } else {
3670 DEBUG_VALID_MSG("pcdata failed");
3671 ret = 0;
3672 break;
3673 }
3674 break;
3675 case XML_ELEMENT_CONTENT_ELEMENT:
3676 if (NODE == NULL) {
3677 DEBUG_VALID_MSG("element failed no node");
3678 ret = 0;
3679 break;
3680 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003681 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3682 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003683 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003684 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3685 ret = (CONT->prefix == NULL);
3686 } else if (CONT->prefix == NULL) {
3687 ret = 0;
3688 } else {
3689 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3690 }
3691 }
3692 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003693 DEBUG_VALID_MSG("element found, skip to next");
3694 /*
3695 * go to next element in the content model
3696 * skipping ignorable elems
3697 */
3698 do {
3699 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003700 NODE = xmlValidateSkipIgnorable(NODE);
3701 if ((NODE != NULL) &&
3702 (NODE->type == XML_ENTITY_REF_NODE))
3703 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003704 } while ((NODE != NULL) &&
3705 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003706 (NODE->type != XML_TEXT_NODE) &&
3707 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003708 } else {
3709 DEBUG_VALID_MSG("element failed");
3710 ret = 0;
3711 break;
3712 }
3713 break;
3714 case XML_ELEMENT_CONTENT_OR:
3715 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003716 * Small optimization.
3717 */
3718 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3719 if ((NODE == NULL) ||
3720 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3721 DEPTH++;
3722 CONT = CONT->c2;
3723 goto cont;
3724 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003725 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3726 ret = (CONT->c1->prefix == NULL);
3727 } else if (CONT->c1->prefix == NULL) {
3728 ret = 0;
3729 } else {
3730 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3731 }
3732 if (ret == 0) {
3733 DEPTH++;
3734 CONT = CONT->c2;
3735 goto cont;
3736 }
Daniel Veillard85349052001-04-20 13:48:21 +00003737 }
3738
3739 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003740 * save the second branch 'or' branch
3741 */
3742 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003743 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3744 OCCURS, ROLLBACK_OR) < 0)
3745 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003746 DEPTH++;
3747 CONT = CONT->c1;
3748 goto cont;
3749 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003750 /*
3751 * Small optimization.
3752 */
3753 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3754 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3755 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3756 if ((NODE == NULL) ||
3757 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3758 DEPTH++;
3759 CONT = CONT->c2;
3760 goto cont;
3761 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003762 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3763 ret = (CONT->c1->prefix == NULL);
3764 } else if (CONT->c1->prefix == NULL) {
3765 ret = 0;
3766 } else {
3767 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3768 }
3769 if (ret == 0) {
3770 DEPTH++;
3771 CONT = CONT->c2;
3772 goto cont;
3773 }
Daniel Veillard1d047672001-06-09 16:41:01 +00003774 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003775 DEPTH++;
3776 CONT = CONT->c1;
3777 goto cont;
3778 }
3779
3780 /*
3781 * At this point handle going up in the tree
3782 */
3783 if (ret == -1) {
3784 DEBUG_VALID_MSG("error found returning");
3785 return(ret);
3786 }
3787analyze:
3788 while (CONT != NULL) {
3789 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003790 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003791 * this level.
3792 */
3793 if (ret == 0) {
3794 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003795 xmlNodePtr cur;
3796
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003797 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003798 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003799 DEBUG_VALID_MSG("Once branch failed, rollback");
3800 if (vstateVPop(ctxt) < 0 ) {
3801 DEBUG_VALID_MSG("exhaustion, failed");
3802 return(0);
3803 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003804 if (cur != ctxt->vstate->node)
3805 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003806 goto cont;
3807 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00003808 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003809 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003810 DEBUG_VALID_MSG("Plus branch failed, rollback");
3811 if (vstateVPop(ctxt) < 0 ) {
3812 DEBUG_VALID_MSG("exhaustion, failed");
3813 return(0);
3814 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003815 if (cur != ctxt->vstate->node)
3816 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003817 goto cont;
3818 }
3819 DEBUG_VALID_MSG("Plus branch found");
3820 ret = 1;
3821 break;
3822 case XML_ELEMENT_CONTENT_MULT:
3823#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00003824 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003825 DEBUG_VALID_MSG("Mult branch failed");
3826 } else {
3827 DEBUG_VALID_MSG("Mult branch found");
3828 }
3829#endif
3830 ret = 1;
3831 break;
3832 case XML_ELEMENT_CONTENT_OPT:
3833 DEBUG_VALID_MSG("Option branch failed");
3834 ret = 1;
3835 break;
3836 }
3837 } else {
3838 switch (CONT->ocur) {
3839 case XML_ELEMENT_CONTENT_OPT:
3840 DEBUG_VALID_MSG("Option branch succeeded");
3841 ret = 1;
3842 break;
3843 case XML_ELEMENT_CONTENT_ONCE:
3844 DEBUG_VALID_MSG("Once branch succeeded");
3845 ret = 1;
3846 break;
3847 case XML_ELEMENT_CONTENT_PLUS:
3848 if (STATE == ROLLBACK_PARENT) {
3849 DEBUG_VALID_MSG("Plus branch rollback");
3850 ret = 1;
3851 break;
3852 }
3853 if (NODE == NULL) {
3854 DEBUG_VALID_MSG("Plus branch exhausted");
3855 ret = 1;
3856 break;
3857 }
3858 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003859 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003860 goto cont;
3861 case XML_ELEMENT_CONTENT_MULT:
3862 if (STATE == ROLLBACK_PARENT) {
3863 DEBUG_VALID_MSG("Mult branch rollback");
3864 ret = 1;
3865 break;
3866 }
3867 if (NODE == NULL) {
3868 DEBUG_VALID_MSG("Mult branch exhausted");
3869 ret = 1;
3870 break;
3871 }
3872 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003873 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003874 goto cont;
3875 }
3876 }
3877 STATE = 0;
3878
3879 /*
3880 * Then act accordingly at the parent level
3881 */
Daniel Veillard5344c602001-12-31 16:37:34 +00003882 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003883 if (CONT->parent == NULL)
3884 break;
3885
3886 switch (CONT->parent->type) {
3887 case XML_ELEMENT_CONTENT_PCDATA:
3888 DEBUG_VALID_MSG("Error: parent pcdata");
3889 return(-1);
3890 case XML_ELEMENT_CONTENT_ELEMENT:
3891 DEBUG_VALID_MSG("Error: parent element");
3892 return(-1);
3893 case XML_ELEMENT_CONTENT_OR:
3894 if (ret == 1) {
3895 DEBUG_VALID_MSG("Or succeeded");
3896 CONT = CONT->parent;
3897 DEPTH--;
3898 } else {
3899 DEBUG_VALID_MSG("Or failed");
3900 CONT = CONT->parent;
3901 DEPTH--;
3902 }
3903 break;
3904 case XML_ELEMENT_CONTENT_SEQ:
3905 if (ret == 0) {
3906 DEBUG_VALID_MSG("Sequence failed");
3907 CONT = CONT->parent;
3908 DEPTH--;
3909 } else if (CONT == CONT->parent->c1) {
3910 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3911 CONT = CONT->parent->c2;
3912 goto cont;
3913 } else {
3914 DEBUG_VALID_MSG("Sequence succeeded");
3915 CONT = CONT->parent;
3916 DEPTH--;
3917 }
3918 }
3919 }
3920 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003921 xmlNodePtr cur;
3922
3923 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003924 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3925 if (vstateVPop(ctxt) < 0 ) {
3926 DEBUG_VALID_MSG("exhaustion, failed");
3927 return(0);
3928 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003929 if (cur != ctxt->vstate->node)
3930 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003931 goto cont;
3932 }
3933 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003934 xmlNodePtr cur;
3935
3936 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003937 DEBUG_VALID_MSG("Failure, rollback");
3938 if (vstateVPop(ctxt) < 0 ) {
3939 DEBUG_VALID_MSG("exhaustion, failed");
3940 return(0);
3941 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003942 if (cur != ctxt->vstate->node)
3943 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003944 goto cont;
3945 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003946 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003947}
3948
3949/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00003950 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003951 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00003952 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003953 * @content: An element
3954 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3955 *
3956 * This will dump the list of elements to the buffer
3957 * Intended just for the debug routine
3958 */
3959static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00003960xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003961 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00003962 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003963
3964 if (node == NULL) return;
3965 if (glob) strcat(buf, "(");
3966 cur = node;
3967 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00003968 len = strlen(buf);
3969 if (size - len < 50) {
3970 if ((size - len > 4) && (buf[len - 1] != '.'))
3971 strcat(buf, " ...");
3972 return;
3973 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003974 switch (cur->type) {
3975 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003976 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00003977 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003978 if ((size - len > 4) && (buf[len - 1] != '.'))
3979 strcat(buf, " ...");
3980 return;
3981 }
3982 strcat(buf, (char *) cur->ns->prefix);
3983 strcat(buf, ":");
3984 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00003985 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00003986 if ((size - len > 4) && (buf[len - 1] != '.'))
3987 strcat(buf, " ...");
3988 return;
3989 }
3990 strcat(buf, (char *) cur->name);
3991 if (cur->next != NULL)
3992 strcat(buf, " ");
3993 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003994 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003995 if (xmlIsBlankNode(cur))
3996 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003997 case XML_CDATA_SECTION_NODE:
3998 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003999 strcat(buf, "CDATA");
4000 if (cur->next != NULL)
4001 strcat(buf, " ");
4002 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004003 case XML_ATTRIBUTE_NODE:
4004 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00004005#ifdef LIBXML_DOCB_ENABLED
4006 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004007#endif
4008 case XML_HTML_DOCUMENT_NODE:
4009 case XML_DOCUMENT_TYPE_NODE:
4010 case XML_DOCUMENT_FRAG_NODE:
4011 case XML_NOTATION_NODE:
4012 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004013 strcat(buf, "???");
4014 if (cur->next != NULL)
4015 strcat(buf, " ");
4016 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004017 case XML_ENTITY_NODE:
4018 case XML_PI_NODE:
4019 case XML_DTD_NODE:
4020 case XML_COMMENT_NODE:
4021 case XML_ELEMENT_DECL:
4022 case XML_ATTRIBUTE_DECL:
4023 case XML_ENTITY_DECL:
4024 case XML_XINCLUDE_START:
4025 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004026 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004027 }
4028 cur = cur->next;
4029 }
4030 if (glob) strcat(buf, ")");
4031}
4032
4033/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004034 * xmlValidateElementContent:
4035 * @ctxt: the validation context
4036 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004037 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004038 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004039 *
4040 * Try to validate the content model of an element
4041 *
4042 * returns 1 if valid or 0 if not and -1 in case of error
4043 */
4044
4045static int
4046xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004047 xmlElementPtr elemDecl, int warn) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004048 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004049 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004050 xmlElementContentPtr cont;
4051 const xmlChar *name;
4052
4053 if (elemDecl == NULL)
4054 return(-1);
4055 cont = elemDecl->content;
4056 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004057
4058 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004059 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004060 */
4061 ctxt->vstateMax = 8;
4062 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4063 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4064 if (ctxt->vstateTab == NULL) {
4065 xmlGenericError(xmlGenericErrorContext,
4066 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004067 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004068 }
4069 /*
4070 * The first entry in the stack is reserved to the current state
4071 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004072 ctxt->nodeMax = 0;
4073 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004074 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004075 ctxt->vstate = &ctxt->vstateTab[0];
4076 ctxt->vstateNr = 1;
4077 CONT = cont;
4078 NODE = child;
4079 DEPTH = 0;
4080 OCCURS = 0;
4081 STATE = 0;
4082 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004083 if ((ret == -3) && (warn)) {
4084 VWARNING(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004085 "Content model for Element %s is ambiguous\n", name);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004086 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004087 /*
4088 * An entities reference appeared at this level.
4089 * Buid a minimal representation of this node content
4090 * sufficient to run the validation process on it
4091 */
4092 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004093 cur = child;
4094 while (cur != NULL) {
4095 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004096 case XML_ENTITY_REF_NODE:
4097 /*
4098 * Push the current node to be able to roll back
4099 * and process within the entity
4100 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004101 if ((cur->children != NULL) &&
4102 (cur->children->children != NULL)) {
4103 nodeVPush(ctxt, cur);
4104 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004105 continue;
4106 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004107 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004108 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004109 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004110 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004111 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004112 case XML_CDATA_SECTION_NODE:
4113 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004114 case XML_ELEMENT_NODE:
4115 /*
4116 * Allocate a new node and minimally fills in
4117 * what's required
4118 */
4119 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4120 if (tmp == NULL) {
4121 xmlGenericError(xmlGenericErrorContext,
4122 "xmlValidateElementContent : malloc failed\n");
4123 xmlFreeNodeList(repl);
4124 ret = -1;
4125 goto done;
4126 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004127 tmp->type = cur->type;
4128 tmp->name = cur->name;
4129 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004130 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004131 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004132 if (repl == NULL)
4133 repl = last = tmp;
4134 else {
4135 last->next = tmp;
4136 last = tmp;
4137 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004138 if (cur->type == XML_CDATA_SECTION_NODE) {
4139 /*
4140 * E59 spaces in CDATA does not match the
4141 * nonterminal S
4142 */
4143 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4144 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004145 break;
4146 default:
4147 break;
4148 }
4149 /*
4150 * Switch to next element
4151 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004152 cur = cur->next;
4153 while (cur == NULL) {
4154 cur = nodeVPop(ctxt);
4155 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004156 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004157 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004158 }
4159 }
4160
4161 /*
4162 * Relaunch the validation
4163 */
4164 ctxt->vstate = &ctxt->vstateTab[0];
4165 ctxt->vstateNr = 1;
4166 CONT = cont;
4167 NODE = repl;
4168 DEPTH = 0;
4169 OCCURS = 0;
4170 STATE = 0;
4171 ret = xmlValidateElementType(ctxt);
4172 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004173 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004174 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4175 char expr[5000];
4176 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004177
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004178 expr[0] = 0;
4179 xmlSnprintfElementContent(expr, 5000, cont, 1);
4180 list[0] = 0;
4181 if (repl != NULL)
4182 xmlSnprintfElements(list, 5000, repl, 1);
4183 else
4184 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004185
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004186 if (name != NULL) {
4187 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004188 "Element %s content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004189 name, expr, list);
4190 } else {
4191 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004192 "Element content does not follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004193 expr, list);
4194 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004195 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004196 if (name != NULL) {
4197 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004198 "Element %s content does not follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004199 name);
4200 } else {
4201 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004202 "Element content does not follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004203 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004204 }
4205 ret = 0;
4206 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004207 if (ret == -3)
4208 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004209
4210
4211done:
4212 /*
4213 * Deallocate the copy if done, and free up the validation stack
4214 */
4215 while (repl != NULL) {
4216 tmp = repl->next;
4217 xmlFree(repl);
4218 repl = tmp;
4219 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004220 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004221 if (ctxt->vstateTab != NULL) {
4222 xmlFree(ctxt->vstateTab);
4223 ctxt->vstateTab = NULL;
4224 }
4225 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004226 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004227 if (ctxt->nodeTab != NULL) {
4228 xmlFree(ctxt->nodeTab);
4229 ctxt->nodeTab = NULL;
4230 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004231 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004232
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004233}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004234
Owen Taylor3473f882001-02-23 17:55:21 +00004235/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004236 * xmlValidateCdataElement:
4237 * @ctxt: the validation context
4238 * @doc: a document instance
4239 * @elem: an element instance
4240 *
4241 * Check that an element follows #CDATA
4242 *
4243 * returns 1 if valid or 0 otherwise
4244 */
4245static int
4246xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4247 xmlNodePtr elem) {
4248 int ret = 1;
4249 xmlNodePtr cur, child;
4250
4251 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4252 return(0);
4253
4254 child = elem->children;
4255
4256 cur = child;
4257 while (cur != NULL) {
4258 switch (cur->type) {
4259 case XML_ENTITY_REF_NODE:
4260 /*
4261 * Push the current node to be able to roll back
4262 * and process within the entity
4263 */
4264 if ((cur->children != NULL) &&
4265 (cur->children->children != NULL)) {
4266 nodeVPush(ctxt, cur);
4267 cur = cur->children->children;
4268 continue;
4269 }
4270 break;
4271 case XML_COMMENT_NODE:
4272 case XML_PI_NODE:
4273 case XML_TEXT_NODE:
4274 case XML_CDATA_SECTION_NODE:
4275 break;
4276 default:
4277 ret = 0;
4278 goto done;
4279 }
4280 /*
4281 * Switch to next element
4282 */
4283 cur = cur->next;
4284 while (cur == NULL) {
4285 cur = nodeVPop(ctxt);
4286 if (cur == NULL)
4287 break;
4288 cur = cur->next;
4289 }
4290 }
4291done:
4292 ctxt->nodeMax = 0;
4293 ctxt->nodeNr = 0;
4294 if (ctxt->nodeTab != NULL) {
4295 xmlFree(ctxt->nodeTab);
4296 ctxt->nodeTab = NULL;
4297 }
4298 return(ret);
4299}
4300
4301/**
Owen Taylor3473f882001-02-23 17:55:21 +00004302 * xmlValidateOneElement:
4303 * @ctxt: the validation context
4304 * @doc: a document instance
4305 * @elem: an element instance
4306 *
4307 * Try to validate a single element and it's attributes,
4308 * basically it does the following checks as described by the
4309 * XML-1.0 recommendation:
4310 * - [ VC: Element Valid ]
4311 * - [ VC: Required Attribute ]
4312 * Then call xmlValidateOneAttribute() for each attribute present.
4313 *
4314 * The ID/IDREF checkings are done separately
4315 *
4316 * returns 1 if valid or 0 otherwise
4317 */
4318
4319int
4320xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4321 xmlNodePtr elem) {
4322 xmlElementPtr elemDecl = NULL;
4323 xmlElementContentPtr cont;
4324 xmlAttributePtr attr;
4325 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004326 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004327 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004328 const xmlChar *prefix = NULL;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004329 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004330
4331 CHECK_DTD;
4332
4333 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004334 switch (elem->type) {
4335 case XML_ATTRIBUTE_NODE:
4336 VERROR(ctxt->userData,
4337 "Attribute element not expected here\n");
4338 return(0);
4339 case XML_TEXT_NODE:
4340 if (elem->children != NULL) {
4341 VERROR(ctxt->userData, "Text element has childs !\n");
4342 return(0);
4343 }
4344 if (elem->properties != NULL) {
4345 VERROR(ctxt->userData, "Text element has attributes !\n");
4346 return(0);
4347 }
4348 if (elem->ns != NULL) {
4349 VERROR(ctxt->userData, "Text element has namespace !\n");
4350 return(0);
4351 }
4352 if (elem->nsDef != NULL) {
4353 VERROR(ctxt->userData,
4354 "Text element carries namespace definitions !\n");
4355 return(0);
4356 }
4357 if (elem->content == NULL) {
4358 VERROR(ctxt->userData,
4359 "Text element has no content !\n");
4360 return(0);
4361 }
4362 return(1);
4363 case XML_XINCLUDE_START:
4364 case XML_XINCLUDE_END:
4365 return(1);
4366 case XML_CDATA_SECTION_NODE:
4367 case XML_ENTITY_REF_NODE:
4368 case XML_PI_NODE:
4369 case XML_COMMENT_NODE:
4370 return(1);
4371 case XML_ENTITY_NODE:
4372 VERROR(ctxt->userData,
4373 "Entity element not expected here\n");
4374 return(0);
4375 case XML_NOTATION_NODE:
4376 VERROR(ctxt->userData,
4377 "Notation element not expected here\n");
4378 return(0);
4379 case XML_DOCUMENT_NODE:
4380 case XML_DOCUMENT_TYPE_NODE:
4381 case XML_DOCUMENT_FRAG_NODE:
4382 VERROR(ctxt->userData,
4383 "Document element not expected here\n");
4384 return(0);
4385 case XML_HTML_DOCUMENT_NODE:
4386 VERROR(ctxt->userData,
4387 "\n");
4388 return(0);
4389 case XML_ELEMENT_NODE:
4390 break;
4391 default:
4392 VERROR(ctxt->userData,
4393 "unknown element type %d\n", elem->type);
4394 return(0);
4395 }
4396 if (elem->name == NULL) return(0);
4397
4398 /*
4399 * Fetch the declaration for the qualified name
4400 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004401 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4402 prefix = elem->ns->prefix;
4403
4404 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004405 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004406 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004407 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004408 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004409 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004410 if (elemDecl != NULL)
4411 extsubset = 1;
4412 }
Owen Taylor3473f882001-02-23 17:55:21 +00004413 }
4414
4415 /*
4416 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004417 * This is "non-strict" validation should be done on the
4418 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004419 */
4420 if (elemDecl == NULL) {
4421 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004422 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004423 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004424 if (elemDecl != NULL)
4425 extsubset = 1;
4426 }
Owen Taylor3473f882001-02-23 17:55:21 +00004427 }
4428 if (elemDecl == NULL) {
4429 VERROR(ctxt->userData, "No declaration for element %s\n",
4430 elem->name);
4431 return(0);
4432 }
4433
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004434 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004435 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004436 case XML_ELEMENT_TYPE_UNDEFINED:
4437 VERROR(ctxt->userData, "No declaration for element %s\n",
4438 elem->name);
4439 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004440 case XML_ELEMENT_TYPE_EMPTY:
4441 if (elem->children != NULL) {
4442 VERROR(ctxt->userData,
4443 "Element %s was declared EMPTY this one has content\n",
4444 elem->name);
4445 ret = 0;
4446 }
4447 break;
4448 case XML_ELEMENT_TYPE_ANY:
4449 /* I don't think anything is required then */
4450 break;
4451 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004452
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004453 /* simple case of declared as #PCDATA */
4454 if ((elemDecl->content != NULL) &&
4455 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4456 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4457 if (!ret) {
4458 VERROR(ctxt->userData,
4459 "Element %s was declared #PCDATA but contains non text nodes\n",
4460 elem->name);
4461 }
4462 break;
4463 }
Owen Taylor3473f882001-02-23 17:55:21 +00004464 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004465 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004466 while (child != NULL) {
4467 if (child->type == XML_ELEMENT_NODE) {
4468 name = child->name;
4469 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4470 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004471 snprintf((char *) qname, sizeof(qname), "%s:%s",
4472 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004473 qname[sizeof(qname) - 1] = 0;
4474 cont = elemDecl->content;
4475 while (cont != NULL) {
4476 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4477 if (xmlStrEqual(cont->name, qname)) break;
4478 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4479 (cont->c1 != NULL) &&
4480 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4481 if (xmlStrEqual(cont->c1->name, qname)) break;
4482 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4483 (cont->c1 == NULL) ||
4484 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4485 /* Internal error !!! */
4486 xmlGenericError(xmlGenericErrorContext,
4487 "Internal: MIXED struct bad\n");
4488 break;
4489 }
4490 cont = cont->c2;
4491 }
4492 if (cont != NULL)
4493 goto child_ok;
4494 }
4495 cont = elemDecl->content;
4496 while (cont != NULL) {
4497 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4498 if (xmlStrEqual(cont->name, name)) break;
4499 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4500 (cont->c1 != NULL) &&
4501 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4502 if (xmlStrEqual(cont->c1->name, name)) break;
4503 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4504 (cont->c1 == NULL) ||
4505 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4506 /* Internal error !!! */
4507 xmlGenericError(xmlGenericErrorContext,
4508 "Internal: MIXED struct bad\n");
4509 break;
4510 }
4511 cont = cont->c2;
4512 }
4513 if (cont == NULL) {
4514 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004515 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004516 name, elem->name);
4517 ret = 0;
4518 }
4519 }
4520child_ok:
4521 child = child->next;
4522 }
4523 break;
4524 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004525 if ((doc->standalone == 1) && (extsubset == 1)) {
4526 /*
4527 * VC: Standalone Document Declaration
4528 * - element types with element content, if white space
4529 * occurs directly within any instance of those types.
4530 */
4531 child = elem->children;
4532 while (child != NULL) {
4533 if (child->type == XML_TEXT_NODE) {
4534 const xmlChar *content = child->content;
4535
4536 while (IS_BLANK(*content))
4537 content++;
4538 if (*content == 0) {
4539 VERROR(ctxt->userData,
4540"standalone: %s declared in the external subset contains white spaces nodes\n",
4541 elem->name);
4542 ret = 0;
4543 break;
4544 }
4545 }
4546 child =child->next;
4547 }
4548 }
Owen Taylor3473f882001-02-23 17:55:21 +00004549 child = elem->children;
4550 cont = elemDecl->content;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004551 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1);
4552 if (tmp <= 0)
4553 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004554 break;
4555 }
4556
4557 /* [ VC: Required Attribute ] */
4558 attr = elemDecl->attributes;
4559 while (attr != NULL) {
4560 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004561 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00004562
Daniel Veillarde4301c82002-02-13 13:32:35 +00004563 if ((attr->prefix == NULL) &&
4564 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4565 xmlNsPtr ns;
4566
4567 ns = elem->nsDef;
4568 while (ns != NULL) {
4569 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00004570 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004571 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00004572 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004573 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4574 xmlNsPtr ns;
4575
4576 ns = elem->nsDef;
4577 while (ns != NULL) {
4578 if (xmlStrEqual(attr->name, ns->prefix))
4579 goto found;
4580 ns = ns->next;
4581 }
4582 } else {
4583 xmlAttrPtr attrib;
4584
4585 attrib = elem->properties;
4586 while (attrib != NULL) {
4587 if (xmlStrEqual(attrib->name, attr->name)) {
4588 if (attr->prefix != NULL) {
4589 xmlNsPtr nameSpace = attrib->ns;
4590
4591 if (nameSpace == NULL)
4592 nameSpace = elem->ns;
4593 /*
4594 * qualified names handling is problematic, having a
4595 * different prefix should be possible but DTDs don't
4596 * allow to define the URI instead of the prefix :-(
4597 */
4598 if (nameSpace == NULL) {
4599 if (qualified < 0)
4600 qualified = 0;
4601 } else if (!xmlStrEqual(nameSpace->prefix,
4602 attr->prefix)) {
4603 if (qualified < 1)
4604 qualified = 1;
4605 } else
4606 goto found;
4607 } else {
4608 /*
4609 * We should allow applications to define namespaces
4610 * for their application even if the DTD doesn't
4611 * carry one, otherwise, basically we would always
4612 * break.
4613 */
4614 goto found;
4615 }
4616 }
4617 attrib = attrib->next;
4618 }
Owen Taylor3473f882001-02-23 17:55:21 +00004619 }
4620 if (qualified == -1) {
4621 if (attr->prefix == NULL) {
4622 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004623 "Element %s does not carry attribute %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004624 elem->name, attr->name);
4625 ret = 0;
4626 } else {
4627 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004628 "Element %s does not carry attribute %s:%s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004629 elem->name, attr->prefix,attr->name);
4630 ret = 0;
4631 }
4632 } else if (qualified == 0) {
4633 VWARNING(ctxt->userData,
4634 "Element %s required attribute %s:%s has no prefix\n",
4635 elem->name, attr->prefix,attr->name);
4636 } else if (qualified == 1) {
4637 VWARNING(ctxt->userData,
4638 "Element %s required attribute %s:%s has different prefix\n",
4639 elem->name, attr->prefix,attr->name);
4640 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004641 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
4642 /*
4643 * Special tests checking #FIXED namespace declarations
4644 * have the right value since this is not done as an
4645 * attribute checking
4646 */
4647 if ((attr->prefix == NULL) &&
4648 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4649 xmlNsPtr ns;
4650
4651 ns = elem->nsDef;
4652 while (ns != NULL) {
4653 if (ns->prefix == NULL) {
4654 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
4655 VERROR(ctxt->userData,
4656 "Element %s namespace name for default namespace does not match the DTD\n",
4657 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00004658 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004659 }
4660 goto found;
4661 }
4662 ns = ns->next;
4663 }
4664 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4665 xmlNsPtr ns;
4666
4667 ns = elem->nsDef;
4668 while (ns != NULL) {
4669 if (xmlStrEqual(attr->name, ns->prefix)) {
4670 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
4671 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004672 "Element %s namespace name for %s does not match the DTD\n",
Daniel Veillarde4301c82002-02-13 13:32:35 +00004673 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00004674 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004675 }
4676 goto found;
4677 }
4678 ns = ns->next;
4679 }
4680 }
Owen Taylor3473f882001-02-23 17:55:21 +00004681 }
4682found:
4683 attr = attr->nexth;
4684 }
4685 return(ret);
4686}
4687
4688/**
4689 * xmlValidateRoot:
4690 * @ctxt: the validation context
4691 * @doc: a document instance
4692 *
4693 * Try to validate a the root element
4694 * basically it does the following check as described by the
4695 * XML-1.0 recommendation:
4696 * - [ VC: Root Element Type ]
4697 * it doesn't try to recurse or apply other check to the element
4698 *
4699 * returns 1 if valid or 0 otherwise
4700 */
4701
4702int
4703xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4704 xmlNodePtr root;
4705 if (doc == NULL) return(0);
4706
4707 root = xmlDocGetRootElement(doc);
4708 if ((root == NULL) || (root->name == NULL)) {
4709 VERROR(ctxt->userData, "Not valid: no root element\n");
4710 return(0);
4711 }
4712
4713 /*
4714 * When doing post validation against a separate DTD, those may
4715 * no internal subset has been generated
4716 */
4717 if ((doc->intSubset != NULL) &&
4718 (doc->intSubset->name != NULL)) {
4719 /*
4720 * Check first the document root against the NQName
4721 */
4722 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4723 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4724 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004725 snprintf((char *) qname, sizeof(qname), "%s:%s",
4726 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004727 qname[sizeof(qname) - 1] = 0;
4728 if (xmlStrEqual(doc->intSubset->name, qname))
4729 goto name_ok;
4730 }
4731 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4732 (xmlStrEqual(root->name, BAD_CAST "html")))
4733 goto name_ok;
4734 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004735 "Not valid: root and DTD name do not match '%s' and '%s'\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004736 root->name, doc->intSubset->name);
4737 return(0);
4738
4739 }
4740 }
4741name_ok:
4742 return(1);
4743}
4744
4745
4746/**
4747 * xmlValidateElement:
4748 * @ctxt: the validation context
4749 * @doc: a document instance
4750 * @elem: an element instance
4751 *
4752 * Try to validate the subtree under an element
4753 *
4754 * returns 1 if valid or 0 otherwise
4755 */
4756
4757int
4758xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4759 xmlNodePtr child;
4760 xmlAttrPtr attr;
4761 xmlChar *value;
4762 int ret = 1;
4763
4764 if (elem == NULL) return(0);
4765
4766 /*
4767 * XInclude elements were added after parsing in the infoset,
4768 * they don't really mean anything validation wise.
4769 */
4770 if ((elem->type == XML_XINCLUDE_START) ||
4771 (elem->type == XML_XINCLUDE_END))
4772 return(1);
4773
4774 CHECK_DTD;
4775
Daniel Veillard10ea86c2001-06-20 13:55:33 +00004776 /*
4777 * Entities references have to be handled separately
4778 */
4779 if (elem->type == XML_ENTITY_REF_NODE) {
4780 return(1);
4781 }
4782
Owen Taylor3473f882001-02-23 17:55:21 +00004783 ret &= xmlValidateOneElement(ctxt, doc, elem);
4784 attr = elem->properties;
4785 while(attr != NULL) {
4786 value = xmlNodeListGetString(doc, attr->children, 0);
4787 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4788 if (value != NULL)
4789 xmlFree(value);
4790 attr= attr->next;
4791 }
4792 child = elem->children;
4793 while (child != NULL) {
4794 ret &= xmlValidateElement(ctxt, doc, child);
4795 child = child->next;
4796 }
4797
4798 return(ret);
4799}
4800
Daniel Veillard8730c562001-02-26 10:49:57 +00004801/**
4802 * xmlValidateRef:
4803 * @ref: A reference to be validated
4804 * @ctxt: Validation context
4805 * @name: Name of ID we are searching for
4806 *
4807 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004808static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004809xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004810 const xmlChar *name) {
4811 xmlAttrPtr id;
4812 xmlAttrPtr attr;
4813
4814 if (ref == NULL)
4815 return;
4816 attr = ref->attr;
4817 if (attr == NULL)
4818 return;
4819 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4820 id = xmlGetID(ctxt->doc, name);
4821 if (id == NULL) {
4822 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004823 "IDREF attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004824 attr->name, name);
4825 ctxt->valid = 0;
4826 }
4827 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4828 xmlChar *dup, *str = NULL, *cur, save;
4829
4830 dup = xmlStrdup(name);
4831 if (dup == NULL) {
4832 ctxt->valid = 0;
4833 return;
4834 }
4835 cur = dup;
4836 while (*cur != 0) {
4837 str = cur;
4838 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4839 save = *cur;
4840 *cur = 0;
4841 id = xmlGetID(ctxt->doc, str);
4842 if (id == NULL) {
4843 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00004844 "IDREFS attribute %s references an unknown ID \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004845 attr->name, str);
4846 ctxt->valid = 0;
4847 }
4848 if (save == 0)
4849 break;
4850 *cur = save;
4851 while (IS_BLANK(*cur)) cur++;
4852 }
4853 xmlFree(dup);
4854 }
4855}
4856
4857/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004858 * xmlWalkValidateList:
4859 * @data: Contents of current link
4860 * @user: Value supplied by the user
4861 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004862 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00004863 */
4864static int
4865xmlWalkValidateList(const void *data, const void *user)
4866{
4867 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4868 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4869 return 1;
4870}
4871
4872/**
4873 * xmlValidateCheckRefCallback:
4874 * @ref_list: List of references
4875 * @ctxt: Validation context
4876 * @name: Name of ID we are searching for
4877 *
4878 */
4879static void
4880xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4881 const xmlChar *name) {
4882 xmlValidateMemo memo;
4883
4884 if (ref_list == NULL)
4885 return;
4886 memo.ctxt = ctxt;
4887 memo.name = name;
4888
4889 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4890
4891}
4892
4893/**
Owen Taylor3473f882001-02-23 17:55:21 +00004894 * xmlValidateDocumentFinal:
4895 * @ctxt: the validation context
4896 * @doc: a document instance
4897 *
4898 * Does the final step for the document validation once all the
4899 * incremental validation steps have been completed
4900 *
4901 * basically it does the following checks described by the XML Rec
4902 *
4903 *
4904 * returns 1 if valid or 0 otherwise
4905 */
4906
4907int
4908xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4909 xmlRefTablePtr table;
4910
4911 if (doc == NULL) {
4912 xmlGenericError(xmlGenericErrorContext,
4913 "xmlValidateDocumentFinal: doc == NULL\n");
4914 return(0);
4915 }
4916
4917 /*
4918 * Check all the NOTATION/NOTATIONS attributes
4919 */
4920 /*
4921 * Check all the ENTITY/ENTITIES attributes definition for validity
4922 */
4923 /*
4924 * Check all the IDREF/IDREFS attributes definition for validity
4925 */
4926 table = (xmlRefTablePtr) doc->refs;
4927 ctxt->doc = doc;
4928 ctxt->valid = 1;
4929 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4930 return(ctxt->valid);
4931}
4932
4933/**
4934 * xmlValidateDtd:
4935 * @ctxt: the validation context
4936 * @doc: a document instance
4937 * @dtd: a dtd instance
4938 *
4939 * Try to validate the document against the dtd instance
4940 *
4941 * basically it does check all the definitions in the DtD.
4942 *
4943 * returns 1 if valid or 0 otherwise
4944 */
4945
4946int
4947xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4948 int ret;
4949 xmlDtdPtr oldExt;
4950 xmlNodePtr root;
4951
4952 if (dtd == NULL) return(0);
4953 if (doc == NULL) return(0);
4954 oldExt = doc->extSubset;
4955 doc->extSubset = dtd;
4956 ret = xmlValidateRoot(ctxt, doc);
4957 if (ret == 0) {
4958 doc->extSubset = oldExt;
4959 return(ret);
4960 }
4961 if (doc->ids != NULL) {
4962 xmlFreeIDTable(doc->ids);
4963 doc->ids = NULL;
4964 }
4965 if (doc->refs != NULL) {
4966 xmlFreeRefTable(doc->refs);
4967 doc->refs = NULL;
4968 }
4969 root = xmlDocGetRootElement(doc);
4970 ret = xmlValidateElement(ctxt, doc, root);
4971 ret &= xmlValidateDocumentFinal(ctxt, doc);
4972 doc->extSubset = oldExt;
4973 return(ret);
4974}
4975
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004976static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004977xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
4978 const xmlChar *name ATTRIBUTE_UNUSED) {
4979 if (cur == NULL)
4980 return;
4981 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
4982 xmlChar *notation = cur->content;
4983
Daniel Veillard878eab02002-02-19 13:46:09 +00004984 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004985 int ret;
4986
4987 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
4988 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00004989 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004990 }
4991 }
4992 }
4993}
4994
4995static void
Owen Taylor3473f882001-02-23 17:55:21 +00004996xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004997 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004998 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00004999 xmlDocPtr doc;
5000 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005001
Owen Taylor3473f882001-02-23 17:55:21 +00005002 if (cur == NULL)
5003 return;
5004 switch (cur->atype) {
5005 case XML_ATTRIBUTE_CDATA:
5006 case XML_ATTRIBUTE_ID:
5007 case XML_ATTRIBUTE_IDREF :
5008 case XML_ATTRIBUTE_IDREFS:
5009 case XML_ATTRIBUTE_NMTOKEN:
5010 case XML_ATTRIBUTE_NMTOKENS:
5011 case XML_ATTRIBUTE_ENUMERATION:
5012 break;
5013 case XML_ATTRIBUTE_ENTITY:
5014 case XML_ATTRIBUTE_ENTITIES:
5015 case XML_ATTRIBUTE_NOTATION:
5016 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005017
5018 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
5019 cur->atype, cur->defaultValue);
5020 if ((ret == 0) && (ctxt->valid == 1))
5021 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005022 }
5023 if (cur->tree != NULL) {
5024 xmlEnumerationPtr tree = cur->tree;
5025 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005026 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00005027 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005028 if ((ret == 0) && (ctxt->valid == 1))
5029 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005030 tree = tree->next;
5031 }
5032 }
5033 }
Daniel Veillard878eab02002-02-19 13:46:09 +00005034 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
5035 doc = cur->doc;
5036 if ((doc == NULL) || (cur->elem == NULL)) {
5037 VERROR(ctxt->userData,
5038 "xmlValidateAttributeCallback(%s): internal error\n",
5039 cur->name);
5040 return;
5041 }
5042 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
5043 if (elem == NULL)
5044 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
5045 if (elem == NULL) {
5046 VERROR(ctxt->userData,
5047 "attribute %s: could not find decl for element %s\n",
5048 cur->name, cur->elem);
5049 return;
5050 }
5051 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
5052 VERROR(ctxt->userData,
Daniel Veillard58e44c92002-08-02 22:19:49 +00005053 "NOTATION attribute %s declared for EMPTY element %s\n",
Daniel Veillard878eab02002-02-19 13:46:09 +00005054 cur->name, cur->elem);
5055 ctxt->valid = 0;
5056 }
5057 }
Owen Taylor3473f882001-02-23 17:55:21 +00005058}
5059
5060/**
5061 * xmlValidateDtdFinal:
5062 * @ctxt: the validation context
5063 * @doc: a document instance
5064 *
5065 * Does the final step for the dtds validation once all the
5066 * subsets have been parsed
5067 *
5068 * basically it does the following checks described by the XML Rec
5069 * - check that ENTITY and ENTITIES type attributes default or
5070 * possible values matches one of the defined entities.
5071 * - check that NOTATION type attributes default or
5072 * possible values matches one of the defined notations.
5073 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005074 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00005075 */
5076
5077int
5078xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00005079 xmlDtdPtr dtd;
5080 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005081 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00005082
5083 if (doc == NULL) return(0);
5084 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5085 return(0);
5086 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005087 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005088 dtd = doc->intSubset;
5089 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5090 table = (xmlAttributeTablePtr) dtd->attributes;
5091 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005092 }
5093 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005094 entities = (xmlEntitiesTablePtr) dtd->entities;
5095 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5096 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005097 }
5098 dtd = doc->extSubset;
5099 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5100 table = (xmlAttributeTablePtr) dtd->attributes;
5101 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005102 }
5103 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005104 entities = (xmlEntitiesTablePtr) dtd->entities;
5105 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5106 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005107 }
5108 return(ctxt->valid);
5109}
5110
5111/**
5112 * xmlValidateDocument:
5113 * @ctxt: the validation context
5114 * @doc: a document instance
5115 *
5116 * Try to validate the document instance
5117 *
5118 * basically it does the all the checks described by the XML Rec
5119 * i.e. validates the internal and external subset (if present)
5120 * and validate the document tree.
5121 *
5122 * returns 1 if valid or 0 otherwise
5123 */
5124
5125int
5126xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5127 int ret;
5128 xmlNodePtr root;
5129
5130 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5131 return(0);
5132 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
5133 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
5134 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
5135 doc->intSubset->SystemID);
5136 if (doc->extSubset == NULL) {
5137 if (doc->intSubset->SystemID != NULL) {
5138 VERROR(ctxt->userData,
5139 "Could not load the external subset \"%s\"\n",
5140 doc->intSubset->SystemID);
5141 } else {
5142 VERROR(ctxt->userData,
5143 "Could not load the external subset \"%s\"\n",
5144 doc->intSubset->ExternalID);
5145 }
5146 return(0);
5147 }
5148 }
5149
5150 if (doc->ids != NULL) {
5151 xmlFreeIDTable(doc->ids);
5152 doc->ids = NULL;
5153 }
5154 if (doc->refs != NULL) {
5155 xmlFreeRefTable(doc->refs);
5156 doc->refs = NULL;
5157 }
5158 ret = xmlValidateDtdFinal(ctxt, doc);
5159 if (!xmlValidateRoot(ctxt, doc)) return(0);
5160
5161 root = xmlDocGetRootElement(doc);
5162 ret &= xmlValidateElement(ctxt, doc, root);
5163 ret &= xmlValidateDocumentFinal(ctxt, doc);
5164 return(ret);
5165}
5166
5167
5168/************************************************************************
5169 * *
5170 * Routines for dynamic validation editing *
5171 * *
5172 ************************************************************************/
5173
5174/**
5175 * xmlValidGetPotentialChildren:
5176 * @ctree: an element content tree
5177 * @list: an array to store the list of child names
5178 * @len: a pointer to the number of element in the list
5179 * @max: the size of the array
5180 *
5181 * Build/extend a list of potential children allowed by the content tree
5182 *
5183 * returns the number of element in the list, or -1 in case of error.
5184 */
5185
5186int
5187xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
5188 int *len, int max) {
5189 int i;
5190
5191 if ((ctree == NULL) || (list == NULL) || (len == NULL))
5192 return(-1);
5193 if (*len >= max) return(*len);
5194
5195 switch (ctree->type) {
5196 case XML_ELEMENT_CONTENT_PCDATA:
5197 for (i = 0; i < *len;i++)
5198 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
5199 list[(*len)++] = BAD_CAST "#PCDATA";
5200 break;
5201 case XML_ELEMENT_CONTENT_ELEMENT:
5202 for (i = 0; i < *len;i++)
5203 if (xmlStrEqual(ctree->name, list[i])) return(*len);
5204 list[(*len)++] = ctree->name;
5205 break;
5206 case XML_ELEMENT_CONTENT_SEQ:
5207 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5208 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5209 break;
5210 case XML_ELEMENT_CONTENT_OR:
5211 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5212 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5213 break;
5214 }
5215
5216 return(*len);
5217}
5218
5219/**
5220 * xmlValidGetValidElements:
5221 * @prev: an element to insert after
5222 * @next: an element to insert next
5223 * @list: an array to store the list of child names
5224 * @max: the size of the array
5225 *
5226 * This function returns the list of authorized children to insert
5227 * within an existing tree while respecting the validity constraints
5228 * forced by the Dtd. The insertion point is defined using @prev and
5229 * @next in the following ways:
5230 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
5231 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
5232 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
5233 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
5234 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
5235 *
5236 * pointers to the element names are inserted at the beginning of the array
5237 * and do not need to be freed.
5238 *
5239 * returns the number of element in the list, or -1 in case of error. If
5240 * the function returns the value @max the caller is invited to grow the
5241 * receiving array and retry.
5242 */
5243
5244int
5245xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
5246 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005247 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005248 int nb_valid_elements = 0;
5249 const xmlChar *elements[256];
5250 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005251 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005252
5253 xmlNode *ref_node;
5254 xmlNode *parent;
5255 xmlNode *test_node;
5256
5257 xmlNode *prev_next;
5258 xmlNode *next_prev;
5259 xmlNode *parent_childs;
5260 xmlNode *parent_last;
5261
5262 xmlElement *element_desc;
5263
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00005264 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005265
Owen Taylor3473f882001-02-23 17:55:21 +00005266 if (prev == NULL && next == NULL)
5267 return(-1);
5268
5269 if (list == NULL) return(-1);
5270 if (max <= 0) return(-1);
5271
5272 nb_valid_elements = 0;
5273 ref_node = prev ? prev : next;
5274 parent = ref_node->parent;
5275
5276 /*
5277 * Retrieves the parent element declaration
5278 */
5279 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5280 parent->name);
5281 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5282 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5283 parent->name);
5284 if (element_desc == NULL) return(-1);
5285
5286 /*
5287 * Do a backup of the current tree structure
5288 */
5289 prev_next = prev ? prev->next : NULL;
5290 next_prev = next ? next->prev : NULL;
5291 parent_childs = parent->children;
5292 parent_last = parent->last;
5293
5294 /*
5295 * Creates a dummy node and insert it into the tree
5296 */
5297 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5298 test_node->doc = ref_node->doc;
5299 test_node->parent = parent;
5300 test_node->prev = prev;
5301 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005302 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005303
5304 if (prev) prev->next = test_node;
5305 else parent->children = test_node;
5306
5307 if (next) next->prev = test_node;
5308 else parent->last = test_node;
5309
5310 /*
5311 * Insert each potential child node and check if the parent is
5312 * still valid
5313 */
5314 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5315 elements, &nb_elements, 256);
5316
5317 for (i = 0;i < nb_elements;i++) {
5318 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005319 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005320 int j;
5321
5322 for (j = 0; j < nb_valid_elements;j++)
5323 if (xmlStrEqual(elements[i], list[j])) break;
5324 list[nb_valid_elements++] = elements[i];
5325 if (nb_valid_elements >= max) break;
5326 }
5327 }
5328
5329 /*
5330 * Restore the tree structure
5331 */
5332 if (prev) prev->next = prev_next;
5333 if (next) next->prev = next_prev;
5334 parent->children = parent_childs;
5335 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005336
5337 /*
5338 * Free up the dummy node
5339 */
5340 test_node->name = name;
5341 xmlFreeNode(test_node);
5342
Owen Taylor3473f882001-02-23 17:55:21 +00005343 return(nb_valid_elements);
5344}