blob: 48f7e15abf2a9606ac560ada4d0a17e633d23bc0 [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 *
7 * Daniel.Veillard@w3.org
8 */
9
10#ifdef WIN32
11#include "win32config.h"
12#else
13#include "config.h"
14#endif
15
16#include <stdio.h>
17#include <string.h>
18
19#ifdef HAVE_STDLIB_H
20#include <stdlib.h>
21#endif
22
23#include <libxml/xmlmemory.h>
24#include <libxml/hash.h>
25#include <libxml/valid.h>
26#include <libxml/parser.h>
27#include <libxml/parserInternals.h>
28#include <libxml/xmlerror.h>
29#include <libxml/list.h>
30
Daniel Veillarddab4cb32001-04-20 13:03:48 +000031#define ALLOW_UNDETERMINISTIC_MODELS
32
Owen Taylor3473f882001-02-23 17:55:21 +000033/*
34 * Generic function for accessing stacks in the Validity Context
35 */
36
37#define PUSH_AND_POP(scope, type, name) \
38scope int name##VPush(xmlValidCtxtPtr ctxt, type value) { \
39 if (ctxt->name##Nr >= ctxt->name##Max) { \
40 ctxt->name##Max *= 2; \
41 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
42 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
43 if (ctxt->name##Tab == NULL) { \
44 xmlGenericError(xmlGenericErrorContext, \
45 "realloc failed !\n"); \
46 return(0); \
47 } \
48 } \
49 ctxt->name##Tab[ctxt->name##Nr] = value; \
50 ctxt->name = value; \
51 return(ctxt->name##Nr++); \
52} \
53scope type name##VPop(xmlValidCtxtPtr ctxt) { \
54 type ret; \
55 if (ctxt->name##Nr <= 0) return(0); \
56 ctxt->name##Nr--; \
57 if (ctxt->name##Nr > 0) \
58 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
59 else \
60 ctxt->name = NULL; \
61 ret = ctxt->name##Tab[ctxt->name##Nr]; \
62 ctxt->name##Tab[ctxt->name##Nr] = 0; \
63 return(ret); \
64} \
65
Daniel Veillarddab4cb32001-04-20 13:03:48 +000066#ifdef ALLOW_UNDETERMINISTIC_MODELS
67
68/*
69 * I will use a home made algorithm less complex and easier to
70 * debug/maintin than a generic NFA -> DFA state based algo. The
71 * only restriction is on the deepness of the tree limited by the
72 * size of the occurs bitfield
73 *
74 * this is the content of a saved state for rollbacks
75 */
76
77#define ROLLBACK_OR 0
78#define ROLLBACK_PARENT 1
79
80struct _xmlValidState {
81 xmlElementContentPtr cont; /* pointer to the content model subtree */
82 xmlNodePtr node; /* pointer to the current node in the list */
83 long occurs;/* bitfield for multiple occurences */
84 unsigned char depth; /* current depth in the overall tree */
85 unsigned char state; /* ROLLBACK_XXX */
86} _xmlValidState;
87
88#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
89#define CONT ctxt->vstate->cont
90#define NODE ctxt->vstate->node
91#define DEPTH ctxt->vstate->depth
92#define OCCURS ctxt->vstate->occurs
93#define STATE ctxt->vstate->state
94
95#define OCCURENCE (ctxt->vstate->occurs & (1 << DEPTH))
96#define PARENT_OCCURENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
97
98#define SET_OCCURENCE ctxt->vstate->occurs |= (1 << DEPTH)
99#define RESET_OCCURENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
100
101static int
102vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
103 xmlNodePtr node, unsigned char depth, long occurs,
104 unsigned char state) {
105 if (ctxt->vstateNr >= ctxt->vstateMax) {
106 ctxt->vstateMax *= 2;
107 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
108 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
109 if (ctxt->vstateTab == NULL) {
110 xmlGenericError(xmlGenericErrorContext,
111 "realloc failed !n");
112 return(0);
113 }
114 }
115 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
116 ctxt->vstateTab[ctxt->vstateNr].node = node;
117 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
118 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
119 ctxt->vstateTab[ctxt->vstateNr].state = state;
120 return(ctxt->vstateNr++);
121}
122
123static int
124vstateVPop(xmlValidCtxtPtr ctxt) {
125 if (ctxt->vstateNr <= 1) return(-1);
126 ctxt->vstateNr--;
127 ctxt->vstate = &ctxt->vstateTab[0];
128 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
129 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
130 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
131 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
132 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
133 return(ctxt->vstateNr);
134}
135
136
137#endif /* ALLOW_UNDETERMINISTIC_MODELS */
138
Owen Taylor3473f882001-02-23 17:55:21 +0000139PUSH_AND_POP(static, xmlNodePtr, node)
140
141/* #define DEBUG_VALID_ALGO */
142
143#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000144static void
145xmlValidPrintNode(xmlNodePtr cur) {
146 if (cur == NULL) {
147 xmlGenericError(xmlGenericErrorContext, "null");
148 return;
149 }
150 switch (cur->type) {
151 case XML_ELEMENT_NODE:
152 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
153 break;
154 case XML_TEXT_NODE:
155 xmlGenericError(xmlGenericErrorContext, "text ");
156 break;
157 case XML_CDATA_SECTION_NODE:
158 xmlGenericError(xmlGenericErrorContext, "cdata ");
159 break;
160 case XML_ENTITY_REF_NODE:
161 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
162 break;
163 case XML_PI_NODE:
164 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
165 break;
166 case XML_COMMENT_NODE:
167 xmlGenericError(xmlGenericErrorContext, "comment ");
168 break;
169 case XML_ATTRIBUTE_NODE:
170 xmlGenericError(xmlGenericErrorContext, "?attr? ");
171 break;
172 case XML_ENTITY_NODE:
173 xmlGenericError(xmlGenericErrorContext, "?ent? ");
174 break;
175 case XML_DOCUMENT_NODE:
176 xmlGenericError(xmlGenericErrorContext, "?doc? ");
177 break;
178 case XML_DOCUMENT_TYPE_NODE:
179 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
180 break;
181 case XML_DOCUMENT_FRAG_NODE:
182 xmlGenericError(xmlGenericErrorContext, "?frag? ");
183 break;
184 case XML_NOTATION_NODE:
185 xmlGenericError(xmlGenericErrorContext, "?nota? ");
186 break;
187 case XML_HTML_DOCUMENT_NODE:
188 xmlGenericError(xmlGenericErrorContext, "?html? ");
189 break;
190 case XML_DTD_NODE:
191 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
192 break;
193 case XML_ELEMENT_DECL:
194 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
195 break;
196 case XML_ATTRIBUTE_DECL:
197 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
198 break;
199 case XML_ENTITY_DECL:
200 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
201 break;
202 case XML_NAMESPACE_DECL:
203 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
204 break;
205 case XML_XINCLUDE_START:
206 xmlGenericError(xmlGenericErrorContext, "incstart ");
207 break;
208 case XML_XINCLUDE_END:
209 xmlGenericError(xmlGenericErrorContext, "incend ");
210 break;
211 }
212}
213
214static void
215xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000216 if (cur == NULL)
217 xmlGenericError(xmlGenericErrorContext, "null ");
218 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000219 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000220 cur = cur->next;
221 }
222}
223
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000224static void
225xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000226 char expr[1000];
227
228 expr[0] = 0;
229 xmlGenericError(xmlGenericErrorContext, "valid: ");
230 xmlValidPrintNodeList(cur);
231 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000232 xmlSprintfElementContent(expr, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000233 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
234}
235
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000236static void
237xmlValidDebugState(xmlValidStatePtr state) {
238 xmlGenericError(xmlGenericErrorContext, "(");
239 if (state->cont == NULL)
240 xmlGenericError(xmlGenericErrorContext, "null,");
241 else
242 switch (state->cont->type) {
243 case XML_ELEMENT_CONTENT_PCDATA:
244 xmlGenericError(xmlGenericErrorContext, "pcdata,");
245 break;
246 case XML_ELEMENT_CONTENT_ELEMENT:
247 xmlGenericError(xmlGenericErrorContext, "%s,",
248 state->cont->name);
249 break;
250 case XML_ELEMENT_CONTENT_SEQ:
251 xmlGenericError(xmlGenericErrorContext, "seq,");
252 break;
253 case XML_ELEMENT_CONTENT_OR:
254 xmlGenericError(xmlGenericErrorContext, "or,");
255 break;
256 }
257 xmlValidPrintNode(state->node);
258 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
259 state->depth, state->occurs, state->state);
260}
261
262static void
263xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
264 int i, j;
265
266 xmlGenericError(xmlGenericErrorContext, "state: ");
267 xmlValidDebugState(ctxt->vstate);
268 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
269 ctxt->vstateNr - 1);
270 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
271 xmlValidDebugState(&ctxt->vstateTab[j]);
272 xmlGenericError(xmlGenericErrorContext, "\n");
273}
274
275/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000276#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000277 *****/
278
279#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000280#define DEBUG_VALID_MSG(m) \
281 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
282
Owen Taylor3473f882001-02-23 17:55:21 +0000283#else
284#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000285#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000286#endif
287
288/* TODO: use hash table for accesses to elem and attribute dedinitions */
289
290#define VERROR \
291 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
292
293#define VWARNING \
294 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
295
296#define CHECK_DTD \
297 if (doc == NULL) return(0); \
298 else if ((doc->intSubset == NULL) && \
299 (doc->extSubset == NULL)) return(0)
300
301xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000302static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
303 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000304xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
305
306/************************************************************************
307 * *
308 * QName handling helper *
309 * *
310 ************************************************************************/
311
312/**
313 * xmlSplitQName2:
314 * @name: an XML parser context
315 * @prefix: a xmlChar **
316 *
317 * parse an XML qualified name string
318 *
319 * [NS 5] QName ::= (Prefix ':')? LocalPart
320 *
321 * [NS 6] Prefix ::= NCName
322 *
323 * [NS 7] LocalPart ::= NCName
324 *
325 * Returns NULL if not a QName, otherwise the local part, and prefix
326 * is updated to get the Prefix if any.
327 */
328
329xmlChar *
330xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
331 int len = 0;
332 xmlChar *ret = NULL;
333
334 *prefix = NULL;
335
336 /* xml: prefix is not really a namespace */
337 if ((name[0] == 'x') && (name[1] == 'm') &&
338 (name[2] == 'l') && (name[3] == ':'))
339 return(NULL);
340
341 /* nasty but valid */
342 if (name[0] == ':')
343 return(NULL);
344
345 /*
346 * we are not trying to validate but just to cut, and yes it will
347 * work even if this is as set of UTF-8 encoded chars
348 */
349 while ((name[len] != 0) && (name[len] != ':'))
350 len++;
351
352 if (name[len] == 0)
353 return(NULL);
354
355 *prefix = xmlStrndup(name, len);
356 ret = xmlStrdup(&name[len + 1]);
357
358 return(ret);
359}
360
361/****************************************************************
362 * *
363 * Util functions for data allocation/deallocation *
364 * *
365 ****************************************************************/
366
367/**
368 * xmlNewElementContent:
369 * @name: the subelement name or NULL
370 * @type: the type of element content decl
371 *
372 * Allocate an element content structure.
373 *
374 * Returns NULL if not, othervise the new element content structure
375 */
376xmlElementContentPtr
377xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
378 xmlElementContentPtr ret;
379
380 switch(type) {
381 case XML_ELEMENT_CONTENT_ELEMENT:
382 if (name == NULL) {
383 xmlGenericError(xmlGenericErrorContext,
384 "xmlNewElementContent : name == NULL !\n");
385 }
386 break;
387 case XML_ELEMENT_CONTENT_PCDATA:
388 case XML_ELEMENT_CONTENT_SEQ:
389 case XML_ELEMENT_CONTENT_OR:
390 if (name != NULL) {
391 xmlGenericError(xmlGenericErrorContext,
392 "xmlNewElementContent : name != NULL !\n");
393 }
394 break;
395 default:
396 xmlGenericError(xmlGenericErrorContext,
397 "xmlNewElementContent: unknown type %d\n", type);
398 return(NULL);
399 }
400 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
401 if (ret == NULL) {
402 xmlGenericError(xmlGenericErrorContext,
403 "xmlNewElementContent : out of memory!\n");
404 return(NULL);
405 }
406 ret->type = type;
407 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
408 if (name != NULL)
409 ret->name = xmlStrdup(name);
410 else
411 ret->name = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000412 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000413 return(ret);
414}
415
416/**
417 * xmlCopyElementContent:
418 * @content: An element content pointer.
419 *
420 * Build a copy of an element content description.
421 *
422 * Returns the new xmlElementContentPtr or NULL in case of error.
423 */
424xmlElementContentPtr
425xmlCopyElementContent(xmlElementContentPtr cur) {
426 xmlElementContentPtr ret;
427
428 if (cur == NULL) return(NULL);
429 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
430 if (ret == NULL) {
431 xmlGenericError(xmlGenericErrorContext,
432 "xmlCopyElementContent : out of memory\n");
433 return(NULL);
434 }
435 ret->ocur = cur->ocur;
436 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000437 if (ret->c1 != NULL)
438 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000439 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000440 if (ret->c2 != NULL)
441 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000442 return(ret);
443}
444
445/**
446 * xmlFreeElementContent:
447 * @cur: the element content tree to free
448 *
449 * Free an element content structure. This is a recursive call !
450 */
451void
452xmlFreeElementContent(xmlElementContentPtr cur) {
453 if (cur == NULL) return;
454 switch (cur->type) {
455 case XML_ELEMENT_CONTENT_PCDATA:
456 case XML_ELEMENT_CONTENT_ELEMENT:
457 case XML_ELEMENT_CONTENT_SEQ:
458 case XML_ELEMENT_CONTENT_OR:
459 break;
460 default:
461 xmlGenericError(xmlGenericErrorContext,
462 "xmlFreeElementContent : type %d\n", cur->type);
463 return;
464 }
465 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
466 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
467 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +0000468 xmlFree(cur);
469}
470
471/**
472 * xmlDumpElementContent:
473 * @buf: An XML buffer
474 * @content: An element table
475 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
476 *
477 * This will dump the content of the element table as an XML DTD definition
478 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000479static void
Owen Taylor3473f882001-02-23 17:55:21 +0000480xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
481 if (content == NULL) return;
482
483 if (glob) xmlBufferWriteChar(buf, "(");
484 switch (content->type) {
485 case XML_ELEMENT_CONTENT_PCDATA:
486 xmlBufferWriteChar(buf, "#PCDATA");
487 break;
488 case XML_ELEMENT_CONTENT_ELEMENT:
489 xmlBufferWriteCHAR(buf, content->name);
490 break;
491 case XML_ELEMENT_CONTENT_SEQ:
492 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
493 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
494 xmlDumpElementContent(buf, content->c1, 1);
495 else
496 xmlDumpElementContent(buf, content->c1, 0);
497 xmlBufferWriteChar(buf, " , ");
498 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
499 xmlDumpElementContent(buf, content->c2, 1);
500 else
501 xmlDumpElementContent(buf, content->c2, 0);
502 break;
503 case XML_ELEMENT_CONTENT_OR:
504 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
505 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
506 xmlDumpElementContent(buf, content->c1, 1);
507 else
508 xmlDumpElementContent(buf, content->c1, 0);
509 xmlBufferWriteChar(buf, " | ");
510 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
511 xmlDumpElementContent(buf, content->c2, 1);
512 else
513 xmlDumpElementContent(buf, content->c2, 0);
514 break;
515 default:
516 xmlGenericError(xmlGenericErrorContext,
517 "xmlDumpElementContent: unknown type %d\n",
518 content->type);
519 }
520 if (glob)
521 xmlBufferWriteChar(buf, ")");
522 switch (content->ocur) {
523 case XML_ELEMENT_CONTENT_ONCE:
524 break;
525 case XML_ELEMENT_CONTENT_OPT:
526 xmlBufferWriteChar(buf, "?");
527 break;
528 case XML_ELEMENT_CONTENT_MULT:
529 xmlBufferWriteChar(buf, "*");
530 break;
531 case XML_ELEMENT_CONTENT_PLUS:
532 xmlBufferWriteChar(buf, "+");
533 break;
534 }
535}
536
537/**
538 * xmlSprintfElementContent:
539 * @buf: an output buffer
540 * @content: An element table
541 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
542 *
543 * This will dump the content of the element content definition
544 * Intended just for the debug routine
545 */
546void
547xmlSprintfElementContent(char *buf, xmlElementContentPtr content, int glob) {
548 if (content == NULL) return;
549 if (glob) strcat(buf, "(");
550 switch (content->type) {
551 case XML_ELEMENT_CONTENT_PCDATA:
552 strcat(buf, "#PCDATA");
553 break;
554 case XML_ELEMENT_CONTENT_ELEMENT:
555 strcat(buf, (char *) content->name);
556 break;
557 case XML_ELEMENT_CONTENT_SEQ:
558 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
559 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
560 xmlSprintfElementContent(buf, content->c1, 1);
561 else
562 xmlSprintfElementContent(buf, content->c1, 0);
563 strcat(buf, " , ");
564 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
565 xmlSprintfElementContent(buf, content->c2, 1);
566 else
567 xmlSprintfElementContent(buf, content->c2, 0);
568 break;
569 case XML_ELEMENT_CONTENT_OR:
570 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
571 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
572 xmlSprintfElementContent(buf, content->c1, 1);
573 else
574 xmlSprintfElementContent(buf, content->c1, 0);
575 strcat(buf, " | ");
576 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
577 xmlSprintfElementContent(buf, content->c2, 1);
578 else
579 xmlSprintfElementContent(buf, content->c2, 0);
580 break;
581 }
582 if (glob)
583 strcat(buf, ")");
584 switch (content->ocur) {
585 case XML_ELEMENT_CONTENT_ONCE:
586 break;
587 case XML_ELEMENT_CONTENT_OPT:
588 strcat(buf, "?");
589 break;
590 case XML_ELEMENT_CONTENT_MULT:
591 strcat(buf, "*");
592 break;
593 case XML_ELEMENT_CONTENT_PLUS:
594 strcat(buf, "+");
595 break;
596 }
597}
598
599/****************************************************************
600 * *
601 * Registration of DTD declarations *
602 * *
603 ****************************************************************/
604
605/**
606 * xmlCreateElementTable:
607 *
608 * create and initialize an empty element hash table.
609 *
610 * Returns the xmlElementTablePtr just created or NULL in case of error.
611 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000612static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000613xmlCreateElementTable(void) {
614 return(xmlHashCreate(0));
615}
616
617/**
618 * xmlFreeElement:
619 * @elem: An element
620 *
621 * Deallocate the memory used by an element definition
622 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000623static void
Owen Taylor3473f882001-02-23 17:55:21 +0000624xmlFreeElement(xmlElementPtr elem) {
625 if (elem == NULL) return;
626 xmlUnlinkNode((xmlNodePtr) elem);
627 xmlFreeElementContent(elem->content);
628 if (elem->name != NULL)
629 xmlFree((xmlChar *) elem->name);
630 if (elem->prefix != NULL)
631 xmlFree((xmlChar *) elem->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000632 xmlFree(elem);
633}
634
635
636/**
637 * xmlAddElementDecl:
638 * @ctxt: the validation context
639 * @dtd: pointer to the DTD
640 * @name: the entity name
641 * @type: the element type
642 * @content: the element content tree or NULL
643 *
644 * Register a new element declaration
645 *
646 * Returns NULL if not, othervise the entity
647 */
648xmlElementPtr
649xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
650 xmlElementTypeVal type,
651 xmlElementContentPtr content) {
652 xmlElementPtr ret;
653 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000654 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000655 xmlChar *ns, *uqname;
656
657 if (dtd == NULL) {
658 xmlGenericError(xmlGenericErrorContext,
659 "xmlAddElementDecl: dtd == NULL\n");
660 return(NULL);
661 }
662 if (name == NULL) {
663 xmlGenericError(xmlGenericErrorContext,
664 "xmlAddElementDecl: name == NULL\n");
665 return(NULL);
666 }
667 switch (type) {
668 case XML_ELEMENT_TYPE_EMPTY:
669 if (content != NULL) {
670 xmlGenericError(xmlGenericErrorContext,
671 "xmlAddElementDecl: content != NULL for EMPTY\n");
672 return(NULL);
673 }
674 break;
675 case XML_ELEMENT_TYPE_ANY:
676 if (content != NULL) {
677 xmlGenericError(xmlGenericErrorContext,
678 "xmlAddElementDecl: content != NULL for ANY\n");
679 return(NULL);
680 }
681 break;
682 case XML_ELEMENT_TYPE_MIXED:
683 if (content == NULL) {
684 xmlGenericError(xmlGenericErrorContext,
685 "xmlAddElementDecl: content == NULL for MIXED\n");
686 return(NULL);
687 }
688 break;
689 case XML_ELEMENT_TYPE_ELEMENT:
690 if (content == NULL) {
691 xmlGenericError(xmlGenericErrorContext,
692 "xmlAddElementDecl: content == NULL for ELEMENT\n");
693 return(NULL);
694 }
695 break;
696 default:
697 xmlGenericError(xmlGenericErrorContext,
698 "xmlAddElementDecl: unknown type %d\n", type);
699 return(NULL);
700 }
701
702 /*
703 * check if name is a QName
704 */
705 uqname = xmlSplitQName2(name, &ns);
706 if (uqname != NULL)
707 name = uqname;
708
709 /*
710 * Create the Element table if needed.
711 */
712 table = (xmlElementTablePtr) dtd->elements;
713 if (table == NULL) {
714 table = xmlCreateElementTable();
715 dtd->elements = (void *) table;
716 }
717 if (table == NULL) {
718 xmlGenericError(xmlGenericErrorContext,
719 "xmlAddElementDecl: Table creation failed!\n");
720 return(NULL);
721 }
722
Daniel Veillarda10efa82001-04-18 13:09:01 +0000723 /*
724 * lookup old attributes inserted on an undefined element in the
725 * internal subset.
726 */
727 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
728 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
729 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
730 oldAttributes = ret->attributes;
731 ret->attributes = NULL;
732 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
733 xmlFreeElement(ret);
734 }
Owen Taylor3473f882001-02-23 17:55:21 +0000735 }
Owen Taylor3473f882001-02-23 17:55:21 +0000736
737 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +0000738 * The element may already be present if one of its attribute
739 * was registered first
740 */
741 ret = xmlHashLookup2(table, name, ns);
742 if (ret != NULL) {
743 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
744 /*
745 * The element is already defined in this Dtd.
746 */
747 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
748 if (uqname != NULL)
749 xmlFree(uqname);
750 return(NULL);
751 }
752 } else {
753 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
754 if (ret == NULL) {
755 xmlGenericError(xmlGenericErrorContext,
756 "xmlAddElementDecl: out of memory\n");
757 return(NULL);
758 }
759 memset(ret, 0, sizeof(xmlElement));
760 ret->type = XML_ELEMENT_DECL;
761
762 /*
763 * fill the structure.
764 */
765 ret->name = xmlStrdup(name);
766 ret->prefix = ns;
767
768 /*
769 * Validity Check:
770 * Insertion must not fail
771 */
772 if (xmlHashAddEntry2(table, name, ns, ret)) {
773 /*
774 * The element is already defined in this Dtd.
775 */
776 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
777 xmlFreeElement(ret);
778 if (uqname != NULL)
779 xmlFree(uqname);
780 return(NULL);
781 }
782 }
783
784 /*
785 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +0000786 */
787 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +0000788 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000789 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +0000790
791 /*
792 * Link it to the Dtd
793 */
794 ret->parent = dtd;
795 ret->doc = dtd->doc;
796 if (dtd->last == NULL) {
797 dtd->children = dtd->last = (xmlNodePtr) ret;
798 } else {
799 dtd->last->next = (xmlNodePtr) ret;
800 ret->prev = dtd->last;
801 dtd->last = (xmlNodePtr) ret;
802 }
803 if (uqname != NULL)
804 xmlFree(uqname);
805 return(ret);
806}
807
808/**
809 * xmlFreeElementTable:
810 * @table: An element table
811 *
812 * Deallocate the memory used by an element hash table.
813 */
814void
815xmlFreeElementTable(xmlElementTablePtr table) {
816 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
817}
818
819/**
820 * xmlCopyElement:
821 * @elem: An element
822 *
823 * Build a copy of an element.
824 *
825 * Returns the new xmlElementPtr or NULL in case of error.
826 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000827static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000828xmlCopyElement(xmlElementPtr elem) {
829 xmlElementPtr cur;
830
831 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
832 if (cur == NULL) {
833 xmlGenericError(xmlGenericErrorContext,
834 "xmlCopyElement: out of memory !\n");
835 return(NULL);
836 }
837 memset(cur, 0, sizeof(xmlElement));
838 cur->type = XML_ELEMENT_DECL;
839 cur->etype = elem->etype;
840 if (elem->name != NULL)
841 cur->name = xmlStrdup(elem->name);
842 else
843 cur->name = NULL;
844 if (elem->prefix != NULL)
845 cur->prefix = xmlStrdup(elem->prefix);
846 else
847 cur->prefix = NULL;
848 cur->content = xmlCopyElementContent(elem->content);
849 /* TODO : rebuild the attribute list on the copy */
850 cur->attributes = NULL;
851 return(cur);
852}
853
854/**
855 * xmlCopyElementTable:
856 * @table: An element table
857 *
858 * Build a copy of an element table.
859 *
860 * Returns the new xmlElementTablePtr or NULL in case of error.
861 */
862xmlElementTablePtr
863xmlCopyElementTable(xmlElementTablePtr table) {
864 return((xmlElementTablePtr) xmlHashCopy(table,
865 (xmlHashCopier) xmlCopyElement));
866}
867
868/**
869 * xmlDumpElementDecl:
870 * @buf: the XML buffer output
871 * @elem: An element table
872 *
873 * This will dump the content of the element declaration as an XML
874 * DTD definition
875 */
876void
877xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
878 switch (elem->etype) {
879 case XML_ELEMENT_TYPE_EMPTY:
880 xmlBufferWriteChar(buf, "<!ELEMENT ");
881 xmlBufferWriteCHAR(buf, elem->name);
882 xmlBufferWriteChar(buf, " EMPTY>\n");
883 break;
884 case XML_ELEMENT_TYPE_ANY:
885 xmlBufferWriteChar(buf, "<!ELEMENT ");
886 xmlBufferWriteCHAR(buf, elem->name);
887 xmlBufferWriteChar(buf, " ANY>\n");
888 break;
889 case XML_ELEMENT_TYPE_MIXED:
890 xmlBufferWriteChar(buf, "<!ELEMENT ");
891 xmlBufferWriteCHAR(buf, elem->name);
892 xmlBufferWriteChar(buf, " ");
893 xmlDumpElementContent(buf, elem->content, 1);
894 xmlBufferWriteChar(buf, ">\n");
895 break;
896 case XML_ELEMENT_TYPE_ELEMENT:
897 xmlBufferWriteChar(buf, "<!ELEMENT ");
898 xmlBufferWriteCHAR(buf, elem->name);
899 xmlBufferWriteChar(buf, " ");
900 xmlDumpElementContent(buf, elem->content, 1);
901 xmlBufferWriteChar(buf, ">\n");
902 break;
903 default:
904 xmlGenericError(xmlGenericErrorContext,
905 "xmlDumpElementDecl: internal: unknown type %d\n",
906 elem->etype);
907 }
908}
909
910/**
911 * xmlDumpElementTable:
912 * @buf: the XML buffer output
913 * @table: An element table
914 *
915 * This will dump the content of the element table as an XML DTD definition
916 */
917void
918xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
919 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
920}
921
922/**
923 * xmlCreateEnumeration:
924 * @name: the enumeration name or NULL
925 *
926 * create and initialize an enumeration attribute node.
927 *
928 * Returns the xmlEnumerationPtr just created or NULL in case
929 * of error.
930 */
931xmlEnumerationPtr
932xmlCreateEnumeration(xmlChar *name) {
933 xmlEnumerationPtr ret;
934
935 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
936 if (ret == NULL) {
937 xmlGenericError(xmlGenericErrorContext,
938 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
939 (long)sizeof(xmlEnumeration));
940 return(NULL);
941 }
942 memset(ret, 0, sizeof(xmlEnumeration));
943
944 if (name != NULL)
945 ret->name = xmlStrdup(name);
946 return(ret);
947}
948
949/**
950 * xmlFreeEnumeration:
951 * @cur: the tree to free.
952 *
953 * free an enumeration attribute node (recursive).
954 */
955void
956xmlFreeEnumeration(xmlEnumerationPtr cur) {
957 if (cur == NULL) return;
958
959 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
960
961 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +0000962 xmlFree(cur);
963}
964
965/**
966 * xmlCopyEnumeration:
967 * @cur: the tree to copy.
968 *
969 * Copy an enumeration attribute node (recursive).
970 *
971 * Returns the xmlEnumerationPtr just created or NULL in case
972 * of error.
973 */
974xmlEnumerationPtr
975xmlCopyEnumeration(xmlEnumerationPtr cur) {
976 xmlEnumerationPtr ret;
977
978 if (cur == NULL) return(NULL);
979 ret = xmlCreateEnumeration((xmlChar *) cur->name);
980
981 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
982 else ret->next = NULL;
983
984 return(ret);
985}
986
987/**
988 * xmlDumpEnumeration:
989 * @buf: the XML buffer output
990 * @enum: An enumeration
991 *
992 * This will dump the content of the enumeration
993 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000994static void
Owen Taylor3473f882001-02-23 17:55:21 +0000995xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
996 if (cur == NULL) return;
997
998 xmlBufferWriteCHAR(buf, cur->name);
999 if (cur->next == NULL)
1000 xmlBufferWriteChar(buf, ")");
1001 else {
1002 xmlBufferWriteChar(buf, " | ");
1003 xmlDumpEnumeration(buf, cur->next);
1004 }
1005}
1006
1007/**
1008 * xmlCreateAttributeTable:
1009 *
1010 * create and initialize an empty attribute hash table.
1011 *
1012 * Returns the xmlAttributeTablePtr just created or NULL in case
1013 * of error.
1014 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001015static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001016xmlCreateAttributeTable(void) {
1017 return(xmlHashCreate(0));
1018}
1019
1020/**
1021 * xmlScanAttributeDeclCallback:
1022 * @attr: the attribute decl
1023 * @list: the list to update
1024 *
1025 * Callback called by xmlScanAttributeDecl when a new attribute
1026 * has to be entered in the list.
1027 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001028static void
Owen Taylor3473f882001-02-23 17:55:21 +00001029xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001030 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001031 attr->nexth = *list;
1032 *list = attr;
1033}
1034
1035/**
1036 * xmlScanAttributeDecl:
1037 * @dtd: pointer to the DTD
1038 * @elem: the element name
1039 *
1040 * When inserting a new element scan the DtD for existing attributes
1041 * for taht element and initialize the Attribute chain
1042 *
1043 * Returns the pointer to the first attribute decl in the chain,
1044 * possibly NULL.
1045 */
1046xmlAttributePtr
1047xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1048 xmlAttributePtr ret = NULL;
1049 xmlAttributeTablePtr table;
1050
1051 if (dtd == NULL) {
1052 xmlGenericError(xmlGenericErrorContext,
1053 "xmlScanAttributeDecl: dtd == NULL\n");
1054 return(NULL);
1055 }
1056 if (elem == NULL) {
1057 xmlGenericError(xmlGenericErrorContext,
1058 "xmlScanAttributeDecl: elem == NULL\n");
1059 return(NULL);
1060 }
1061 table = (xmlAttributeTablePtr) dtd->attributes;
1062 if (table == NULL)
1063 return(NULL);
1064
1065 /* WRONG !!! */
1066 xmlHashScan3(table, NULL, NULL, elem,
1067 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1068 return(ret);
1069}
1070
1071/**
1072 * xmlScanIDAttributeDecl:
1073 * @ctxt: the validation context
1074 * @elem: the element name
1075 *
1076 * Verify that the element don't have too many ID attributes
1077 * declared.
1078 *
1079 * Returns the number of ID attributes found.
1080 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001081static int
Owen Taylor3473f882001-02-23 17:55:21 +00001082xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1083 xmlAttributePtr cur;
1084 int ret = 0;
1085
1086 if (elem == NULL) return(0);
1087 cur = elem->attributes;
1088 while (cur != NULL) {
1089 if (cur->atype == XML_ATTRIBUTE_ID) {
1090 ret ++;
1091 if (ret > 1)
1092 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001093 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001094 elem->name, cur->name);
1095 }
1096 cur = cur->nexth;
1097 }
1098 return(ret);
1099}
1100
1101/**
1102 * xmlFreeAttribute:
1103 * @elem: An attribute
1104 *
1105 * Deallocate the memory used by an attribute definition
1106 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001107static void
Owen Taylor3473f882001-02-23 17:55:21 +00001108xmlFreeAttribute(xmlAttributePtr attr) {
1109 if (attr == NULL) return;
1110 xmlUnlinkNode((xmlNodePtr) attr);
1111 if (attr->tree != NULL)
1112 xmlFreeEnumeration(attr->tree);
1113 if (attr->elem != NULL)
1114 xmlFree((xmlChar *) attr->elem);
1115 if (attr->name != NULL)
1116 xmlFree((xmlChar *) attr->name);
1117 if (attr->defaultValue != NULL)
1118 xmlFree((xmlChar *) attr->defaultValue);
1119 if (attr->prefix != NULL)
1120 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001121 xmlFree(attr);
1122}
1123
1124
1125/**
1126 * xmlAddAttributeDecl:
1127 * @ctxt: the validation context
1128 * @dtd: pointer to the DTD
1129 * @elem: the element name
1130 * @name: the attribute name
1131 * @ns: the attribute namespace prefix
1132 * @type: the attribute type
1133 * @def: the attribute default type
1134 * @defaultValue: the attribute default value
1135 * @tree: if it's an enumeration, the associated list
1136 *
1137 * Register a new attribute declaration
1138 * Note that @tree becomes the ownership of the DTD
1139 *
1140 * Returns NULL if not new, othervise the attribute decl
1141 */
1142xmlAttributePtr
1143xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1144 const xmlChar *name, const xmlChar *ns,
1145 xmlAttributeType type, xmlAttributeDefault def,
1146 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1147 xmlAttributePtr ret;
1148 xmlAttributeTablePtr table;
1149 xmlElementPtr elemDef;
1150
1151 if (dtd == NULL) {
1152 xmlGenericError(xmlGenericErrorContext,
1153 "xmlAddAttributeDecl: dtd == NULL\n");
1154 xmlFreeEnumeration(tree);
1155 return(NULL);
1156 }
1157 if (name == NULL) {
1158 xmlGenericError(xmlGenericErrorContext,
1159 "xmlAddAttributeDecl: name == NULL\n");
1160 xmlFreeEnumeration(tree);
1161 return(NULL);
1162 }
1163 if (elem == NULL) {
1164 xmlGenericError(xmlGenericErrorContext,
1165 "xmlAddAttributeDecl: elem == NULL\n");
1166 xmlFreeEnumeration(tree);
1167 return(NULL);
1168 }
1169 /*
1170 * Check the type and possibly the default value.
1171 */
1172 switch (type) {
1173 case XML_ATTRIBUTE_CDATA:
1174 break;
1175 case XML_ATTRIBUTE_ID:
1176 break;
1177 case XML_ATTRIBUTE_IDREF:
1178 break;
1179 case XML_ATTRIBUTE_IDREFS:
1180 break;
1181 case XML_ATTRIBUTE_ENTITY:
1182 break;
1183 case XML_ATTRIBUTE_ENTITIES:
1184 break;
1185 case XML_ATTRIBUTE_NMTOKEN:
1186 break;
1187 case XML_ATTRIBUTE_NMTOKENS:
1188 break;
1189 case XML_ATTRIBUTE_ENUMERATION:
1190 break;
1191 case XML_ATTRIBUTE_NOTATION:
1192 break;
1193 default:
1194 xmlGenericError(xmlGenericErrorContext,
1195 "xmlAddAttributeDecl: unknown type %d\n", type);
1196 xmlFreeEnumeration(tree);
1197 return(NULL);
1198 }
1199 if ((defaultValue != NULL) &&
1200 (!xmlValidateAttributeValue(type, defaultValue))) {
1201 VERROR(ctxt->userData, "Attribute %s on %s: invalid default value\n",
1202 elem, name, defaultValue);
1203 defaultValue = NULL;
1204 }
1205
1206 /*
1207 * Create the Attribute table if needed.
1208 */
1209 table = (xmlAttributeTablePtr) dtd->attributes;
1210 if (table == NULL) {
1211 table = xmlCreateAttributeTable();
1212 dtd->attributes = (void *) table;
1213 }
1214 if (table == NULL) {
1215 xmlGenericError(xmlGenericErrorContext,
1216 "xmlAddAttributeDecl: Table creation failed!\n");
1217 return(NULL);
1218 }
1219
1220
1221 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1222 if (ret == NULL) {
1223 xmlGenericError(xmlGenericErrorContext,
1224 "xmlAddAttributeDecl: out of memory\n");
1225 return(NULL);
1226 }
1227 memset(ret, 0, sizeof(xmlAttribute));
1228 ret->type = XML_ATTRIBUTE_DECL;
1229
1230 /*
1231 * fill the structure.
1232 */
1233 ret->atype = type;
1234 ret->name = xmlStrdup(name);
1235 ret->prefix = xmlStrdup(ns);
1236 ret->elem = xmlStrdup(elem);
1237 ret->def = def;
1238 ret->tree = tree;
1239 if (defaultValue != NULL)
1240 ret->defaultValue = xmlStrdup(defaultValue);
1241
1242 /*
1243 * Validity Check:
1244 * Search the DTD for previous declarations of the ATTLIST
1245 */
1246 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1247 /*
1248 * The attribute is already defined in this Dtd.
1249 */
1250 VWARNING(ctxt->userData,
1251 "Attribute %s on %s: already defined\n",
1252 name, elem);
1253 xmlFreeAttribute(ret);
1254 return(NULL);
1255 }
1256
1257 /*
1258 * Validity Check:
1259 * Multiple ID per element
1260 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001261 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001262 if (elemDef != NULL) {
1263 if ((type == XML_ATTRIBUTE_ID) &&
1264 (xmlScanIDAttributeDecl(NULL, elemDef) != 0))
1265 VERROR(ctxt->userData,
1266 "Element %s has too may ID attributes defined : %s\n",
1267 elem, name);
1268 ret->nexth = elemDef->attributes;
1269 elemDef->attributes = ret;
1270 }
1271
1272 /*
1273 * Link it to the Dtd
1274 */
1275 ret->parent = dtd;
1276 ret->doc = dtd->doc;
1277 if (dtd->last == NULL) {
1278 dtd->children = dtd->last = (xmlNodePtr) ret;
1279 } else {
1280 dtd->last->next = (xmlNodePtr) ret;
1281 ret->prev = dtd->last;
1282 dtd->last = (xmlNodePtr) ret;
1283 }
1284 return(ret);
1285}
1286
1287/**
1288 * xmlFreeAttributeTable:
1289 * @table: An attribute table
1290 *
1291 * Deallocate the memory used by an entities hash table.
1292 */
1293void
1294xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1295 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1296}
1297
1298/**
1299 * xmlCopyAttribute:
1300 * @attr: An attribute
1301 *
1302 * Build a copy of an attribute.
1303 *
1304 * Returns the new xmlAttributePtr or NULL in case of error.
1305 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001306static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001307xmlCopyAttribute(xmlAttributePtr attr) {
1308 xmlAttributePtr cur;
1309
1310 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1311 if (cur == NULL) {
1312 xmlGenericError(xmlGenericErrorContext,
1313 "xmlCopyAttribute: out of memory !\n");
1314 return(NULL);
1315 }
1316 memset(cur, 0, sizeof(xmlAttribute));
1317 cur->atype = attr->atype;
1318 cur->def = attr->def;
1319 cur->tree = xmlCopyEnumeration(attr->tree);
1320 if (attr->elem != NULL)
1321 cur->elem = xmlStrdup(attr->elem);
1322 if (attr->name != NULL)
1323 cur->name = xmlStrdup(attr->name);
1324 if (attr->defaultValue != NULL)
1325 cur->defaultValue = xmlStrdup(attr->defaultValue);
1326 return(cur);
1327}
1328
1329/**
1330 * xmlCopyAttributeTable:
1331 * @table: An attribute table
1332 *
1333 * Build a copy of an attribute table.
1334 *
1335 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1336 */
1337xmlAttributeTablePtr
1338xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1339 return((xmlAttributeTablePtr) xmlHashCopy(table,
1340 (xmlHashCopier) xmlCopyAttribute));
1341}
1342
1343/**
1344 * xmlDumpAttributeDecl:
1345 * @buf: the XML buffer output
1346 * @attr: An attribute declaration
1347 *
1348 * This will dump the content of the attribute declaration as an XML
1349 * DTD definition
1350 */
1351void
1352xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1353 xmlBufferWriteChar(buf, "<!ATTLIST ");
1354 xmlBufferWriteCHAR(buf, attr->elem);
1355 xmlBufferWriteChar(buf, " ");
1356 if (attr->prefix != NULL) {
1357 xmlBufferWriteCHAR(buf, attr->prefix);
1358 xmlBufferWriteChar(buf, ":");
1359 }
1360 xmlBufferWriteCHAR(buf, attr->name);
1361 switch (attr->atype) {
1362 case XML_ATTRIBUTE_CDATA:
1363 xmlBufferWriteChar(buf, " CDATA");
1364 break;
1365 case XML_ATTRIBUTE_ID:
1366 xmlBufferWriteChar(buf, " ID");
1367 break;
1368 case XML_ATTRIBUTE_IDREF:
1369 xmlBufferWriteChar(buf, " IDREF");
1370 break;
1371 case XML_ATTRIBUTE_IDREFS:
1372 xmlBufferWriteChar(buf, " IDREFS");
1373 break;
1374 case XML_ATTRIBUTE_ENTITY:
1375 xmlBufferWriteChar(buf, " ENTITY");
1376 break;
1377 case XML_ATTRIBUTE_ENTITIES:
1378 xmlBufferWriteChar(buf, " ENTITIES");
1379 break;
1380 case XML_ATTRIBUTE_NMTOKEN:
1381 xmlBufferWriteChar(buf, " NMTOKEN");
1382 break;
1383 case XML_ATTRIBUTE_NMTOKENS:
1384 xmlBufferWriteChar(buf, " NMTOKENS");
1385 break;
1386 case XML_ATTRIBUTE_ENUMERATION:
1387 xmlBufferWriteChar(buf, " (");
1388 xmlDumpEnumeration(buf, attr->tree);
1389 break;
1390 case XML_ATTRIBUTE_NOTATION:
1391 xmlBufferWriteChar(buf, " NOTATION (");
1392 xmlDumpEnumeration(buf, attr->tree);
1393 break;
1394 default:
1395 xmlGenericError(xmlGenericErrorContext,
1396 "xmlDumpAttributeTable: internal: unknown type %d\n",
1397 attr->atype);
1398 }
1399 switch (attr->def) {
1400 case XML_ATTRIBUTE_NONE:
1401 break;
1402 case XML_ATTRIBUTE_REQUIRED:
1403 xmlBufferWriteChar(buf, " #REQUIRED");
1404 break;
1405 case XML_ATTRIBUTE_IMPLIED:
1406 xmlBufferWriteChar(buf, " #IMPLIED");
1407 break;
1408 case XML_ATTRIBUTE_FIXED:
1409 xmlBufferWriteChar(buf, " #FIXED");
1410 break;
1411 default:
1412 xmlGenericError(xmlGenericErrorContext,
1413 "xmlDumpAttributeTable: internal: unknown default %d\n",
1414 attr->def);
1415 }
1416 if (attr->defaultValue != NULL) {
1417 xmlBufferWriteChar(buf, " ");
1418 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1419 }
1420 xmlBufferWriteChar(buf, ">\n");
1421}
1422
1423/**
1424 * xmlDumpAttributeTable:
1425 * @buf: the XML buffer output
1426 * @table: An attribute table
1427 *
1428 * This will dump the content of the attribute table as an XML DTD definition
1429 */
1430void
1431xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1432 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1433}
1434
1435/************************************************************************
1436 * *
1437 * NOTATIONs *
1438 * *
1439 ************************************************************************/
1440/**
1441 * xmlCreateNotationTable:
1442 *
1443 * create and initialize an empty notation hash table.
1444 *
1445 * Returns the xmlNotationTablePtr just created or NULL in case
1446 * of error.
1447 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001448static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001449xmlCreateNotationTable(void) {
1450 return(xmlHashCreate(0));
1451}
1452
1453/**
1454 * xmlFreeNotation:
1455 * @not: A notation
1456 *
1457 * Deallocate the memory used by an notation definition
1458 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001459static void
Owen Taylor3473f882001-02-23 17:55:21 +00001460xmlFreeNotation(xmlNotationPtr nota) {
1461 if (nota == NULL) return;
1462 if (nota->name != NULL)
1463 xmlFree((xmlChar *) nota->name);
1464 if (nota->PublicID != NULL)
1465 xmlFree((xmlChar *) nota->PublicID);
1466 if (nota->SystemID != NULL)
1467 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001468 xmlFree(nota);
1469}
1470
1471
1472/**
1473 * xmlAddNotationDecl:
1474 * @dtd: pointer to the DTD
1475 * @ctxt: the validation context
1476 * @name: the entity name
1477 * @PublicID: the public identifier or NULL
1478 * @SystemID: the system identifier or NULL
1479 *
1480 * Register a new notation declaration
1481 *
1482 * Returns NULL if not, othervise the entity
1483 */
1484xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001485xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001486 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001487 const xmlChar *PublicID, const xmlChar *SystemID) {
1488 xmlNotationPtr ret;
1489 xmlNotationTablePtr table;
1490
1491 if (dtd == NULL) {
1492 xmlGenericError(xmlGenericErrorContext,
1493 "xmlAddNotationDecl: dtd == NULL\n");
1494 return(NULL);
1495 }
1496 if (name == NULL) {
1497 xmlGenericError(xmlGenericErrorContext,
1498 "xmlAddNotationDecl: name == NULL\n");
1499 return(NULL);
1500 }
1501 if ((PublicID == NULL) && (SystemID == NULL)) {
1502 xmlGenericError(xmlGenericErrorContext,
1503 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
1504 }
1505
1506 /*
1507 * Create the Notation table if needed.
1508 */
1509 table = (xmlNotationTablePtr) dtd->notations;
1510 if (table == NULL)
1511 dtd->notations = table = xmlCreateNotationTable();
1512 if (table == NULL) {
1513 xmlGenericError(xmlGenericErrorContext,
1514 "xmlAddNotationDecl: Table creation failed!\n");
1515 return(NULL);
1516 }
1517
1518 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1519 if (ret == NULL) {
1520 xmlGenericError(xmlGenericErrorContext,
1521 "xmlAddNotationDecl: out of memory\n");
1522 return(NULL);
1523 }
1524 memset(ret, 0, sizeof(xmlNotation));
1525
1526 /*
1527 * fill the structure.
1528 */
1529 ret->name = xmlStrdup(name);
1530 if (SystemID != NULL)
1531 ret->SystemID = xmlStrdup(SystemID);
1532 if (PublicID != NULL)
1533 ret->PublicID = xmlStrdup(PublicID);
1534
1535 /*
1536 * Validity Check:
1537 * Check the DTD for previous declarations of the ATTLIST
1538 */
1539 if (xmlHashAddEntry(table, name, ret)) {
1540 xmlGenericError(xmlGenericErrorContext,
1541 "xmlAddNotationDecl: %s already defined\n", name);
1542 xmlFreeNotation(ret);
1543 return(NULL);
1544 }
1545 return(ret);
1546}
1547
1548/**
1549 * xmlFreeNotationTable:
1550 * @table: An notation table
1551 *
1552 * Deallocate the memory used by an entities hash table.
1553 */
1554void
1555xmlFreeNotationTable(xmlNotationTablePtr table) {
1556 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1557}
1558
1559/**
1560 * xmlCopyNotation:
1561 * @nota: A notation
1562 *
1563 * Build a copy of a notation.
1564 *
1565 * Returns the new xmlNotationPtr or NULL in case of error.
1566 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001567static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001568xmlCopyNotation(xmlNotationPtr nota) {
1569 xmlNotationPtr cur;
1570
1571 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1572 if (cur == NULL) {
1573 xmlGenericError(xmlGenericErrorContext,
1574 "xmlCopyNotation: out of memory !\n");
1575 return(NULL);
1576 }
1577 if (nota->name != NULL)
1578 cur->name = xmlStrdup(nota->name);
1579 else
1580 cur->name = NULL;
1581 if (nota->PublicID != NULL)
1582 cur->PublicID = xmlStrdup(nota->PublicID);
1583 else
1584 cur->PublicID = NULL;
1585 if (nota->SystemID != NULL)
1586 cur->SystemID = xmlStrdup(nota->SystemID);
1587 else
1588 cur->SystemID = NULL;
1589 return(cur);
1590}
1591
1592/**
1593 * xmlCopyNotationTable:
1594 * @table: A notation table
1595 *
1596 * Build a copy of a notation table.
1597 *
1598 * Returns the new xmlNotationTablePtr or NULL in case of error.
1599 */
1600xmlNotationTablePtr
1601xmlCopyNotationTable(xmlNotationTablePtr table) {
1602 return((xmlNotationTablePtr) xmlHashCopy(table,
1603 (xmlHashCopier) xmlCopyNotation));
1604}
1605
1606/**
1607 * xmlDumpNotationDecl:
1608 * @buf: the XML buffer output
1609 * @nota: A notation declaration
1610 *
1611 * This will dump the content the notation declaration as an XML DTD definition
1612 */
1613void
1614xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1615 xmlBufferWriteChar(buf, "<!NOTATION ");
1616 xmlBufferWriteCHAR(buf, nota->name);
1617 if (nota->PublicID != NULL) {
1618 xmlBufferWriteChar(buf, " PUBLIC ");
1619 xmlBufferWriteQuotedString(buf, nota->PublicID);
1620 if (nota->SystemID != NULL) {
1621 xmlBufferWriteChar(buf, " ");
1622 xmlBufferWriteCHAR(buf, nota->SystemID);
1623 }
1624 } else {
1625 xmlBufferWriteChar(buf, " SYSTEM ");
1626 xmlBufferWriteCHAR(buf, nota->SystemID);
1627 }
1628 xmlBufferWriteChar(buf, " >\n");
1629}
1630
1631/**
1632 * xmlDumpNotationTable:
1633 * @buf: the XML buffer output
1634 * @table: A notation table
1635 *
1636 * This will dump the content of the notation table as an XML DTD definition
1637 */
1638void
1639xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1640 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1641}
1642
1643/************************************************************************
1644 * *
1645 * IDs *
1646 * *
1647 ************************************************************************/
1648/**
1649 * xmlCreateIDTable:
1650 *
1651 * create and initialize an empty id hash table.
1652 *
1653 * Returns the xmlIDTablePtr just created or NULL in case
1654 * of error.
1655 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001656static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001657xmlCreateIDTable(void) {
1658 return(xmlHashCreate(0));
1659}
1660
1661/**
1662 * xmlFreeID:
1663 * @not: A id
1664 *
1665 * Deallocate the memory used by an id definition
1666 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001667static void
Owen Taylor3473f882001-02-23 17:55:21 +00001668xmlFreeID(xmlIDPtr id) {
1669 if (id == NULL) return;
1670 if (id->value != NULL)
1671 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001672 xmlFree(id);
1673}
1674
1675/**
1676 * xmlAddID:
1677 * @ctxt: the validation context
1678 * @doc: pointer to the document
1679 * @value: the value name
1680 * @attr: the attribute holding the ID
1681 *
1682 * Register a new id declaration
1683 *
1684 * Returns NULL if not, othervise the new xmlIDPtr
1685 */
1686xmlIDPtr
1687xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1688 xmlAttrPtr attr) {
1689 xmlIDPtr ret;
1690 xmlIDTablePtr table;
1691
1692 if (doc == NULL) {
1693 xmlGenericError(xmlGenericErrorContext,
1694 "xmlAddIDDecl: doc == NULL\n");
1695 return(NULL);
1696 }
1697 if (value == NULL) {
1698 xmlGenericError(xmlGenericErrorContext,
1699 "xmlAddIDDecl: value == NULL\n");
1700 return(NULL);
1701 }
1702 if (attr == NULL) {
1703 xmlGenericError(xmlGenericErrorContext,
1704 "xmlAddIDDecl: attr == NULL\n");
1705 return(NULL);
1706 }
1707
1708 /*
1709 * Create the ID table if needed.
1710 */
1711 table = (xmlIDTablePtr) doc->ids;
1712 if (table == NULL)
1713 doc->ids = table = xmlCreateIDTable();
1714 if (table == NULL) {
1715 xmlGenericError(xmlGenericErrorContext,
1716 "xmlAddID: Table creation failed!\n");
1717 return(NULL);
1718 }
1719
1720 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1721 if (ret == NULL) {
1722 xmlGenericError(xmlGenericErrorContext,
1723 "xmlAddID: out of memory\n");
1724 return(NULL);
1725 }
1726
1727 /*
1728 * fill the structure.
1729 */
1730 ret->value = xmlStrdup(value);
1731 ret->attr = attr;
1732
1733 if (xmlHashAddEntry(table, value, ret) < 0) {
1734 /*
1735 * The id is already defined in this Dtd.
1736 */
1737 VERROR(ctxt->userData, "ID %s already defined\n", value);
1738 xmlFreeID(ret);
1739 return(NULL);
1740 }
1741 return(ret);
1742}
1743
1744/**
1745 * xmlFreeIDTable:
1746 * @table: An id table
1747 *
1748 * Deallocate the memory used by an ID hash table.
1749 */
1750void
1751xmlFreeIDTable(xmlIDTablePtr table) {
1752 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1753}
1754
1755/**
1756 * xmlIsID:
1757 * @doc: the document
1758 * @elem: the element carrying the attribute
1759 * @attr: the attribute
1760 *
1761 * Determine whether an attribute is of type ID. In case we have Dtd(s)
1762 * then this is simple, otherwise we use an heuristic: name ID (upper
1763 * or lowercase).
1764 *
1765 * Returns 0 or 1 depending on the lookup result
1766 */
1767int
1768xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1769 if (doc == NULL) return(0);
1770 if (attr == NULL) return(0);
1771 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1772 return(0);
1773 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1774 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1775 (xmlStrEqual(BAD_CAST "name", attr->name)))
1776 return(1);
1777 return(0);
1778 } else {
1779 xmlAttributePtr attrDecl;
1780
1781 if (elem == NULL) return(0);
1782 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1783 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1784 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1785 attr->name);
1786
1787 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1788 return(1);
1789 }
1790 return(0);
1791}
1792
1793/**
1794 * xmlRemoveID
1795 * @doc: the document
1796 * @attr: the attribute
1797 *
1798 * Remove the given attribute from the ID table maintained internally.
1799 *
1800 * Returns -1 if the lookup failed and 0 otherwise
1801 */
1802int
1803xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1804 xmlAttrPtr cur;
1805 xmlIDTablePtr table;
1806 xmlChar *ID;
1807
1808 if (doc == NULL) return(-1);
1809 if (attr == NULL) return(-1);
1810 table = (xmlIDTablePtr) doc->ids;
1811 if (table == NULL)
1812 return(-1);
1813
1814 if (attr == NULL)
1815 return(-1);
1816 ID = xmlNodeListGetString(doc, attr->children, 1);
1817 if (ID == NULL)
1818 return(-1);
1819 cur = xmlHashLookup(table, ID);
1820 if (cur != attr) {
1821 xmlFree(ID);
1822 return(-1);
1823 }
1824 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1825 xmlFree(ID);
1826 return(0);
1827}
1828
1829/**
1830 * xmlGetID:
1831 * @doc: pointer to the document
1832 * @ID: the ID value
1833 *
1834 * Search the attribute declaring the given ID
1835 *
1836 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1837 */
1838xmlAttrPtr
1839xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
1840 xmlIDTablePtr table;
1841 xmlIDPtr id;
1842
1843 if (doc == NULL) {
1844 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
1845 return(NULL);
1846 }
1847
1848 if (ID == NULL) {
1849 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
1850 return(NULL);
1851 }
1852
1853 table = (xmlIDTablePtr) doc->ids;
1854 if (table == NULL)
1855 return(NULL);
1856
1857 id = xmlHashLookup(table, ID);
1858 if (id == NULL)
1859 return(NULL);
1860 return(id->attr);
1861}
1862
1863/************************************************************************
1864 * *
1865 * Refs *
1866 * *
1867 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00001868typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00001869{
1870 xmlListPtr l;
1871 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00001872} xmlRemoveMemo;
1873
1874typedef xmlRemoveMemo *xmlRemoveMemoPtr;
1875
1876typedef struct xmlValidateMemo_t
1877{
1878 xmlValidCtxtPtr ctxt;
1879 const xmlChar *name;
1880} xmlValidateMemo;
1881
1882typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00001883
1884/**
1885 * xmlCreateRefTable:
1886 *
1887 * create and initialize an empty ref hash table.
1888 *
1889 * Returns the xmlRefTablePtr just created or NULL in case
1890 * of error.
1891 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001892static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001893xmlCreateRefTable(void) {
1894 return(xmlHashCreate(0));
1895}
1896
1897/**
1898 * xmlFreeRef:
1899 * @lk: A list link
1900 *
1901 * Deallocate the memory used by a ref definition
1902 */
1903static void
1904xmlFreeRef(xmlLinkPtr lk) {
1905 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
1906 if (ref == NULL) return;
1907 if (ref->value != NULL)
1908 xmlFree((xmlChar *)ref->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001909 xmlFree(ref);
1910}
1911
1912/**
1913 * xmlFreeRefList:
1914 * @list_ref: A list of references.
1915 *
1916 * Deallocate the memory used by a list of references
1917 */
1918static void
1919xmlFreeRefList(xmlListPtr list_ref) {
1920 if (list_ref == NULL) return;
1921 xmlListDelete(list_ref);
1922}
1923
1924/**
1925 * xmlWalkRemoveRef:
1926 * @data: Contents of current link
1927 * @user: Value supplied by the user
1928 *
1929 * Return 0 to abort the walk or 1 to continue
1930 */
1931static int
1932xmlWalkRemoveRef(const void *data, const void *user)
1933{
1934 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
Daniel Veillard8730c562001-02-26 10:49:57 +00001935 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
1936 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00001937
1938 if (attr0 == attr1) { /* Matched: remove and terminate walk */
1939 xmlListRemoveFirst(ref_list, (void *)data);
1940 return 0;
1941 }
1942 return 1;
1943}
1944
1945/**
1946 * xmlAddRef:
1947 * @ctxt: the validation context
1948 * @doc: pointer to the document
1949 * @value: the value name
1950 * @attr: the attribute holding the Ref
1951 *
1952 * Register a new ref declaration
1953 *
1954 * Returns NULL if not, othervise the new xmlRefPtr
1955 */
1956xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001957xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00001958 xmlAttrPtr attr) {
1959 xmlRefPtr ret;
1960 xmlRefTablePtr table;
1961 xmlListPtr ref_list;
1962
1963 if (doc == NULL) {
1964 xmlGenericError(xmlGenericErrorContext,
1965 "xmlAddRefDecl: doc == NULL\n");
1966 return(NULL);
1967 }
1968 if (value == NULL) {
1969 xmlGenericError(xmlGenericErrorContext,
1970 "xmlAddRefDecl: value == NULL\n");
1971 return(NULL);
1972 }
1973 if (attr == NULL) {
1974 xmlGenericError(xmlGenericErrorContext,
1975 "xmlAddRefDecl: attr == NULL\n");
1976 return(NULL);
1977 }
1978
1979 /*
1980 * Create the Ref table if needed.
1981 */
1982 table = (xmlRefTablePtr) doc->refs;
1983 if (table == NULL)
1984 doc->refs = table = xmlCreateRefTable();
1985 if (table == NULL) {
1986 xmlGenericError(xmlGenericErrorContext,
1987 "xmlAddRef: Table creation failed!\n");
1988 return(NULL);
1989 }
1990
1991 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
1992 if (ret == NULL) {
1993 xmlGenericError(xmlGenericErrorContext,
1994 "xmlAddRef: out of memory\n");
1995 return(NULL);
1996 }
1997
1998 /*
1999 * fill the structure.
2000 */
2001 ret->value = xmlStrdup(value);
2002 ret->attr = attr;
2003
2004 /* To add a reference :-
2005 * References are maintained as a list of references,
2006 * Lookup the entry, if no entry create new nodelist
2007 * Add the owning node to the NodeList
2008 * Return the ref
2009 */
2010
2011 if(NULL == (ref_list = xmlHashLookup(table, value))) {
2012 if(NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2013 xmlGenericError(xmlGenericErrorContext,
2014 "xmlAddRef: Reference list creation failed!\n");
2015 return(NULL);
2016 }
2017 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2018 xmlListDelete(ref_list);
2019 xmlGenericError(xmlGenericErrorContext,
2020 "xmlAddRef: Reference list insertion failed!\n");
2021 return(NULL);
2022 }
2023 }
2024 xmlListInsert(ref_list, ret);
2025 return(ret);
2026}
2027
2028/**
2029 * xmlFreeRefTable:
2030 * @table: An ref table
2031 *
2032 * Deallocate the memory used by an Ref hash table.
2033 */
2034void
2035xmlFreeRefTable(xmlRefTablePtr table) {
2036 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
2037}
2038
2039/**
2040 * xmlIsRef:
2041 * @doc: the document
2042 * @elem: the element carrying the attribute
2043 * @attr: the attribute
2044 *
2045 * Determine whether an attribute is of type Ref. In case we have Dtd(s)
2046 * then this is simple, otherwise we use an heuristic: name Ref (upper
2047 * or lowercase).
2048 *
2049 * Returns 0 or 1 depending on the lookup result
2050 */
2051int
2052xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2053 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2054 return(0);
2055 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2056 /* TODO @@@ */
2057 return(0);
2058 } else {
2059 xmlAttributePtr attrDecl;
2060
2061 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2062 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2063 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2064 attr->name);
2065
2066 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_IDREF))
2067 return(1);
2068 }
2069 return(0);
2070}
2071
2072/**
2073 * xmlRemoveRef
2074 * @doc: the document
2075 * @attr: the attribute
2076 *
2077 * Remove the given attribute from the Ref table maintained internally.
2078 *
2079 * Returns -1 if the lookup failed and 0 otherwise
2080 */
2081int
2082xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
2083 xmlListPtr ref_list;
2084 xmlRefTablePtr table;
2085 xmlChar *ID;
Daniel Veillard8730c562001-02-26 10:49:57 +00002086 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002087
2088 if (doc == NULL) return(-1);
2089 if (attr == NULL) return(-1);
2090 table = (xmlRefTablePtr) doc->refs;
2091 if (table == NULL)
2092 return(-1);
2093
2094 if (attr == NULL)
2095 return(-1);
2096 ID = xmlNodeListGetString(doc, attr->children, 1);
2097 if (ID == NULL)
2098 return(-1);
2099 ref_list = xmlHashLookup(table, ID);
2100
2101 if(ref_list == NULL) {
2102 xmlFree(ID);
2103 return (-1);
2104 }
2105 /* At this point, ref_list refers to a list of references which
2106 * have the same key as the supplied attr. Our list of references
2107 * is ordered by reference address and we don't have that information
2108 * here to use when removing. We'll have to walk the list and
2109 * check for a matching attribute, when we find one stop the walk
2110 * and remove the entry.
2111 * The list is ordered by reference, so that means we don't have the
2112 * key. Passing the list and the reference to the walker means we
2113 * will have enough data to be able to remove the entry.
2114 */
2115 target.l = ref_list;
2116 target.ap = attr;
2117
2118 /* Remove the supplied attr from our list */
2119 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
2120
2121 /*If the list is empty then remove the list entry in the hash */
2122 if (xmlListEmpty(ref_list))
2123 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2124 xmlFreeRefList);
2125 xmlFree(ID);
2126 return(0);
2127}
2128
2129/**
2130 * xmlGetRefs:
2131 * @doc: pointer to the document
2132 * @ID: the ID value
2133 *
2134 * Find the set of references for the supplied ID.
2135 *
2136 * Returns NULL if not found, otherwise node set for the ID.
2137 */
2138xmlListPtr
2139xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
2140 xmlRefTablePtr table;
2141
2142 if (doc == NULL) {
2143 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: doc == NULL\n");
2144 return(NULL);
2145 }
2146
2147 if (ID == NULL) {
2148 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: ID == NULL\n");
2149 return(NULL);
2150 }
2151
2152 table = (xmlRefTablePtr) doc->refs;
2153 if (table == NULL)
2154 return(NULL);
2155
2156 return (xmlHashLookup(table, ID));
2157}
2158
2159/************************************************************************
2160 * *
2161 * Routines for validity checking *
2162 * *
2163 ************************************************************************/
2164
2165/**
2166 * xmlGetDtdElementDesc:
2167 * @dtd: a pointer to the DtD to search
2168 * @name: the element name
2169 *
2170 * Search the Dtd for the description of this element
2171 *
2172 * returns the xmlElementPtr if found or NULL
2173 */
2174
2175xmlElementPtr
2176xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2177 xmlElementTablePtr table;
2178 xmlElementPtr cur;
2179 xmlChar *uqname = NULL, *prefix = NULL;
2180
2181 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002182 if (dtd->elements == NULL)
2183 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002184 table = (xmlElementTablePtr) dtd->elements;
2185
2186 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002187 if (uqname != NULL)
2188 name = uqname;
2189 cur = xmlHashLookup2(table, name, prefix);
2190 if (prefix != NULL) xmlFree(prefix);
2191 if (uqname != NULL) xmlFree(uqname);
2192 return(cur);
2193}
2194/**
2195 * xmlGetDtdElementDesc2:
2196 * @dtd: a pointer to the DtD to search
2197 * @name: the element name
2198 * @create: create an empty description if not found
2199 *
2200 * Search the Dtd for the description of this element
2201 *
2202 * returns the xmlElementPtr if found or NULL
2203 */
2204
2205xmlElementPtr
2206xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2207 xmlElementTablePtr table;
2208 xmlElementPtr cur;
2209 xmlChar *uqname = NULL, *prefix = NULL;
2210
2211 if (dtd == NULL) return(NULL);
2212 if (dtd->elements == NULL) {
2213 if (!create)
2214 return(NULL);
2215 /*
2216 * Create the Element table if needed.
2217 */
2218 table = (xmlElementTablePtr) dtd->elements;
2219 if (table == NULL) {
2220 table = xmlCreateElementTable();
2221 dtd->elements = (void *) table;
2222 }
2223 if (table == NULL) {
2224 xmlGenericError(xmlGenericErrorContext,
2225 "xmlGetDtdElementDesc: Table creation failed!\n");
2226 return(NULL);
2227 }
2228 }
2229 table = (xmlElementTablePtr) dtd->elements;
2230
2231 uqname = xmlSplitQName2(name, &prefix);
2232 if (uqname != NULL)
2233 name = uqname;
2234 cur = xmlHashLookup2(table, name, prefix);
2235 if ((cur == NULL) && (create)) {
2236 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2237 if (cur == NULL) {
2238 xmlGenericError(xmlGenericErrorContext,
2239 "xmlGetDtdElementDesc: out of memory\n");
2240 return(NULL);
2241 }
2242 memset(cur, 0, sizeof(xmlElement));
2243 cur->type = XML_ELEMENT_DECL;
2244
2245 /*
2246 * fill the structure.
2247 */
2248 cur->name = xmlStrdup(name);
2249 cur->prefix = xmlStrdup(prefix);
2250 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2251
2252 xmlHashAddEntry2(table, name, prefix, cur);
2253 }
2254 if (prefix != NULL) xmlFree(prefix);
2255 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002256 return(cur);
2257}
2258
2259/**
2260 * xmlGetDtdQElementDesc:
2261 * @dtd: a pointer to the DtD to search
2262 * @name: the element name
2263 * @prefix: the element namespace prefix
2264 *
2265 * Search the Dtd for the description of this element
2266 *
2267 * returns the xmlElementPtr if found or NULL
2268 */
2269
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002270static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002271xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2272 const xmlChar *prefix) {
2273 xmlElementTablePtr table;
2274
2275 if (dtd == NULL) return(NULL);
2276 if (dtd->elements == NULL) return(NULL);
2277 table = (xmlElementTablePtr) dtd->elements;
2278
2279 return(xmlHashLookup2(table, name, prefix));
2280}
2281
2282/**
2283 * xmlGetDtdAttrDesc:
2284 * @dtd: a pointer to the DtD to search
2285 * @elem: the element name
2286 * @name: the attribute name
2287 *
2288 * Search the Dtd for the description of this attribute on
2289 * this element.
2290 *
2291 * returns the xmlAttributePtr if found or NULL
2292 */
2293
2294xmlAttributePtr
2295xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2296 xmlAttributeTablePtr table;
2297 xmlAttributePtr cur;
2298 xmlChar *uqname = NULL, *prefix = NULL;
2299
2300 if (dtd == NULL) return(NULL);
2301 if (dtd->attributes == NULL) return(NULL);
2302
2303 table = (xmlAttributeTablePtr) dtd->attributes;
2304 if (table == NULL)
2305 return(NULL);
2306
2307 uqname = xmlSplitQName2(name, &prefix);
2308
2309 if (uqname != NULL) {
2310 cur = xmlHashLookup3(table, uqname, prefix, elem);
2311 if (prefix != NULL) xmlFree(prefix);
2312 if (uqname != NULL) xmlFree(uqname);
2313 } else
2314 cur = xmlHashLookup3(table, name, NULL, elem);
2315 return(cur);
2316}
2317
2318/**
2319 * xmlGetDtdQAttrDesc:
2320 * @dtd: a pointer to the DtD to search
2321 * @elem: the element name
2322 * @name: the attribute name
2323 * @prefix: the attribute namespace prefix
2324 *
2325 * Search the Dtd for the description of this qualified attribute on
2326 * this element.
2327 *
2328 * returns the xmlAttributePtr if found or NULL
2329 */
2330
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002331static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002332xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2333 const xmlChar *prefix) {
2334 xmlAttributeTablePtr table;
2335
2336 if (dtd == NULL) return(NULL);
2337 if (dtd->attributes == NULL) return(NULL);
2338 table = (xmlAttributeTablePtr) dtd->attributes;
2339
2340 return(xmlHashLookup3(table, name, prefix, elem));
2341}
2342
2343/**
2344 * xmlGetDtdNotationDesc:
2345 * @dtd: a pointer to the DtD to search
2346 * @name: the notation name
2347 *
2348 * Search the Dtd for the description of this notation
2349 *
2350 * returns the xmlNotationPtr if found or NULL
2351 */
2352
2353xmlNotationPtr
2354xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2355 xmlNotationTablePtr table;
2356
2357 if (dtd == NULL) return(NULL);
2358 if (dtd->notations == NULL) return(NULL);
2359 table = (xmlNotationTablePtr) dtd->notations;
2360
2361 return(xmlHashLookup(table, name));
2362}
2363
2364/**
2365 * xmlValidateNotationUse:
2366 * @ctxt: the validation context
2367 * @doc: the document
2368 * @notationName: the notation name to check
2369 *
2370 * Validate that the given mame match a notation declaration.
2371 * - [ VC: Notation Declared ]
2372 *
2373 * returns 1 if valid or 0 otherwise
2374 */
2375
2376int
2377xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2378 const xmlChar *notationName) {
2379 xmlNotationPtr notaDecl;
2380 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2381
2382 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2383 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2384 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2385
2386 if (notaDecl == NULL) {
2387 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2388 notationName);
2389 return(0);
2390 }
2391 return(1);
2392}
2393
2394/**
2395 * xmlIsMixedElement
2396 * @doc: the document
2397 * @name: the element name
2398 *
2399 * Search in the DtDs whether an element accept Mixed content (or ANY)
2400 * basically if it is supposed to accept text childs
2401 *
2402 * returns 0 if no, 1 if yes, and -1 if no element description is available
2403 */
2404
2405int
2406xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2407 xmlElementPtr elemDecl;
2408
2409 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2410
2411 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2412 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2413 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2414 if (elemDecl == NULL) return(-1);
2415 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002416 case XML_ELEMENT_TYPE_UNDEFINED:
2417 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002418 case XML_ELEMENT_TYPE_ELEMENT:
2419 return(0);
2420 case XML_ELEMENT_TYPE_EMPTY:
2421 /*
2422 * return 1 for EMPTY since we want VC error to pop up
2423 * on <empty> </empty> for example
2424 */
2425 case XML_ELEMENT_TYPE_ANY:
2426 case XML_ELEMENT_TYPE_MIXED:
2427 return(1);
2428 }
2429 return(1);
2430}
2431
2432/**
2433 * xmlValidateNameValue:
2434 * @value: an Name value
2435 *
2436 * Validate that the given value match Name production
2437 *
2438 * returns 1 if valid or 0 otherwise
2439 */
2440
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002441static int
Owen Taylor3473f882001-02-23 17:55:21 +00002442xmlValidateNameValue(const xmlChar *value) {
2443 const xmlChar *cur;
2444
2445 if (value == NULL) return(0);
2446 cur = value;
2447
2448 if (!IS_LETTER(*cur) && (*cur != '_') &&
2449 (*cur != ':')) {
2450 return(0);
2451 }
2452
2453 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2454 (*cur == '.') || (*cur == '-') ||
2455 (*cur == '_') || (*cur == ':') ||
2456 (IS_COMBINING(*cur)) ||
2457 (IS_EXTENDER(*cur)))
2458 cur++;
2459
2460 if (*cur != 0) return(0);
2461
2462 return(1);
2463}
2464
2465/**
2466 * xmlValidateNamesValue:
2467 * @value: an Names value
2468 *
2469 * Validate that the given value match Names production
2470 *
2471 * returns 1 if valid or 0 otherwise
2472 */
2473
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002474static int
Owen Taylor3473f882001-02-23 17:55:21 +00002475xmlValidateNamesValue(const xmlChar *value) {
2476 const xmlChar *cur;
2477
2478 if (value == NULL) return(0);
2479 cur = value;
2480
2481 if (!IS_LETTER(*cur) && (*cur != '_') &&
2482 (*cur != ':')) {
2483 return(0);
2484 }
2485
2486 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2487 (*cur == '.') || (*cur == '-') ||
2488 (*cur == '_') || (*cur == ':') ||
2489 (IS_COMBINING(*cur)) ||
2490 (IS_EXTENDER(*cur)))
2491 cur++;
2492
2493 while (IS_BLANK(*cur)) {
2494 while (IS_BLANK(*cur)) cur++;
2495
2496 if (!IS_LETTER(*cur) && (*cur != '_') &&
2497 (*cur != ':')) {
2498 return(0);
2499 }
2500
2501 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2502 (*cur == '.') || (*cur == '-') ||
2503 (*cur == '_') || (*cur == ':') ||
2504 (IS_COMBINING(*cur)) ||
2505 (IS_EXTENDER(*cur)))
2506 cur++;
2507 }
2508
2509 if (*cur != 0) return(0);
2510
2511 return(1);
2512}
2513
2514/**
2515 * xmlValidateNmtokenValue:
2516 * @value: an Mntoken value
2517 *
2518 * Validate that the given value match Nmtoken production
2519 *
2520 * [ VC: Name Token ]
2521 *
2522 * returns 1 if valid or 0 otherwise
2523 */
2524
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002525static int
Owen Taylor3473f882001-02-23 17:55:21 +00002526xmlValidateNmtokenValue(const xmlChar *value) {
2527 const xmlChar *cur;
2528
2529 if (value == NULL) return(0);
2530 cur = value;
2531
2532 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2533 (*cur != '.') && (*cur != '-') &&
2534 (*cur != '_') && (*cur != ':') &&
2535 (!IS_COMBINING(*cur)) &&
2536 (!IS_EXTENDER(*cur)))
2537 return(0);
2538
2539 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2540 (*cur == '.') || (*cur == '-') ||
2541 (*cur == '_') || (*cur == ':') ||
2542 (IS_COMBINING(*cur)) ||
2543 (IS_EXTENDER(*cur)))
2544 cur++;
2545
2546 if (*cur != 0) return(0);
2547
2548 return(1);
2549}
2550
2551/**
2552 * xmlValidateNmtokensValue:
2553 * @value: an Mntokens value
2554 *
2555 * Validate that the given value match Nmtokens production
2556 *
2557 * [ VC: Name Token ]
2558 *
2559 * returns 1 if valid or 0 otherwise
2560 */
2561
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002562static int
Owen Taylor3473f882001-02-23 17:55:21 +00002563xmlValidateNmtokensValue(const xmlChar *value) {
2564 const xmlChar *cur;
2565
2566 if (value == NULL) return(0);
2567 cur = value;
2568
2569 while (IS_BLANK(*cur)) cur++;
2570 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2571 (*cur != '.') && (*cur != '-') &&
2572 (*cur != '_') && (*cur != ':') &&
2573 (!IS_COMBINING(*cur)) &&
2574 (!IS_EXTENDER(*cur)))
2575 return(0);
2576
2577 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2578 (*cur == '.') || (*cur == '-') ||
2579 (*cur == '_') || (*cur == ':') ||
2580 (IS_COMBINING(*cur)) ||
2581 (IS_EXTENDER(*cur)))
2582 cur++;
2583
2584 while (IS_BLANK(*cur)) {
2585 while (IS_BLANK(*cur)) cur++;
2586 if (*cur == 0) return(1);
2587
2588 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2589 (*cur != '.') && (*cur != '-') &&
2590 (*cur != '_') && (*cur != ':') &&
2591 (!IS_COMBINING(*cur)) &&
2592 (!IS_EXTENDER(*cur)))
2593 return(0);
2594
2595 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2596 (*cur == '.') || (*cur == '-') ||
2597 (*cur == '_') || (*cur == ':') ||
2598 (IS_COMBINING(*cur)) ||
2599 (IS_EXTENDER(*cur)))
2600 cur++;
2601 }
2602
2603 if (*cur != 0) return(0);
2604
2605 return(1);
2606}
2607
2608/**
2609 * xmlValidateNotationDecl:
2610 * @ctxt: the validation context
2611 * @doc: a document instance
2612 * @nota: a notation definition
2613 *
2614 * Try to validate a single notation definition
2615 * basically it does the following checks as described by the
2616 * XML-1.0 recommendation:
2617 * - it seems that no validity constraing exist on notation declarations
2618 * But this function get called anyway ...
2619 *
2620 * returns 1 if valid or 0 otherwise
2621 */
2622
2623int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002624xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2625 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002626 int ret = 1;
2627
2628 return(ret);
2629}
2630
2631/**
2632 * xmlValidateAttributeValue:
2633 * @type: an attribute type
2634 * @value: an attribute value
2635 *
2636 * Validate that the given attribute value match the proper production
2637 *
2638 * [ VC: ID ]
2639 * Values of type ID must match the Name production....
2640 *
2641 * [ VC: IDREF ]
2642 * Values of type IDREF must match the Name production, and values
2643 * of type IDREFS must match Names ...
2644 *
2645 * [ VC: Entity Name ]
2646 * Values of type ENTITY must match the Name production, values
2647 * of type ENTITIES must match Names ...
2648 *
2649 * [ VC: Name Token ]
2650 * Values of type NMTOKEN must match the Nmtoken production; values
2651 * of type NMTOKENS must match Nmtokens.
2652 *
2653 * returns 1 if valid or 0 otherwise
2654 */
2655
2656int
2657xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2658 switch (type) {
2659 case XML_ATTRIBUTE_ENTITIES:
2660 case XML_ATTRIBUTE_IDREFS:
2661 return(xmlValidateNamesValue(value));
2662 case XML_ATTRIBUTE_ENTITY:
2663 case XML_ATTRIBUTE_IDREF:
2664 case XML_ATTRIBUTE_ID:
2665 case XML_ATTRIBUTE_NOTATION:
2666 return(xmlValidateNameValue(value));
2667 case XML_ATTRIBUTE_NMTOKENS:
2668 case XML_ATTRIBUTE_ENUMERATION:
2669 return(xmlValidateNmtokensValue(value));
2670 case XML_ATTRIBUTE_NMTOKEN:
2671 return(xmlValidateNmtokenValue(value));
2672 case XML_ATTRIBUTE_CDATA:
2673 break;
2674 }
2675 return(1);
2676}
2677
2678/**
2679 * xmlValidateAttributeValue2:
2680 * @ctxt: the validation context
2681 * @doc: the document
2682 * @name: the attribute name (used for error reporting only)
2683 * @type: the attribute type
2684 * @value: the attribute value
2685 *
2686 * Validate that the given attribute value match a given type.
2687 * This typically cannot be done before having finished parsing
2688 * the subsets.
2689 *
2690 * [ VC: IDREF ]
2691 * Values of type IDREF must match one of the declared IDs
2692 * Values of type IDREFS must match a sequence of the declared IDs
2693 * each Name must match the value of an ID attribute on some element
2694 * in the XML document; i.e. IDREF values must match the value of
2695 * some ID attribute
2696 *
2697 * [ VC: Entity Name ]
2698 * Values of type ENTITY must match one declared entity
2699 * Values of type ENTITIES must match a sequence of declared entities
2700 *
2701 * [ VC: Notation Attributes ]
2702 * all notation names in the declaration must be declared.
2703 *
2704 * returns 1 if valid or 0 otherwise
2705 */
2706
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002707static int
Owen Taylor3473f882001-02-23 17:55:21 +00002708xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2709 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2710 int ret = 1;
2711 switch (type) {
2712 case XML_ATTRIBUTE_IDREFS:
2713 case XML_ATTRIBUTE_IDREF:
2714 case XML_ATTRIBUTE_ID:
2715 case XML_ATTRIBUTE_NMTOKENS:
2716 case XML_ATTRIBUTE_ENUMERATION:
2717 case XML_ATTRIBUTE_NMTOKEN:
2718 case XML_ATTRIBUTE_CDATA:
2719 break;
2720 case XML_ATTRIBUTE_ENTITY: {
2721 xmlEntityPtr ent;
2722
2723 ent = xmlGetDocEntity(doc, value);
2724 if (ent == NULL) {
2725 VERROR(ctxt->userData,
2726 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2727 name, value);
2728 ret = 0;
2729 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2730 VERROR(ctxt->userData,
2731 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2732 name, value);
2733 ret = 0;
2734 }
2735 break;
2736 }
2737 case XML_ATTRIBUTE_ENTITIES: {
2738 xmlChar *dup, *nam = NULL, *cur, save;
2739 xmlEntityPtr ent;
2740
2741 dup = xmlStrdup(value);
2742 if (dup == NULL)
2743 return(0);
2744 cur = dup;
2745 while (*cur != 0) {
2746 nam = cur;
2747 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2748 save = *cur;
2749 *cur = 0;
2750 ent = xmlGetDocEntity(doc, nam);
2751 if (ent == NULL) {
2752 VERROR(ctxt->userData,
2753 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2754 name, nam);
2755 ret = 0;
2756 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2757 VERROR(ctxt->userData,
2758 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2759 name, nam);
2760 ret = 0;
2761 }
2762 if (save == 0)
2763 break;
2764 *cur = save;
2765 while (IS_BLANK(*cur)) cur++;
2766 }
2767 xmlFree(dup);
2768 break;
2769 }
2770 case XML_ATTRIBUTE_NOTATION: {
2771 xmlNotationPtr nota;
2772
2773 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2774 if ((nota == NULL) && (doc->extSubset != NULL))
2775 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2776
2777 if (nota == NULL) {
2778 VERROR(ctxt->userData,
2779 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2780 name, value);
2781 ret = 0;
2782 }
2783 break;
2784 }
2785 }
2786 return(ret);
2787}
2788
2789/**
2790 * xmlValidNormalizeAttributeValue:
2791 * @doc: the document
2792 * @elem: the parent
2793 * @name: the attribute name
2794 * @value: the attribute value
2795 *
2796 * Does the validation related extra step of the normalization of attribute
2797 * values:
2798 *
2799 * If the declared value is not CDATA, then the XML processor must further
2800 * process the normalized attribute value by discarding any leading and
2801 * trailing space (#x20) characters, and by replacing sequences of space
2802 * (#x20) characters by single space (#x20) character.
2803 *
2804 * returns a new normalized string if normalization is needed, NULL otherwise
2805 * the caller must free the returned value.
2806 */
2807
2808xmlChar *
2809xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
2810 const xmlChar *name, const xmlChar *value) {
2811 xmlChar *ret, *dst;
2812 const xmlChar *src;
2813 xmlAttributePtr attrDecl = NULL;
2814
2815 if (doc == NULL) return(NULL);
2816 if (elem == NULL) return(NULL);
2817 if (name == NULL) return(NULL);
2818 if (value == NULL) return(NULL);
2819
2820 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2821 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00002822 snprintf((char *) qname, sizeof(qname), "%s:%s",
2823 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002824 qname[sizeof(qname) - 1] = 0;
2825 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
2826 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2827 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
2828 }
2829 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
2830 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2831 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
2832
2833 if (attrDecl == NULL)
2834 return(NULL);
2835 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
2836 return(NULL);
2837
2838 ret = xmlStrdup(value);
2839 if (ret == NULL)
2840 return(NULL);
2841 src = value;
2842 dst = ret;
2843 while (*src == 0x20) src++;
2844 while (*src != 0) {
2845 if (*src == 0x20) {
2846 while (*src == 0x20) src++;
2847 if (*src != 0)
2848 *dst++ = 0x20;
2849 } else {
2850 *dst++ = *src++;
2851 }
2852 }
2853 *dst = 0;
2854 return(ret);
2855}
2856
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002857static void
Owen Taylor3473f882001-02-23 17:55:21 +00002858xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002859 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002860 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
2861}
2862
2863/**
2864 * xmlValidateAttributeDecl:
2865 * @ctxt: the validation context
2866 * @doc: a document instance
2867 * @attr: an attribute definition
2868 *
2869 * Try to validate a single attribute definition
2870 * basically it does the following checks as described by the
2871 * XML-1.0 recommendation:
2872 * - [ VC: Attribute Default Legal ]
2873 * - [ VC: Enumeration ]
2874 * - [ VC: ID Attribute Default ]
2875 *
2876 * The ID/IDREF uniqueness and matching are done separately
2877 *
2878 * returns 1 if valid or 0 otherwise
2879 */
2880
2881int
2882xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2883 xmlAttributePtr attr) {
2884 int ret = 1;
2885 int val;
2886 CHECK_DTD;
2887 if(attr == NULL) return(1);
2888
2889 /* Attribute Default Legal */
2890 /* Enumeration */
2891 if (attr->defaultValue != NULL) {
2892 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
2893 if (val == 0) {
2894 VERROR(ctxt->userData,
2895 "Syntax of default value for attribute %s on %s is not valid\n",
2896 attr->name, attr->elem);
2897 }
2898 ret &= val;
2899 }
2900
2901 /* ID Attribute Default */
2902 if ((attr->atype == XML_ATTRIBUTE_ID)&&
2903 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
2904 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
2905 VERROR(ctxt->userData,
2906 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
2907 attr->name, attr->elem);
2908 ret = 0;
2909 }
2910
2911 /* One ID per Element Type */
2912 if (attr->atype == XML_ATTRIBUTE_ID) {
2913 int nbId;
2914
2915 /* the trick is taht we parse DtD as their own internal subset */
2916 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
2917 attr->elem);
2918 if (elem != NULL) {
2919 nbId = xmlScanIDAttributeDecl(NULL, elem);
2920 } else {
2921 xmlAttributeTablePtr table;
2922
2923 /*
2924 * The attribute may be declared in the internal subset and the
2925 * element in the external subset.
2926 */
2927 nbId = 0;
2928 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
2929 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
2930 xmlValidateAttributeIdCallback, &nbId);
2931 }
2932 if (nbId > 1) {
2933 VERROR(ctxt->userData,
2934 "Element %s has %d ID attribute defined in the internal subset : %s\n",
2935 attr->elem, nbId, attr->name);
2936 } else if (doc->extSubset != NULL) {
2937 int extId = 0;
2938 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
2939 if (elem != NULL) {
2940 extId = xmlScanIDAttributeDecl(NULL, elem);
2941 }
2942 if (extId > 1) {
2943 VERROR(ctxt->userData,
2944 "Element %s has %d ID attribute defined in the external subset : %s\n",
2945 attr->elem, extId, attr->name);
2946 } else if (extId + nbId > 1) {
2947 VERROR(ctxt->userData,
2948"Element %s has ID attributes defined in the internal and external subset : %s\n",
2949 attr->elem, attr->name);
2950 }
2951 }
2952 }
2953
2954 /* Validity Constraint: Enumeration */
2955 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
2956 xmlEnumerationPtr tree = attr->tree;
2957 while (tree != NULL) {
2958 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
2959 tree = tree->next;
2960 }
2961 if (tree == NULL) {
2962 VERROR(ctxt->userData,
2963"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
2964 attr->defaultValue, attr->name, attr->elem);
2965 ret = 0;
2966 }
2967 }
2968
2969 return(ret);
2970}
2971
2972/**
2973 * xmlValidateElementDecl:
2974 * @ctxt: the validation context
2975 * @doc: a document instance
2976 * @elem: an element definition
2977 *
2978 * Try to validate a single element definition
2979 * basically it does the following checks as described by the
2980 * XML-1.0 recommendation:
2981 * - [ VC: One ID per Element Type ]
2982 * - [ VC: No Duplicate Types ]
2983 * - [ VC: Unique Element Type Declaration ]
2984 *
2985 * returns 1 if valid or 0 otherwise
2986 */
2987
2988int
2989xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2990 xmlElementPtr elem) {
2991 int ret = 1;
2992 xmlElementPtr tst;
2993
2994 CHECK_DTD;
2995
2996 if (elem == NULL) return(1);
2997
2998 /* No Duplicate Types */
2999 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3000 xmlElementContentPtr cur, next;
3001 const xmlChar *name;
3002
3003 cur = elem->content;
3004 while (cur != NULL) {
3005 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3006 if (cur->c1 == NULL) break;
3007 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3008 name = cur->c1->name;
3009 next = cur->c2;
3010 while (next != NULL) {
3011 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3012 if (xmlStrEqual(next->name, name)) {
3013 VERROR(ctxt->userData,
3014 "Definition of %s has duplicate references of %s\n",
3015 elem->name, name);
3016 ret = 0;
3017 }
3018 break;
3019 }
3020 if (next->c1 == NULL) break;
3021 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3022 if (xmlStrEqual(next->c1->name, name)) {
3023 VERROR(ctxt->userData,
3024 "Definition of %s has duplicate references of %s\n",
3025 elem->name, name);
3026 ret = 0;
3027 }
3028 next = next->c2;
3029 }
3030 }
3031 cur = cur->c2;
3032 }
3033 }
3034
3035 /* VC: Unique Element Type Declaration */
3036 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003037 if ((tst != NULL ) && (tst != elem) &&
3038 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003039 VERROR(ctxt->userData, "Redefinition of element %s\n",
3040 elem->name);
3041 ret = 0;
3042 }
3043 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003044 if ((tst != NULL ) && (tst != elem) &&
3045 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003046 VERROR(ctxt->userData, "Redefinition of element %s\n",
3047 elem->name);
3048 ret = 0;
3049 }
3050
Daniel Veillarda10efa82001-04-18 13:09:01 +00003051 /* One ID per Element Type
3052 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003053 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3054 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003055 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003056 return(ret);
3057}
3058
3059/**
3060 * xmlValidateOneAttribute:
3061 * @ctxt: the validation context
3062 * @doc: a document instance
3063 * @elem: an element instance
3064 * @attr: an attribute instance
3065 * @value: the attribute value (without entities processing)
3066 *
3067 * Try to validate a single attribute for an element
3068 * basically it does the following checks as described by the
3069 * XML-1.0 recommendation:
3070 * - [ VC: Attribute Value Type ]
3071 * - [ VC: Fixed Attribute Default ]
3072 * - [ VC: Entity Name ]
3073 * - [ VC: Name Token ]
3074 * - [ VC: ID ]
3075 * - [ VC: IDREF ]
3076 * - [ VC: Entity Name ]
3077 * - [ VC: Notation Attributes ]
3078 *
3079 * The ID/IDREF uniqueness and matching are done separately
3080 *
3081 * returns 1 if valid or 0 otherwise
3082 */
3083
3084int
3085xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3086 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3087 /* xmlElementPtr elemDecl; */
3088 xmlAttributePtr attrDecl = NULL;
3089 int val;
3090 int ret = 1;
3091
3092 CHECK_DTD;
3093 if ((elem == NULL) || (elem->name == NULL)) return(0);
3094 if ((attr == NULL) || (attr->name == NULL)) return(0);
3095
3096 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3097 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003098 snprintf((char *) qname, sizeof(qname), "%s:%s",
3099 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003100 qname[sizeof(qname) - 1] = 0;
3101 if (attr->ns != NULL) {
3102 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3103 attr->name, attr->ns->prefix);
3104 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3105 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3106 attr->name, attr->ns->prefix);
3107 } else {
3108 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3109 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3110 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3111 qname, attr->name);
3112 }
3113 }
3114 if (attrDecl == NULL) {
3115 if (attr->ns != NULL) {
3116 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3117 attr->name, attr->ns->prefix);
3118 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3119 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3120 attr->name, attr->ns->prefix);
3121 } else {
3122 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3123 elem->name, attr->name);
3124 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3125 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3126 elem->name, attr->name);
3127 }
3128 }
3129
3130
3131 /* Validity Constraint: Attribute Value Type */
3132 if (attrDecl == NULL) {
3133 VERROR(ctxt->userData,
3134 "No declaration for attribute %s on element %s\n",
3135 attr->name, elem->name);
3136 return(0);
3137 }
3138 attr->atype = attrDecl->atype;
3139
3140 val = xmlValidateAttributeValue(attrDecl->atype, value);
3141 if (val == 0) {
3142 VERROR(ctxt->userData,
3143 "Syntax of value for attribute %s on %s is not valid\n",
3144 attr->name, elem->name);
3145 ret = 0;
3146 }
3147
3148 /* Validity constraint: Fixed Attribute Default */
3149 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3150 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3151 VERROR(ctxt->userData,
3152 "Value for attribute %s on %s is differnt from default \"%s\"\n",
3153 attr->name, elem->name, attrDecl->defaultValue);
3154 ret = 0;
3155 }
3156 }
3157
3158 /* Validity Constraint: ID uniqueness */
3159 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3160 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3161 ret = 0;
3162 }
3163
3164 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3165 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3166 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3167 ret = 0;
3168 }
3169
3170 /* Validity Constraint: Notation Attributes */
3171 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3172 xmlEnumerationPtr tree = attrDecl->tree;
3173 xmlNotationPtr nota;
3174
3175 /* First check that the given NOTATION was declared */
3176 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3177 if (nota == NULL)
3178 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3179
3180 if (nota == NULL) {
3181 VERROR(ctxt->userData,
3182 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
3183 value, attr->name, elem->name);
3184 ret = 0;
3185 }
3186
3187 /* Second, verify that it's among the list */
3188 while (tree != NULL) {
3189 if (xmlStrEqual(tree->name, value)) break;
3190 tree = tree->next;
3191 }
3192 if (tree == NULL) {
3193 VERROR(ctxt->userData,
3194"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
3195 value, attr->name, elem->name);
3196 ret = 0;
3197 }
3198 }
3199
3200 /* Validity Constraint: Enumeration */
3201 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3202 xmlEnumerationPtr tree = attrDecl->tree;
3203 while (tree != NULL) {
3204 if (xmlStrEqual(tree->name, value)) break;
3205 tree = tree->next;
3206 }
3207 if (tree == NULL) {
3208 VERROR(ctxt->userData,
3209 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3210 value, attr->name, elem->name);
3211 ret = 0;
3212 }
3213 }
3214
3215 /* Fixed Attribute Default */
3216 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3217 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3218 VERROR(ctxt->userData,
3219 "Value for attribute %s on %s must be \"%s\"\n",
3220 attr->name, elem->name, attrDecl->defaultValue);
3221 ret = 0;
3222 }
3223
3224 /* Extra check for the attribute value */
3225 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3226 attrDecl->atype, value);
3227
3228 return(ret);
3229}
3230
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003231#ifndef ALLOW_UNDETERMINISTIC_MODELS
3232
Owen Taylor3473f882001-02-23 17:55:21 +00003233/* Find the next XML_ELEMENT_NODE, subject to the content constraints.
3234 * Return -1 if we found something unexpected, or 1 otherwise.
3235 */
3236
3237static int
3238xmlValidateFindNextElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
3239 xmlElementContentPtr cont)
3240{
Daniel Veillarde356c282001-03-10 12:32:04 +00003241 DEBUG_VALID_MSG("skipping to next element");
3242 while (*child && (*child)->type != XML_ELEMENT_NODE) {
3243 switch ((*child)->type) {
3244 /*
3245 * If there is an entity declared and it's not empty
3246 * Push the current node on the stack and process with the
3247 * entity content.
3248 */
3249 case XML_ENTITY_REF_NODE:
3250 if (((*child)->children != NULL) &&
3251 ((*child)->children->children != NULL)) {
3252 nodeVPush(ctxt, *child);
3253 *child = (*child)->children->children;
3254 continue;
3255 }
3256 break;
Owen Taylor3473f882001-02-23 17:55:21 +00003257
Daniel Veillarde356c282001-03-10 12:32:04 +00003258 /* These things are ignored (skipped) during validation. */
3259 case XML_PI_NODE:
3260 case XML_COMMENT_NODE:
3261 case XML_XINCLUDE_START:
3262 case XML_XINCLUDE_END:
3263 break;
Owen Taylor3473f882001-02-23 17:55:21 +00003264
Daniel Veillarde356c282001-03-10 12:32:04 +00003265 case XML_TEXT_NODE:
3266 if (xmlIsBlankNode(*child)
3267 && (cont->type == XML_ELEMENT_CONTENT_ELEMENT
3268 || cont->type == XML_ELEMENT_CONTENT_SEQ
3269 || cont->type == XML_ELEMENT_CONTENT_OR))
3270 break;
3271 DEBUG_VALID_MSG("failed non-blank");
3272 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003273
Daniel Veillarde356c282001-03-10 12:32:04 +00003274 default:
3275 DEBUG_VALID_MSG("failed unknown type");
3276 return(-1);
3277 }
3278 *child = (*child)->next;
Owen Taylor3473f882001-02-23 17:55:21 +00003279 }
Daniel Veillarde356c282001-03-10 12:32:04 +00003280#ifdef DEBUG_VALID_ALGO
3281 if (*child != NULL) {
3282 DEBUG_VALID_MSG((*child)->name);
3283 }
3284 DEBUG_VALID_MSG("found ...");
3285#endif
Owen Taylor3473f882001-02-23 17:55:21 +00003286
Daniel Veillarde356c282001-03-10 12:32:04 +00003287 return(1);
Owen Taylor3473f882001-02-23 17:55:21 +00003288}
3289
3290int xmlValidateElementTypeElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
3291 xmlElementContentPtr cont);
3292
3293/**
3294 * xmlValidateElementTypeExpr:
3295 * @ctxt: the validation context
3296 * @child: pointer to the child list
3297 * @cont: pointer to the content declaration
3298 *
3299 * Try to validate the content of an element of type element
3300 * but don't handle the occurence factor
3301 *
3302 * returns 1 if valid or 0 and -1 if PCDATA stuff is found,
3303 * also update child value in-situ.
3304 */
3305
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003306static int
Owen Taylor3473f882001-02-23 17:55:21 +00003307xmlValidateElementTypeExpr(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
3308 xmlElementContentPtr cont) {
3309 xmlNodePtr cur;
3310 int ret = 1;
3311
3312 if (cont == NULL) return(-1);
3313 DEBUG_VALID_STATE(*child, cont)
3314 ret = xmlValidateFindNextElement(ctxt, child, cont);
3315 if (ret < 0)
3316 return(-1);
3317 DEBUG_VALID_STATE(*child, cont)
3318 switch (cont->type) {
3319 case XML_ELEMENT_CONTENT_PCDATA:
3320 if (*child == NULL) return(0);
Daniel Veillarde356c282001-03-10 12:32:04 +00003321 if ((*child)->type == XML_TEXT_NODE) {
3322 DEBUG_VALID_MSG("pcdata found");
3323 return(1);
3324 }
Owen Taylor3473f882001-02-23 17:55:21 +00003325 return(0);
3326 case XML_ELEMENT_CONTENT_ELEMENT:
3327 if (*child == NULL) return(0);
3328 ret = (xmlStrEqual((*child)->name, cont->name));
3329 if (ret == 1) {
Daniel Veillarde356c282001-03-10 12:32:04 +00003330 DEBUG_VALID_MSG("element found, skip to next");
Owen Taylor3473f882001-02-23 17:55:21 +00003331 while ((*child)->next == NULL) {
3332 if (((*child)->parent != NULL) &&
3333 ((*child)->parent->type == XML_ENTITY_DECL)) {
3334 *child = nodeVPop(ctxt);
3335 } else
3336 break;
3337 }
3338 *child = (*child)->next;
3339 }
3340 return(ret);
3341 case XML_ELEMENT_CONTENT_OR:
3342 cur = *child;
3343 ret = xmlValidateElementTypeElement(ctxt, child, cont->c1);
3344 if (ret == -1) return(-1);
3345 if (ret == 1) {
Daniel Veillarde356c282001-03-10 12:32:04 +00003346 DEBUG_VALID_MSG("or succeeded first branch");
3347 return(1);
Owen Taylor3473f882001-02-23 17:55:21 +00003348 }
3349 /* rollback and retry the other path */
3350 *child = cur;
3351 ret = xmlValidateElementTypeElement(ctxt, child, cont->c2);
3352 if (ret == -1) return(-1);
3353 if (ret == 0) {
Daniel Veillarde356c282001-03-10 12:32:04 +00003354 DEBUG_VALID_MSG("or failed both branches");
Owen Taylor3473f882001-02-23 17:55:21 +00003355 *child = cur;
3356 return(0);
3357 }
Daniel Veillarde356c282001-03-10 12:32:04 +00003358 DEBUG_VALID_MSG("or succeeded second branch");
Owen Taylor3473f882001-02-23 17:55:21 +00003359 return(1);
3360 case XML_ELEMENT_CONTENT_SEQ:
3361 cur = *child;
3362 ret = xmlValidateElementTypeElement(ctxt, child, cont->c1);
3363 if (ret == -1) return(-1);
3364 if (ret == 0) {
Daniel Veillarde356c282001-03-10 12:32:04 +00003365 DEBUG_VALID_MSG("sequence failed");
Owen Taylor3473f882001-02-23 17:55:21 +00003366 *child = cur;
3367 return(0);
3368 }
3369 ret = xmlValidateElementTypeElement(ctxt, child, cont->c2);
3370 if (ret == -1) return(-1);
3371 if (ret == 0) {
3372 *child = cur;
3373 return(0);
3374 }
Daniel Veillarde356c282001-03-10 12:32:04 +00003375 DEBUG_VALID_MSG("sequence succeeded");
Owen Taylor3473f882001-02-23 17:55:21 +00003376 return(1);
3377 }
3378 return(ret);
3379}
3380
3381/**
3382 * xmlValidateElementTypeElement:
3383 * @ctxt: the validation context
3384 * @child: pointer to the child list
3385 * @cont: pointer to the content declaration
3386 *
3387 * Try to validate the content of an element of type element
3388 * yeah, Yet Another Regexp Implementation, and recursive
3389 *
3390 * returns 1 if valid or 0 and -1 if PCDATA stuff is found,
3391 * also update child and content values in-situ.
3392 */
3393
3394int
3395xmlValidateElementTypeElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
3396 xmlElementContentPtr cont) {
3397 xmlNodePtr cur;
3398 int ret;
3399
3400 if (cont == NULL) return(-1);
3401
3402 DEBUG_VALID_STATE(*child, cont)
3403 ret = xmlValidateFindNextElement(ctxt, child, cont);
3404 if (ret < 0)
3405 return(-1);
3406 DEBUG_VALID_STATE(*child, cont)
3407 cur = *child;
3408 ret = xmlValidateElementTypeExpr(ctxt, child, cont);
3409 if (ret == -1) return(-1);
3410 switch (cont->ocur) {
3411 case XML_ELEMENT_CONTENT_ONCE:
3412 if (ret == 1) {
Daniel Veillarde356c282001-03-10 12:32:04 +00003413 DEBUG_VALID_MSG("once found, skip to next");
Owen Taylor3473f882001-02-23 17:55:21 +00003414 /* skip ignorable elems */
3415 while ((*child != NULL) &&
3416 ((*child)->type == XML_PI_NODE
3417 || (*child)->type == XML_COMMENT_NODE
3418 || (*child)->type == XML_XINCLUDE_START
3419 || (*child)->type == XML_XINCLUDE_END)) {
3420 while ((*child)->next == NULL) {
3421 if (((*child)->parent != NULL) &&
3422 ((*child)->parent->type == XML_ENTITY_REF_NODE)) {
3423 *child = (*child)->parent;
3424 } else
3425 break;
3426 }
3427 *child = (*child)->next;
3428 }
3429 return(1);
3430 }
3431 *child = cur;
3432 return(0);
3433 case XML_ELEMENT_CONTENT_OPT:
3434 if (ret == 0) {
3435 *child = cur;
3436 return(1);
3437 }
Daniel Veillarde356c282001-03-10 12:32:04 +00003438 if (ret == 1) {
3439 DEBUG_VALID_MSG("optional found, skip to next");
3440 /* skip ignorable elems */
3441 while ((*child != NULL) &&
3442 ((*child)->type == XML_PI_NODE
3443 || (*child)->type == XML_COMMENT_NODE
3444 || (*child)->type == XML_XINCLUDE_START
3445 || (*child)->type == XML_XINCLUDE_END)) {
3446 while ((*child)->next == NULL) {
3447 if (((*child)->parent != NULL) &&
3448 ((*child)->parent->type == XML_ENTITY_REF_NODE)) {
3449 *child = (*child)->parent;
3450 } else
3451 break;
3452 }
3453 *child = (*child)->next;
3454 }
3455 return(1);
3456 }
Owen Taylor3473f882001-02-23 17:55:21 +00003457 break;
3458 case XML_ELEMENT_CONTENT_MULT:
3459 if (ret == 0) {
3460 *child = cur;
3461 break;
3462 }
3463 /* no break on purpose */
3464 case XML_ELEMENT_CONTENT_PLUS:
3465 if (ret == 0) {
3466 *child = cur;
3467 return(0);
3468 }
Daniel Veillarde356c282001-03-10 12:32:04 +00003469 DEBUG_VALID_MSG("mult/plus found");
Owen Taylor3473f882001-02-23 17:55:21 +00003470 if (ret == -1) return(-1);
3471 cur = *child;
3472 do {
3473 if (*child == NULL)
3474 break; /* while */
3475 if ((*child)->type == XML_TEXT_NODE
3476 && xmlIsBlankNode(*child)) {
3477 *child = (*child)->next;
3478 continue;
3479 }
3480 ret = xmlValidateElementTypeExpr(ctxt, child, cont);
3481 if (ret == 1)
3482 cur = *child;
3483 } while (ret == 1);
3484 if (ret == -1) return(-1);
3485 *child = cur;
3486 break;
3487 }
Daniel Veillarde356c282001-03-10 12:32:04 +00003488 if (ret == -1) return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003489
Daniel Veillarde356c282001-03-10 12:32:04 +00003490 return(xmlValidateFindNextElement(ctxt, child, cont));
Owen Taylor3473f882001-02-23 17:55:21 +00003491}
3492
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003493#else /* ALLOW_UNDETERMINISTIC_MODELS */
3494
3495/**
3496 * xmlValidateSkipIgnorable:
3497 * @ctxt: the validation context
3498 * @child: the child list
3499 *
3500 * Skip ignorable elements w.r.t. the validation process
3501 *
3502 * returns the first element to consider for validation of the content model
3503 */
3504
3505static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003506xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003507 while (child != NULL) {
3508 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003509 /* These things are ignored (skipped) during validation. */
3510 case XML_PI_NODE:
3511 case XML_COMMENT_NODE:
3512 case XML_XINCLUDE_START:
3513 case XML_XINCLUDE_END:
3514 child = child->next;
3515 break;
3516 case XML_TEXT_NODE:
3517 if (xmlIsBlankNode(child))
3518 child = child->next;
3519 else
3520 return(child);
3521 break;
3522 /* keep current node */
3523 default:
3524 return(child);
3525 }
3526 }
3527 return(child);
3528}
3529
3530/**
3531 * xmlValidateElementType:
3532 * @ctxt: the validation context
3533 *
3534 * Try to validate the content model of an element internal function
3535 *
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003536 * returns 1 if valid or 0 ,-1 in case of error, and -2 if an entity
3537 * reference is found.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003538 */
3539
3540static int
3541xmlValidateElementType(xmlValidCtxtPtr ctxt) {
3542 int ret = -1;
3543 int consumed = 1;
3544
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003545 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003546 if ((NODE == NULL) && (CONT == NULL))
3547 return(1);
3548 if ((NODE == NULL) &&
3549 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3550 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3551 return(1);
3552 }
3553 if (CONT == NULL) return(-1);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003554 if (NODE->type == XML_ENTITY_REF_NODE)
3555 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003556
3557 /*
3558 * We arrive here when more states need to be examined
3559 */
3560cont:
3561
3562 /*
3563 * We just recovered from a rollback generated by a possible
3564 * epsilon transition, go directly to the analysis phase
3565 */
3566 if (STATE == ROLLBACK_PARENT) {
3567 DEBUG_VALID_MSG("restaured parent branch");
3568 DEBUG_VALID_STATE(NODE, CONT)
3569 ret = 1;
3570 consumed = 0;
3571 goto analyze;
3572 }
3573
3574 DEBUG_VALID_STATE(NODE, CONT)
3575 /*
3576 * we may have to save a backup state here. This is the equivalent
3577 * of handling epsilon transition in NFAs.
3578 */
3579 if ((consumed) && (CONT != NULL) &&
3580 (CONT->parent != NULL) &&
3581 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003582 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
3583 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003584 DEBUG_VALID_MSG("saving parent branch");
3585 vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
3586 }
3587
3588
3589 /*
3590 * Check first if the content matches
3591 */
3592 switch (CONT->type) {
3593 case XML_ELEMENT_CONTENT_PCDATA:
3594 if (NODE == NULL) {
3595 DEBUG_VALID_MSG("pcdata failed no node");
3596 ret = 0;
3597 break;
3598 }
3599 if (NODE->type == XML_TEXT_NODE) {
3600 DEBUG_VALID_MSG("pcdata found, skip to next");
3601 /*
3602 * go to next element in the content model
3603 * skipping ignorable elems
3604 */
3605 do {
3606 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003607 NODE = xmlValidateSkipIgnorable(NODE);
3608 if ((NODE != NULL) &&
3609 (NODE->type == XML_ENTITY_REF_NODE))
3610 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003611 } while ((NODE != NULL) &&
3612 ((NODE->type != XML_ELEMENT_NODE) &&
3613 (NODE->type != XML_TEXT_NODE)));
3614 consumed = 1;
3615 ret = 1;
3616 break;
3617 } else {
3618 DEBUG_VALID_MSG("pcdata failed");
3619 ret = 0;
3620 break;
3621 }
3622 break;
3623 case XML_ELEMENT_CONTENT_ELEMENT:
3624 if (NODE == NULL) {
3625 DEBUG_VALID_MSG("element failed no node");
3626 ret = 0;
3627 break;
3628 }
3629 ret = (xmlStrEqual(NODE->name, CONT->name));
3630 if (ret == 1) {
3631 DEBUG_VALID_MSG("element 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) &&
3644 (NODE->type != XML_TEXT_NODE)));
3645 consumed = 1;
3646 } else {
3647 DEBUG_VALID_MSG("element failed");
3648 ret = 0;
3649 break;
3650 }
3651 break;
3652 case XML_ELEMENT_CONTENT_OR:
3653 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003654 * Small optimization.
3655 */
3656 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3657 if ((NODE == NULL) ||
3658 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3659 DEPTH++;
3660 CONT = CONT->c2;
3661 goto cont;
3662 }
3663 }
3664
3665 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003666 * save the second branch 'or' branch
3667 */
3668 DEBUG_VALID_MSG("saving 'or' branch");
3669 vstateVPush(ctxt, CONT->c2, NODE, DEPTH + 1, OCCURS, ROLLBACK_OR);
3670
3671 DEPTH++;
3672 CONT = CONT->c1;
3673 goto cont;
3674 case XML_ELEMENT_CONTENT_SEQ:
3675 DEPTH++;
3676 CONT = CONT->c1;
3677 goto cont;
3678 }
3679
3680 /*
3681 * At this point handle going up in the tree
3682 */
3683 if (ret == -1) {
3684 DEBUG_VALID_MSG("error found returning");
3685 return(ret);
3686 }
3687analyze:
3688 while (CONT != NULL) {
3689 /*
3690 * First do the analysis depending on the occurence model at
3691 * this level.
3692 */
3693 if (ret == 0) {
3694 switch (CONT->ocur) {
3695 case XML_ELEMENT_CONTENT_ONCE:
3696 DEBUG_VALID_MSG("Once branch failed, rollback");
3697 if (vstateVPop(ctxt) < 0 ) {
3698 DEBUG_VALID_MSG("exhaustion, failed");
3699 return(0);
3700 }
3701 consumed = 0;
3702 goto cont;
3703 case XML_ELEMENT_CONTENT_PLUS:
3704 if (OCCURENCE == 0) {
3705 DEBUG_VALID_MSG("Plus branch failed, rollback");
3706 if (vstateVPop(ctxt) < 0 ) {
3707 DEBUG_VALID_MSG("exhaustion, failed");
3708 return(0);
3709 }
3710 consumed = 0;
3711 goto cont;
3712 }
3713 DEBUG_VALID_MSG("Plus branch found");
3714 ret = 1;
3715 break;
3716 case XML_ELEMENT_CONTENT_MULT:
3717#ifdef DEBUG_VALID_ALGO
3718 if (OCCURENCE == 0) {
3719 DEBUG_VALID_MSG("Mult branch failed");
3720 } else {
3721 DEBUG_VALID_MSG("Mult branch found");
3722 }
3723#endif
3724 ret = 1;
3725 break;
3726 case XML_ELEMENT_CONTENT_OPT:
3727 DEBUG_VALID_MSG("Option branch failed");
3728 ret = 1;
3729 break;
3730 }
3731 } else {
3732 switch (CONT->ocur) {
3733 case XML_ELEMENT_CONTENT_OPT:
3734 DEBUG_VALID_MSG("Option branch succeeded");
3735 ret = 1;
3736 break;
3737 case XML_ELEMENT_CONTENT_ONCE:
3738 DEBUG_VALID_MSG("Once branch succeeded");
3739 ret = 1;
3740 break;
3741 case XML_ELEMENT_CONTENT_PLUS:
3742 if (STATE == ROLLBACK_PARENT) {
3743 DEBUG_VALID_MSG("Plus branch rollback");
3744 ret = 1;
3745 break;
3746 }
3747 if (NODE == NULL) {
3748 DEBUG_VALID_MSG("Plus branch exhausted");
3749 ret = 1;
3750 break;
3751 }
3752 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
3753 SET_OCCURENCE;
3754 goto cont;
3755 case XML_ELEMENT_CONTENT_MULT:
3756 if (STATE == ROLLBACK_PARENT) {
3757 DEBUG_VALID_MSG("Mult branch rollback");
3758 ret = 1;
3759 break;
3760 }
3761 if (NODE == NULL) {
3762 DEBUG_VALID_MSG("Mult branch exhausted");
3763 ret = 1;
3764 break;
3765 }
3766 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
3767 SET_OCCURENCE;
3768 goto cont;
3769 }
3770 }
3771 STATE = 0;
3772
3773 /*
3774 * Then act accordingly at the parent level
3775 */
3776 RESET_OCCURENCE;
3777 if (CONT->parent == NULL)
3778 break;
3779
3780 switch (CONT->parent->type) {
3781 case XML_ELEMENT_CONTENT_PCDATA:
3782 DEBUG_VALID_MSG("Error: parent pcdata");
3783 return(-1);
3784 case XML_ELEMENT_CONTENT_ELEMENT:
3785 DEBUG_VALID_MSG("Error: parent element");
3786 return(-1);
3787 case XML_ELEMENT_CONTENT_OR:
3788 if (ret == 1) {
3789 DEBUG_VALID_MSG("Or succeeded");
3790 CONT = CONT->parent;
3791 DEPTH--;
3792 } else {
3793 DEBUG_VALID_MSG("Or failed");
3794 CONT = CONT->parent;
3795 DEPTH--;
3796 }
3797 break;
3798 case XML_ELEMENT_CONTENT_SEQ:
3799 if (ret == 0) {
3800 DEBUG_VALID_MSG("Sequence failed");
3801 CONT = CONT->parent;
3802 DEPTH--;
3803 } else if (CONT == CONT->parent->c1) {
3804 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3805 CONT = CONT->parent->c2;
3806 goto cont;
3807 } else {
3808 DEBUG_VALID_MSG("Sequence succeeded");
3809 CONT = CONT->parent;
3810 DEPTH--;
3811 }
3812 }
3813 }
3814 if (NODE != NULL) {
3815 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3816 if (vstateVPop(ctxt) < 0 ) {
3817 DEBUG_VALID_MSG("exhaustion, failed");
3818 return(0);
3819 }
3820 consumed = 0;
3821 goto cont;
3822 }
3823 if (ret == 0) {
3824 DEBUG_VALID_MSG("Failure, rollback");
3825 if (vstateVPop(ctxt) < 0 ) {
3826 DEBUG_VALID_MSG("exhaustion, failed");
3827 return(0);
3828 }
3829 consumed = 0;
3830 goto cont;
3831 }
3832 return(1);
3833}
3834
3835/**
3836 * xmlValidateElementContent:
3837 * @ctxt: the validation context
3838 * @child: the child list
3839 * @cont: pointer to the content declaration
3840 *
3841 * Try to validate the content model of an element
3842 *
3843 * returns 1 if valid or 0 if not and -1 in case of error
3844 */
3845
3846static int
3847xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
3848 xmlElementContentPtr cont) {
3849 int ret;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003850 xmlNodePtr repl = NULL, last = NULL, tmp;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003851
3852 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003853 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003854 */
3855 ctxt->vstateMax = 8;
3856 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
3857 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
3858 if (ctxt->vstateTab == NULL) {
3859 xmlGenericError(xmlGenericErrorContext,
3860 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003861 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003862 }
3863 /*
3864 * The first entry in the stack is reserved to the current state
3865 */
3866 ctxt->vstate = &ctxt->vstateTab[0];
3867 ctxt->vstateNr = 1;
3868 CONT = cont;
3869 NODE = child;
3870 DEPTH = 0;
3871 OCCURS = 0;
3872 STATE = 0;
3873 ret = xmlValidateElementType(ctxt);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003874 if (ret == -2) {
3875 /*
3876 * An entities reference appeared at this level.
3877 * Buid a minimal representation of this node content
3878 * sufficient to run the validation process on it
3879 */
3880 DEBUG_VALID_MSG("Found an entity reference, linearizing");
3881 while (child != NULL) {
3882 switch (child->type) {
3883 case XML_ENTITY_REF_NODE:
3884 /*
3885 * Push the current node to be able to roll back
3886 * and process within the entity
3887 */
3888 if ((child->children != NULL) &&
3889 (child->children->children != NULL)) {
3890 nodeVPush(ctxt, child);
3891 child = child->children->children;
3892 continue;
3893 }
3894 case XML_TEXT_NODE:
3895 if (xmlIsBlankNode(child))
3896 break;
3897 case XML_ELEMENT_NODE:
3898 /*
3899 * Allocate a new node and minimally fills in
3900 * what's required
3901 */
3902 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
3903 if (tmp == NULL) {
3904 xmlGenericError(xmlGenericErrorContext,
3905 "xmlValidateElementContent : malloc failed\n");
3906 xmlFreeNodeList(repl);
3907 ret = -1;
3908 goto done;
3909 }
3910 tmp->type = child->type;
3911 tmp->name = child->name;
3912 tmp->ns = child->ns;
3913 tmp->next = NULL;
3914 if (repl == NULL)
3915 repl = last = tmp;
3916 else {
3917 last->next = tmp;
3918 last = tmp;
3919 }
3920 break;
3921 default:
3922 break;
3923 }
3924 /*
3925 * Switch to next element
3926 */
3927 child = child->next;
3928 while (child == NULL) {
3929 child = nodeVPop(ctxt);
3930 if (child == NULL)
3931 break;
3932 child = child->next;
3933 }
3934 }
3935
3936 /*
3937 * Relaunch the validation
3938 */
3939 ctxt->vstate = &ctxt->vstateTab[0];
3940 ctxt->vstateNr = 1;
3941 CONT = cont;
3942 NODE = repl;
3943 DEPTH = 0;
3944 OCCURS = 0;
3945 STATE = 0;
3946 ret = xmlValidateElementType(ctxt);
3947 }
3948
3949
3950done:
3951 /*
3952 * Deallocate the copy if done, and free up the validation stack
3953 */
3954 while (repl != NULL) {
3955 tmp = repl->next;
3956 xmlFree(repl);
3957 repl = tmp;
3958 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003959 ctxt->vstateMax = 0;
3960 xmlFree(ctxt->vstateTab);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003961 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003962
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003963}
3964#endif /* ALLOW_UNDETERMINISTIC_MODELS */
3965
Owen Taylor3473f882001-02-23 17:55:21 +00003966/**
3967 * xmlSprintfElementChilds:
3968 * @buf: an output buffer
3969 * @content: An element
3970 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3971 *
3972 * This will dump the list of childs to the buffer
3973 * Intended just for the debug routine
3974 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003975static void
Owen Taylor3473f882001-02-23 17:55:21 +00003976xmlSprintfElementChilds(char *buf, xmlNodePtr node, int glob) {
3977 xmlNodePtr cur;
3978
3979 if (node == NULL) return;
3980 if (glob) strcat(buf, "(");
3981 cur = node->children;
3982 while (cur != NULL) {
3983 switch (cur->type) {
3984 case XML_ELEMENT_NODE:
3985 strcat(buf, (char *) cur->name);
3986 if (cur->next != NULL)
3987 strcat(buf, " ");
3988 break;
3989 case XML_TEXT_NODE:
3990 if (xmlIsBlankNode(cur))
3991 break;
3992 case XML_CDATA_SECTION_NODE:
3993 case XML_ENTITY_REF_NODE:
3994 strcat(buf, "CDATA");
3995 if (cur->next != NULL)
3996 strcat(buf, " ");
3997 break;
3998 case XML_ATTRIBUTE_NODE:
3999 case XML_DOCUMENT_NODE:
4000#ifdef LIBXML_SGML_ENABLED
4001 case XML_SGML_DOCUMENT_NODE:
4002#endif
4003 case XML_HTML_DOCUMENT_NODE:
4004 case XML_DOCUMENT_TYPE_NODE:
4005 case XML_DOCUMENT_FRAG_NODE:
4006 case XML_NOTATION_NODE:
4007 case XML_NAMESPACE_DECL:
4008 strcat(buf, "???");
4009 if (cur->next != NULL)
4010 strcat(buf, " ");
4011 break;
4012 case XML_ENTITY_NODE:
4013 case XML_PI_NODE:
4014 case XML_DTD_NODE:
4015 case XML_COMMENT_NODE:
4016 case XML_ELEMENT_DECL:
4017 case XML_ATTRIBUTE_DECL:
4018 case XML_ENTITY_DECL:
4019 case XML_XINCLUDE_START:
4020 case XML_XINCLUDE_END:
4021 break;
4022 }
4023 cur = cur->next;
4024 }
4025 if (glob) strcat(buf, ")");
4026}
4027
4028
4029/**
4030 * xmlValidateOneElement:
4031 * @ctxt: the validation context
4032 * @doc: a document instance
4033 * @elem: an element instance
4034 *
4035 * Try to validate a single element and it's attributes,
4036 * basically it does the following checks as described by the
4037 * XML-1.0 recommendation:
4038 * - [ VC: Element Valid ]
4039 * - [ VC: Required Attribute ]
4040 * Then call xmlValidateOneAttribute() for each attribute present.
4041 *
4042 * The ID/IDREF checkings are done separately
4043 *
4044 * returns 1 if valid or 0 otherwise
4045 */
4046
4047int
4048xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4049 xmlNodePtr elem) {
4050 xmlElementPtr elemDecl = NULL;
4051 xmlElementContentPtr cont;
4052 xmlAttributePtr attr;
4053 xmlNodePtr child;
4054 int ret = 1;
4055 const xmlChar *name;
4056
4057 CHECK_DTD;
4058
4059 if (elem == NULL) return(0);
4060 if (elem->type == XML_TEXT_NODE) {
4061 }
4062 switch (elem->type) {
4063 case XML_ATTRIBUTE_NODE:
4064 VERROR(ctxt->userData,
4065 "Attribute element not expected here\n");
4066 return(0);
4067 case XML_TEXT_NODE:
4068 if (elem->children != NULL) {
4069 VERROR(ctxt->userData, "Text element has childs !\n");
4070 return(0);
4071 }
4072 if (elem->properties != NULL) {
4073 VERROR(ctxt->userData, "Text element has attributes !\n");
4074 return(0);
4075 }
4076 if (elem->ns != NULL) {
4077 VERROR(ctxt->userData, "Text element has namespace !\n");
4078 return(0);
4079 }
4080 if (elem->nsDef != NULL) {
4081 VERROR(ctxt->userData,
4082 "Text element carries namespace definitions !\n");
4083 return(0);
4084 }
4085 if (elem->content == NULL) {
4086 VERROR(ctxt->userData,
4087 "Text element has no content !\n");
4088 return(0);
4089 }
4090 return(1);
4091 case XML_XINCLUDE_START:
4092 case XML_XINCLUDE_END:
4093 return(1);
4094 case XML_CDATA_SECTION_NODE:
4095 case XML_ENTITY_REF_NODE:
4096 case XML_PI_NODE:
4097 case XML_COMMENT_NODE:
4098 return(1);
4099 case XML_ENTITY_NODE:
4100 VERROR(ctxt->userData,
4101 "Entity element not expected here\n");
4102 return(0);
4103 case XML_NOTATION_NODE:
4104 VERROR(ctxt->userData,
4105 "Notation element not expected here\n");
4106 return(0);
4107 case XML_DOCUMENT_NODE:
4108 case XML_DOCUMENT_TYPE_NODE:
4109 case XML_DOCUMENT_FRAG_NODE:
4110 VERROR(ctxt->userData,
4111 "Document element not expected here\n");
4112 return(0);
4113 case XML_HTML_DOCUMENT_NODE:
4114 VERROR(ctxt->userData,
4115 "\n");
4116 return(0);
4117 case XML_ELEMENT_NODE:
4118 break;
4119 default:
4120 VERROR(ctxt->userData,
4121 "unknown element type %d\n", elem->type);
4122 return(0);
4123 }
4124 if (elem->name == NULL) return(0);
4125
4126 /*
4127 * Fetch the declaration for the qualified name
4128 */
4129 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
4130 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
4131 elem->name, elem->ns->prefix);
4132 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4133 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
4134 elem->name, elem->ns->prefix);
4135 }
4136
4137 /*
4138 * Fetch the declaration for the non qualified name
4139 */
4140 if (elemDecl == NULL) {
4141 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
4142 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4143 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
4144 }
4145 if (elemDecl == NULL) {
4146 VERROR(ctxt->userData, "No declaration for element %s\n",
4147 elem->name);
4148 return(0);
4149 }
4150
4151 /* Check taht the element content matches the definition */
4152 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004153 case XML_ELEMENT_TYPE_UNDEFINED:
4154 VERROR(ctxt->userData, "No declaration for element %s\n",
4155 elem->name);
4156 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004157 case XML_ELEMENT_TYPE_EMPTY:
4158 if (elem->children != NULL) {
4159 VERROR(ctxt->userData,
4160 "Element %s was declared EMPTY this one has content\n",
4161 elem->name);
4162 ret = 0;
4163 }
4164 break;
4165 case XML_ELEMENT_TYPE_ANY:
4166 /* I don't think anything is required then */
4167 break;
4168 case XML_ELEMENT_TYPE_MIXED:
4169 /* Hum, this start to get messy */
4170 child = elem->children;
4171 while (child != NULL) {
4172 if (child->type == XML_ELEMENT_NODE) {
4173 name = child->name;
4174 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4175 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004176 snprintf((char *) qname, sizeof(qname), "%s:%s",
4177 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004178 qname[sizeof(qname) - 1] = 0;
4179 cont = elemDecl->content;
4180 while (cont != NULL) {
4181 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4182 if (xmlStrEqual(cont->name, qname)) break;
4183 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4184 (cont->c1 != NULL) &&
4185 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4186 if (xmlStrEqual(cont->c1->name, qname)) break;
4187 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4188 (cont->c1 == NULL) ||
4189 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4190 /* Internal error !!! */
4191 xmlGenericError(xmlGenericErrorContext,
4192 "Internal: MIXED struct bad\n");
4193 break;
4194 }
4195 cont = cont->c2;
4196 }
4197 if (cont != NULL)
4198 goto child_ok;
4199 }
4200 cont = elemDecl->content;
4201 while (cont != NULL) {
4202 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4203 if (xmlStrEqual(cont->name, name)) break;
4204 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4205 (cont->c1 != NULL) &&
4206 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4207 if (xmlStrEqual(cont->c1->name, name)) break;
4208 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4209 (cont->c1 == NULL) ||
4210 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4211 /* Internal error !!! */
4212 xmlGenericError(xmlGenericErrorContext,
4213 "Internal: MIXED struct bad\n");
4214 break;
4215 }
4216 cont = cont->c2;
4217 }
4218 if (cont == NULL) {
4219 VERROR(ctxt->userData,
4220 "Element %s is not declared in %s list of possible childs\n",
4221 name, elem->name);
4222 ret = 0;
4223 }
4224 }
4225child_ok:
4226 child = child->next;
4227 }
4228 break;
4229 case XML_ELEMENT_TYPE_ELEMENT:
4230 child = elem->children;
4231 cont = elemDecl->content;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004232#ifdef ALLOW_UNDETERMINISTIC_MODELS
4233 ret = xmlValidateElementContent(ctxt, child, cont);
4234 if (ret != 1) {
4235 char expr[1000];
4236 char list[2000];
4237
4238 expr[0] = 0;
4239 xmlSprintfElementContent(expr, cont, 1);
4240 list[0] = 0;
4241 xmlSprintfElementChilds(list, elem, 1);
4242
4243 VERROR(ctxt->userData,
4244 "Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
4245 elem->name, expr, list);
4246 ret = 0;
4247 }
4248#else
Owen Taylor3473f882001-02-23 17:55:21 +00004249 ret = xmlValidateElementTypeElement(ctxt, &child, cont);
4250 while ((child != NULL) && (child->type == XML_TEXT_NODE) &&
4251 (xmlIsBlankNode(child))) {
4252 child = child->next;
4253 continue;
4254 }
4255 if ((ret == 0) || (child != NULL)) {
4256 char expr[1000];
4257 char list[2000];
4258
4259 expr[0] = 0;
4260 xmlSprintfElementContent(expr, cont, 1);
4261 list[0] = 0;
4262 xmlSprintfElementChilds(list, elem, 1);
4263
4264 VERROR(ctxt->userData,
4265 "Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
4266 elem->name, expr, list);
4267 ret = 0;
4268 }
4269 break;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004270#endif
Owen Taylor3473f882001-02-23 17:55:21 +00004271 }
4272
4273 /* [ VC: Required Attribute ] */
4274 attr = elemDecl->attributes;
4275 while (attr != NULL) {
4276 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
4277 xmlAttrPtr attrib;
4278 int qualified = -1;
4279
4280 attrib = elem->properties;
4281 while (attrib != NULL) {
4282 if (xmlStrEqual(attrib->name, attr->name)) {
4283 if (attr->prefix != NULL) {
4284 xmlNsPtr nameSpace = attrib->ns;
4285
4286 if (nameSpace == NULL)
4287 nameSpace = elem->ns;
4288 /*
4289 * qualified names handling is problematic, having a
4290 * different prefix should be possible but DTDs don't
4291 * allow to define the URI instead of the prefix :-(
4292 */
4293 if (nameSpace == NULL) {
4294 if (qualified < 0)
4295 qualified = 0;
4296 } else if (!xmlStrEqual(nameSpace->prefix, attr->prefix)) {
4297 if (qualified < 1)
4298 qualified = 1;
4299 } else
4300 goto found;
4301 } else {
4302 /*
4303 * We should allow applications to define namespaces
4304 * for their application even if the DTD doesn't
4305 * carry one, otherwise, basically we would always
4306 * break.
4307 */
4308 goto found;
4309 }
4310 }
4311 attrib = attrib->next;
4312 }
4313 if (qualified == -1) {
4314 if (attr->prefix == NULL) {
4315 VERROR(ctxt->userData,
4316 "Element %s doesn't carry attribute %s\n",
4317 elem->name, attr->name);
4318 ret = 0;
4319 } else {
4320 VERROR(ctxt->userData,
4321 "Element %s doesn't carry attribute %s:%s\n",
4322 elem->name, attr->prefix,attr->name);
4323 ret = 0;
4324 }
4325 } else if (qualified == 0) {
4326 VWARNING(ctxt->userData,
4327 "Element %s required attribute %s:%s has no prefix\n",
4328 elem->name, attr->prefix,attr->name);
4329 } else if (qualified == 1) {
4330 VWARNING(ctxt->userData,
4331 "Element %s required attribute %s:%s has different prefix\n",
4332 elem->name, attr->prefix,attr->name);
4333 }
4334 }
4335found:
4336 attr = attr->nexth;
4337 }
4338 return(ret);
4339}
4340
4341/**
4342 * xmlValidateRoot:
4343 * @ctxt: the validation context
4344 * @doc: a document instance
4345 *
4346 * Try to validate a the root element
4347 * basically it does the following check as described by the
4348 * XML-1.0 recommendation:
4349 * - [ VC: Root Element Type ]
4350 * it doesn't try to recurse or apply other check to the element
4351 *
4352 * returns 1 if valid or 0 otherwise
4353 */
4354
4355int
4356xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4357 xmlNodePtr root;
4358 if (doc == NULL) return(0);
4359
4360 root = xmlDocGetRootElement(doc);
4361 if ((root == NULL) || (root->name == NULL)) {
4362 VERROR(ctxt->userData, "Not valid: no root element\n");
4363 return(0);
4364 }
4365
4366 /*
4367 * When doing post validation against a separate DTD, those may
4368 * no internal subset has been generated
4369 */
4370 if ((doc->intSubset != NULL) &&
4371 (doc->intSubset->name != NULL)) {
4372 /*
4373 * Check first the document root against the NQName
4374 */
4375 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4376 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4377 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004378 snprintf((char *) qname, sizeof(qname), "%s:%s",
4379 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004380 qname[sizeof(qname) - 1] = 0;
4381 if (xmlStrEqual(doc->intSubset->name, qname))
4382 goto name_ok;
4383 }
4384 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4385 (xmlStrEqual(root->name, BAD_CAST "html")))
4386 goto name_ok;
4387 VERROR(ctxt->userData,
4388 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4389 root->name, doc->intSubset->name);
4390 return(0);
4391
4392 }
4393 }
4394name_ok:
4395 return(1);
4396}
4397
4398
4399/**
4400 * xmlValidateElement:
4401 * @ctxt: the validation context
4402 * @doc: a document instance
4403 * @elem: an element instance
4404 *
4405 * Try to validate the subtree under an element
4406 *
4407 * returns 1 if valid or 0 otherwise
4408 */
4409
4410int
4411xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4412 xmlNodePtr child;
4413 xmlAttrPtr attr;
4414 xmlChar *value;
4415 int ret = 1;
4416
4417 if (elem == NULL) return(0);
4418
4419 /*
4420 * XInclude elements were added after parsing in the infoset,
4421 * they don't really mean anything validation wise.
4422 */
4423 if ((elem->type == XML_XINCLUDE_START) ||
4424 (elem->type == XML_XINCLUDE_END))
4425 return(1);
4426
4427 CHECK_DTD;
4428
4429 ret &= xmlValidateOneElement(ctxt, doc, elem);
4430 attr = elem->properties;
4431 while(attr != NULL) {
4432 value = xmlNodeListGetString(doc, attr->children, 0);
4433 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4434 if (value != NULL)
4435 xmlFree(value);
4436 attr= attr->next;
4437 }
4438 child = elem->children;
4439 while (child != NULL) {
4440 ret &= xmlValidateElement(ctxt, doc, child);
4441 child = child->next;
4442 }
4443
4444 return(ret);
4445}
4446
Daniel Veillard8730c562001-02-26 10:49:57 +00004447/**
4448 * xmlValidateRef:
4449 * @ref: A reference to be validated
4450 * @ctxt: Validation context
4451 * @name: Name of ID we are searching for
4452 *
4453 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004454static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004455xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004456 const xmlChar *name) {
4457 xmlAttrPtr id;
4458 xmlAttrPtr attr;
4459
4460 if (ref == NULL)
4461 return;
4462 attr = ref->attr;
4463 if (attr == NULL)
4464 return;
4465 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4466 id = xmlGetID(ctxt->doc, name);
4467 if (id == NULL) {
4468 VERROR(ctxt->userData,
4469 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4470 attr->name, name);
4471 ctxt->valid = 0;
4472 }
4473 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4474 xmlChar *dup, *str = NULL, *cur, save;
4475
4476 dup = xmlStrdup(name);
4477 if (dup == NULL) {
4478 ctxt->valid = 0;
4479 return;
4480 }
4481 cur = dup;
4482 while (*cur != 0) {
4483 str = cur;
4484 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4485 save = *cur;
4486 *cur = 0;
4487 id = xmlGetID(ctxt->doc, str);
4488 if (id == NULL) {
4489 VERROR(ctxt->userData,
4490 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4491 attr->name, str);
4492 ctxt->valid = 0;
4493 }
4494 if (save == 0)
4495 break;
4496 *cur = save;
4497 while (IS_BLANK(*cur)) cur++;
4498 }
4499 xmlFree(dup);
4500 }
4501}
4502
4503/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004504 * xmlWalkValidateList:
4505 * @data: Contents of current link
4506 * @user: Value supplied by the user
4507 *
4508 * Return 0 to abort the walk or 1 to continue
4509 */
4510static int
4511xmlWalkValidateList(const void *data, const void *user)
4512{
4513 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4514 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4515 return 1;
4516}
4517
4518/**
4519 * xmlValidateCheckRefCallback:
4520 * @ref_list: List of references
4521 * @ctxt: Validation context
4522 * @name: Name of ID we are searching for
4523 *
4524 */
4525static void
4526xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4527 const xmlChar *name) {
4528 xmlValidateMemo memo;
4529
4530 if (ref_list == NULL)
4531 return;
4532 memo.ctxt = ctxt;
4533 memo.name = name;
4534
4535 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4536
4537}
4538
4539/**
Owen Taylor3473f882001-02-23 17:55:21 +00004540 * xmlValidateDocumentFinal:
4541 * @ctxt: the validation context
4542 * @doc: a document instance
4543 *
4544 * Does the final step for the document validation once all the
4545 * incremental validation steps have been completed
4546 *
4547 * basically it does the following checks described by the XML Rec
4548 *
4549 *
4550 * returns 1 if valid or 0 otherwise
4551 */
4552
4553int
4554xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4555 xmlRefTablePtr table;
4556
4557 if (doc == NULL) {
4558 xmlGenericError(xmlGenericErrorContext,
4559 "xmlValidateDocumentFinal: doc == NULL\n");
4560 return(0);
4561 }
4562
4563 /*
4564 * Check all the NOTATION/NOTATIONS attributes
4565 */
4566 /*
4567 * Check all the ENTITY/ENTITIES attributes definition for validity
4568 */
4569 /*
4570 * Check all the IDREF/IDREFS attributes definition for validity
4571 */
4572 table = (xmlRefTablePtr) doc->refs;
4573 ctxt->doc = doc;
4574 ctxt->valid = 1;
4575 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4576 return(ctxt->valid);
4577}
4578
4579/**
4580 * xmlValidateDtd:
4581 * @ctxt: the validation context
4582 * @doc: a document instance
4583 * @dtd: a dtd instance
4584 *
4585 * Try to validate the document against the dtd instance
4586 *
4587 * basically it does check all the definitions in the DtD.
4588 *
4589 * returns 1 if valid or 0 otherwise
4590 */
4591
4592int
4593xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4594 int ret;
4595 xmlDtdPtr oldExt;
4596 xmlNodePtr root;
4597
4598 if (dtd == NULL) return(0);
4599 if (doc == NULL) return(0);
4600 oldExt = doc->extSubset;
4601 doc->extSubset = dtd;
4602 ret = xmlValidateRoot(ctxt, doc);
4603 if (ret == 0) {
4604 doc->extSubset = oldExt;
4605 return(ret);
4606 }
4607 if (doc->ids != NULL) {
4608 xmlFreeIDTable(doc->ids);
4609 doc->ids = NULL;
4610 }
4611 if (doc->refs != NULL) {
4612 xmlFreeRefTable(doc->refs);
4613 doc->refs = NULL;
4614 }
4615 root = xmlDocGetRootElement(doc);
4616 ret = xmlValidateElement(ctxt, doc, root);
4617 ret &= xmlValidateDocumentFinal(ctxt, doc);
4618 doc->extSubset = oldExt;
4619 return(ret);
4620}
4621
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004622static void
Owen Taylor3473f882001-02-23 17:55:21 +00004623xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004624 const xmlChar *name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004625 if (cur == NULL)
4626 return;
4627 switch (cur->atype) {
4628 case XML_ATTRIBUTE_CDATA:
4629 case XML_ATTRIBUTE_ID:
4630 case XML_ATTRIBUTE_IDREF :
4631 case XML_ATTRIBUTE_IDREFS:
4632 case XML_ATTRIBUTE_NMTOKEN:
4633 case XML_ATTRIBUTE_NMTOKENS:
4634 case XML_ATTRIBUTE_ENUMERATION:
4635 break;
4636 case XML_ATTRIBUTE_ENTITY:
4637 case XML_ATTRIBUTE_ENTITIES:
4638 case XML_ATTRIBUTE_NOTATION:
4639 if (cur->defaultValue != NULL) {
4640 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4641 cur->name, cur->atype, cur->defaultValue);
4642 }
4643 if (cur->tree != NULL) {
4644 xmlEnumerationPtr tree = cur->tree;
4645 while (tree != NULL) {
4646 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4647 cur->name, cur->atype, tree->name);
4648 tree = tree->next;
4649 }
4650 }
4651 }
4652}
4653
4654/**
4655 * xmlValidateDtdFinal:
4656 * @ctxt: the validation context
4657 * @doc: a document instance
4658 *
4659 * Does the final step for the dtds validation once all the
4660 * subsets have been parsed
4661 *
4662 * basically it does the following checks described by the XML Rec
4663 * - check that ENTITY and ENTITIES type attributes default or
4664 * possible values matches one of the defined entities.
4665 * - check that NOTATION type attributes default or
4666 * possible values matches one of the defined notations.
4667 *
4668 * returns 1 if valid or 0 otherwise
4669 */
4670
4671int
4672xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4673 int ret = 1;
4674 xmlDtdPtr dtd;
4675 xmlAttributeTablePtr table;
4676
4677 if (doc == NULL) return(0);
4678 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4679 return(0);
4680 ctxt->doc = doc;
4681 ctxt->valid = ret;
4682 dtd = doc->intSubset;
4683 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4684 table = (xmlAttributeTablePtr) dtd->attributes;
4685 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4686 }
4687 dtd = doc->extSubset;
4688 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4689 table = (xmlAttributeTablePtr) dtd->attributes;
4690 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4691 }
4692 return(ctxt->valid);
4693}
4694
4695/**
4696 * xmlValidateDocument:
4697 * @ctxt: the validation context
4698 * @doc: a document instance
4699 *
4700 * Try to validate the document instance
4701 *
4702 * basically it does the all the checks described by the XML Rec
4703 * i.e. validates the internal and external subset (if present)
4704 * and validate the document tree.
4705 *
4706 * returns 1 if valid or 0 otherwise
4707 */
4708
4709int
4710xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4711 int ret;
4712 xmlNodePtr root;
4713
4714 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4715 return(0);
4716 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4717 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4718 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4719 doc->intSubset->SystemID);
4720 if (doc->extSubset == NULL) {
4721 if (doc->intSubset->SystemID != NULL) {
4722 VERROR(ctxt->userData,
4723 "Could not load the external subset \"%s\"\n",
4724 doc->intSubset->SystemID);
4725 } else {
4726 VERROR(ctxt->userData,
4727 "Could not load the external subset \"%s\"\n",
4728 doc->intSubset->ExternalID);
4729 }
4730 return(0);
4731 }
4732 }
4733
4734 if (doc->ids != NULL) {
4735 xmlFreeIDTable(doc->ids);
4736 doc->ids = NULL;
4737 }
4738 if (doc->refs != NULL) {
4739 xmlFreeRefTable(doc->refs);
4740 doc->refs = NULL;
4741 }
4742 ret = xmlValidateDtdFinal(ctxt, doc);
4743 if (!xmlValidateRoot(ctxt, doc)) return(0);
4744
4745 root = xmlDocGetRootElement(doc);
4746 ret &= xmlValidateElement(ctxt, doc, root);
4747 ret &= xmlValidateDocumentFinal(ctxt, doc);
4748 return(ret);
4749}
4750
4751
4752/************************************************************************
4753 * *
4754 * Routines for dynamic validation editing *
4755 * *
4756 ************************************************************************/
4757
4758/**
4759 * xmlValidGetPotentialChildren:
4760 * @ctree: an element content tree
4761 * @list: an array to store the list of child names
4762 * @len: a pointer to the number of element in the list
4763 * @max: the size of the array
4764 *
4765 * Build/extend a list of potential children allowed by the content tree
4766 *
4767 * returns the number of element in the list, or -1 in case of error.
4768 */
4769
4770int
4771xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4772 int *len, int max) {
4773 int i;
4774
4775 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4776 return(-1);
4777 if (*len >= max) return(*len);
4778
4779 switch (ctree->type) {
4780 case XML_ELEMENT_CONTENT_PCDATA:
4781 for (i = 0; i < *len;i++)
4782 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4783 list[(*len)++] = BAD_CAST "#PCDATA";
4784 break;
4785 case XML_ELEMENT_CONTENT_ELEMENT:
4786 for (i = 0; i < *len;i++)
4787 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4788 list[(*len)++] = ctree->name;
4789 break;
4790 case XML_ELEMENT_CONTENT_SEQ:
4791 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4792 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4793 break;
4794 case XML_ELEMENT_CONTENT_OR:
4795 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4796 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4797 break;
4798 }
4799
4800 return(*len);
4801}
4802
4803/**
4804 * xmlValidGetValidElements:
4805 * @prev: an element to insert after
4806 * @next: an element to insert next
4807 * @list: an array to store the list of child names
4808 * @max: the size of the array
4809 *
4810 * This function returns the list of authorized children to insert
4811 * within an existing tree while respecting the validity constraints
4812 * forced by the Dtd. The insertion point is defined using @prev and
4813 * @next in the following ways:
4814 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4815 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4816 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4817 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4818 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4819 *
4820 * pointers to the element names are inserted at the beginning of the array
4821 * and do not need to be freed.
4822 *
4823 * returns the number of element in the list, or -1 in case of error. If
4824 * the function returns the value @max the caller is invited to grow the
4825 * receiving array and retry.
4826 */
4827
4828int
4829xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4830 int max) {
4831 int nb_valid_elements = 0;
4832 const xmlChar *elements[256];
4833 int nb_elements = 0, i;
4834
4835 xmlNode *ref_node;
4836 xmlNode *parent;
4837 xmlNode *test_node;
4838
4839 xmlNode *prev_next;
4840 xmlNode *next_prev;
4841 xmlNode *parent_childs;
4842 xmlNode *parent_last;
4843
4844 xmlElement *element_desc;
4845
4846 if (prev == NULL && next == NULL)
4847 return(-1);
4848
4849 if (list == NULL) return(-1);
4850 if (max <= 0) return(-1);
4851
4852 nb_valid_elements = 0;
4853 ref_node = prev ? prev : next;
4854 parent = ref_node->parent;
4855
4856 /*
4857 * Retrieves the parent element declaration
4858 */
4859 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
4860 parent->name);
4861 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
4862 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
4863 parent->name);
4864 if (element_desc == NULL) return(-1);
4865
4866 /*
4867 * Do a backup of the current tree structure
4868 */
4869 prev_next = prev ? prev->next : NULL;
4870 next_prev = next ? next->prev : NULL;
4871 parent_childs = parent->children;
4872 parent_last = parent->last;
4873
4874 /*
4875 * Creates a dummy node and insert it into the tree
4876 */
4877 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
4878 test_node->doc = ref_node->doc;
4879 test_node->parent = parent;
4880 test_node->prev = prev;
4881 test_node->next = next;
4882
4883 if (prev) prev->next = test_node;
4884 else parent->children = test_node;
4885
4886 if (next) next->prev = test_node;
4887 else parent->last = test_node;
4888
4889 /*
4890 * Insert each potential child node and check if the parent is
4891 * still valid
4892 */
4893 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
4894 elements, &nb_elements, 256);
4895
4896 for (i = 0;i < nb_elements;i++) {
4897 test_node->name = elements[i];
4898 if (xmlValidateOneElement(NULL, parent->doc, parent)) {
4899 int j;
4900
4901 for (j = 0; j < nb_valid_elements;j++)
4902 if (xmlStrEqual(elements[i], list[j])) break;
4903 list[nb_valid_elements++] = elements[i];
4904 if (nb_valid_elements >= max) break;
4905 }
4906 }
4907
4908 /*
4909 * Restore the tree structure
4910 */
4911 if (prev) prev->next = prev_next;
4912 if (next) next->prev = next_prev;
4913 parent->children = parent_childs;
4914 parent->last = parent_last;
4915
4916 return(nb_valid_elements);
4917}