blob: 5476f844a59b09c0f3281ee050377d62080ce70e [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 Veillard940492d2002-04-15 10:15:25 +000094#define MAX_RECURSE 1024
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))) {
1302 VERROR(ctxt->userData, "Attribute %s on %s: invalid default value\n",
1303 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,
1365 "Attribute %s on %s: already defined\n",
1366 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);
1931 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1932 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1933 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1934 attr->name);
1935
1936 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1937 return(1);
1938 }
1939 return(0);
1940}
1941
1942/**
1943 * xmlRemoveID
1944 * @doc: the document
1945 * @attr: the attribute
1946 *
1947 * Remove the given attribute from the ID table maintained internally.
1948 *
1949 * Returns -1 if the lookup failed and 0 otherwise
1950 */
1951int
1952xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1953 xmlAttrPtr cur;
1954 xmlIDTablePtr table;
1955 xmlChar *ID;
1956
1957 if (doc == NULL) return(-1);
1958 if (attr == NULL) return(-1);
1959 table = (xmlIDTablePtr) doc->ids;
1960 if (table == NULL)
1961 return(-1);
1962
1963 if (attr == NULL)
1964 return(-1);
1965 ID = xmlNodeListGetString(doc, attr->children, 1);
1966 if (ID == NULL)
1967 return(-1);
1968 cur = xmlHashLookup(table, ID);
1969 if (cur != attr) {
1970 xmlFree(ID);
1971 return(-1);
1972 }
1973 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1974 xmlFree(ID);
1975 return(0);
1976}
1977
1978/**
1979 * xmlGetID:
1980 * @doc: pointer to the document
1981 * @ID: the ID value
1982 *
1983 * Search the attribute declaring the given ID
1984 *
1985 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1986 */
1987xmlAttrPtr
1988xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
1989 xmlIDTablePtr table;
1990 xmlIDPtr id;
1991
1992 if (doc == NULL) {
1993 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
1994 return(NULL);
1995 }
1996
1997 if (ID == NULL) {
1998 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
1999 return(NULL);
2000 }
2001
2002 table = (xmlIDTablePtr) doc->ids;
2003 if (table == NULL)
2004 return(NULL);
2005
2006 id = xmlHashLookup(table, ID);
2007 if (id == NULL)
2008 return(NULL);
2009 return(id->attr);
2010}
2011
2012/************************************************************************
2013 * *
2014 * Refs *
2015 * *
2016 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00002017typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00002018{
2019 xmlListPtr l;
2020 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002021} xmlRemoveMemo;
2022
2023typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2024
2025typedef struct xmlValidateMemo_t
2026{
2027 xmlValidCtxtPtr ctxt;
2028 const xmlChar *name;
2029} xmlValidateMemo;
2030
2031typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002032
2033/**
2034 * xmlCreateRefTable:
2035 *
2036 * create and initialize an empty ref hash table.
2037 *
2038 * Returns the xmlRefTablePtr just created or NULL in case
2039 * of error.
2040 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002041static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002042xmlCreateRefTable(void) {
2043 return(xmlHashCreate(0));
2044}
2045
2046/**
2047 * xmlFreeRef:
2048 * @lk: A list link
2049 *
2050 * Deallocate the memory used by a ref definition
2051 */
2052static void
2053xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002054 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2055 if (ref == NULL) return;
2056 if (ref->value != NULL)
2057 xmlFree((xmlChar *)ref->value);
2058 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002059}
2060
2061/**
2062 * xmlFreeRefList:
2063 * @list_ref: A list of references.
2064 *
2065 * Deallocate the memory used by a list of references
2066 */
2067static void
2068xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002069 if (list_ref == NULL) return;
2070 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002071}
2072
2073/**
2074 * xmlWalkRemoveRef:
2075 * @data: Contents of current link
2076 * @user: Value supplied by the user
2077 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002078 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002079 */
2080static int
2081xmlWalkRemoveRef(const void *data, const void *user)
2082{
Daniel Veillard37721922001-05-04 15:21:12 +00002083 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2084 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2085 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002086
Daniel Veillard37721922001-05-04 15:21:12 +00002087 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2088 xmlListRemoveFirst(ref_list, (void *)data);
2089 return 0;
2090 }
2091 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002092}
2093
2094/**
2095 * xmlAddRef:
2096 * @ctxt: the validation context
2097 * @doc: pointer to the document
2098 * @value: the value name
2099 * @attr: the attribute holding the Ref
2100 *
2101 * Register a new ref declaration
2102 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002103 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002104 */
2105xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002106xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002107 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002108 xmlRefPtr ret;
2109 xmlRefTablePtr table;
2110 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002111
Daniel Veillard37721922001-05-04 15:21:12 +00002112 if (doc == NULL) {
2113 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002114 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002115 return(NULL);
2116 }
2117 if (value == NULL) {
2118 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002119 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002120 return(NULL);
2121 }
2122 if (attr == NULL) {
2123 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002124 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002125 return(NULL);
2126 }
Owen Taylor3473f882001-02-23 17:55:21 +00002127
Daniel Veillard37721922001-05-04 15:21:12 +00002128 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002129 * Create the Ref table if needed.
2130 */
Daniel Veillard37721922001-05-04 15:21:12 +00002131 table = (xmlRefTablePtr) doc->refs;
2132 if (table == NULL)
2133 doc->refs = table = xmlCreateRefTable();
2134 if (table == NULL) {
2135 xmlGenericError(xmlGenericErrorContext,
2136 "xmlAddRef: Table creation failed!\n");
2137 return(NULL);
2138 }
Owen Taylor3473f882001-02-23 17:55:21 +00002139
Daniel Veillard37721922001-05-04 15:21:12 +00002140 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2141 if (ret == NULL) {
2142 xmlGenericError(xmlGenericErrorContext,
2143 "xmlAddRef: out of memory\n");
2144 return(NULL);
2145 }
Owen Taylor3473f882001-02-23 17:55:21 +00002146
Daniel Veillard37721922001-05-04 15:21:12 +00002147 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002148 * fill the structure.
2149 */
Daniel Veillard37721922001-05-04 15:21:12 +00002150 ret->value = xmlStrdup(value);
2151 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002152
Daniel Veillard37721922001-05-04 15:21:12 +00002153 /* To add a reference :-
2154 * References are maintained as a list of references,
2155 * Lookup the entry, if no entry create new nodelist
2156 * Add the owning node to the NodeList
2157 * Return the ref
2158 */
Owen Taylor3473f882001-02-23 17:55:21 +00002159
Daniel Veillard37721922001-05-04 15:21:12 +00002160 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2161 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2162 xmlGenericError(xmlGenericErrorContext,
2163 "xmlAddRef: Reference list creation failed!\n");
2164 return(NULL);
2165 }
2166 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2167 xmlListDelete(ref_list);
2168 xmlGenericError(xmlGenericErrorContext,
2169 "xmlAddRef: Reference list insertion failed!\n");
2170 return(NULL);
2171 }
2172 }
2173 xmlListInsert(ref_list, ret);
2174 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002175}
2176
2177/**
2178 * xmlFreeRefTable:
2179 * @table: An ref table
2180 *
2181 * Deallocate the memory used by an Ref hash table.
2182 */
2183void
2184xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002185 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002186}
2187
2188/**
2189 * xmlIsRef:
2190 * @doc: the document
2191 * @elem: the element carrying the attribute
2192 * @attr: the attribute
2193 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002194 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002195 * then this is simple, otherwise we use an heuristic: name Ref (upper
2196 * or lowercase).
2197 *
2198 * Returns 0 or 1 depending on the lookup result
2199 */
2200int
2201xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002202 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2203 return(0);
2204 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2205 /* TODO @@@ */
2206 return(0);
2207 } else {
2208 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002209
Daniel Veillard37721922001-05-04 15:21:12 +00002210 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2211 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2212 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2213 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002214
Daniel Veillard37721922001-05-04 15:21:12 +00002215 if ((attrDecl != NULL) &&
2216 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2217 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2218 return(1);
2219 }
2220 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002221}
2222
2223/**
2224 * xmlRemoveRef
2225 * @doc: the document
2226 * @attr: the attribute
2227 *
2228 * Remove the given attribute from the Ref table maintained internally.
2229 *
2230 * Returns -1 if the lookup failed and 0 otherwise
2231 */
2232int
2233xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002234 xmlListPtr ref_list;
2235 xmlRefTablePtr table;
2236 xmlChar *ID;
2237 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002238
Daniel Veillard37721922001-05-04 15:21:12 +00002239 if (doc == NULL) return(-1);
2240 if (attr == NULL) return(-1);
2241 table = (xmlRefTablePtr) doc->refs;
2242 if (table == NULL)
2243 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002244
Daniel Veillard37721922001-05-04 15:21:12 +00002245 if (attr == NULL)
2246 return(-1);
2247 ID = xmlNodeListGetString(doc, attr->children, 1);
2248 if (ID == NULL)
2249 return(-1);
2250 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002251
Daniel Veillard37721922001-05-04 15:21:12 +00002252 if(ref_list == NULL) {
2253 xmlFree(ID);
2254 return (-1);
2255 }
2256 /* At this point, ref_list refers to a list of references which
2257 * have the same key as the supplied attr. Our list of references
2258 * is ordered by reference address and we don't have that information
2259 * here to use when removing. We'll have to walk the list and
2260 * check for a matching attribute, when we find one stop the walk
2261 * and remove the entry.
2262 * The list is ordered by reference, so that means we don't have the
2263 * key. Passing the list and the reference to the walker means we
2264 * will have enough data to be able to remove the entry.
2265 */
2266 target.l = ref_list;
2267 target.ap = attr;
2268
2269 /* Remove the supplied attr from our list */
2270 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002271
Daniel Veillard37721922001-05-04 15:21:12 +00002272 /*If the list is empty then remove the list entry in the hash */
2273 if (xmlListEmpty(ref_list))
2274 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2275 xmlFreeRefList);
2276 xmlFree(ID);
2277 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002278}
2279
2280/**
2281 * xmlGetRefs:
2282 * @doc: pointer to the document
2283 * @ID: the ID value
2284 *
2285 * Find the set of references for the supplied ID.
2286 *
2287 * Returns NULL if not found, otherwise node set for the ID.
2288 */
2289xmlListPtr
2290xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002291 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002292
Daniel Veillard37721922001-05-04 15:21:12 +00002293 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002294 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002295 return(NULL);
2296 }
Owen Taylor3473f882001-02-23 17:55:21 +00002297
Daniel Veillard37721922001-05-04 15:21:12 +00002298 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002299 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002300 return(NULL);
2301 }
Owen Taylor3473f882001-02-23 17:55:21 +00002302
Daniel Veillard37721922001-05-04 15:21:12 +00002303 table = (xmlRefTablePtr) doc->refs;
2304 if (table == NULL)
2305 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002306
Daniel Veillard37721922001-05-04 15:21:12 +00002307 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002308}
2309
2310/************************************************************************
2311 * *
2312 * Routines for validity checking *
2313 * *
2314 ************************************************************************/
2315
2316/**
2317 * xmlGetDtdElementDesc:
2318 * @dtd: a pointer to the DtD to search
2319 * @name: the element name
2320 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002321 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002322 *
2323 * returns the xmlElementPtr if found or NULL
2324 */
2325
2326xmlElementPtr
2327xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2328 xmlElementTablePtr table;
2329 xmlElementPtr cur;
2330 xmlChar *uqname = NULL, *prefix = NULL;
2331
2332 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002333 if (dtd->elements == NULL)
2334 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002335 table = (xmlElementTablePtr) dtd->elements;
2336
2337 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002338 if (uqname != NULL)
2339 name = uqname;
2340 cur = xmlHashLookup2(table, name, prefix);
2341 if (prefix != NULL) xmlFree(prefix);
2342 if (uqname != NULL) xmlFree(uqname);
2343 return(cur);
2344}
2345/**
2346 * xmlGetDtdElementDesc2:
2347 * @dtd: a pointer to the DtD to search
2348 * @name: the element name
2349 * @create: create an empty description if not found
2350 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002351 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002352 *
2353 * returns the xmlElementPtr if found or NULL
2354 */
2355
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002356static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002357xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2358 xmlElementTablePtr table;
2359 xmlElementPtr cur;
2360 xmlChar *uqname = NULL, *prefix = NULL;
2361
2362 if (dtd == NULL) return(NULL);
2363 if (dtd->elements == NULL) {
2364 if (!create)
2365 return(NULL);
2366 /*
2367 * Create the Element table if needed.
2368 */
2369 table = (xmlElementTablePtr) dtd->elements;
2370 if (table == NULL) {
2371 table = xmlCreateElementTable();
2372 dtd->elements = (void *) table;
2373 }
2374 if (table == NULL) {
2375 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002376 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002377 return(NULL);
2378 }
2379 }
2380 table = (xmlElementTablePtr) dtd->elements;
2381
2382 uqname = xmlSplitQName2(name, &prefix);
2383 if (uqname != NULL)
2384 name = uqname;
2385 cur = xmlHashLookup2(table, name, prefix);
2386 if ((cur == NULL) && (create)) {
2387 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2388 if (cur == NULL) {
2389 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002390 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002391 return(NULL);
2392 }
2393 memset(cur, 0, sizeof(xmlElement));
2394 cur->type = XML_ELEMENT_DECL;
2395
2396 /*
2397 * fill the structure.
2398 */
2399 cur->name = xmlStrdup(name);
2400 cur->prefix = xmlStrdup(prefix);
2401 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2402
2403 xmlHashAddEntry2(table, name, prefix, cur);
2404 }
2405 if (prefix != NULL) xmlFree(prefix);
2406 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002407 return(cur);
2408}
2409
2410/**
2411 * xmlGetDtdQElementDesc:
2412 * @dtd: a pointer to the DtD to search
2413 * @name: the element name
2414 * @prefix: the element namespace prefix
2415 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002416 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002417 *
2418 * returns the xmlElementPtr if found or NULL
2419 */
2420
Daniel Veillard48da9102001-08-07 01:10:10 +00002421xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002422xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2423 const xmlChar *prefix) {
2424 xmlElementTablePtr table;
2425
2426 if (dtd == NULL) return(NULL);
2427 if (dtd->elements == NULL) return(NULL);
2428 table = (xmlElementTablePtr) dtd->elements;
2429
2430 return(xmlHashLookup2(table, name, prefix));
2431}
2432
2433/**
2434 * xmlGetDtdAttrDesc:
2435 * @dtd: a pointer to the DtD to search
2436 * @elem: the element name
2437 * @name: the attribute name
2438 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002439 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002440 * this element.
2441 *
2442 * returns the xmlAttributePtr if found or NULL
2443 */
2444
2445xmlAttributePtr
2446xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2447 xmlAttributeTablePtr table;
2448 xmlAttributePtr cur;
2449 xmlChar *uqname = NULL, *prefix = NULL;
2450
2451 if (dtd == NULL) return(NULL);
2452 if (dtd->attributes == NULL) return(NULL);
2453
2454 table = (xmlAttributeTablePtr) dtd->attributes;
2455 if (table == NULL)
2456 return(NULL);
2457
2458 uqname = xmlSplitQName2(name, &prefix);
2459
2460 if (uqname != NULL) {
2461 cur = xmlHashLookup3(table, uqname, prefix, elem);
2462 if (prefix != NULL) xmlFree(prefix);
2463 if (uqname != NULL) xmlFree(uqname);
2464 } else
2465 cur = xmlHashLookup3(table, name, NULL, elem);
2466 return(cur);
2467}
2468
2469/**
2470 * xmlGetDtdQAttrDesc:
2471 * @dtd: a pointer to the DtD to search
2472 * @elem: the element name
2473 * @name: the attribute name
2474 * @prefix: the attribute namespace prefix
2475 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002476 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002477 * this element.
2478 *
2479 * returns the xmlAttributePtr if found or NULL
2480 */
2481
Daniel Veillard48da9102001-08-07 01:10:10 +00002482xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002483xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2484 const xmlChar *prefix) {
2485 xmlAttributeTablePtr table;
2486
2487 if (dtd == NULL) return(NULL);
2488 if (dtd->attributes == NULL) return(NULL);
2489 table = (xmlAttributeTablePtr) dtd->attributes;
2490
2491 return(xmlHashLookup3(table, name, prefix, elem));
2492}
2493
2494/**
2495 * xmlGetDtdNotationDesc:
2496 * @dtd: a pointer to the DtD to search
2497 * @name: the notation name
2498 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002499 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002500 *
2501 * returns the xmlNotationPtr if found or NULL
2502 */
2503
2504xmlNotationPtr
2505xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2506 xmlNotationTablePtr table;
2507
2508 if (dtd == NULL) return(NULL);
2509 if (dtd->notations == NULL) return(NULL);
2510 table = (xmlNotationTablePtr) dtd->notations;
2511
2512 return(xmlHashLookup(table, name));
2513}
2514
2515/**
2516 * xmlValidateNotationUse:
2517 * @ctxt: the validation context
2518 * @doc: the document
2519 * @notationName: the notation name to check
2520 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002521 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002522 * - [ VC: Notation Declared ]
2523 *
2524 * returns 1 if valid or 0 otherwise
2525 */
2526
2527int
2528xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2529 const xmlChar *notationName) {
2530 xmlNotationPtr notaDecl;
2531 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2532
2533 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2534 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2535 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2536
2537 if (notaDecl == NULL) {
2538 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2539 notationName);
2540 return(0);
2541 }
2542 return(1);
2543}
2544
2545/**
2546 * xmlIsMixedElement
2547 * @doc: the document
2548 * @name: the element name
2549 *
2550 * Search in the DtDs whether an element accept Mixed content (or ANY)
2551 * basically if it is supposed to accept text childs
2552 *
2553 * returns 0 if no, 1 if yes, and -1 if no element description is available
2554 */
2555
2556int
2557xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2558 xmlElementPtr elemDecl;
2559
2560 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2561
2562 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2563 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2564 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2565 if (elemDecl == NULL) return(-1);
2566 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002567 case XML_ELEMENT_TYPE_UNDEFINED:
2568 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002569 case XML_ELEMENT_TYPE_ELEMENT:
2570 return(0);
2571 case XML_ELEMENT_TYPE_EMPTY:
2572 /*
2573 * return 1 for EMPTY since we want VC error to pop up
2574 * on <empty> </empty> for example
2575 */
2576 case XML_ELEMENT_TYPE_ANY:
2577 case XML_ELEMENT_TYPE_MIXED:
2578 return(1);
2579 }
2580 return(1);
2581}
2582
2583/**
2584 * xmlValidateNameValue:
2585 * @value: an Name value
2586 *
2587 * Validate that the given value match Name production
2588 *
2589 * returns 1 if valid or 0 otherwise
2590 */
2591
Daniel Veillard9b731d72002-04-14 12:56:08 +00002592int
Owen Taylor3473f882001-02-23 17:55:21 +00002593xmlValidateNameValue(const xmlChar *value) {
2594 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002595 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002596
2597 if (value == NULL) return(0);
2598 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002599 val = xmlStringCurrentChar(NULL, cur, &len);
2600 cur += len;
2601 if (!IS_LETTER(val) && (val != '_') &&
2602 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002603 return(0);
2604 }
2605
Daniel Veillardd8224e02002-01-13 15:43:22 +00002606 val = xmlStringCurrentChar(NULL, cur, &len);
2607 cur += len;
2608 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2609 (val == '.') || (val == '-') ||
2610 (val == '_') || (val == ':') ||
2611 (IS_COMBINING(val)) ||
2612 (IS_EXTENDER(val))) {
2613 val = xmlStringCurrentChar(NULL, cur, &len);
2614 cur += len;
2615 }
Owen Taylor3473f882001-02-23 17:55:21 +00002616
Daniel Veillardd8224e02002-01-13 15:43:22 +00002617 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002618
2619 return(1);
2620}
2621
2622/**
2623 * xmlValidateNamesValue:
2624 * @value: an Names value
2625 *
2626 * Validate that the given value match Names production
2627 *
2628 * returns 1 if valid or 0 otherwise
2629 */
2630
Daniel Veillard9b731d72002-04-14 12:56:08 +00002631int
Owen Taylor3473f882001-02-23 17:55:21 +00002632xmlValidateNamesValue(const xmlChar *value) {
2633 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002634 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002635
2636 if (value == NULL) return(0);
2637 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002638 val = xmlStringCurrentChar(NULL, cur, &len);
2639 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002640
Daniel Veillardd8224e02002-01-13 15:43:22 +00002641 if (!IS_LETTER(val) && (val != '_') &&
2642 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002643 return(0);
2644 }
2645
Daniel Veillardd8224e02002-01-13 15:43:22 +00002646 val = xmlStringCurrentChar(NULL, cur, &len);
2647 cur += len;
2648 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2649 (val == '.') || (val == '-') ||
2650 (val == '_') || (val == ':') ||
2651 (IS_COMBINING(val)) ||
2652 (IS_EXTENDER(val))) {
2653 val = xmlStringCurrentChar(NULL, cur, &len);
2654 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002655 }
2656
Daniel Veillardd8224e02002-01-13 15:43:22 +00002657 while (IS_BLANK(val)) {
2658 while (IS_BLANK(val)) {
2659 val = xmlStringCurrentChar(NULL, cur, &len);
2660 cur += len;
2661 }
2662
2663 if (!IS_LETTER(val) && (val != '_') &&
2664 (val != ':')) {
2665 return(0);
2666 }
2667 val = xmlStringCurrentChar(NULL, cur, &len);
2668 cur += len;
2669
2670 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2671 (val == '.') || (val == '-') ||
2672 (val == '_') || (val == ':') ||
2673 (IS_COMBINING(val)) ||
2674 (IS_EXTENDER(val))) {
2675 val = xmlStringCurrentChar(NULL, cur, &len);
2676 cur += len;
2677 }
2678 }
2679
2680 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002681
2682 return(1);
2683}
2684
2685/**
2686 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002687 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002688 *
2689 * Validate that the given value match Nmtoken production
2690 *
2691 * [ VC: Name Token ]
2692 *
2693 * returns 1 if valid or 0 otherwise
2694 */
2695
Daniel Veillard9b731d72002-04-14 12:56:08 +00002696int
Owen Taylor3473f882001-02-23 17:55:21 +00002697xmlValidateNmtokenValue(const xmlChar *value) {
2698 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002699 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002700
2701 if (value == NULL) return(0);
2702 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002703 val = xmlStringCurrentChar(NULL, cur, &len);
2704 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002705
Daniel Veillardd8224e02002-01-13 15:43:22 +00002706 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2707 (val != '.') && (val != '-') &&
2708 (val != '_') && (val != ':') &&
2709 (!IS_COMBINING(val)) &&
2710 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002711 return(0);
2712
Daniel Veillardd8224e02002-01-13 15:43:22 +00002713 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2714 (val == '.') || (val == '-') ||
2715 (val == '_') || (val == ':') ||
2716 (IS_COMBINING(val)) ||
2717 (IS_EXTENDER(val))) {
2718 val = xmlStringCurrentChar(NULL, cur, &len);
2719 cur += len;
2720 }
Owen Taylor3473f882001-02-23 17:55:21 +00002721
Daniel Veillardd8224e02002-01-13 15:43:22 +00002722 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002723
2724 return(1);
2725}
2726
2727/**
2728 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002729 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00002730 *
2731 * Validate that the given value match Nmtokens production
2732 *
2733 * [ VC: Name Token ]
2734 *
2735 * returns 1 if valid or 0 otherwise
2736 */
2737
Daniel Veillard9b731d72002-04-14 12:56:08 +00002738int
Owen Taylor3473f882001-02-23 17:55:21 +00002739xmlValidateNmtokensValue(const xmlChar *value) {
2740 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002741 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002742
2743 if (value == NULL) return(0);
2744 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002745 val = xmlStringCurrentChar(NULL, cur, &len);
2746 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002747
Daniel Veillardd8224e02002-01-13 15:43:22 +00002748 while (IS_BLANK(val)) {
2749 val = xmlStringCurrentChar(NULL, cur, &len);
2750 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002751 }
2752
Daniel Veillardd8224e02002-01-13 15:43:22 +00002753 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2754 (val != '.') && (val != '-') &&
2755 (val != '_') && (val != ':') &&
2756 (!IS_COMBINING(val)) &&
2757 (!IS_EXTENDER(val)))
2758 return(0);
2759
2760 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2761 (val == '.') || (val == '-') ||
2762 (val == '_') || (val == ':') ||
2763 (IS_COMBINING(val)) ||
2764 (IS_EXTENDER(val))) {
2765 val = xmlStringCurrentChar(NULL, cur, &len);
2766 cur += len;
2767 }
2768
2769 while (IS_BLANK(val)) {
2770 while (IS_BLANK(val)) {
2771 val = xmlStringCurrentChar(NULL, cur, &len);
2772 cur += len;
2773 }
2774 if (val == 0) return(1);
2775
2776 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2777 (val != '.') && (val != '-') &&
2778 (val != '_') && (val != ':') &&
2779 (!IS_COMBINING(val)) &&
2780 (!IS_EXTENDER(val)))
2781 return(0);
2782
2783 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2784 (val == '.') || (val == '-') ||
2785 (val == '_') || (val == ':') ||
2786 (IS_COMBINING(val)) ||
2787 (IS_EXTENDER(val))) {
2788 val = xmlStringCurrentChar(NULL, cur, &len);
2789 cur += len;
2790 }
2791 }
2792
2793 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002794
2795 return(1);
2796}
2797
2798/**
2799 * xmlValidateNotationDecl:
2800 * @ctxt: the validation context
2801 * @doc: a document instance
2802 * @nota: a notation definition
2803 *
2804 * Try to validate a single notation definition
2805 * basically it does the following checks as described by the
2806 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002807 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00002808 * But this function get called anyway ...
2809 *
2810 * returns 1 if valid or 0 otherwise
2811 */
2812
2813int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002814xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2815 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002816 int ret = 1;
2817
2818 return(ret);
2819}
2820
2821/**
2822 * xmlValidateAttributeValue:
2823 * @type: an attribute type
2824 * @value: an attribute value
2825 *
2826 * Validate that the given attribute value match the proper production
2827 *
2828 * [ VC: ID ]
2829 * Values of type ID must match the Name production....
2830 *
2831 * [ VC: IDREF ]
2832 * Values of type IDREF must match the Name production, and values
2833 * of type IDREFS must match Names ...
2834 *
2835 * [ VC: Entity Name ]
2836 * Values of type ENTITY must match the Name production, values
2837 * of type ENTITIES must match Names ...
2838 *
2839 * [ VC: Name Token ]
2840 * Values of type NMTOKEN must match the Nmtoken production; values
2841 * of type NMTOKENS must match Nmtokens.
2842 *
2843 * returns 1 if valid or 0 otherwise
2844 */
2845
2846int
2847xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2848 switch (type) {
2849 case XML_ATTRIBUTE_ENTITIES:
2850 case XML_ATTRIBUTE_IDREFS:
2851 return(xmlValidateNamesValue(value));
2852 case XML_ATTRIBUTE_ENTITY:
2853 case XML_ATTRIBUTE_IDREF:
2854 case XML_ATTRIBUTE_ID:
2855 case XML_ATTRIBUTE_NOTATION:
2856 return(xmlValidateNameValue(value));
2857 case XML_ATTRIBUTE_NMTOKENS:
2858 case XML_ATTRIBUTE_ENUMERATION:
2859 return(xmlValidateNmtokensValue(value));
2860 case XML_ATTRIBUTE_NMTOKEN:
2861 return(xmlValidateNmtokenValue(value));
2862 case XML_ATTRIBUTE_CDATA:
2863 break;
2864 }
2865 return(1);
2866}
2867
2868/**
2869 * xmlValidateAttributeValue2:
2870 * @ctxt: the validation context
2871 * @doc: the document
2872 * @name: the attribute name (used for error reporting only)
2873 * @type: the attribute type
2874 * @value: the attribute value
2875 *
2876 * Validate that the given attribute value match a given type.
2877 * This typically cannot be done before having finished parsing
2878 * the subsets.
2879 *
2880 * [ VC: IDREF ]
2881 * Values of type IDREF must match one of the declared IDs
2882 * Values of type IDREFS must match a sequence of the declared IDs
2883 * each Name must match the value of an ID attribute on some element
2884 * in the XML document; i.e. IDREF values must match the value of
2885 * some ID attribute
2886 *
2887 * [ VC: Entity Name ]
2888 * Values of type ENTITY must match one declared entity
2889 * Values of type ENTITIES must match a sequence of declared entities
2890 *
2891 * [ VC: Notation Attributes ]
2892 * all notation names in the declaration must be declared.
2893 *
2894 * returns 1 if valid or 0 otherwise
2895 */
2896
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002897static int
Owen Taylor3473f882001-02-23 17:55:21 +00002898xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2899 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2900 int ret = 1;
2901 switch (type) {
2902 case XML_ATTRIBUTE_IDREFS:
2903 case XML_ATTRIBUTE_IDREF:
2904 case XML_ATTRIBUTE_ID:
2905 case XML_ATTRIBUTE_NMTOKENS:
2906 case XML_ATTRIBUTE_ENUMERATION:
2907 case XML_ATTRIBUTE_NMTOKEN:
2908 case XML_ATTRIBUTE_CDATA:
2909 break;
2910 case XML_ATTRIBUTE_ENTITY: {
2911 xmlEntityPtr ent;
2912
2913 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00002914 if ((ent == NULL) && (doc->standalone == 1)) {
2915 doc->standalone = 0;
2916 ent = xmlGetDocEntity(doc, value);
2917 if (ent != NULL) {
2918 VERROR(ctxt->userData,
2919"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
2920 name, value);
2921 /* WAIT to get answer from the Core WG on this
2922 ret = 0;
2923 */
2924 }
2925 }
Owen Taylor3473f882001-02-23 17:55:21 +00002926 if (ent == NULL) {
2927 VERROR(ctxt->userData,
2928 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2929 name, value);
2930 ret = 0;
2931 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2932 VERROR(ctxt->userData,
2933 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2934 name, value);
2935 ret = 0;
2936 }
2937 break;
2938 }
2939 case XML_ATTRIBUTE_ENTITIES: {
2940 xmlChar *dup, *nam = NULL, *cur, save;
2941 xmlEntityPtr ent;
2942
2943 dup = xmlStrdup(value);
2944 if (dup == NULL)
2945 return(0);
2946 cur = dup;
2947 while (*cur != 0) {
2948 nam = cur;
2949 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2950 save = *cur;
2951 *cur = 0;
2952 ent = xmlGetDocEntity(doc, nam);
2953 if (ent == NULL) {
2954 VERROR(ctxt->userData,
2955 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2956 name, nam);
2957 ret = 0;
2958 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2959 VERROR(ctxt->userData,
2960 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2961 name, nam);
2962 ret = 0;
2963 }
2964 if (save == 0)
2965 break;
2966 *cur = save;
2967 while (IS_BLANK(*cur)) cur++;
2968 }
2969 xmlFree(dup);
2970 break;
2971 }
2972 case XML_ATTRIBUTE_NOTATION: {
2973 xmlNotationPtr nota;
2974
2975 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2976 if ((nota == NULL) && (doc->extSubset != NULL))
2977 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2978
2979 if (nota == NULL) {
2980 VERROR(ctxt->userData,
2981 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2982 name, value);
2983 ret = 0;
2984 }
2985 break;
2986 }
2987 }
2988 return(ret);
2989}
2990
2991/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00002992 * xmlValidCtxtNormalizeAttributeValue:
2993 * @ctxt: the validation context
2994 * @doc: the document
2995 * @elem: the parent
2996 * @name: the attribute name
2997 * @value: the attribute value
2998 * @ctxt: the validation context or NULL
2999 *
3000 * Does the validation related extra step of the normalization of attribute
3001 * values:
3002 *
3003 * If the declared value is not CDATA, then the XML processor must further
3004 * process the normalized attribute value by discarding any leading and
3005 * trailing space (#x20) characters, and by replacing sequences of space
3006 * (#x20) characters by single space (#x20) character.
3007 *
3008 * Also check VC: Standalone Document Declaration in P32, and update
3009 * ctxt->valid accordingly
3010 *
3011 * returns a new normalized string if normalization is needed, NULL otherwise
3012 * the caller must free the returned value.
3013 */
3014
3015xmlChar *
3016xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3017 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
3018 xmlChar *ret, *dst;
3019 const xmlChar *src;
3020 xmlAttributePtr attrDecl = NULL;
3021 int extsubset = 0;
3022
3023 if (doc == NULL) return(NULL);
3024 if (elem == NULL) return(NULL);
3025 if (name == NULL) return(NULL);
3026 if (value == NULL) return(NULL);
3027
3028 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3029 xmlChar qname[500];
3030 snprintf((char *) qname, sizeof(qname), "%s:%s",
3031 elem->ns->prefix, elem->name);
3032 qname[sizeof(qname) - 1] = 0;
3033 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3034 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3035 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3036 if (attrDecl != NULL)
3037 extsubset = 1;
3038 }
3039 }
3040 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3041 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3042 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3043 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3044 if (attrDecl != NULL)
3045 extsubset = 1;
3046 }
3047
3048 if (attrDecl == NULL)
3049 return(NULL);
3050 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3051 return(NULL);
3052
3053 ret = xmlStrdup(value);
3054 if (ret == NULL)
3055 return(NULL);
3056 src = value;
3057 dst = ret;
3058 while (*src == 0x20) src++;
3059 while (*src != 0) {
3060 if (*src == 0x20) {
3061 while (*src == 0x20) src++;
3062 if (*src != 0)
3063 *dst++ = 0x20;
3064 } else {
3065 *dst++ = *src++;
3066 }
3067 }
3068 *dst = 0;
3069 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3070 VERROR(ctxt->userData,
3071"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3072 name, elem->name);
3073 ctxt->valid = 0;
3074 }
3075 return(ret);
3076}
3077
3078/**
Owen Taylor3473f882001-02-23 17:55:21 +00003079 * xmlValidNormalizeAttributeValue:
3080 * @doc: the document
3081 * @elem: the parent
3082 * @name: the attribute name
3083 * @value: the attribute value
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003084 * @ctxt: the validation context or NULL
Owen Taylor3473f882001-02-23 17:55:21 +00003085 *
3086 * Does the validation related extra step of the normalization of attribute
3087 * values:
3088 *
3089 * If the declared value is not CDATA, then the XML processor must further
3090 * process the normalized attribute value by discarding any leading and
3091 * trailing space (#x20) characters, and by replacing sequences of space
3092 * (#x20) characters by single space (#x20) character.
3093 *
3094 * returns a new normalized string if normalization is needed, NULL otherwise
3095 * the caller must free the returned value.
3096 */
3097
3098xmlChar *
3099xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3100 const xmlChar *name, const xmlChar *value) {
3101 xmlChar *ret, *dst;
3102 const xmlChar *src;
3103 xmlAttributePtr attrDecl = NULL;
3104
3105 if (doc == NULL) return(NULL);
3106 if (elem == NULL) return(NULL);
3107 if (name == NULL) return(NULL);
3108 if (value == NULL) return(NULL);
3109
3110 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3111 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003112 snprintf((char *) qname, sizeof(qname), "%s:%s",
3113 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003114 qname[sizeof(qname) - 1] = 0;
3115 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3116 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3117 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3118 }
3119 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3120 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3121 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3122
3123 if (attrDecl == NULL)
3124 return(NULL);
3125 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3126 return(NULL);
3127
3128 ret = xmlStrdup(value);
3129 if (ret == NULL)
3130 return(NULL);
3131 src = value;
3132 dst = ret;
3133 while (*src == 0x20) src++;
3134 while (*src != 0) {
3135 if (*src == 0x20) {
3136 while (*src == 0x20) src++;
3137 if (*src != 0)
3138 *dst++ = 0x20;
3139 } else {
3140 *dst++ = *src++;
3141 }
3142 }
3143 *dst = 0;
3144 return(ret);
3145}
3146
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003147static void
Owen Taylor3473f882001-02-23 17:55:21 +00003148xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003149 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003150 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3151}
3152
3153/**
3154 * xmlValidateAttributeDecl:
3155 * @ctxt: the validation context
3156 * @doc: a document instance
3157 * @attr: an attribute definition
3158 *
3159 * Try to validate a single attribute definition
3160 * basically it does the following checks as described by the
3161 * XML-1.0 recommendation:
3162 * - [ VC: Attribute Default Legal ]
3163 * - [ VC: Enumeration ]
3164 * - [ VC: ID Attribute Default ]
3165 *
3166 * The ID/IDREF uniqueness and matching are done separately
3167 *
3168 * returns 1 if valid or 0 otherwise
3169 */
3170
3171int
3172xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3173 xmlAttributePtr attr) {
3174 int ret = 1;
3175 int val;
3176 CHECK_DTD;
3177 if(attr == NULL) return(1);
3178
3179 /* Attribute Default Legal */
3180 /* Enumeration */
3181 if (attr->defaultValue != NULL) {
3182 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3183 if (val == 0) {
3184 VERROR(ctxt->userData,
3185 "Syntax of default value for attribute %s on %s is not valid\n",
3186 attr->name, attr->elem);
3187 }
3188 ret &= val;
3189 }
3190
3191 /* ID Attribute Default */
3192 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3193 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3194 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3195 VERROR(ctxt->userData,
3196 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
3197 attr->name, attr->elem);
3198 ret = 0;
3199 }
3200
3201 /* One ID per Element Type */
3202 if (attr->atype == XML_ATTRIBUTE_ID) {
3203 int nbId;
3204
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003205 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003206 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3207 attr->elem);
3208 if (elem != NULL) {
3209 nbId = xmlScanIDAttributeDecl(NULL, elem);
3210 } else {
3211 xmlAttributeTablePtr table;
3212
3213 /*
3214 * The attribute may be declared in the internal subset and the
3215 * element in the external subset.
3216 */
3217 nbId = 0;
3218 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3219 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3220 xmlValidateAttributeIdCallback, &nbId);
3221 }
3222 if (nbId > 1) {
3223 VERROR(ctxt->userData,
3224 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3225 attr->elem, nbId, attr->name);
3226 } else if (doc->extSubset != NULL) {
3227 int extId = 0;
3228 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3229 if (elem != NULL) {
3230 extId = xmlScanIDAttributeDecl(NULL, elem);
3231 }
3232 if (extId > 1) {
3233 VERROR(ctxt->userData,
3234 "Element %s has %d ID attribute defined in the external subset : %s\n",
3235 attr->elem, extId, attr->name);
3236 } else if (extId + nbId > 1) {
3237 VERROR(ctxt->userData,
3238"Element %s has ID attributes defined in the internal and external subset : %s\n",
3239 attr->elem, attr->name);
3240 }
3241 }
3242 }
3243
3244 /* Validity Constraint: Enumeration */
3245 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3246 xmlEnumerationPtr tree = attr->tree;
3247 while (tree != NULL) {
3248 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3249 tree = tree->next;
3250 }
3251 if (tree == NULL) {
3252 VERROR(ctxt->userData,
3253"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3254 attr->defaultValue, attr->name, attr->elem);
3255 ret = 0;
3256 }
3257 }
3258
3259 return(ret);
3260}
3261
3262/**
3263 * xmlValidateElementDecl:
3264 * @ctxt: the validation context
3265 * @doc: a document instance
3266 * @elem: an element definition
3267 *
3268 * Try to validate a single element definition
3269 * basically it does the following checks as described by the
3270 * XML-1.0 recommendation:
3271 * - [ VC: One ID per Element Type ]
3272 * - [ VC: No Duplicate Types ]
3273 * - [ VC: Unique Element Type Declaration ]
3274 *
3275 * returns 1 if valid or 0 otherwise
3276 */
3277
3278int
3279xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3280 xmlElementPtr elem) {
3281 int ret = 1;
3282 xmlElementPtr tst;
3283
3284 CHECK_DTD;
3285
3286 if (elem == NULL) return(1);
3287
3288 /* No Duplicate Types */
3289 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3290 xmlElementContentPtr cur, next;
3291 const xmlChar *name;
3292
3293 cur = elem->content;
3294 while (cur != NULL) {
3295 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3296 if (cur->c1 == NULL) break;
3297 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3298 name = cur->c1->name;
3299 next = cur->c2;
3300 while (next != NULL) {
3301 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3302 if (xmlStrEqual(next->name, name)) {
3303 VERROR(ctxt->userData,
3304 "Definition of %s has duplicate references of %s\n",
3305 elem->name, name);
3306 ret = 0;
3307 }
3308 break;
3309 }
3310 if (next->c1 == NULL) break;
3311 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3312 if (xmlStrEqual(next->c1->name, name)) {
3313 VERROR(ctxt->userData,
3314 "Definition of %s has duplicate references of %s\n",
3315 elem->name, name);
3316 ret = 0;
3317 }
3318 next = next->c2;
3319 }
3320 }
3321 cur = cur->c2;
3322 }
3323 }
3324
3325 /* VC: Unique Element Type Declaration */
3326 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003327 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003328 ((tst->prefix == elem->prefix) ||
3329 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003330 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003331 VERROR(ctxt->userData, "Redefinition of element %s\n",
3332 elem->name);
3333 ret = 0;
3334 }
3335 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003336 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003337 ((tst->prefix == elem->prefix) ||
3338 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003339 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003340 VERROR(ctxt->userData, "Redefinition of element %s\n",
3341 elem->name);
3342 ret = 0;
3343 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003344 /* One ID per Element Type
3345 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003346 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3347 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003348 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003349 return(ret);
3350}
3351
3352/**
3353 * xmlValidateOneAttribute:
3354 * @ctxt: the validation context
3355 * @doc: a document instance
3356 * @elem: an element instance
3357 * @attr: an attribute instance
3358 * @value: the attribute value (without entities processing)
3359 *
3360 * Try to validate a single attribute for an element
3361 * basically it does the following checks as described by the
3362 * XML-1.0 recommendation:
3363 * - [ VC: Attribute Value Type ]
3364 * - [ VC: Fixed Attribute Default ]
3365 * - [ VC: Entity Name ]
3366 * - [ VC: Name Token ]
3367 * - [ VC: ID ]
3368 * - [ VC: IDREF ]
3369 * - [ VC: Entity Name ]
3370 * - [ VC: Notation Attributes ]
3371 *
3372 * The ID/IDREF uniqueness and matching are done separately
3373 *
3374 * returns 1 if valid or 0 otherwise
3375 */
3376
3377int
3378xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3379 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3380 /* xmlElementPtr elemDecl; */
3381 xmlAttributePtr attrDecl = NULL;
3382 int val;
3383 int ret = 1;
3384
3385 CHECK_DTD;
3386 if ((elem == NULL) || (elem->name == NULL)) return(0);
3387 if ((attr == NULL) || (attr->name == NULL)) return(0);
3388
3389 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3390 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003391 snprintf((char *) qname, sizeof(qname), "%s:%s",
3392 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003393 qname[sizeof(qname) - 1] = 0;
3394 if (attr->ns != NULL) {
3395 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3396 attr->name, attr->ns->prefix);
3397 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3398 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3399 attr->name, attr->ns->prefix);
3400 } else {
3401 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3402 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3403 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3404 qname, attr->name);
3405 }
3406 }
3407 if (attrDecl == NULL) {
3408 if (attr->ns != NULL) {
3409 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3410 attr->name, attr->ns->prefix);
3411 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3412 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3413 attr->name, attr->ns->prefix);
3414 } else {
3415 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3416 elem->name, attr->name);
3417 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3418 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3419 elem->name, attr->name);
3420 }
3421 }
3422
3423
3424 /* Validity Constraint: Attribute Value Type */
3425 if (attrDecl == NULL) {
3426 VERROR(ctxt->userData,
3427 "No declaration for attribute %s on element %s\n",
3428 attr->name, elem->name);
3429 return(0);
3430 }
3431 attr->atype = attrDecl->atype;
3432
3433 val = xmlValidateAttributeValue(attrDecl->atype, value);
3434 if (val == 0) {
3435 VERROR(ctxt->userData,
3436 "Syntax of value for attribute %s on %s is not valid\n",
3437 attr->name, elem->name);
3438 ret = 0;
3439 }
3440
3441 /* Validity constraint: Fixed Attribute Default */
3442 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3443 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3444 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003445 "Value for attribute %s on %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003446 attr->name, elem->name, attrDecl->defaultValue);
3447 ret = 0;
3448 }
3449 }
3450
3451 /* Validity Constraint: ID uniqueness */
3452 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3453 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3454 ret = 0;
3455 }
3456
3457 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3458 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3459 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3460 ret = 0;
3461 }
3462
3463 /* Validity Constraint: Notation Attributes */
3464 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3465 xmlEnumerationPtr tree = attrDecl->tree;
3466 xmlNotationPtr nota;
3467
3468 /* First check that the given NOTATION was declared */
3469 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3470 if (nota == NULL)
3471 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3472
3473 if (nota == NULL) {
3474 VERROR(ctxt->userData,
3475 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
3476 value, attr->name, elem->name);
3477 ret = 0;
3478 }
3479
3480 /* Second, verify that it's among the list */
3481 while (tree != NULL) {
3482 if (xmlStrEqual(tree->name, value)) break;
3483 tree = tree->next;
3484 }
3485 if (tree == NULL) {
3486 VERROR(ctxt->userData,
3487"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
3488 value, attr->name, elem->name);
3489 ret = 0;
3490 }
3491 }
3492
3493 /* Validity Constraint: Enumeration */
3494 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3495 xmlEnumerationPtr tree = attrDecl->tree;
3496 while (tree != NULL) {
3497 if (xmlStrEqual(tree->name, value)) break;
3498 tree = tree->next;
3499 }
3500 if (tree == NULL) {
3501 VERROR(ctxt->userData,
3502 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3503 value, attr->name, elem->name);
3504 ret = 0;
3505 }
3506 }
3507
3508 /* Fixed Attribute Default */
3509 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3510 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3511 VERROR(ctxt->userData,
3512 "Value for attribute %s on %s must be \"%s\"\n",
3513 attr->name, elem->name, attrDecl->defaultValue);
3514 ret = 0;
3515 }
3516
3517 /* Extra check for the attribute value */
3518 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3519 attrDecl->atype, value);
3520
3521 return(ret);
3522}
3523
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003524/**
3525 * xmlValidateSkipIgnorable:
3526 * @ctxt: the validation context
3527 * @child: the child list
3528 *
3529 * Skip ignorable elements w.r.t. the validation process
3530 *
3531 * returns the first element to consider for validation of the content model
3532 */
3533
3534static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003535xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003536 while (child != NULL) {
3537 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003538 /* These things are ignored (skipped) during validation. */
3539 case XML_PI_NODE:
3540 case XML_COMMENT_NODE:
3541 case XML_XINCLUDE_START:
3542 case XML_XINCLUDE_END:
3543 child = child->next;
3544 break;
3545 case XML_TEXT_NODE:
3546 if (xmlIsBlankNode(child))
3547 child = child->next;
3548 else
3549 return(child);
3550 break;
3551 /* keep current node */
3552 default:
3553 return(child);
3554 }
3555 }
3556 return(child);
3557}
3558
3559/**
3560 * xmlValidateElementType:
3561 * @ctxt: the validation context
3562 *
3563 * Try to validate the content model of an element internal function
3564 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003565 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3566 * reference is found and -3 if the validation succeeded but
3567 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003568 */
3569
3570static int
3571xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003572 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003573 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003574
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003575 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003576 if ((NODE == NULL) && (CONT == NULL))
3577 return(1);
3578 if ((NODE == NULL) &&
3579 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3580 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3581 return(1);
3582 }
3583 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003584 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003585 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003586
3587 /*
3588 * We arrive here when more states need to be examined
3589 */
3590cont:
3591
3592 /*
3593 * We just recovered from a rollback generated by a possible
3594 * epsilon transition, go directly to the analysis phase
3595 */
3596 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003597 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003598 DEBUG_VALID_STATE(NODE, CONT)
3599 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003600 goto analyze;
3601 }
3602
3603 DEBUG_VALID_STATE(NODE, CONT)
3604 /*
3605 * we may have to save a backup state here. This is the equivalent
3606 * of handling epsilon transition in NFAs.
3607 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003608 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003609 ((CONT->parent == NULL) ||
3610 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003611 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003612 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003613 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003614 DEBUG_VALID_MSG("saving parent branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003615 if (vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT) < 0)
3616 return(0);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003617 }
3618
3619
3620 /*
3621 * Check first if the content matches
3622 */
3623 switch (CONT->type) {
3624 case XML_ELEMENT_CONTENT_PCDATA:
3625 if (NODE == NULL) {
3626 DEBUG_VALID_MSG("pcdata failed no node");
3627 ret = 0;
3628 break;
3629 }
3630 if (NODE->type == XML_TEXT_NODE) {
3631 DEBUG_VALID_MSG("pcdata found, skip to next");
3632 /*
3633 * go to next element in the content model
3634 * skipping ignorable elems
3635 */
3636 do {
3637 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003638 NODE = xmlValidateSkipIgnorable(NODE);
3639 if ((NODE != NULL) &&
3640 (NODE->type == XML_ENTITY_REF_NODE))
3641 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003642 } while ((NODE != NULL) &&
3643 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003644 (NODE->type != XML_TEXT_NODE) &&
3645 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003646 ret = 1;
3647 break;
3648 } else {
3649 DEBUG_VALID_MSG("pcdata failed");
3650 ret = 0;
3651 break;
3652 }
3653 break;
3654 case XML_ELEMENT_CONTENT_ELEMENT:
3655 if (NODE == NULL) {
3656 DEBUG_VALID_MSG("element failed no node");
3657 ret = 0;
3658 break;
3659 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003660 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3661 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003662 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003663 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3664 ret = (CONT->prefix == NULL);
3665 } else if (CONT->prefix == NULL) {
3666 ret = 0;
3667 } else {
3668 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3669 }
3670 }
3671 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003672 DEBUG_VALID_MSG("element found, skip to next");
3673 /*
3674 * go to next element in the content model
3675 * skipping ignorable elems
3676 */
3677 do {
3678 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003679 NODE = xmlValidateSkipIgnorable(NODE);
3680 if ((NODE != NULL) &&
3681 (NODE->type == XML_ENTITY_REF_NODE))
3682 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003683 } while ((NODE != NULL) &&
3684 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003685 (NODE->type != XML_TEXT_NODE) &&
3686 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003687 } else {
3688 DEBUG_VALID_MSG("element failed");
3689 ret = 0;
3690 break;
3691 }
3692 break;
3693 case XML_ELEMENT_CONTENT_OR:
3694 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003695 * Small optimization.
3696 */
3697 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3698 if ((NODE == NULL) ||
3699 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3700 DEPTH++;
3701 CONT = CONT->c2;
3702 goto cont;
3703 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003704 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3705 ret = (CONT->c1->prefix == NULL);
3706 } else if (CONT->c1->prefix == NULL) {
3707 ret = 0;
3708 } else {
3709 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3710 }
3711 if (ret == 0) {
3712 DEPTH++;
3713 CONT = CONT->c2;
3714 goto cont;
3715 }
Daniel Veillard85349052001-04-20 13:48:21 +00003716 }
3717
3718 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003719 * save the second branch 'or' branch
3720 */
3721 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard940492d2002-04-15 10:15:25 +00003722 if (vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3723 OCCURS, ROLLBACK_OR) < 0)
3724 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003725 DEPTH++;
3726 CONT = CONT->c1;
3727 goto cont;
3728 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003729 /*
3730 * Small optimization.
3731 */
3732 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3733 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3734 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3735 if ((NODE == NULL) ||
3736 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3737 DEPTH++;
3738 CONT = CONT->c2;
3739 goto cont;
3740 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003741 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3742 ret = (CONT->c1->prefix == NULL);
3743 } else if (CONT->c1->prefix == NULL) {
3744 ret = 0;
3745 } else {
3746 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3747 }
3748 if (ret == 0) {
3749 DEPTH++;
3750 CONT = CONT->c2;
3751 goto cont;
3752 }
Daniel Veillard1d047672001-06-09 16:41:01 +00003753 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003754 DEPTH++;
3755 CONT = CONT->c1;
3756 goto cont;
3757 }
3758
3759 /*
3760 * At this point handle going up in the tree
3761 */
3762 if (ret == -1) {
3763 DEBUG_VALID_MSG("error found returning");
3764 return(ret);
3765 }
3766analyze:
3767 while (CONT != NULL) {
3768 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003769 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003770 * this level.
3771 */
3772 if (ret == 0) {
3773 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003774 xmlNodePtr cur;
3775
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003776 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003777 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003778 DEBUG_VALID_MSG("Once branch failed, rollback");
3779 if (vstateVPop(ctxt) < 0 ) {
3780 DEBUG_VALID_MSG("exhaustion, failed");
3781 return(0);
3782 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003783 if (cur != ctxt->vstate->node)
3784 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003785 goto cont;
3786 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00003787 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003788 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003789 DEBUG_VALID_MSG("Plus branch failed, rollback");
3790 if (vstateVPop(ctxt) < 0 ) {
3791 DEBUG_VALID_MSG("exhaustion, failed");
3792 return(0);
3793 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003794 if (cur != ctxt->vstate->node)
3795 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003796 goto cont;
3797 }
3798 DEBUG_VALID_MSG("Plus branch found");
3799 ret = 1;
3800 break;
3801 case XML_ELEMENT_CONTENT_MULT:
3802#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00003803 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003804 DEBUG_VALID_MSG("Mult branch failed");
3805 } else {
3806 DEBUG_VALID_MSG("Mult branch found");
3807 }
3808#endif
3809 ret = 1;
3810 break;
3811 case XML_ELEMENT_CONTENT_OPT:
3812 DEBUG_VALID_MSG("Option branch failed");
3813 ret = 1;
3814 break;
3815 }
3816 } else {
3817 switch (CONT->ocur) {
3818 case XML_ELEMENT_CONTENT_OPT:
3819 DEBUG_VALID_MSG("Option branch succeeded");
3820 ret = 1;
3821 break;
3822 case XML_ELEMENT_CONTENT_ONCE:
3823 DEBUG_VALID_MSG("Once branch succeeded");
3824 ret = 1;
3825 break;
3826 case XML_ELEMENT_CONTENT_PLUS:
3827 if (STATE == ROLLBACK_PARENT) {
3828 DEBUG_VALID_MSG("Plus branch rollback");
3829 ret = 1;
3830 break;
3831 }
3832 if (NODE == NULL) {
3833 DEBUG_VALID_MSG("Plus branch exhausted");
3834 ret = 1;
3835 break;
3836 }
3837 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003838 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003839 goto cont;
3840 case XML_ELEMENT_CONTENT_MULT:
3841 if (STATE == ROLLBACK_PARENT) {
3842 DEBUG_VALID_MSG("Mult branch rollback");
3843 ret = 1;
3844 break;
3845 }
3846 if (NODE == NULL) {
3847 DEBUG_VALID_MSG("Mult branch exhausted");
3848 ret = 1;
3849 break;
3850 }
3851 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003852 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003853 goto cont;
3854 }
3855 }
3856 STATE = 0;
3857
3858 /*
3859 * Then act accordingly at the parent level
3860 */
Daniel Veillard5344c602001-12-31 16:37:34 +00003861 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003862 if (CONT->parent == NULL)
3863 break;
3864
3865 switch (CONT->parent->type) {
3866 case XML_ELEMENT_CONTENT_PCDATA:
3867 DEBUG_VALID_MSG("Error: parent pcdata");
3868 return(-1);
3869 case XML_ELEMENT_CONTENT_ELEMENT:
3870 DEBUG_VALID_MSG("Error: parent element");
3871 return(-1);
3872 case XML_ELEMENT_CONTENT_OR:
3873 if (ret == 1) {
3874 DEBUG_VALID_MSG("Or succeeded");
3875 CONT = CONT->parent;
3876 DEPTH--;
3877 } else {
3878 DEBUG_VALID_MSG("Or failed");
3879 CONT = CONT->parent;
3880 DEPTH--;
3881 }
3882 break;
3883 case XML_ELEMENT_CONTENT_SEQ:
3884 if (ret == 0) {
3885 DEBUG_VALID_MSG("Sequence failed");
3886 CONT = CONT->parent;
3887 DEPTH--;
3888 } else if (CONT == CONT->parent->c1) {
3889 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3890 CONT = CONT->parent->c2;
3891 goto cont;
3892 } else {
3893 DEBUG_VALID_MSG("Sequence succeeded");
3894 CONT = CONT->parent;
3895 DEPTH--;
3896 }
3897 }
3898 }
3899 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003900 xmlNodePtr cur;
3901
3902 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003903 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3904 if (vstateVPop(ctxt) < 0 ) {
3905 DEBUG_VALID_MSG("exhaustion, failed");
3906 return(0);
3907 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003908 if (cur != ctxt->vstate->node)
3909 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003910 goto cont;
3911 }
3912 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003913 xmlNodePtr cur;
3914
3915 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003916 DEBUG_VALID_MSG("Failure, rollback");
3917 if (vstateVPop(ctxt) < 0 ) {
3918 DEBUG_VALID_MSG("exhaustion, failed");
3919 return(0);
3920 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003921 if (cur != ctxt->vstate->node)
3922 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003923 goto cont;
3924 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003925 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003926}
3927
3928/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00003929 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003930 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00003931 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003932 * @content: An element
3933 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3934 *
3935 * This will dump the list of elements to the buffer
3936 * Intended just for the debug routine
3937 */
3938static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00003939xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003940 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00003941 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003942
3943 if (node == NULL) return;
3944 if (glob) strcat(buf, "(");
3945 cur = node;
3946 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00003947 len = strlen(buf);
3948 if (size - len < 50) {
3949 if ((size - len > 4) && (buf[len - 1] != '.'))
3950 strcat(buf, " ...");
3951 return;
3952 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003953 switch (cur->type) {
3954 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003955 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00003956 if (size - len < xmlStrlen(cur->ns->prefix) + 10) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003957 if ((size - len > 4) && (buf[len - 1] != '.'))
3958 strcat(buf, " ...");
3959 return;
3960 }
3961 strcat(buf, (char *) cur->ns->prefix);
3962 strcat(buf, ":");
3963 }
Daniel Veillard4b3a84f2002-03-19 14:36:46 +00003964 if (size - len < xmlStrlen(cur->name) + 10) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00003965 if ((size - len > 4) && (buf[len - 1] != '.'))
3966 strcat(buf, " ...");
3967 return;
3968 }
3969 strcat(buf, (char *) cur->name);
3970 if (cur->next != NULL)
3971 strcat(buf, " ");
3972 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003973 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003974 if (xmlIsBlankNode(cur))
3975 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003976 case XML_CDATA_SECTION_NODE:
3977 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003978 strcat(buf, "CDATA");
3979 if (cur->next != NULL)
3980 strcat(buf, " ");
3981 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003982 case XML_ATTRIBUTE_NODE:
3983 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003984#ifdef LIBXML_DOCB_ENABLED
3985 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003986#endif
3987 case XML_HTML_DOCUMENT_NODE:
3988 case XML_DOCUMENT_TYPE_NODE:
3989 case XML_DOCUMENT_FRAG_NODE:
3990 case XML_NOTATION_NODE:
3991 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003992 strcat(buf, "???");
3993 if (cur->next != NULL)
3994 strcat(buf, " ");
3995 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003996 case XML_ENTITY_NODE:
3997 case XML_PI_NODE:
3998 case XML_DTD_NODE:
3999 case XML_COMMENT_NODE:
4000 case XML_ELEMENT_DECL:
4001 case XML_ATTRIBUTE_DECL:
4002 case XML_ENTITY_DECL:
4003 case XML_XINCLUDE_START:
4004 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00004005 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004006 }
4007 cur = cur->next;
4008 }
4009 if (glob) strcat(buf, ")");
4010}
4011
4012/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004013 * xmlValidateElementContent:
4014 * @ctxt: the validation context
4015 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004016 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004017 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004018 *
4019 * Try to validate the content model of an element
4020 *
4021 * returns 1 if valid or 0 if not and -1 in case of error
4022 */
4023
4024static int
4025xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004026 xmlElementPtr elemDecl, int warn) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004027 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004028 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004029 xmlElementContentPtr cont;
4030 const xmlChar *name;
4031
4032 if (elemDecl == NULL)
4033 return(-1);
4034 cont = elemDecl->content;
4035 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004036
4037 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004038 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004039 */
4040 ctxt->vstateMax = 8;
4041 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4042 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4043 if (ctxt->vstateTab == NULL) {
4044 xmlGenericError(xmlGenericErrorContext,
4045 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004046 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004047 }
4048 /*
4049 * The first entry in the stack is reserved to the current state
4050 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004051 ctxt->nodeMax = 0;
4052 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004053 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004054 ctxt->vstate = &ctxt->vstateTab[0];
4055 ctxt->vstateNr = 1;
4056 CONT = cont;
4057 NODE = child;
4058 DEPTH = 0;
4059 OCCURS = 0;
4060 STATE = 0;
4061 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004062 if ((ret == -3) && (warn)) {
4063 VWARNING(ctxt->userData,
4064 "Element %s content model is ambiguous\n", name);
4065 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004066 /*
4067 * An entities reference appeared at this level.
4068 * Buid a minimal representation of this node content
4069 * sufficient to run the validation process on it
4070 */
4071 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004072 cur = child;
4073 while (cur != NULL) {
4074 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004075 case XML_ENTITY_REF_NODE:
4076 /*
4077 * Push the current node to be able to roll back
4078 * and process within the entity
4079 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004080 if ((cur->children != NULL) &&
4081 (cur->children->children != NULL)) {
4082 nodeVPush(ctxt, cur);
4083 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004084 continue;
4085 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004086 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004087 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004088 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004089 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004090 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004091 case XML_CDATA_SECTION_NODE:
4092 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004093 case XML_ELEMENT_NODE:
4094 /*
4095 * Allocate a new node and minimally fills in
4096 * what's required
4097 */
4098 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4099 if (tmp == NULL) {
4100 xmlGenericError(xmlGenericErrorContext,
4101 "xmlValidateElementContent : malloc failed\n");
4102 xmlFreeNodeList(repl);
4103 ret = -1;
4104 goto done;
4105 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004106 tmp->type = cur->type;
4107 tmp->name = cur->name;
4108 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004109 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004110 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004111 if (repl == NULL)
4112 repl = last = tmp;
4113 else {
4114 last->next = tmp;
4115 last = tmp;
4116 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004117 if (cur->type == XML_CDATA_SECTION_NODE) {
4118 /*
4119 * E59 spaces in CDATA does not match the
4120 * nonterminal S
4121 */
4122 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4123 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004124 break;
4125 default:
4126 break;
4127 }
4128 /*
4129 * Switch to next element
4130 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004131 cur = cur->next;
4132 while (cur == NULL) {
4133 cur = nodeVPop(ctxt);
4134 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004135 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004136 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004137 }
4138 }
4139
4140 /*
4141 * Relaunch the validation
4142 */
4143 ctxt->vstate = &ctxt->vstateTab[0];
4144 ctxt->vstateNr = 1;
4145 CONT = cont;
4146 NODE = repl;
4147 DEPTH = 0;
4148 OCCURS = 0;
4149 STATE = 0;
4150 ret = xmlValidateElementType(ctxt);
4151 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004152 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004153 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4154 char expr[5000];
4155 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004156
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004157 expr[0] = 0;
4158 xmlSnprintfElementContent(expr, 5000, cont, 1);
4159 list[0] = 0;
4160 if (repl != NULL)
4161 xmlSnprintfElements(list, 5000, repl, 1);
4162 else
4163 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004164
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004165 if (name != NULL) {
4166 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004167 "Element %s content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004168 name, expr, list);
4169 } else {
4170 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004171 "Element content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004172 expr, list);
4173 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004174 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004175 if (name != NULL) {
4176 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004177 "Element %s content doesn't follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004178 name);
4179 } else {
4180 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004181 "Element content doesn't follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004182 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004183 }
4184 ret = 0;
4185 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004186 if (ret == -3)
4187 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004188
4189
4190done:
4191 /*
4192 * Deallocate the copy if done, and free up the validation stack
4193 */
4194 while (repl != NULL) {
4195 tmp = repl->next;
4196 xmlFree(repl);
4197 repl = tmp;
4198 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004199 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004200 if (ctxt->vstateTab != NULL) {
4201 xmlFree(ctxt->vstateTab);
4202 ctxt->vstateTab = NULL;
4203 }
4204 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004205 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004206 if (ctxt->nodeTab != NULL) {
4207 xmlFree(ctxt->nodeTab);
4208 ctxt->nodeTab = NULL;
4209 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004210 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004211
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004212}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004213
Owen Taylor3473f882001-02-23 17:55:21 +00004214/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004215 * xmlValidateCdataElement:
4216 * @ctxt: the validation context
4217 * @doc: a document instance
4218 * @elem: an element instance
4219 *
4220 * Check that an element follows #CDATA
4221 *
4222 * returns 1 if valid or 0 otherwise
4223 */
4224static int
4225xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4226 xmlNodePtr elem) {
4227 int ret = 1;
4228 xmlNodePtr cur, child;
4229
4230 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4231 return(0);
4232
4233 child = elem->children;
4234
4235 cur = child;
4236 while (cur != NULL) {
4237 switch (cur->type) {
4238 case XML_ENTITY_REF_NODE:
4239 /*
4240 * Push the current node to be able to roll back
4241 * and process within the entity
4242 */
4243 if ((cur->children != NULL) &&
4244 (cur->children->children != NULL)) {
4245 nodeVPush(ctxt, cur);
4246 cur = cur->children->children;
4247 continue;
4248 }
4249 break;
4250 case XML_COMMENT_NODE:
4251 case XML_PI_NODE:
4252 case XML_TEXT_NODE:
4253 case XML_CDATA_SECTION_NODE:
4254 break;
4255 default:
4256 ret = 0;
4257 goto done;
4258 }
4259 /*
4260 * Switch to next element
4261 */
4262 cur = cur->next;
4263 while (cur == NULL) {
4264 cur = nodeVPop(ctxt);
4265 if (cur == NULL)
4266 break;
4267 cur = cur->next;
4268 }
4269 }
4270done:
4271 ctxt->nodeMax = 0;
4272 ctxt->nodeNr = 0;
4273 if (ctxt->nodeTab != NULL) {
4274 xmlFree(ctxt->nodeTab);
4275 ctxt->nodeTab = NULL;
4276 }
4277 return(ret);
4278}
4279
4280/**
Owen Taylor3473f882001-02-23 17:55:21 +00004281 * xmlValidateOneElement:
4282 * @ctxt: the validation context
4283 * @doc: a document instance
4284 * @elem: an element instance
4285 *
4286 * Try to validate a single element and it's attributes,
4287 * basically it does the following checks as described by the
4288 * XML-1.0 recommendation:
4289 * - [ VC: Element Valid ]
4290 * - [ VC: Required Attribute ]
4291 * Then call xmlValidateOneAttribute() for each attribute present.
4292 *
4293 * The ID/IDREF checkings are done separately
4294 *
4295 * returns 1 if valid or 0 otherwise
4296 */
4297
4298int
4299xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4300 xmlNodePtr elem) {
4301 xmlElementPtr elemDecl = NULL;
4302 xmlElementContentPtr cont;
4303 xmlAttributePtr attr;
4304 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004305 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004306 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004307 const xmlChar *prefix = NULL;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004308 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004309
4310 CHECK_DTD;
4311
4312 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004313 switch (elem->type) {
4314 case XML_ATTRIBUTE_NODE:
4315 VERROR(ctxt->userData,
4316 "Attribute element not expected here\n");
4317 return(0);
4318 case XML_TEXT_NODE:
4319 if (elem->children != NULL) {
4320 VERROR(ctxt->userData, "Text element has childs !\n");
4321 return(0);
4322 }
4323 if (elem->properties != NULL) {
4324 VERROR(ctxt->userData, "Text element has attributes !\n");
4325 return(0);
4326 }
4327 if (elem->ns != NULL) {
4328 VERROR(ctxt->userData, "Text element has namespace !\n");
4329 return(0);
4330 }
4331 if (elem->nsDef != NULL) {
4332 VERROR(ctxt->userData,
4333 "Text element carries namespace definitions !\n");
4334 return(0);
4335 }
4336 if (elem->content == NULL) {
4337 VERROR(ctxt->userData,
4338 "Text element has no content !\n");
4339 return(0);
4340 }
4341 return(1);
4342 case XML_XINCLUDE_START:
4343 case XML_XINCLUDE_END:
4344 return(1);
4345 case XML_CDATA_SECTION_NODE:
4346 case XML_ENTITY_REF_NODE:
4347 case XML_PI_NODE:
4348 case XML_COMMENT_NODE:
4349 return(1);
4350 case XML_ENTITY_NODE:
4351 VERROR(ctxt->userData,
4352 "Entity element not expected here\n");
4353 return(0);
4354 case XML_NOTATION_NODE:
4355 VERROR(ctxt->userData,
4356 "Notation element not expected here\n");
4357 return(0);
4358 case XML_DOCUMENT_NODE:
4359 case XML_DOCUMENT_TYPE_NODE:
4360 case XML_DOCUMENT_FRAG_NODE:
4361 VERROR(ctxt->userData,
4362 "Document element not expected here\n");
4363 return(0);
4364 case XML_HTML_DOCUMENT_NODE:
4365 VERROR(ctxt->userData,
4366 "\n");
4367 return(0);
4368 case XML_ELEMENT_NODE:
4369 break;
4370 default:
4371 VERROR(ctxt->userData,
4372 "unknown element type %d\n", elem->type);
4373 return(0);
4374 }
4375 if (elem->name == NULL) return(0);
4376
4377 /*
4378 * Fetch the declaration for the qualified name
4379 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004380 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4381 prefix = elem->ns->prefix;
4382
4383 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004384 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004385 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004386 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004387 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004388 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004389 if (elemDecl != NULL)
4390 extsubset = 1;
4391 }
Owen Taylor3473f882001-02-23 17:55:21 +00004392 }
4393
4394 /*
4395 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004396 * This is "non-strict" validation should be done on the
4397 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004398 */
4399 if (elemDecl == NULL) {
4400 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004401 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004402 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004403 if (elemDecl != NULL)
4404 extsubset = 1;
4405 }
Owen Taylor3473f882001-02-23 17:55:21 +00004406 }
4407 if (elemDecl == NULL) {
4408 VERROR(ctxt->userData, "No declaration for element %s\n",
4409 elem->name);
4410 return(0);
4411 }
4412
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004413 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004414 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004415 case XML_ELEMENT_TYPE_UNDEFINED:
4416 VERROR(ctxt->userData, "No declaration for element %s\n",
4417 elem->name);
4418 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004419 case XML_ELEMENT_TYPE_EMPTY:
4420 if (elem->children != NULL) {
4421 VERROR(ctxt->userData,
4422 "Element %s was declared EMPTY this one has content\n",
4423 elem->name);
4424 ret = 0;
4425 }
4426 break;
4427 case XML_ELEMENT_TYPE_ANY:
4428 /* I don't think anything is required then */
4429 break;
4430 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004431
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004432 /* simple case of declared as #PCDATA */
4433 if ((elemDecl->content != NULL) &&
4434 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4435 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4436 if (!ret) {
4437 VERROR(ctxt->userData,
4438 "Element %s was declared #PCDATA but contains non text nodes\n",
4439 elem->name);
4440 }
4441 break;
4442 }
Owen Taylor3473f882001-02-23 17:55:21 +00004443 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004444 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004445 while (child != NULL) {
4446 if (child->type == XML_ELEMENT_NODE) {
4447 name = child->name;
4448 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4449 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004450 snprintf((char *) qname, sizeof(qname), "%s:%s",
4451 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004452 qname[sizeof(qname) - 1] = 0;
4453 cont = elemDecl->content;
4454 while (cont != NULL) {
4455 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4456 if (xmlStrEqual(cont->name, qname)) break;
4457 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4458 (cont->c1 != NULL) &&
4459 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4460 if (xmlStrEqual(cont->c1->name, qname)) break;
4461 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4462 (cont->c1 == NULL) ||
4463 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4464 /* Internal error !!! */
4465 xmlGenericError(xmlGenericErrorContext,
4466 "Internal: MIXED struct bad\n");
4467 break;
4468 }
4469 cont = cont->c2;
4470 }
4471 if (cont != NULL)
4472 goto child_ok;
4473 }
4474 cont = elemDecl->content;
4475 while (cont != NULL) {
4476 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4477 if (xmlStrEqual(cont->name, name)) 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, name)) 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 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004494 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004495 name, elem->name);
4496 ret = 0;
4497 }
4498 }
4499child_ok:
4500 child = child->next;
4501 }
4502 break;
4503 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004504 if ((doc->standalone == 1) && (extsubset == 1)) {
4505 /*
4506 * VC: Standalone Document Declaration
4507 * - element types with element content, if white space
4508 * occurs directly within any instance of those types.
4509 */
4510 child = elem->children;
4511 while (child != NULL) {
4512 if (child->type == XML_TEXT_NODE) {
4513 const xmlChar *content = child->content;
4514
4515 while (IS_BLANK(*content))
4516 content++;
4517 if (*content == 0) {
4518 VERROR(ctxt->userData,
4519"standalone: %s declared in the external subset contains white spaces nodes\n",
4520 elem->name);
4521 ret = 0;
4522 break;
4523 }
4524 }
4525 child =child->next;
4526 }
4527 }
Owen Taylor3473f882001-02-23 17:55:21 +00004528 child = elem->children;
4529 cont = elemDecl->content;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004530 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1);
4531 if (tmp <= 0)
4532 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004533 break;
4534 }
4535
4536 /* [ VC: Required Attribute ] */
4537 attr = elemDecl->attributes;
4538 while (attr != NULL) {
4539 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004540 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00004541
Daniel Veillarde4301c82002-02-13 13:32:35 +00004542 if ((attr->prefix == NULL) &&
4543 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4544 xmlNsPtr ns;
4545
4546 ns = elem->nsDef;
4547 while (ns != NULL) {
4548 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00004549 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004550 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00004551 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004552 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4553 xmlNsPtr ns;
4554
4555 ns = elem->nsDef;
4556 while (ns != NULL) {
4557 if (xmlStrEqual(attr->name, ns->prefix))
4558 goto found;
4559 ns = ns->next;
4560 }
4561 } else {
4562 xmlAttrPtr attrib;
4563
4564 attrib = elem->properties;
4565 while (attrib != NULL) {
4566 if (xmlStrEqual(attrib->name, attr->name)) {
4567 if (attr->prefix != NULL) {
4568 xmlNsPtr nameSpace = attrib->ns;
4569
4570 if (nameSpace == NULL)
4571 nameSpace = elem->ns;
4572 /*
4573 * qualified names handling is problematic, having a
4574 * different prefix should be possible but DTDs don't
4575 * allow to define the URI instead of the prefix :-(
4576 */
4577 if (nameSpace == NULL) {
4578 if (qualified < 0)
4579 qualified = 0;
4580 } else if (!xmlStrEqual(nameSpace->prefix,
4581 attr->prefix)) {
4582 if (qualified < 1)
4583 qualified = 1;
4584 } else
4585 goto found;
4586 } else {
4587 /*
4588 * We should allow applications to define namespaces
4589 * for their application even if the DTD doesn't
4590 * carry one, otherwise, basically we would always
4591 * break.
4592 */
4593 goto found;
4594 }
4595 }
4596 attrib = attrib->next;
4597 }
Owen Taylor3473f882001-02-23 17:55:21 +00004598 }
4599 if (qualified == -1) {
4600 if (attr->prefix == NULL) {
4601 VERROR(ctxt->userData,
4602 "Element %s doesn't carry attribute %s\n",
4603 elem->name, attr->name);
4604 ret = 0;
4605 } else {
4606 VERROR(ctxt->userData,
4607 "Element %s doesn't carry attribute %s:%s\n",
4608 elem->name, attr->prefix,attr->name);
4609 ret = 0;
4610 }
4611 } else if (qualified == 0) {
4612 VWARNING(ctxt->userData,
4613 "Element %s required attribute %s:%s has no prefix\n",
4614 elem->name, attr->prefix,attr->name);
4615 } else if (qualified == 1) {
4616 VWARNING(ctxt->userData,
4617 "Element %s required attribute %s:%s has different prefix\n",
4618 elem->name, attr->prefix,attr->name);
4619 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004620 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
4621 /*
4622 * Special tests checking #FIXED namespace declarations
4623 * have the right value since this is not done as an
4624 * attribute checking
4625 */
4626 if ((attr->prefix == NULL) &&
4627 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4628 xmlNsPtr ns;
4629
4630 ns = elem->nsDef;
4631 while (ns != NULL) {
4632 if (ns->prefix == NULL) {
4633 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
4634 VERROR(ctxt->userData,
4635 "Element %s namespace name for default namespace does not match the DTD\n",
4636 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00004637 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004638 }
4639 goto found;
4640 }
4641 ns = ns->next;
4642 }
4643 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4644 xmlNsPtr ns;
4645
4646 ns = elem->nsDef;
4647 while (ns != NULL) {
4648 if (xmlStrEqual(attr->name, ns->prefix)) {
4649 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
4650 VERROR(ctxt->userData,
4651 "Element %s namespace name for %s doesn't match the DTD\n",
4652 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00004653 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004654 }
4655 goto found;
4656 }
4657 ns = ns->next;
4658 }
4659 }
Owen Taylor3473f882001-02-23 17:55:21 +00004660 }
4661found:
4662 attr = attr->nexth;
4663 }
4664 return(ret);
4665}
4666
4667/**
4668 * xmlValidateRoot:
4669 * @ctxt: the validation context
4670 * @doc: a document instance
4671 *
4672 * Try to validate a the root element
4673 * basically it does the following check as described by the
4674 * XML-1.0 recommendation:
4675 * - [ VC: Root Element Type ]
4676 * it doesn't try to recurse or apply other check to the element
4677 *
4678 * returns 1 if valid or 0 otherwise
4679 */
4680
4681int
4682xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4683 xmlNodePtr root;
4684 if (doc == NULL) return(0);
4685
4686 root = xmlDocGetRootElement(doc);
4687 if ((root == NULL) || (root->name == NULL)) {
4688 VERROR(ctxt->userData, "Not valid: no root element\n");
4689 return(0);
4690 }
4691
4692 /*
4693 * When doing post validation against a separate DTD, those may
4694 * no internal subset has been generated
4695 */
4696 if ((doc->intSubset != NULL) &&
4697 (doc->intSubset->name != NULL)) {
4698 /*
4699 * Check first the document root against the NQName
4700 */
4701 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4702 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4703 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004704 snprintf((char *) qname, sizeof(qname), "%s:%s",
4705 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004706 qname[sizeof(qname) - 1] = 0;
4707 if (xmlStrEqual(doc->intSubset->name, qname))
4708 goto name_ok;
4709 }
4710 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4711 (xmlStrEqual(root->name, BAD_CAST "html")))
4712 goto name_ok;
4713 VERROR(ctxt->userData,
4714 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4715 root->name, doc->intSubset->name);
4716 return(0);
4717
4718 }
4719 }
4720name_ok:
4721 return(1);
4722}
4723
4724
4725/**
4726 * xmlValidateElement:
4727 * @ctxt: the validation context
4728 * @doc: a document instance
4729 * @elem: an element instance
4730 *
4731 * Try to validate the subtree under an element
4732 *
4733 * returns 1 if valid or 0 otherwise
4734 */
4735
4736int
4737xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4738 xmlNodePtr child;
4739 xmlAttrPtr attr;
4740 xmlChar *value;
4741 int ret = 1;
4742
4743 if (elem == NULL) return(0);
4744
4745 /*
4746 * XInclude elements were added after parsing in the infoset,
4747 * they don't really mean anything validation wise.
4748 */
4749 if ((elem->type == XML_XINCLUDE_START) ||
4750 (elem->type == XML_XINCLUDE_END))
4751 return(1);
4752
4753 CHECK_DTD;
4754
Daniel Veillard10ea86c2001-06-20 13:55:33 +00004755 /*
4756 * Entities references have to be handled separately
4757 */
4758 if (elem->type == XML_ENTITY_REF_NODE) {
4759 return(1);
4760 }
4761
Owen Taylor3473f882001-02-23 17:55:21 +00004762 ret &= xmlValidateOneElement(ctxt, doc, elem);
4763 attr = elem->properties;
4764 while(attr != NULL) {
4765 value = xmlNodeListGetString(doc, attr->children, 0);
4766 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4767 if (value != NULL)
4768 xmlFree(value);
4769 attr= attr->next;
4770 }
4771 child = elem->children;
4772 while (child != NULL) {
4773 ret &= xmlValidateElement(ctxt, doc, child);
4774 child = child->next;
4775 }
4776
4777 return(ret);
4778}
4779
Daniel Veillard8730c562001-02-26 10:49:57 +00004780/**
4781 * xmlValidateRef:
4782 * @ref: A reference to be validated
4783 * @ctxt: Validation context
4784 * @name: Name of ID we are searching for
4785 *
4786 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004787static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004788xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004789 const xmlChar *name) {
4790 xmlAttrPtr id;
4791 xmlAttrPtr attr;
4792
4793 if (ref == NULL)
4794 return;
4795 attr = ref->attr;
4796 if (attr == NULL)
4797 return;
4798 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4799 id = xmlGetID(ctxt->doc, name);
4800 if (id == NULL) {
4801 VERROR(ctxt->userData,
4802 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4803 attr->name, name);
4804 ctxt->valid = 0;
4805 }
4806 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4807 xmlChar *dup, *str = NULL, *cur, save;
4808
4809 dup = xmlStrdup(name);
4810 if (dup == NULL) {
4811 ctxt->valid = 0;
4812 return;
4813 }
4814 cur = dup;
4815 while (*cur != 0) {
4816 str = cur;
4817 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4818 save = *cur;
4819 *cur = 0;
4820 id = xmlGetID(ctxt->doc, str);
4821 if (id == NULL) {
4822 VERROR(ctxt->userData,
4823 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4824 attr->name, str);
4825 ctxt->valid = 0;
4826 }
4827 if (save == 0)
4828 break;
4829 *cur = save;
4830 while (IS_BLANK(*cur)) cur++;
4831 }
4832 xmlFree(dup);
4833 }
4834}
4835
4836/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004837 * xmlWalkValidateList:
4838 * @data: Contents of current link
4839 * @user: Value supplied by the user
4840 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004841 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00004842 */
4843static int
4844xmlWalkValidateList(const void *data, const void *user)
4845{
4846 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4847 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4848 return 1;
4849}
4850
4851/**
4852 * xmlValidateCheckRefCallback:
4853 * @ref_list: List of references
4854 * @ctxt: Validation context
4855 * @name: Name of ID we are searching for
4856 *
4857 */
4858static void
4859xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4860 const xmlChar *name) {
4861 xmlValidateMemo memo;
4862
4863 if (ref_list == NULL)
4864 return;
4865 memo.ctxt = ctxt;
4866 memo.name = name;
4867
4868 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4869
4870}
4871
4872/**
Owen Taylor3473f882001-02-23 17:55:21 +00004873 * xmlValidateDocumentFinal:
4874 * @ctxt: the validation context
4875 * @doc: a document instance
4876 *
4877 * Does the final step for the document validation once all the
4878 * incremental validation steps have been completed
4879 *
4880 * basically it does the following checks described by the XML Rec
4881 *
4882 *
4883 * returns 1 if valid or 0 otherwise
4884 */
4885
4886int
4887xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4888 xmlRefTablePtr table;
4889
4890 if (doc == NULL) {
4891 xmlGenericError(xmlGenericErrorContext,
4892 "xmlValidateDocumentFinal: doc == NULL\n");
4893 return(0);
4894 }
4895
4896 /*
4897 * Check all the NOTATION/NOTATIONS attributes
4898 */
4899 /*
4900 * Check all the ENTITY/ENTITIES attributes definition for validity
4901 */
4902 /*
4903 * Check all the IDREF/IDREFS attributes definition for validity
4904 */
4905 table = (xmlRefTablePtr) doc->refs;
4906 ctxt->doc = doc;
4907 ctxt->valid = 1;
4908 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4909 return(ctxt->valid);
4910}
4911
4912/**
4913 * xmlValidateDtd:
4914 * @ctxt: the validation context
4915 * @doc: a document instance
4916 * @dtd: a dtd instance
4917 *
4918 * Try to validate the document against the dtd instance
4919 *
4920 * basically it does check all the definitions in the DtD.
4921 *
4922 * returns 1 if valid or 0 otherwise
4923 */
4924
4925int
4926xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4927 int ret;
4928 xmlDtdPtr oldExt;
4929 xmlNodePtr root;
4930
4931 if (dtd == NULL) return(0);
4932 if (doc == NULL) return(0);
4933 oldExt = doc->extSubset;
4934 doc->extSubset = dtd;
4935 ret = xmlValidateRoot(ctxt, doc);
4936 if (ret == 0) {
4937 doc->extSubset = oldExt;
4938 return(ret);
4939 }
4940 if (doc->ids != NULL) {
4941 xmlFreeIDTable(doc->ids);
4942 doc->ids = NULL;
4943 }
4944 if (doc->refs != NULL) {
4945 xmlFreeRefTable(doc->refs);
4946 doc->refs = NULL;
4947 }
4948 root = xmlDocGetRootElement(doc);
4949 ret = xmlValidateElement(ctxt, doc, root);
4950 ret &= xmlValidateDocumentFinal(ctxt, doc);
4951 doc->extSubset = oldExt;
4952 return(ret);
4953}
4954
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004955static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004956xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
4957 const xmlChar *name ATTRIBUTE_UNUSED) {
4958 if (cur == NULL)
4959 return;
4960 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
4961 xmlChar *notation = cur->content;
4962
Daniel Veillard878eab02002-02-19 13:46:09 +00004963 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004964 int ret;
4965
4966 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
4967 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00004968 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004969 }
4970 }
4971 }
4972}
4973
4974static void
Owen Taylor3473f882001-02-23 17:55:21 +00004975xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004976 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004977 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00004978 xmlDocPtr doc;
4979 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004980
Owen Taylor3473f882001-02-23 17:55:21 +00004981 if (cur == NULL)
4982 return;
4983 switch (cur->atype) {
4984 case XML_ATTRIBUTE_CDATA:
4985 case XML_ATTRIBUTE_ID:
4986 case XML_ATTRIBUTE_IDREF :
4987 case XML_ATTRIBUTE_IDREFS:
4988 case XML_ATTRIBUTE_NMTOKEN:
4989 case XML_ATTRIBUTE_NMTOKENS:
4990 case XML_ATTRIBUTE_ENUMERATION:
4991 break;
4992 case XML_ATTRIBUTE_ENTITY:
4993 case XML_ATTRIBUTE_ENTITIES:
4994 case XML_ATTRIBUTE_NOTATION:
4995 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004996
4997 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
4998 cur->atype, cur->defaultValue);
4999 if ((ret == 0) && (ctxt->valid == 1))
5000 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005001 }
5002 if (cur->tree != NULL) {
5003 xmlEnumerationPtr tree = cur->tree;
5004 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005005 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00005006 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005007 if ((ret == 0) && (ctxt->valid == 1))
5008 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00005009 tree = tree->next;
5010 }
5011 }
5012 }
Daniel Veillard878eab02002-02-19 13:46:09 +00005013 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
5014 doc = cur->doc;
5015 if ((doc == NULL) || (cur->elem == NULL)) {
5016 VERROR(ctxt->userData,
5017 "xmlValidateAttributeCallback(%s): internal error\n",
5018 cur->name);
5019 return;
5020 }
5021 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
5022 if (elem == NULL)
5023 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
5024 if (elem == NULL) {
5025 VERROR(ctxt->userData,
5026 "attribute %s: could not find decl for element %s\n",
5027 cur->name, cur->elem);
5028 return;
5029 }
5030 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
5031 VERROR(ctxt->userData,
5032 "NOTATION attribute %s declared on EMPTY element %s\n",
5033 cur->name, cur->elem);
5034 ctxt->valid = 0;
5035 }
5036 }
Owen Taylor3473f882001-02-23 17:55:21 +00005037}
5038
5039/**
5040 * xmlValidateDtdFinal:
5041 * @ctxt: the validation context
5042 * @doc: a document instance
5043 *
5044 * Does the final step for the dtds validation once all the
5045 * subsets have been parsed
5046 *
5047 * basically it does the following checks described by the XML Rec
5048 * - check that ENTITY and ENTITIES type attributes default or
5049 * possible values matches one of the defined entities.
5050 * - check that NOTATION type attributes default or
5051 * possible values matches one of the defined notations.
5052 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005053 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00005054 */
5055
5056int
5057xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00005058 xmlDtdPtr dtd;
5059 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005060 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00005061
5062 if (doc == NULL) return(0);
5063 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5064 return(0);
5065 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005066 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005067 dtd = doc->intSubset;
5068 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5069 table = (xmlAttributeTablePtr) dtd->attributes;
5070 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005071 }
5072 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005073 entities = (xmlEntitiesTablePtr) dtd->entities;
5074 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5075 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005076 }
5077 dtd = doc->extSubset;
5078 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5079 table = (xmlAttributeTablePtr) dtd->attributes;
5080 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005081 }
5082 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005083 entities = (xmlEntitiesTablePtr) dtd->entities;
5084 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5085 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005086 }
5087 return(ctxt->valid);
5088}
5089
5090/**
5091 * xmlValidateDocument:
5092 * @ctxt: the validation context
5093 * @doc: a document instance
5094 *
5095 * Try to validate the document instance
5096 *
5097 * basically it does the all the checks described by the XML Rec
5098 * i.e. validates the internal and external subset (if present)
5099 * and validate the document tree.
5100 *
5101 * returns 1 if valid or 0 otherwise
5102 */
5103
5104int
5105xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5106 int ret;
5107 xmlNodePtr root;
5108
5109 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5110 return(0);
5111 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
5112 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
5113 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
5114 doc->intSubset->SystemID);
5115 if (doc->extSubset == NULL) {
5116 if (doc->intSubset->SystemID != NULL) {
5117 VERROR(ctxt->userData,
5118 "Could not load the external subset \"%s\"\n",
5119 doc->intSubset->SystemID);
5120 } else {
5121 VERROR(ctxt->userData,
5122 "Could not load the external subset \"%s\"\n",
5123 doc->intSubset->ExternalID);
5124 }
5125 return(0);
5126 }
5127 }
5128
5129 if (doc->ids != NULL) {
5130 xmlFreeIDTable(doc->ids);
5131 doc->ids = NULL;
5132 }
5133 if (doc->refs != NULL) {
5134 xmlFreeRefTable(doc->refs);
5135 doc->refs = NULL;
5136 }
5137 ret = xmlValidateDtdFinal(ctxt, doc);
5138 if (!xmlValidateRoot(ctxt, doc)) return(0);
5139
5140 root = xmlDocGetRootElement(doc);
5141 ret &= xmlValidateElement(ctxt, doc, root);
5142 ret &= xmlValidateDocumentFinal(ctxt, doc);
5143 return(ret);
5144}
5145
5146
5147/************************************************************************
5148 * *
5149 * Routines for dynamic validation editing *
5150 * *
5151 ************************************************************************/
5152
5153/**
5154 * xmlValidGetPotentialChildren:
5155 * @ctree: an element content tree
5156 * @list: an array to store the list of child names
5157 * @len: a pointer to the number of element in the list
5158 * @max: the size of the array
5159 *
5160 * Build/extend a list of potential children allowed by the content tree
5161 *
5162 * returns the number of element in the list, or -1 in case of error.
5163 */
5164
5165int
5166xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
5167 int *len, int max) {
5168 int i;
5169
5170 if ((ctree == NULL) || (list == NULL) || (len == NULL))
5171 return(-1);
5172 if (*len >= max) return(*len);
5173
5174 switch (ctree->type) {
5175 case XML_ELEMENT_CONTENT_PCDATA:
5176 for (i = 0; i < *len;i++)
5177 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
5178 list[(*len)++] = BAD_CAST "#PCDATA";
5179 break;
5180 case XML_ELEMENT_CONTENT_ELEMENT:
5181 for (i = 0; i < *len;i++)
5182 if (xmlStrEqual(ctree->name, list[i])) return(*len);
5183 list[(*len)++] = ctree->name;
5184 break;
5185 case XML_ELEMENT_CONTENT_SEQ:
5186 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5187 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5188 break;
5189 case XML_ELEMENT_CONTENT_OR:
5190 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5191 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5192 break;
5193 }
5194
5195 return(*len);
5196}
5197
5198/**
5199 * xmlValidGetValidElements:
5200 * @prev: an element to insert after
5201 * @next: an element to insert next
5202 * @list: an array to store the list of child names
5203 * @max: the size of the array
5204 *
5205 * This function returns the list of authorized children to insert
5206 * within an existing tree while respecting the validity constraints
5207 * forced by the Dtd. The insertion point is defined using @prev and
5208 * @next in the following ways:
5209 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
5210 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
5211 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
5212 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
5213 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
5214 *
5215 * pointers to the element names are inserted at the beginning of the array
5216 * and do not need to be freed.
5217 *
5218 * returns the number of element in the list, or -1 in case of error. If
5219 * the function returns the value @max the caller is invited to grow the
5220 * receiving array and retry.
5221 */
5222
5223int
5224xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
5225 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005226 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005227 int nb_valid_elements = 0;
5228 const xmlChar *elements[256];
5229 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005230 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005231
5232 xmlNode *ref_node;
5233 xmlNode *parent;
5234 xmlNode *test_node;
5235
5236 xmlNode *prev_next;
5237 xmlNode *next_prev;
5238 xmlNode *parent_childs;
5239 xmlNode *parent_last;
5240
5241 xmlElement *element_desc;
5242
Daniel Veillardbb4e46d2002-03-10 16:49:08 +00005243 memset(&vctxt, 0, sizeof (xmlValidCtxt));
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005244
Owen Taylor3473f882001-02-23 17:55:21 +00005245 if (prev == NULL && next == NULL)
5246 return(-1);
5247
5248 if (list == NULL) return(-1);
5249 if (max <= 0) return(-1);
5250
5251 nb_valid_elements = 0;
5252 ref_node = prev ? prev : next;
5253 parent = ref_node->parent;
5254
5255 /*
5256 * Retrieves the parent element declaration
5257 */
5258 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5259 parent->name);
5260 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5261 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5262 parent->name);
5263 if (element_desc == NULL) return(-1);
5264
5265 /*
5266 * Do a backup of the current tree structure
5267 */
5268 prev_next = prev ? prev->next : NULL;
5269 next_prev = next ? next->prev : NULL;
5270 parent_childs = parent->children;
5271 parent_last = parent->last;
5272
5273 /*
5274 * Creates a dummy node and insert it into the tree
5275 */
5276 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5277 test_node->doc = ref_node->doc;
5278 test_node->parent = parent;
5279 test_node->prev = prev;
5280 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005281 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005282
5283 if (prev) prev->next = test_node;
5284 else parent->children = test_node;
5285
5286 if (next) next->prev = test_node;
5287 else parent->last = test_node;
5288
5289 /*
5290 * Insert each potential child node and check if the parent is
5291 * still valid
5292 */
5293 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5294 elements, &nb_elements, 256);
5295
5296 for (i = 0;i < nb_elements;i++) {
5297 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005298 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005299 int j;
5300
5301 for (j = 0; j < nb_valid_elements;j++)
5302 if (xmlStrEqual(elements[i], list[j])) break;
5303 list[nb_valid_elements++] = elements[i];
5304 if (nb_valid_elements >= max) break;
5305 }
5306 }
5307
5308 /*
5309 * Restore the tree structure
5310 */
5311 if (prev) prev->next = prev_next;
5312 if (next) next->prev = next_prev;
5313 parent->children = parent_childs;
5314 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005315
5316 /*
5317 * Free up the dummy node
5318 */
5319 test_node->name = name;
5320 xmlFreeNode(test_node);
5321
Owen Taylor3473f882001-02-23 17:55:21 +00005322 return(nb_valid_elements);
5323}