blob: 3ae61e2298cdb2e564e9840312e902fd9a11e596 [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 Veillard56a4cb82001-03-24 17:00:36 +000031/************************************************************************
32 * *
33 * When running GCC in vaacum cleaner mode *
34 * *
35 ************************************************************************/
36
37#ifdef __GNUC__
38#define UNUSED __attribute__((__unused__))
39#else
40#define UNUSED
41#endif
42
Owen Taylor3473f882001-02-23 17:55:21 +000043/*
44 * Generic function for accessing stacks in the Validity Context
45 */
46
47#define PUSH_AND_POP(scope, type, name) \
48scope int name##VPush(xmlValidCtxtPtr ctxt, type value) { \
49 if (ctxt->name##Nr >= ctxt->name##Max) { \
50 ctxt->name##Max *= 2; \
51 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
52 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
53 if (ctxt->name##Tab == NULL) { \
54 xmlGenericError(xmlGenericErrorContext, \
55 "realloc failed !\n"); \
56 return(0); \
57 } \
58 } \
59 ctxt->name##Tab[ctxt->name##Nr] = value; \
60 ctxt->name = value; \
61 return(ctxt->name##Nr++); \
62} \
63scope type name##VPop(xmlValidCtxtPtr ctxt) { \
64 type ret; \
65 if (ctxt->name##Nr <= 0) return(0); \
66 ctxt->name##Nr--; \
67 if (ctxt->name##Nr > 0) \
68 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
69 else \
70 ctxt->name = NULL; \
71 ret = ctxt->name##Tab[ctxt->name##Nr]; \
72 ctxt->name##Tab[ctxt->name##Nr] = 0; \
73 return(ret); \
74} \
75
76PUSH_AND_POP(static, xmlNodePtr, node)
77
78/* #define DEBUG_VALID_ALGO */
79
80#ifdef DEBUG_VALID_ALGO
81void xmlValidPrintNodeList(xmlNodePtr cur) {
82 if (cur == NULL)
83 xmlGenericError(xmlGenericErrorContext, "null ");
84 while (cur != NULL) {
85 switch (cur->type) {
86 case XML_ELEMENT_NODE:
87 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
88 break;
89 case XML_TEXT_NODE:
90 xmlGenericError(xmlGenericErrorContext, "text ");
91 break;
92 case XML_CDATA_SECTION_NODE:
93 xmlGenericError(xmlGenericErrorContext, "cdata ");
94 break;
95 case XML_ENTITY_REF_NODE:
96 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
97 break;
98 case XML_PI_NODE:
99 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
100 break;
101 case XML_COMMENT_NODE:
102 xmlGenericError(xmlGenericErrorContext, "comment ");
103 break;
104 case XML_ATTRIBUTE_NODE:
105 xmlGenericError(xmlGenericErrorContext, "?attr? ");
106 break;
107 case XML_ENTITY_NODE:
108 xmlGenericError(xmlGenericErrorContext, "?ent? ");
109 break;
110 case XML_DOCUMENT_NODE:
111 xmlGenericError(xmlGenericErrorContext, "?doc? ");
112 break;
113 case XML_DOCUMENT_TYPE_NODE:
114 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
115 break;
116 case XML_DOCUMENT_FRAG_NODE:
117 xmlGenericError(xmlGenericErrorContext, "?frag? ");
118 break;
119 case XML_NOTATION_NODE:
120 xmlGenericError(xmlGenericErrorContext, "?nota? ");
121 break;
122 case XML_HTML_DOCUMENT_NODE:
123 xmlGenericError(xmlGenericErrorContext, "?html? ");
124 break;
125 case XML_DTD_NODE:
126 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
127 break;
128 case XML_ELEMENT_DECL:
129 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
130 break;
131 case XML_ATTRIBUTE_DECL:
132 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
133 break;
134 case XML_ENTITY_DECL:
135 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
136 break;
137 }
138 cur = cur->next;
139 }
140}
141
142void xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
143 char expr[1000];
144
145 expr[0] = 0;
146 xmlGenericError(xmlGenericErrorContext, "valid: ");
147 xmlValidPrintNodeList(cur);
148 xmlGenericError(xmlGenericErrorContext, "against ");
149 xmlSprintfElementContent(expr, cont, 0);
150 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
151}
152
153#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarde356c282001-03-10 12:32:04 +0000154#define DEBUG_VALID_MSG(m) \
155 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
156
Owen Taylor3473f882001-02-23 17:55:21 +0000157#else
158#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000159#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000160#endif
161
162/* TODO: use hash table for accesses to elem and attribute dedinitions */
163
164#define VERROR \
165 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
166
167#define VWARNING \
168 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
169
170#define CHECK_DTD \
171 if (doc == NULL) return(0); \
172 else if ((doc->intSubset == NULL) && \
173 (doc->extSubset == NULL)) return(0)
174
175xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name);
176xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
177
178/************************************************************************
179 * *
180 * QName handling helper *
181 * *
182 ************************************************************************/
183
184/**
185 * xmlSplitQName2:
186 * @name: an XML parser context
187 * @prefix: a xmlChar **
188 *
189 * parse an XML qualified name string
190 *
191 * [NS 5] QName ::= (Prefix ':')? LocalPart
192 *
193 * [NS 6] Prefix ::= NCName
194 *
195 * [NS 7] LocalPart ::= NCName
196 *
197 * Returns NULL if not a QName, otherwise the local part, and prefix
198 * is updated to get the Prefix if any.
199 */
200
201xmlChar *
202xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
203 int len = 0;
204 xmlChar *ret = NULL;
205
206 *prefix = NULL;
207
208 /* xml: prefix is not really a namespace */
209 if ((name[0] == 'x') && (name[1] == 'm') &&
210 (name[2] == 'l') && (name[3] == ':'))
211 return(NULL);
212
213 /* nasty but valid */
214 if (name[0] == ':')
215 return(NULL);
216
217 /*
218 * we are not trying to validate but just to cut, and yes it will
219 * work even if this is as set of UTF-8 encoded chars
220 */
221 while ((name[len] != 0) && (name[len] != ':'))
222 len++;
223
224 if (name[len] == 0)
225 return(NULL);
226
227 *prefix = xmlStrndup(name, len);
228 ret = xmlStrdup(&name[len + 1]);
229
230 return(ret);
231}
232
233/****************************************************************
234 * *
235 * Util functions for data allocation/deallocation *
236 * *
237 ****************************************************************/
238
239/**
240 * xmlNewElementContent:
241 * @name: the subelement name or NULL
242 * @type: the type of element content decl
243 *
244 * Allocate an element content structure.
245 *
246 * Returns NULL if not, othervise the new element content structure
247 */
248xmlElementContentPtr
249xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
250 xmlElementContentPtr ret;
251
252 switch(type) {
253 case XML_ELEMENT_CONTENT_ELEMENT:
254 if (name == NULL) {
255 xmlGenericError(xmlGenericErrorContext,
256 "xmlNewElementContent : name == NULL !\n");
257 }
258 break;
259 case XML_ELEMENT_CONTENT_PCDATA:
260 case XML_ELEMENT_CONTENT_SEQ:
261 case XML_ELEMENT_CONTENT_OR:
262 if (name != NULL) {
263 xmlGenericError(xmlGenericErrorContext,
264 "xmlNewElementContent : name != NULL !\n");
265 }
266 break;
267 default:
268 xmlGenericError(xmlGenericErrorContext,
269 "xmlNewElementContent: unknown type %d\n", type);
270 return(NULL);
271 }
272 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
273 if (ret == NULL) {
274 xmlGenericError(xmlGenericErrorContext,
275 "xmlNewElementContent : out of memory!\n");
276 return(NULL);
277 }
278 ret->type = type;
279 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
280 if (name != NULL)
281 ret->name = xmlStrdup(name);
282 else
283 ret->name = NULL;
284 ret->c1 = ret->c2 = NULL;
285 return(ret);
286}
287
288/**
289 * xmlCopyElementContent:
290 * @content: An element content pointer.
291 *
292 * Build a copy of an element content description.
293 *
294 * Returns the new xmlElementContentPtr or NULL in case of error.
295 */
296xmlElementContentPtr
297xmlCopyElementContent(xmlElementContentPtr cur) {
298 xmlElementContentPtr ret;
299
300 if (cur == NULL) return(NULL);
301 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
302 if (ret == NULL) {
303 xmlGenericError(xmlGenericErrorContext,
304 "xmlCopyElementContent : out of memory\n");
305 return(NULL);
306 }
307 ret->ocur = cur->ocur;
308 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
309 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
310 return(ret);
311}
312
313/**
314 * xmlFreeElementContent:
315 * @cur: the element content tree to free
316 *
317 * Free an element content structure. This is a recursive call !
318 */
319void
320xmlFreeElementContent(xmlElementContentPtr cur) {
321 if (cur == NULL) return;
322 switch (cur->type) {
323 case XML_ELEMENT_CONTENT_PCDATA:
324 case XML_ELEMENT_CONTENT_ELEMENT:
325 case XML_ELEMENT_CONTENT_SEQ:
326 case XML_ELEMENT_CONTENT_OR:
327 break;
328 default:
329 xmlGenericError(xmlGenericErrorContext,
330 "xmlFreeElementContent : type %d\n", cur->type);
331 return;
332 }
333 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
334 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
335 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillard48b2f892001-02-25 16:11:03 +0000336 MEM_CLEANUP(cur, sizeof(xmlElementContent));
Owen Taylor3473f882001-02-23 17:55:21 +0000337 xmlFree(cur);
338}
339
340/**
341 * xmlDumpElementContent:
342 * @buf: An XML buffer
343 * @content: An element table
344 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
345 *
346 * This will dump the content of the element table as an XML DTD definition
347 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000348static void
Owen Taylor3473f882001-02-23 17:55:21 +0000349xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
350 if (content == NULL) return;
351
352 if (glob) xmlBufferWriteChar(buf, "(");
353 switch (content->type) {
354 case XML_ELEMENT_CONTENT_PCDATA:
355 xmlBufferWriteChar(buf, "#PCDATA");
356 break;
357 case XML_ELEMENT_CONTENT_ELEMENT:
358 xmlBufferWriteCHAR(buf, content->name);
359 break;
360 case XML_ELEMENT_CONTENT_SEQ:
361 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
362 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
363 xmlDumpElementContent(buf, content->c1, 1);
364 else
365 xmlDumpElementContent(buf, content->c1, 0);
366 xmlBufferWriteChar(buf, " , ");
367 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
368 xmlDumpElementContent(buf, content->c2, 1);
369 else
370 xmlDumpElementContent(buf, content->c2, 0);
371 break;
372 case XML_ELEMENT_CONTENT_OR:
373 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
374 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
375 xmlDumpElementContent(buf, content->c1, 1);
376 else
377 xmlDumpElementContent(buf, content->c1, 0);
378 xmlBufferWriteChar(buf, " | ");
379 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
380 xmlDumpElementContent(buf, content->c2, 1);
381 else
382 xmlDumpElementContent(buf, content->c2, 0);
383 break;
384 default:
385 xmlGenericError(xmlGenericErrorContext,
386 "xmlDumpElementContent: unknown type %d\n",
387 content->type);
388 }
389 if (glob)
390 xmlBufferWriteChar(buf, ")");
391 switch (content->ocur) {
392 case XML_ELEMENT_CONTENT_ONCE:
393 break;
394 case XML_ELEMENT_CONTENT_OPT:
395 xmlBufferWriteChar(buf, "?");
396 break;
397 case XML_ELEMENT_CONTENT_MULT:
398 xmlBufferWriteChar(buf, "*");
399 break;
400 case XML_ELEMENT_CONTENT_PLUS:
401 xmlBufferWriteChar(buf, "+");
402 break;
403 }
404}
405
406/**
407 * xmlSprintfElementContent:
408 * @buf: an output buffer
409 * @content: An element table
410 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
411 *
412 * This will dump the content of the element content definition
413 * Intended just for the debug routine
414 */
415void
416xmlSprintfElementContent(char *buf, xmlElementContentPtr content, int glob) {
417 if (content == NULL) return;
418 if (glob) strcat(buf, "(");
419 switch (content->type) {
420 case XML_ELEMENT_CONTENT_PCDATA:
421 strcat(buf, "#PCDATA");
422 break;
423 case XML_ELEMENT_CONTENT_ELEMENT:
424 strcat(buf, (char *) content->name);
425 break;
426 case XML_ELEMENT_CONTENT_SEQ:
427 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
428 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
429 xmlSprintfElementContent(buf, content->c1, 1);
430 else
431 xmlSprintfElementContent(buf, content->c1, 0);
432 strcat(buf, " , ");
433 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
434 xmlSprintfElementContent(buf, content->c2, 1);
435 else
436 xmlSprintfElementContent(buf, content->c2, 0);
437 break;
438 case XML_ELEMENT_CONTENT_OR:
439 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
440 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
441 xmlSprintfElementContent(buf, content->c1, 1);
442 else
443 xmlSprintfElementContent(buf, content->c1, 0);
444 strcat(buf, " | ");
445 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
446 xmlSprintfElementContent(buf, content->c2, 1);
447 else
448 xmlSprintfElementContent(buf, content->c2, 0);
449 break;
450 }
451 if (glob)
452 strcat(buf, ")");
453 switch (content->ocur) {
454 case XML_ELEMENT_CONTENT_ONCE:
455 break;
456 case XML_ELEMENT_CONTENT_OPT:
457 strcat(buf, "?");
458 break;
459 case XML_ELEMENT_CONTENT_MULT:
460 strcat(buf, "*");
461 break;
462 case XML_ELEMENT_CONTENT_PLUS:
463 strcat(buf, "+");
464 break;
465 }
466}
467
468/****************************************************************
469 * *
470 * Registration of DTD declarations *
471 * *
472 ****************************************************************/
473
474/**
475 * xmlCreateElementTable:
476 *
477 * create and initialize an empty element hash table.
478 *
479 * Returns the xmlElementTablePtr just created or NULL in case of error.
480 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000481static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000482xmlCreateElementTable(void) {
483 return(xmlHashCreate(0));
484}
485
486/**
487 * xmlFreeElement:
488 * @elem: An element
489 *
490 * Deallocate the memory used by an element definition
491 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000492static void
Owen Taylor3473f882001-02-23 17:55:21 +0000493xmlFreeElement(xmlElementPtr elem) {
494 if (elem == NULL) return;
495 xmlUnlinkNode((xmlNodePtr) elem);
496 xmlFreeElementContent(elem->content);
497 if (elem->name != NULL)
498 xmlFree((xmlChar *) elem->name);
499 if (elem->prefix != NULL)
500 xmlFree((xmlChar *) elem->prefix);
Daniel Veillard48b2f892001-02-25 16:11:03 +0000501 MEM_CLEANUP(elem, sizeof(xmlElement));
Owen Taylor3473f882001-02-23 17:55:21 +0000502 xmlFree(elem);
503}
504
505
506/**
507 * xmlAddElementDecl:
508 * @ctxt: the validation context
509 * @dtd: pointer to the DTD
510 * @name: the entity name
511 * @type: the element type
512 * @content: the element content tree or NULL
513 *
514 * Register a new element declaration
515 *
516 * Returns NULL if not, othervise the entity
517 */
518xmlElementPtr
519xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
520 xmlElementTypeVal type,
521 xmlElementContentPtr content) {
522 xmlElementPtr ret;
523 xmlElementTablePtr table;
524 xmlChar *ns, *uqname;
525
526 if (dtd == NULL) {
527 xmlGenericError(xmlGenericErrorContext,
528 "xmlAddElementDecl: dtd == NULL\n");
529 return(NULL);
530 }
531 if (name == NULL) {
532 xmlGenericError(xmlGenericErrorContext,
533 "xmlAddElementDecl: name == NULL\n");
534 return(NULL);
535 }
536 switch (type) {
537 case XML_ELEMENT_TYPE_EMPTY:
538 if (content != NULL) {
539 xmlGenericError(xmlGenericErrorContext,
540 "xmlAddElementDecl: content != NULL for EMPTY\n");
541 return(NULL);
542 }
543 break;
544 case XML_ELEMENT_TYPE_ANY:
545 if (content != NULL) {
546 xmlGenericError(xmlGenericErrorContext,
547 "xmlAddElementDecl: content != NULL for ANY\n");
548 return(NULL);
549 }
550 break;
551 case XML_ELEMENT_TYPE_MIXED:
552 if (content == NULL) {
553 xmlGenericError(xmlGenericErrorContext,
554 "xmlAddElementDecl: content == NULL for MIXED\n");
555 return(NULL);
556 }
557 break;
558 case XML_ELEMENT_TYPE_ELEMENT:
559 if (content == NULL) {
560 xmlGenericError(xmlGenericErrorContext,
561 "xmlAddElementDecl: content == NULL for ELEMENT\n");
562 return(NULL);
563 }
564 break;
565 default:
566 xmlGenericError(xmlGenericErrorContext,
567 "xmlAddElementDecl: unknown type %d\n", type);
568 return(NULL);
569 }
570
571 /*
572 * check if name is a QName
573 */
574 uqname = xmlSplitQName2(name, &ns);
575 if (uqname != NULL)
576 name = uqname;
577
578 /*
579 * Create the Element table if needed.
580 */
581 table = (xmlElementTablePtr) dtd->elements;
582 if (table == NULL) {
583 table = xmlCreateElementTable();
584 dtd->elements = (void *) table;
585 }
586 if (table == NULL) {
587 xmlGenericError(xmlGenericErrorContext,
588 "xmlAddElementDecl: Table creation failed!\n");
589 return(NULL);
590 }
591
592 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
593 if (ret == NULL) {
594 xmlGenericError(xmlGenericErrorContext,
595 "xmlAddElementDecl: out of memory\n");
596 return(NULL);
597 }
598 memset(ret, 0, sizeof(xmlElement));
599 ret->type = XML_ELEMENT_DECL;
600
601 /*
602 * fill the structure.
603 */
604 ret->etype = type;
605 ret->name = xmlStrdup(name);
606 ret->prefix = ns;
607 ret->content = xmlCopyElementContent(content);
608 ret->attributes = xmlScanAttributeDecl(dtd, name);
609
610 /*
611 * Validity Check:
612 * Insertion must not fail
613 */
614 if (xmlHashAddEntry2(table, name, ns, ret)) {
615 /*
616 * The element is already defined in this Dtd.
617 */
618 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
619 xmlFreeElement(ret);
620 if (uqname != NULL)
621 xmlFree(uqname);
622 return(NULL);
623 }
624
625 /*
626 * Link it to the Dtd
627 */
628 ret->parent = dtd;
629 ret->doc = dtd->doc;
630 if (dtd->last == NULL) {
631 dtd->children = dtd->last = (xmlNodePtr) ret;
632 } else {
633 dtd->last->next = (xmlNodePtr) ret;
634 ret->prev = dtd->last;
635 dtd->last = (xmlNodePtr) ret;
636 }
637 if (uqname != NULL)
638 xmlFree(uqname);
639 return(ret);
640}
641
642/**
643 * xmlFreeElementTable:
644 * @table: An element table
645 *
646 * Deallocate the memory used by an element hash table.
647 */
648void
649xmlFreeElementTable(xmlElementTablePtr table) {
650 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
651}
652
653/**
654 * xmlCopyElement:
655 * @elem: An element
656 *
657 * Build a copy of an element.
658 *
659 * Returns the new xmlElementPtr or NULL in case of error.
660 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000661static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000662xmlCopyElement(xmlElementPtr elem) {
663 xmlElementPtr cur;
664
665 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
666 if (cur == NULL) {
667 xmlGenericError(xmlGenericErrorContext,
668 "xmlCopyElement: out of memory !\n");
669 return(NULL);
670 }
671 memset(cur, 0, sizeof(xmlElement));
672 cur->type = XML_ELEMENT_DECL;
673 cur->etype = elem->etype;
674 if (elem->name != NULL)
675 cur->name = xmlStrdup(elem->name);
676 else
677 cur->name = NULL;
678 if (elem->prefix != NULL)
679 cur->prefix = xmlStrdup(elem->prefix);
680 else
681 cur->prefix = NULL;
682 cur->content = xmlCopyElementContent(elem->content);
683 /* TODO : rebuild the attribute list on the copy */
684 cur->attributes = NULL;
685 return(cur);
686}
687
688/**
689 * xmlCopyElementTable:
690 * @table: An element table
691 *
692 * Build a copy of an element table.
693 *
694 * Returns the new xmlElementTablePtr or NULL in case of error.
695 */
696xmlElementTablePtr
697xmlCopyElementTable(xmlElementTablePtr table) {
698 return((xmlElementTablePtr) xmlHashCopy(table,
699 (xmlHashCopier) xmlCopyElement));
700}
701
702/**
703 * xmlDumpElementDecl:
704 * @buf: the XML buffer output
705 * @elem: An element table
706 *
707 * This will dump the content of the element declaration as an XML
708 * DTD definition
709 */
710void
711xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
712 switch (elem->etype) {
713 case XML_ELEMENT_TYPE_EMPTY:
714 xmlBufferWriteChar(buf, "<!ELEMENT ");
715 xmlBufferWriteCHAR(buf, elem->name);
716 xmlBufferWriteChar(buf, " EMPTY>\n");
717 break;
718 case XML_ELEMENT_TYPE_ANY:
719 xmlBufferWriteChar(buf, "<!ELEMENT ");
720 xmlBufferWriteCHAR(buf, elem->name);
721 xmlBufferWriteChar(buf, " ANY>\n");
722 break;
723 case XML_ELEMENT_TYPE_MIXED:
724 xmlBufferWriteChar(buf, "<!ELEMENT ");
725 xmlBufferWriteCHAR(buf, elem->name);
726 xmlBufferWriteChar(buf, " ");
727 xmlDumpElementContent(buf, elem->content, 1);
728 xmlBufferWriteChar(buf, ">\n");
729 break;
730 case XML_ELEMENT_TYPE_ELEMENT:
731 xmlBufferWriteChar(buf, "<!ELEMENT ");
732 xmlBufferWriteCHAR(buf, elem->name);
733 xmlBufferWriteChar(buf, " ");
734 xmlDumpElementContent(buf, elem->content, 1);
735 xmlBufferWriteChar(buf, ">\n");
736 break;
737 default:
738 xmlGenericError(xmlGenericErrorContext,
739 "xmlDumpElementDecl: internal: unknown type %d\n",
740 elem->etype);
741 }
742}
743
744/**
745 * xmlDumpElementTable:
746 * @buf: the XML buffer output
747 * @table: An element table
748 *
749 * This will dump the content of the element table as an XML DTD definition
750 */
751void
752xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
753 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
754}
755
756/**
757 * xmlCreateEnumeration:
758 * @name: the enumeration name or NULL
759 *
760 * create and initialize an enumeration attribute node.
761 *
762 * Returns the xmlEnumerationPtr just created or NULL in case
763 * of error.
764 */
765xmlEnumerationPtr
766xmlCreateEnumeration(xmlChar *name) {
767 xmlEnumerationPtr ret;
768
769 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
770 if (ret == NULL) {
771 xmlGenericError(xmlGenericErrorContext,
772 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
773 (long)sizeof(xmlEnumeration));
774 return(NULL);
775 }
776 memset(ret, 0, sizeof(xmlEnumeration));
777
778 if (name != NULL)
779 ret->name = xmlStrdup(name);
780 return(ret);
781}
782
783/**
784 * xmlFreeEnumeration:
785 * @cur: the tree to free.
786 *
787 * free an enumeration attribute node (recursive).
788 */
789void
790xmlFreeEnumeration(xmlEnumerationPtr cur) {
791 if (cur == NULL) return;
792
793 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
794
795 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillard48b2f892001-02-25 16:11:03 +0000796 MEM_CLEANUP(cur, sizeof(xmlEnumeration));
Owen Taylor3473f882001-02-23 17:55:21 +0000797 xmlFree(cur);
798}
799
800/**
801 * xmlCopyEnumeration:
802 * @cur: the tree to copy.
803 *
804 * Copy an enumeration attribute node (recursive).
805 *
806 * Returns the xmlEnumerationPtr just created or NULL in case
807 * of error.
808 */
809xmlEnumerationPtr
810xmlCopyEnumeration(xmlEnumerationPtr cur) {
811 xmlEnumerationPtr ret;
812
813 if (cur == NULL) return(NULL);
814 ret = xmlCreateEnumeration((xmlChar *) cur->name);
815
816 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
817 else ret->next = NULL;
818
819 return(ret);
820}
821
822/**
823 * xmlDumpEnumeration:
824 * @buf: the XML buffer output
825 * @enum: An enumeration
826 *
827 * This will dump the content of the enumeration
828 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000829static void
Owen Taylor3473f882001-02-23 17:55:21 +0000830xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
831 if (cur == NULL) return;
832
833 xmlBufferWriteCHAR(buf, cur->name);
834 if (cur->next == NULL)
835 xmlBufferWriteChar(buf, ")");
836 else {
837 xmlBufferWriteChar(buf, " | ");
838 xmlDumpEnumeration(buf, cur->next);
839 }
840}
841
842/**
843 * xmlCreateAttributeTable:
844 *
845 * create and initialize an empty attribute hash table.
846 *
847 * Returns the xmlAttributeTablePtr just created or NULL in case
848 * of error.
849 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000850static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000851xmlCreateAttributeTable(void) {
852 return(xmlHashCreate(0));
853}
854
855/**
856 * xmlScanAttributeDeclCallback:
857 * @attr: the attribute decl
858 * @list: the list to update
859 *
860 * Callback called by xmlScanAttributeDecl when a new attribute
861 * has to be entered in the list.
862 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000863static void
Owen Taylor3473f882001-02-23 17:55:21 +0000864xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000865 const xmlChar* name UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +0000866 attr->nexth = *list;
867 *list = attr;
868}
869
870/**
871 * xmlScanAttributeDecl:
872 * @dtd: pointer to the DTD
873 * @elem: the element name
874 *
875 * When inserting a new element scan the DtD for existing attributes
876 * for taht element and initialize the Attribute chain
877 *
878 * Returns the pointer to the first attribute decl in the chain,
879 * possibly NULL.
880 */
881xmlAttributePtr
882xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
883 xmlAttributePtr ret = NULL;
884 xmlAttributeTablePtr table;
885
886 if (dtd == NULL) {
887 xmlGenericError(xmlGenericErrorContext,
888 "xmlScanAttributeDecl: dtd == NULL\n");
889 return(NULL);
890 }
891 if (elem == NULL) {
892 xmlGenericError(xmlGenericErrorContext,
893 "xmlScanAttributeDecl: elem == NULL\n");
894 return(NULL);
895 }
896 table = (xmlAttributeTablePtr) dtd->attributes;
897 if (table == NULL)
898 return(NULL);
899
900 /* WRONG !!! */
901 xmlHashScan3(table, NULL, NULL, elem,
902 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
903 return(ret);
904}
905
906/**
907 * xmlScanIDAttributeDecl:
908 * @ctxt: the validation context
909 * @elem: the element name
910 *
911 * Verify that the element don't have too many ID attributes
912 * declared.
913 *
914 * Returns the number of ID attributes found.
915 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000916static int
Owen Taylor3473f882001-02-23 17:55:21 +0000917xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
918 xmlAttributePtr cur;
919 int ret = 0;
920
921 if (elem == NULL) return(0);
922 cur = elem->attributes;
923 while (cur != NULL) {
924 if (cur->atype == XML_ATTRIBUTE_ID) {
925 ret ++;
926 if (ret > 1)
927 VERROR(ctxt->userData,
928 "Element %s has too may ID attributes defined : %s\n",
929 elem->name, cur->name);
930 }
931 cur = cur->nexth;
932 }
933 return(ret);
934}
935
936/**
937 * xmlFreeAttribute:
938 * @elem: An attribute
939 *
940 * Deallocate the memory used by an attribute definition
941 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000942static void
Owen Taylor3473f882001-02-23 17:55:21 +0000943xmlFreeAttribute(xmlAttributePtr attr) {
944 if (attr == NULL) return;
945 xmlUnlinkNode((xmlNodePtr) attr);
946 if (attr->tree != NULL)
947 xmlFreeEnumeration(attr->tree);
948 if (attr->elem != NULL)
949 xmlFree((xmlChar *) attr->elem);
950 if (attr->name != NULL)
951 xmlFree((xmlChar *) attr->name);
952 if (attr->defaultValue != NULL)
953 xmlFree((xmlChar *) attr->defaultValue);
954 if (attr->prefix != NULL)
955 xmlFree((xmlChar *) attr->prefix);
Daniel Veillard48b2f892001-02-25 16:11:03 +0000956 MEM_CLEANUP(attr, sizeof(xmlAttribute));
Owen Taylor3473f882001-02-23 17:55:21 +0000957 xmlFree(attr);
958}
959
960
961/**
962 * xmlAddAttributeDecl:
963 * @ctxt: the validation context
964 * @dtd: pointer to the DTD
965 * @elem: the element name
966 * @name: the attribute name
967 * @ns: the attribute namespace prefix
968 * @type: the attribute type
969 * @def: the attribute default type
970 * @defaultValue: the attribute default value
971 * @tree: if it's an enumeration, the associated list
972 *
973 * Register a new attribute declaration
974 * Note that @tree becomes the ownership of the DTD
975 *
976 * Returns NULL if not new, othervise the attribute decl
977 */
978xmlAttributePtr
979xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
980 const xmlChar *name, const xmlChar *ns,
981 xmlAttributeType type, xmlAttributeDefault def,
982 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
983 xmlAttributePtr ret;
984 xmlAttributeTablePtr table;
985 xmlElementPtr elemDef;
986
987 if (dtd == NULL) {
988 xmlGenericError(xmlGenericErrorContext,
989 "xmlAddAttributeDecl: dtd == NULL\n");
990 xmlFreeEnumeration(tree);
991 return(NULL);
992 }
993 if (name == NULL) {
994 xmlGenericError(xmlGenericErrorContext,
995 "xmlAddAttributeDecl: name == NULL\n");
996 xmlFreeEnumeration(tree);
997 return(NULL);
998 }
999 if (elem == NULL) {
1000 xmlGenericError(xmlGenericErrorContext,
1001 "xmlAddAttributeDecl: elem == NULL\n");
1002 xmlFreeEnumeration(tree);
1003 return(NULL);
1004 }
1005 /*
1006 * Check the type and possibly the default value.
1007 */
1008 switch (type) {
1009 case XML_ATTRIBUTE_CDATA:
1010 break;
1011 case XML_ATTRIBUTE_ID:
1012 break;
1013 case XML_ATTRIBUTE_IDREF:
1014 break;
1015 case XML_ATTRIBUTE_IDREFS:
1016 break;
1017 case XML_ATTRIBUTE_ENTITY:
1018 break;
1019 case XML_ATTRIBUTE_ENTITIES:
1020 break;
1021 case XML_ATTRIBUTE_NMTOKEN:
1022 break;
1023 case XML_ATTRIBUTE_NMTOKENS:
1024 break;
1025 case XML_ATTRIBUTE_ENUMERATION:
1026 break;
1027 case XML_ATTRIBUTE_NOTATION:
1028 break;
1029 default:
1030 xmlGenericError(xmlGenericErrorContext,
1031 "xmlAddAttributeDecl: unknown type %d\n", type);
1032 xmlFreeEnumeration(tree);
1033 return(NULL);
1034 }
1035 if ((defaultValue != NULL) &&
1036 (!xmlValidateAttributeValue(type, defaultValue))) {
1037 VERROR(ctxt->userData, "Attribute %s on %s: invalid default value\n",
1038 elem, name, defaultValue);
1039 defaultValue = NULL;
1040 }
1041
1042 /*
1043 * Create the Attribute table if needed.
1044 */
1045 table = (xmlAttributeTablePtr) dtd->attributes;
1046 if (table == NULL) {
1047 table = xmlCreateAttributeTable();
1048 dtd->attributes = (void *) table;
1049 }
1050 if (table == NULL) {
1051 xmlGenericError(xmlGenericErrorContext,
1052 "xmlAddAttributeDecl: Table creation failed!\n");
1053 return(NULL);
1054 }
1055
1056
1057 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1058 if (ret == NULL) {
1059 xmlGenericError(xmlGenericErrorContext,
1060 "xmlAddAttributeDecl: out of memory\n");
1061 return(NULL);
1062 }
1063 memset(ret, 0, sizeof(xmlAttribute));
1064 ret->type = XML_ATTRIBUTE_DECL;
1065
1066 /*
1067 * fill the structure.
1068 */
1069 ret->atype = type;
1070 ret->name = xmlStrdup(name);
1071 ret->prefix = xmlStrdup(ns);
1072 ret->elem = xmlStrdup(elem);
1073 ret->def = def;
1074 ret->tree = tree;
1075 if (defaultValue != NULL)
1076 ret->defaultValue = xmlStrdup(defaultValue);
1077
1078 /*
1079 * Validity Check:
1080 * Search the DTD for previous declarations of the ATTLIST
1081 */
1082 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1083 /*
1084 * The attribute is already defined in this Dtd.
1085 */
1086 VWARNING(ctxt->userData,
1087 "Attribute %s on %s: already defined\n",
1088 name, elem);
1089 xmlFreeAttribute(ret);
1090 return(NULL);
1091 }
1092
1093 /*
1094 * Validity Check:
1095 * Multiple ID per element
1096 */
1097 elemDef = xmlGetDtdElementDesc(dtd, elem);
1098 if (elemDef != NULL) {
1099 if ((type == XML_ATTRIBUTE_ID) &&
1100 (xmlScanIDAttributeDecl(NULL, elemDef) != 0))
1101 VERROR(ctxt->userData,
1102 "Element %s has too may ID attributes defined : %s\n",
1103 elem, name);
1104 ret->nexth = elemDef->attributes;
1105 elemDef->attributes = ret;
1106 }
1107
1108 /*
1109 * Link it to the Dtd
1110 */
1111 ret->parent = dtd;
1112 ret->doc = dtd->doc;
1113 if (dtd->last == NULL) {
1114 dtd->children = dtd->last = (xmlNodePtr) ret;
1115 } else {
1116 dtd->last->next = (xmlNodePtr) ret;
1117 ret->prev = dtd->last;
1118 dtd->last = (xmlNodePtr) ret;
1119 }
1120 return(ret);
1121}
1122
1123/**
1124 * xmlFreeAttributeTable:
1125 * @table: An attribute table
1126 *
1127 * Deallocate the memory used by an entities hash table.
1128 */
1129void
1130xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1131 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1132}
1133
1134/**
1135 * xmlCopyAttribute:
1136 * @attr: An attribute
1137 *
1138 * Build a copy of an attribute.
1139 *
1140 * Returns the new xmlAttributePtr or NULL in case of error.
1141 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001142static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001143xmlCopyAttribute(xmlAttributePtr attr) {
1144 xmlAttributePtr cur;
1145
1146 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1147 if (cur == NULL) {
1148 xmlGenericError(xmlGenericErrorContext,
1149 "xmlCopyAttribute: out of memory !\n");
1150 return(NULL);
1151 }
1152 memset(cur, 0, sizeof(xmlAttribute));
1153 cur->atype = attr->atype;
1154 cur->def = attr->def;
1155 cur->tree = xmlCopyEnumeration(attr->tree);
1156 if (attr->elem != NULL)
1157 cur->elem = xmlStrdup(attr->elem);
1158 if (attr->name != NULL)
1159 cur->name = xmlStrdup(attr->name);
1160 if (attr->defaultValue != NULL)
1161 cur->defaultValue = xmlStrdup(attr->defaultValue);
1162 return(cur);
1163}
1164
1165/**
1166 * xmlCopyAttributeTable:
1167 * @table: An attribute table
1168 *
1169 * Build a copy of an attribute table.
1170 *
1171 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1172 */
1173xmlAttributeTablePtr
1174xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1175 return((xmlAttributeTablePtr) xmlHashCopy(table,
1176 (xmlHashCopier) xmlCopyAttribute));
1177}
1178
1179/**
1180 * xmlDumpAttributeDecl:
1181 * @buf: the XML buffer output
1182 * @attr: An attribute declaration
1183 *
1184 * This will dump the content of the attribute declaration as an XML
1185 * DTD definition
1186 */
1187void
1188xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1189 xmlBufferWriteChar(buf, "<!ATTLIST ");
1190 xmlBufferWriteCHAR(buf, attr->elem);
1191 xmlBufferWriteChar(buf, " ");
1192 if (attr->prefix != NULL) {
1193 xmlBufferWriteCHAR(buf, attr->prefix);
1194 xmlBufferWriteChar(buf, ":");
1195 }
1196 xmlBufferWriteCHAR(buf, attr->name);
1197 switch (attr->atype) {
1198 case XML_ATTRIBUTE_CDATA:
1199 xmlBufferWriteChar(buf, " CDATA");
1200 break;
1201 case XML_ATTRIBUTE_ID:
1202 xmlBufferWriteChar(buf, " ID");
1203 break;
1204 case XML_ATTRIBUTE_IDREF:
1205 xmlBufferWriteChar(buf, " IDREF");
1206 break;
1207 case XML_ATTRIBUTE_IDREFS:
1208 xmlBufferWriteChar(buf, " IDREFS");
1209 break;
1210 case XML_ATTRIBUTE_ENTITY:
1211 xmlBufferWriteChar(buf, " ENTITY");
1212 break;
1213 case XML_ATTRIBUTE_ENTITIES:
1214 xmlBufferWriteChar(buf, " ENTITIES");
1215 break;
1216 case XML_ATTRIBUTE_NMTOKEN:
1217 xmlBufferWriteChar(buf, " NMTOKEN");
1218 break;
1219 case XML_ATTRIBUTE_NMTOKENS:
1220 xmlBufferWriteChar(buf, " NMTOKENS");
1221 break;
1222 case XML_ATTRIBUTE_ENUMERATION:
1223 xmlBufferWriteChar(buf, " (");
1224 xmlDumpEnumeration(buf, attr->tree);
1225 break;
1226 case XML_ATTRIBUTE_NOTATION:
1227 xmlBufferWriteChar(buf, " NOTATION (");
1228 xmlDumpEnumeration(buf, attr->tree);
1229 break;
1230 default:
1231 xmlGenericError(xmlGenericErrorContext,
1232 "xmlDumpAttributeTable: internal: unknown type %d\n",
1233 attr->atype);
1234 }
1235 switch (attr->def) {
1236 case XML_ATTRIBUTE_NONE:
1237 break;
1238 case XML_ATTRIBUTE_REQUIRED:
1239 xmlBufferWriteChar(buf, " #REQUIRED");
1240 break;
1241 case XML_ATTRIBUTE_IMPLIED:
1242 xmlBufferWriteChar(buf, " #IMPLIED");
1243 break;
1244 case XML_ATTRIBUTE_FIXED:
1245 xmlBufferWriteChar(buf, " #FIXED");
1246 break;
1247 default:
1248 xmlGenericError(xmlGenericErrorContext,
1249 "xmlDumpAttributeTable: internal: unknown default %d\n",
1250 attr->def);
1251 }
1252 if (attr->defaultValue != NULL) {
1253 xmlBufferWriteChar(buf, " ");
1254 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1255 }
1256 xmlBufferWriteChar(buf, ">\n");
1257}
1258
1259/**
1260 * xmlDumpAttributeTable:
1261 * @buf: the XML buffer output
1262 * @table: An attribute table
1263 *
1264 * This will dump the content of the attribute table as an XML DTD definition
1265 */
1266void
1267xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1268 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1269}
1270
1271/************************************************************************
1272 * *
1273 * NOTATIONs *
1274 * *
1275 ************************************************************************/
1276/**
1277 * xmlCreateNotationTable:
1278 *
1279 * create and initialize an empty notation hash table.
1280 *
1281 * Returns the xmlNotationTablePtr just created or NULL in case
1282 * of error.
1283 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001284static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001285xmlCreateNotationTable(void) {
1286 return(xmlHashCreate(0));
1287}
1288
1289/**
1290 * xmlFreeNotation:
1291 * @not: A notation
1292 *
1293 * Deallocate the memory used by an notation definition
1294 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001295static void
Owen Taylor3473f882001-02-23 17:55:21 +00001296xmlFreeNotation(xmlNotationPtr nota) {
1297 if (nota == NULL) return;
1298 if (nota->name != NULL)
1299 xmlFree((xmlChar *) nota->name);
1300 if (nota->PublicID != NULL)
1301 xmlFree((xmlChar *) nota->PublicID);
1302 if (nota->SystemID != NULL)
1303 xmlFree((xmlChar *) nota->SystemID);
Daniel Veillard48b2f892001-02-25 16:11:03 +00001304 MEM_CLEANUP(nota, sizeof(xmlNotation));
Owen Taylor3473f882001-02-23 17:55:21 +00001305 xmlFree(nota);
1306}
1307
1308
1309/**
1310 * xmlAddNotationDecl:
1311 * @dtd: pointer to the DTD
1312 * @ctxt: the validation context
1313 * @name: the entity name
1314 * @PublicID: the public identifier or NULL
1315 * @SystemID: the system identifier or NULL
1316 *
1317 * Register a new notation declaration
1318 *
1319 * Returns NULL if not, othervise the entity
1320 */
1321xmlNotationPtr
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001322xmlAddNotationDecl(xmlValidCtxtPtr ctxt UNUSED, xmlDtdPtr dtd,
1323 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001324 const xmlChar *PublicID, const xmlChar *SystemID) {
1325 xmlNotationPtr ret;
1326 xmlNotationTablePtr table;
1327
1328 if (dtd == NULL) {
1329 xmlGenericError(xmlGenericErrorContext,
1330 "xmlAddNotationDecl: dtd == NULL\n");
1331 return(NULL);
1332 }
1333 if (name == NULL) {
1334 xmlGenericError(xmlGenericErrorContext,
1335 "xmlAddNotationDecl: name == NULL\n");
1336 return(NULL);
1337 }
1338 if ((PublicID == NULL) && (SystemID == NULL)) {
1339 xmlGenericError(xmlGenericErrorContext,
1340 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
1341 }
1342
1343 /*
1344 * Create the Notation table if needed.
1345 */
1346 table = (xmlNotationTablePtr) dtd->notations;
1347 if (table == NULL)
1348 dtd->notations = table = xmlCreateNotationTable();
1349 if (table == NULL) {
1350 xmlGenericError(xmlGenericErrorContext,
1351 "xmlAddNotationDecl: Table creation failed!\n");
1352 return(NULL);
1353 }
1354
1355 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1356 if (ret == NULL) {
1357 xmlGenericError(xmlGenericErrorContext,
1358 "xmlAddNotationDecl: out of memory\n");
1359 return(NULL);
1360 }
1361 memset(ret, 0, sizeof(xmlNotation));
1362
1363 /*
1364 * fill the structure.
1365 */
1366 ret->name = xmlStrdup(name);
1367 if (SystemID != NULL)
1368 ret->SystemID = xmlStrdup(SystemID);
1369 if (PublicID != NULL)
1370 ret->PublicID = xmlStrdup(PublicID);
1371
1372 /*
1373 * Validity Check:
1374 * Check the DTD for previous declarations of the ATTLIST
1375 */
1376 if (xmlHashAddEntry(table, name, ret)) {
1377 xmlGenericError(xmlGenericErrorContext,
1378 "xmlAddNotationDecl: %s already defined\n", name);
1379 xmlFreeNotation(ret);
1380 return(NULL);
1381 }
1382 return(ret);
1383}
1384
1385/**
1386 * xmlFreeNotationTable:
1387 * @table: An notation table
1388 *
1389 * Deallocate the memory used by an entities hash table.
1390 */
1391void
1392xmlFreeNotationTable(xmlNotationTablePtr table) {
1393 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1394}
1395
1396/**
1397 * xmlCopyNotation:
1398 * @nota: A notation
1399 *
1400 * Build a copy of a notation.
1401 *
1402 * Returns the new xmlNotationPtr or NULL in case of error.
1403 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001404static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001405xmlCopyNotation(xmlNotationPtr nota) {
1406 xmlNotationPtr cur;
1407
1408 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1409 if (cur == NULL) {
1410 xmlGenericError(xmlGenericErrorContext,
1411 "xmlCopyNotation: out of memory !\n");
1412 return(NULL);
1413 }
1414 if (nota->name != NULL)
1415 cur->name = xmlStrdup(nota->name);
1416 else
1417 cur->name = NULL;
1418 if (nota->PublicID != NULL)
1419 cur->PublicID = xmlStrdup(nota->PublicID);
1420 else
1421 cur->PublicID = NULL;
1422 if (nota->SystemID != NULL)
1423 cur->SystemID = xmlStrdup(nota->SystemID);
1424 else
1425 cur->SystemID = NULL;
1426 return(cur);
1427}
1428
1429/**
1430 * xmlCopyNotationTable:
1431 * @table: A notation table
1432 *
1433 * Build a copy of a notation table.
1434 *
1435 * Returns the new xmlNotationTablePtr or NULL in case of error.
1436 */
1437xmlNotationTablePtr
1438xmlCopyNotationTable(xmlNotationTablePtr table) {
1439 return((xmlNotationTablePtr) xmlHashCopy(table,
1440 (xmlHashCopier) xmlCopyNotation));
1441}
1442
1443/**
1444 * xmlDumpNotationDecl:
1445 * @buf: the XML buffer output
1446 * @nota: A notation declaration
1447 *
1448 * This will dump the content the notation declaration as an XML DTD definition
1449 */
1450void
1451xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1452 xmlBufferWriteChar(buf, "<!NOTATION ");
1453 xmlBufferWriteCHAR(buf, nota->name);
1454 if (nota->PublicID != NULL) {
1455 xmlBufferWriteChar(buf, " PUBLIC ");
1456 xmlBufferWriteQuotedString(buf, nota->PublicID);
1457 if (nota->SystemID != NULL) {
1458 xmlBufferWriteChar(buf, " ");
1459 xmlBufferWriteCHAR(buf, nota->SystemID);
1460 }
1461 } else {
1462 xmlBufferWriteChar(buf, " SYSTEM ");
1463 xmlBufferWriteCHAR(buf, nota->SystemID);
1464 }
1465 xmlBufferWriteChar(buf, " >\n");
1466}
1467
1468/**
1469 * xmlDumpNotationTable:
1470 * @buf: the XML buffer output
1471 * @table: A notation table
1472 *
1473 * This will dump the content of the notation table as an XML DTD definition
1474 */
1475void
1476xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1477 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1478}
1479
1480/************************************************************************
1481 * *
1482 * IDs *
1483 * *
1484 ************************************************************************/
1485/**
1486 * xmlCreateIDTable:
1487 *
1488 * create and initialize an empty id hash table.
1489 *
1490 * Returns the xmlIDTablePtr just created or NULL in case
1491 * of error.
1492 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001493static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001494xmlCreateIDTable(void) {
1495 return(xmlHashCreate(0));
1496}
1497
1498/**
1499 * xmlFreeID:
1500 * @not: A id
1501 *
1502 * Deallocate the memory used by an id definition
1503 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001504static void
Owen Taylor3473f882001-02-23 17:55:21 +00001505xmlFreeID(xmlIDPtr id) {
1506 if (id == NULL) return;
1507 if (id->value != NULL)
1508 xmlFree((xmlChar *) id->value);
Daniel Veillard48b2f892001-02-25 16:11:03 +00001509 MEM_CLEANUP(id, sizeof(xmlID));
Owen Taylor3473f882001-02-23 17:55:21 +00001510 xmlFree(id);
1511}
1512
1513/**
1514 * xmlAddID:
1515 * @ctxt: the validation context
1516 * @doc: pointer to the document
1517 * @value: the value name
1518 * @attr: the attribute holding the ID
1519 *
1520 * Register a new id declaration
1521 *
1522 * Returns NULL if not, othervise the new xmlIDPtr
1523 */
1524xmlIDPtr
1525xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1526 xmlAttrPtr attr) {
1527 xmlIDPtr ret;
1528 xmlIDTablePtr table;
1529
1530 if (doc == NULL) {
1531 xmlGenericError(xmlGenericErrorContext,
1532 "xmlAddIDDecl: doc == NULL\n");
1533 return(NULL);
1534 }
1535 if (value == NULL) {
1536 xmlGenericError(xmlGenericErrorContext,
1537 "xmlAddIDDecl: value == NULL\n");
1538 return(NULL);
1539 }
1540 if (attr == NULL) {
1541 xmlGenericError(xmlGenericErrorContext,
1542 "xmlAddIDDecl: attr == NULL\n");
1543 return(NULL);
1544 }
1545
1546 /*
1547 * Create the ID table if needed.
1548 */
1549 table = (xmlIDTablePtr) doc->ids;
1550 if (table == NULL)
1551 doc->ids = table = xmlCreateIDTable();
1552 if (table == NULL) {
1553 xmlGenericError(xmlGenericErrorContext,
1554 "xmlAddID: Table creation failed!\n");
1555 return(NULL);
1556 }
1557
1558 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1559 if (ret == NULL) {
1560 xmlGenericError(xmlGenericErrorContext,
1561 "xmlAddID: out of memory\n");
1562 return(NULL);
1563 }
1564
1565 /*
1566 * fill the structure.
1567 */
1568 ret->value = xmlStrdup(value);
1569 ret->attr = attr;
1570
1571 if (xmlHashAddEntry(table, value, ret) < 0) {
1572 /*
1573 * The id is already defined in this Dtd.
1574 */
1575 VERROR(ctxt->userData, "ID %s already defined\n", value);
1576 xmlFreeID(ret);
1577 return(NULL);
1578 }
1579 return(ret);
1580}
1581
1582/**
1583 * xmlFreeIDTable:
1584 * @table: An id table
1585 *
1586 * Deallocate the memory used by an ID hash table.
1587 */
1588void
1589xmlFreeIDTable(xmlIDTablePtr table) {
1590 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1591}
1592
1593/**
1594 * xmlIsID:
1595 * @doc: the document
1596 * @elem: the element carrying the attribute
1597 * @attr: the attribute
1598 *
1599 * Determine whether an attribute is of type ID. In case we have Dtd(s)
1600 * then this is simple, otherwise we use an heuristic: name ID (upper
1601 * or lowercase).
1602 *
1603 * Returns 0 or 1 depending on the lookup result
1604 */
1605int
1606xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1607 if (doc == NULL) return(0);
1608 if (attr == NULL) return(0);
1609 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1610 return(0);
1611 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1612 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1613 (xmlStrEqual(BAD_CAST "name", attr->name)))
1614 return(1);
1615 return(0);
1616 } else {
1617 xmlAttributePtr attrDecl;
1618
1619 if (elem == NULL) return(0);
1620 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1621 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1622 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1623 attr->name);
1624
1625 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1626 return(1);
1627 }
1628 return(0);
1629}
1630
1631/**
1632 * xmlRemoveID
1633 * @doc: the document
1634 * @attr: the attribute
1635 *
1636 * Remove the given attribute from the ID table maintained internally.
1637 *
1638 * Returns -1 if the lookup failed and 0 otherwise
1639 */
1640int
1641xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1642 xmlAttrPtr cur;
1643 xmlIDTablePtr table;
1644 xmlChar *ID;
1645
1646 if (doc == NULL) return(-1);
1647 if (attr == NULL) return(-1);
1648 table = (xmlIDTablePtr) doc->ids;
1649 if (table == NULL)
1650 return(-1);
1651
1652 if (attr == NULL)
1653 return(-1);
1654 ID = xmlNodeListGetString(doc, attr->children, 1);
1655 if (ID == NULL)
1656 return(-1);
1657 cur = xmlHashLookup(table, ID);
1658 if (cur != attr) {
1659 xmlFree(ID);
1660 return(-1);
1661 }
1662 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1663 xmlFree(ID);
1664 return(0);
1665}
1666
1667/**
1668 * xmlGetID:
1669 * @doc: pointer to the document
1670 * @ID: the ID value
1671 *
1672 * Search the attribute declaring the given ID
1673 *
1674 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1675 */
1676xmlAttrPtr
1677xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
1678 xmlIDTablePtr table;
1679 xmlIDPtr id;
1680
1681 if (doc == NULL) {
1682 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
1683 return(NULL);
1684 }
1685
1686 if (ID == NULL) {
1687 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
1688 return(NULL);
1689 }
1690
1691 table = (xmlIDTablePtr) doc->ids;
1692 if (table == NULL)
1693 return(NULL);
1694
1695 id = xmlHashLookup(table, ID);
1696 if (id == NULL)
1697 return(NULL);
1698 return(id->attr);
1699}
1700
1701/************************************************************************
1702 * *
1703 * Refs *
1704 * *
1705 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00001706typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00001707{
1708 xmlListPtr l;
1709 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00001710} xmlRemoveMemo;
1711
1712typedef xmlRemoveMemo *xmlRemoveMemoPtr;
1713
1714typedef struct xmlValidateMemo_t
1715{
1716 xmlValidCtxtPtr ctxt;
1717 const xmlChar *name;
1718} xmlValidateMemo;
1719
1720typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00001721
1722/**
1723 * xmlCreateRefTable:
1724 *
1725 * create and initialize an empty ref hash table.
1726 *
1727 * Returns the xmlRefTablePtr just created or NULL in case
1728 * of error.
1729 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001730static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001731xmlCreateRefTable(void) {
1732 return(xmlHashCreate(0));
1733}
1734
1735/**
1736 * xmlFreeRef:
1737 * @lk: A list link
1738 *
1739 * Deallocate the memory used by a ref definition
1740 */
1741static void
1742xmlFreeRef(xmlLinkPtr lk) {
1743 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
1744 if (ref == NULL) return;
1745 if (ref->value != NULL)
1746 xmlFree((xmlChar *)ref->value);
Daniel Veillard48b2f892001-02-25 16:11:03 +00001747 MEM_CLEANUP(ref, sizeof(xmlRef));
Owen Taylor3473f882001-02-23 17:55:21 +00001748 xmlFree(ref);
1749}
1750
1751/**
1752 * xmlFreeRefList:
1753 * @list_ref: A list of references.
1754 *
1755 * Deallocate the memory used by a list of references
1756 */
1757static void
1758xmlFreeRefList(xmlListPtr list_ref) {
1759 if (list_ref == NULL) return;
1760 xmlListDelete(list_ref);
1761}
1762
1763/**
1764 * xmlWalkRemoveRef:
1765 * @data: Contents of current link
1766 * @user: Value supplied by the user
1767 *
1768 * Return 0 to abort the walk or 1 to continue
1769 */
1770static int
1771xmlWalkRemoveRef(const void *data, const void *user)
1772{
1773 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
Daniel Veillard8730c562001-02-26 10:49:57 +00001774 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
1775 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00001776
1777 if (attr0 == attr1) { /* Matched: remove and terminate walk */
1778 xmlListRemoveFirst(ref_list, (void *)data);
1779 return 0;
1780 }
1781 return 1;
1782}
1783
1784/**
1785 * xmlAddRef:
1786 * @ctxt: the validation context
1787 * @doc: pointer to the document
1788 * @value: the value name
1789 * @attr: the attribute holding the Ref
1790 *
1791 * Register a new ref declaration
1792 *
1793 * Returns NULL if not, othervise the new xmlRefPtr
1794 */
1795xmlRefPtr
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001796xmlAddRef(xmlValidCtxtPtr ctxt UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00001797 xmlAttrPtr attr) {
1798 xmlRefPtr ret;
1799 xmlRefTablePtr table;
1800 xmlListPtr ref_list;
1801
1802 if (doc == NULL) {
1803 xmlGenericError(xmlGenericErrorContext,
1804 "xmlAddRefDecl: doc == NULL\n");
1805 return(NULL);
1806 }
1807 if (value == NULL) {
1808 xmlGenericError(xmlGenericErrorContext,
1809 "xmlAddRefDecl: value == NULL\n");
1810 return(NULL);
1811 }
1812 if (attr == NULL) {
1813 xmlGenericError(xmlGenericErrorContext,
1814 "xmlAddRefDecl: attr == NULL\n");
1815 return(NULL);
1816 }
1817
1818 /*
1819 * Create the Ref table if needed.
1820 */
1821 table = (xmlRefTablePtr) doc->refs;
1822 if (table == NULL)
1823 doc->refs = table = xmlCreateRefTable();
1824 if (table == NULL) {
1825 xmlGenericError(xmlGenericErrorContext,
1826 "xmlAddRef: Table creation failed!\n");
1827 return(NULL);
1828 }
1829
1830 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
1831 if (ret == NULL) {
1832 xmlGenericError(xmlGenericErrorContext,
1833 "xmlAddRef: out of memory\n");
1834 return(NULL);
1835 }
1836
1837 /*
1838 * fill the structure.
1839 */
1840 ret->value = xmlStrdup(value);
1841 ret->attr = attr;
1842
1843 /* To add a reference :-
1844 * References are maintained as a list of references,
1845 * Lookup the entry, if no entry create new nodelist
1846 * Add the owning node to the NodeList
1847 * Return the ref
1848 */
1849
1850 if(NULL == (ref_list = xmlHashLookup(table, value))) {
1851 if(NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
1852 xmlGenericError(xmlGenericErrorContext,
1853 "xmlAddRef: Reference list creation failed!\n");
1854 return(NULL);
1855 }
1856 if (xmlHashAddEntry(table, value, ref_list) < 0) {
1857 xmlListDelete(ref_list);
1858 xmlGenericError(xmlGenericErrorContext,
1859 "xmlAddRef: Reference list insertion failed!\n");
1860 return(NULL);
1861 }
1862 }
1863 xmlListInsert(ref_list, ret);
1864 return(ret);
1865}
1866
1867/**
1868 * xmlFreeRefTable:
1869 * @table: An ref table
1870 *
1871 * Deallocate the memory used by an Ref hash table.
1872 */
1873void
1874xmlFreeRefTable(xmlRefTablePtr table) {
1875 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
1876}
1877
1878/**
1879 * xmlIsRef:
1880 * @doc: the document
1881 * @elem: the element carrying the attribute
1882 * @attr: the attribute
1883 *
1884 * Determine whether an attribute is of type Ref. In case we have Dtd(s)
1885 * then this is simple, otherwise we use an heuristic: name Ref (upper
1886 * or lowercase).
1887 *
1888 * Returns 0 or 1 depending on the lookup result
1889 */
1890int
1891xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1892 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1893 return(0);
1894 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1895 /* TODO @@@ */
1896 return(0);
1897 } else {
1898 xmlAttributePtr attrDecl;
1899
1900 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1901 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1902 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1903 attr->name);
1904
1905 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_IDREF))
1906 return(1);
1907 }
1908 return(0);
1909}
1910
1911/**
1912 * xmlRemoveRef
1913 * @doc: the document
1914 * @attr: the attribute
1915 *
1916 * Remove the given attribute from the Ref table maintained internally.
1917 *
1918 * Returns -1 if the lookup failed and 0 otherwise
1919 */
1920int
1921xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
1922 xmlListPtr ref_list;
1923 xmlRefTablePtr table;
1924 xmlChar *ID;
Daniel Veillard8730c562001-02-26 10:49:57 +00001925 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00001926
1927 if (doc == NULL) return(-1);
1928 if (attr == NULL) return(-1);
1929 table = (xmlRefTablePtr) doc->refs;
1930 if (table == NULL)
1931 return(-1);
1932
1933 if (attr == NULL)
1934 return(-1);
1935 ID = xmlNodeListGetString(doc, attr->children, 1);
1936 if (ID == NULL)
1937 return(-1);
1938 ref_list = xmlHashLookup(table, ID);
1939
1940 if(ref_list == NULL) {
1941 xmlFree(ID);
1942 return (-1);
1943 }
1944 /* At this point, ref_list refers to a list of references which
1945 * have the same key as the supplied attr. Our list of references
1946 * is ordered by reference address and we don't have that information
1947 * here to use when removing. We'll have to walk the list and
1948 * check for a matching attribute, when we find one stop the walk
1949 * and remove the entry.
1950 * The list is ordered by reference, so that means we don't have the
1951 * key. Passing the list and the reference to the walker means we
1952 * will have enough data to be able to remove the entry.
1953 */
1954 target.l = ref_list;
1955 target.ap = attr;
1956
1957 /* Remove the supplied attr from our list */
1958 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
1959
1960 /*If the list is empty then remove the list entry in the hash */
1961 if (xmlListEmpty(ref_list))
1962 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
1963 xmlFreeRefList);
1964 xmlFree(ID);
1965 return(0);
1966}
1967
1968/**
1969 * xmlGetRefs:
1970 * @doc: pointer to the document
1971 * @ID: the ID value
1972 *
1973 * Find the set of references for the supplied ID.
1974 *
1975 * Returns NULL if not found, otherwise node set for the ID.
1976 */
1977xmlListPtr
1978xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
1979 xmlRefTablePtr table;
1980
1981 if (doc == NULL) {
1982 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: doc == NULL\n");
1983 return(NULL);
1984 }
1985
1986 if (ID == NULL) {
1987 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: ID == NULL\n");
1988 return(NULL);
1989 }
1990
1991 table = (xmlRefTablePtr) doc->refs;
1992 if (table == NULL)
1993 return(NULL);
1994
1995 return (xmlHashLookup(table, ID));
1996}
1997
1998/************************************************************************
1999 * *
2000 * Routines for validity checking *
2001 * *
2002 ************************************************************************/
2003
2004/**
2005 * xmlGetDtdElementDesc:
2006 * @dtd: a pointer to the DtD to search
2007 * @name: the element name
2008 *
2009 * Search the Dtd for the description of this element
2010 *
2011 * returns the xmlElementPtr if found or NULL
2012 */
2013
2014xmlElementPtr
2015xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2016 xmlElementTablePtr table;
2017 xmlElementPtr cur;
2018 xmlChar *uqname = NULL, *prefix = NULL;
2019
2020 if (dtd == NULL) return(NULL);
2021 if (dtd->elements == NULL) return(NULL);
2022 table = (xmlElementTablePtr) dtd->elements;
2023
2024 uqname = xmlSplitQName2(name, &prefix);
2025 if (uqname != NULL) {
2026 cur = xmlHashLookup2(table, uqname, prefix);
2027 if (prefix != NULL) xmlFree(prefix);
2028 if (uqname != NULL) xmlFree(uqname);
2029 } else
2030 cur = xmlHashLookup2(table, name, NULL);
2031 return(cur);
2032}
2033
2034/**
2035 * xmlGetDtdQElementDesc:
2036 * @dtd: a pointer to the DtD to search
2037 * @name: the element name
2038 * @prefix: the element namespace prefix
2039 *
2040 * Search the Dtd for the description of this element
2041 *
2042 * returns the xmlElementPtr if found or NULL
2043 */
2044
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002045static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002046xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2047 const xmlChar *prefix) {
2048 xmlElementTablePtr table;
2049
2050 if (dtd == NULL) return(NULL);
2051 if (dtd->elements == NULL) return(NULL);
2052 table = (xmlElementTablePtr) dtd->elements;
2053
2054 return(xmlHashLookup2(table, name, prefix));
2055}
2056
2057/**
2058 * xmlGetDtdAttrDesc:
2059 * @dtd: a pointer to the DtD to search
2060 * @elem: the element name
2061 * @name: the attribute name
2062 *
2063 * Search the Dtd for the description of this attribute on
2064 * this element.
2065 *
2066 * returns the xmlAttributePtr if found or NULL
2067 */
2068
2069xmlAttributePtr
2070xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2071 xmlAttributeTablePtr table;
2072 xmlAttributePtr cur;
2073 xmlChar *uqname = NULL, *prefix = NULL;
2074
2075 if (dtd == NULL) return(NULL);
2076 if (dtd->attributes == NULL) return(NULL);
2077
2078 table = (xmlAttributeTablePtr) dtd->attributes;
2079 if (table == NULL)
2080 return(NULL);
2081
2082 uqname = xmlSplitQName2(name, &prefix);
2083
2084 if (uqname != NULL) {
2085 cur = xmlHashLookup3(table, uqname, prefix, elem);
2086 if (prefix != NULL) xmlFree(prefix);
2087 if (uqname != NULL) xmlFree(uqname);
2088 } else
2089 cur = xmlHashLookup3(table, name, NULL, elem);
2090 return(cur);
2091}
2092
2093/**
2094 * xmlGetDtdQAttrDesc:
2095 * @dtd: a pointer to the DtD to search
2096 * @elem: the element name
2097 * @name: the attribute name
2098 * @prefix: the attribute namespace prefix
2099 *
2100 * Search the Dtd for the description of this qualified attribute on
2101 * this element.
2102 *
2103 * returns the xmlAttributePtr if found or NULL
2104 */
2105
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002106static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002107xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2108 const xmlChar *prefix) {
2109 xmlAttributeTablePtr table;
2110
2111 if (dtd == NULL) return(NULL);
2112 if (dtd->attributes == NULL) return(NULL);
2113 table = (xmlAttributeTablePtr) dtd->attributes;
2114
2115 return(xmlHashLookup3(table, name, prefix, elem));
2116}
2117
2118/**
2119 * xmlGetDtdNotationDesc:
2120 * @dtd: a pointer to the DtD to search
2121 * @name: the notation name
2122 *
2123 * Search the Dtd for the description of this notation
2124 *
2125 * returns the xmlNotationPtr if found or NULL
2126 */
2127
2128xmlNotationPtr
2129xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2130 xmlNotationTablePtr table;
2131
2132 if (dtd == NULL) return(NULL);
2133 if (dtd->notations == NULL) return(NULL);
2134 table = (xmlNotationTablePtr) dtd->notations;
2135
2136 return(xmlHashLookup(table, name));
2137}
2138
2139/**
2140 * xmlValidateNotationUse:
2141 * @ctxt: the validation context
2142 * @doc: the document
2143 * @notationName: the notation name to check
2144 *
2145 * Validate that the given mame match a notation declaration.
2146 * - [ VC: Notation Declared ]
2147 *
2148 * returns 1 if valid or 0 otherwise
2149 */
2150
2151int
2152xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2153 const xmlChar *notationName) {
2154 xmlNotationPtr notaDecl;
2155 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2156
2157 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2158 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2159 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2160
2161 if (notaDecl == NULL) {
2162 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2163 notationName);
2164 return(0);
2165 }
2166 return(1);
2167}
2168
2169/**
2170 * xmlIsMixedElement
2171 * @doc: the document
2172 * @name: the element name
2173 *
2174 * Search in the DtDs whether an element accept Mixed content (or ANY)
2175 * basically if it is supposed to accept text childs
2176 *
2177 * returns 0 if no, 1 if yes, and -1 if no element description is available
2178 */
2179
2180int
2181xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2182 xmlElementPtr elemDecl;
2183
2184 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2185
2186 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2187 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2188 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2189 if (elemDecl == NULL) return(-1);
2190 switch (elemDecl->etype) {
2191 case XML_ELEMENT_TYPE_ELEMENT:
2192 return(0);
2193 case XML_ELEMENT_TYPE_EMPTY:
2194 /*
2195 * return 1 for EMPTY since we want VC error to pop up
2196 * on <empty> </empty> for example
2197 */
2198 case XML_ELEMENT_TYPE_ANY:
2199 case XML_ELEMENT_TYPE_MIXED:
2200 return(1);
2201 }
2202 return(1);
2203}
2204
2205/**
2206 * xmlValidateNameValue:
2207 * @value: an Name value
2208 *
2209 * Validate that the given value match Name production
2210 *
2211 * returns 1 if valid or 0 otherwise
2212 */
2213
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002214static int
Owen Taylor3473f882001-02-23 17:55:21 +00002215xmlValidateNameValue(const xmlChar *value) {
2216 const xmlChar *cur;
2217
2218 if (value == NULL) return(0);
2219 cur = value;
2220
2221 if (!IS_LETTER(*cur) && (*cur != '_') &&
2222 (*cur != ':')) {
2223 return(0);
2224 }
2225
2226 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2227 (*cur == '.') || (*cur == '-') ||
2228 (*cur == '_') || (*cur == ':') ||
2229 (IS_COMBINING(*cur)) ||
2230 (IS_EXTENDER(*cur)))
2231 cur++;
2232
2233 if (*cur != 0) return(0);
2234
2235 return(1);
2236}
2237
2238/**
2239 * xmlValidateNamesValue:
2240 * @value: an Names value
2241 *
2242 * Validate that the given value match Names production
2243 *
2244 * returns 1 if valid or 0 otherwise
2245 */
2246
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002247static int
Owen Taylor3473f882001-02-23 17:55:21 +00002248xmlValidateNamesValue(const xmlChar *value) {
2249 const xmlChar *cur;
2250
2251 if (value == NULL) return(0);
2252 cur = value;
2253
2254 if (!IS_LETTER(*cur) && (*cur != '_') &&
2255 (*cur != ':')) {
2256 return(0);
2257 }
2258
2259 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2260 (*cur == '.') || (*cur == '-') ||
2261 (*cur == '_') || (*cur == ':') ||
2262 (IS_COMBINING(*cur)) ||
2263 (IS_EXTENDER(*cur)))
2264 cur++;
2265
2266 while (IS_BLANK(*cur)) {
2267 while (IS_BLANK(*cur)) cur++;
2268
2269 if (!IS_LETTER(*cur) && (*cur != '_') &&
2270 (*cur != ':')) {
2271 return(0);
2272 }
2273
2274 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2275 (*cur == '.') || (*cur == '-') ||
2276 (*cur == '_') || (*cur == ':') ||
2277 (IS_COMBINING(*cur)) ||
2278 (IS_EXTENDER(*cur)))
2279 cur++;
2280 }
2281
2282 if (*cur != 0) return(0);
2283
2284 return(1);
2285}
2286
2287/**
2288 * xmlValidateNmtokenValue:
2289 * @value: an Mntoken value
2290 *
2291 * Validate that the given value match Nmtoken production
2292 *
2293 * [ VC: Name Token ]
2294 *
2295 * returns 1 if valid or 0 otherwise
2296 */
2297
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002298static int
Owen Taylor3473f882001-02-23 17:55:21 +00002299xmlValidateNmtokenValue(const xmlChar *value) {
2300 const xmlChar *cur;
2301
2302 if (value == NULL) return(0);
2303 cur = value;
2304
2305 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2306 (*cur != '.') && (*cur != '-') &&
2307 (*cur != '_') && (*cur != ':') &&
2308 (!IS_COMBINING(*cur)) &&
2309 (!IS_EXTENDER(*cur)))
2310 return(0);
2311
2312 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2313 (*cur == '.') || (*cur == '-') ||
2314 (*cur == '_') || (*cur == ':') ||
2315 (IS_COMBINING(*cur)) ||
2316 (IS_EXTENDER(*cur)))
2317 cur++;
2318
2319 if (*cur != 0) return(0);
2320
2321 return(1);
2322}
2323
2324/**
2325 * xmlValidateNmtokensValue:
2326 * @value: an Mntokens value
2327 *
2328 * Validate that the given value match Nmtokens production
2329 *
2330 * [ VC: Name Token ]
2331 *
2332 * returns 1 if valid or 0 otherwise
2333 */
2334
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002335static int
Owen Taylor3473f882001-02-23 17:55:21 +00002336xmlValidateNmtokensValue(const xmlChar *value) {
2337 const xmlChar *cur;
2338
2339 if (value == NULL) return(0);
2340 cur = value;
2341
2342 while (IS_BLANK(*cur)) cur++;
2343 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2344 (*cur != '.') && (*cur != '-') &&
2345 (*cur != '_') && (*cur != ':') &&
2346 (!IS_COMBINING(*cur)) &&
2347 (!IS_EXTENDER(*cur)))
2348 return(0);
2349
2350 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2351 (*cur == '.') || (*cur == '-') ||
2352 (*cur == '_') || (*cur == ':') ||
2353 (IS_COMBINING(*cur)) ||
2354 (IS_EXTENDER(*cur)))
2355 cur++;
2356
2357 while (IS_BLANK(*cur)) {
2358 while (IS_BLANK(*cur)) cur++;
2359 if (*cur == 0) return(1);
2360
2361 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2362 (*cur != '.') && (*cur != '-') &&
2363 (*cur != '_') && (*cur != ':') &&
2364 (!IS_COMBINING(*cur)) &&
2365 (!IS_EXTENDER(*cur)))
2366 return(0);
2367
2368 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2369 (*cur == '.') || (*cur == '-') ||
2370 (*cur == '_') || (*cur == ':') ||
2371 (IS_COMBINING(*cur)) ||
2372 (IS_EXTENDER(*cur)))
2373 cur++;
2374 }
2375
2376 if (*cur != 0) return(0);
2377
2378 return(1);
2379}
2380
2381/**
2382 * xmlValidateNotationDecl:
2383 * @ctxt: the validation context
2384 * @doc: a document instance
2385 * @nota: a notation definition
2386 *
2387 * Try to validate a single notation definition
2388 * basically it does the following checks as described by the
2389 * XML-1.0 recommendation:
2390 * - it seems that no validity constraing exist on notation declarations
2391 * But this function get called anyway ...
2392 *
2393 * returns 1 if valid or 0 otherwise
2394 */
2395
2396int
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002397xmlValidateNotationDecl(xmlValidCtxtPtr ctxt UNUSED, xmlDocPtr doc UNUSED,
2398 xmlNotationPtr nota UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002399 int ret = 1;
2400
2401 return(ret);
2402}
2403
2404/**
2405 * xmlValidateAttributeValue:
2406 * @type: an attribute type
2407 * @value: an attribute value
2408 *
2409 * Validate that the given attribute value match the proper production
2410 *
2411 * [ VC: ID ]
2412 * Values of type ID must match the Name production....
2413 *
2414 * [ VC: IDREF ]
2415 * Values of type IDREF must match the Name production, and values
2416 * of type IDREFS must match Names ...
2417 *
2418 * [ VC: Entity Name ]
2419 * Values of type ENTITY must match the Name production, values
2420 * of type ENTITIES must match Names ...
2421 *
2422 * [ VC: Name Token ]
2423 * Values of type NMTOKEN must match the Nmtoken production; values
2424 * of type NMTOKENS must match Nmtokens.
2425 *
2426 * returns 1 if valid or 0 otherwise
2427 */
2428
2429int
2430xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2431 switch (type) {
2432 case XML_ATTRIBUTE_ENTITIES:
2433 case XML_ATTRIBUTE_IDREFS:
2434 return(xmlValidateNamesValue(value));
2435 case XML_ATTRIBUTE_ENTITY:
2436 case XML_ATTRIBUTE_IDREF:
2437 case XML_ATTRIBUTE_ID:
2438 case XML_ATTRIBUTE_NOTATION:
2439 return(xmlValidateNameValue(value));
2440 case XML_ATTRIBUTE_NMTOKENS:
2441 case XML_ATTRIBUTE_ENUMERATION:
2442 return(xmlValidateNmtokensValue(value));
2443 case XML_ATTRIBUTE_NMTOKEN:
2444 return(xmlValidateNmtokenValue(value));
2445 case XML_ATTRIBUTE_CDATA:
2446 break;
2447 }
2448 return(1);
2449}
2450
2451/**
2452 * xmlValidateAttributeValue2:
2453 * @ctxt: the validation context
2454 * @doc: the document
2455 * @name: the attribute name (used for error reporting only)
2456 * @type: the attribute type
2457 * @value: the attribute value
2458 *
2459 * Validate that the given attribute value match a given type.
2460 * This typically cannot be done before having finished parsing
2461 * the subsets.
2462 *
2463 * [ VC: IDREF ]
2464 * Values of type IDREF must match one of the declared IDs
2465 * Values of type IDREFS must match a sequence of the declared IDs
2466 * each Name must match the value of an ID attribute on some element
2467 * in the XML document; i.e. IDREF values must match the value of
2468 * some ID attribute
2469 *
2470 * [ VC: Entity Name ]
2471 * Values of type ENTITY must match one declared entity
2472 * Values of type ENTITIES must match a sequence of declared entities
2473 *
2474 * [ VC: Notation Attributes ]
2475 * all notation names in the declaration must be declared.
2476 *
2477 * returns 1 if valid or 0 otherwise
2478 */
2479
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002480static int
Owen Taylor3473f882001-02-23 17:55:21 +00002481xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2482 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2483 int ret = 1;
2484 switch (type) {
2485 case XML_ATTRIBUTE_IDREFS:
2486 case XML_ATTRIBUTE_IDREF:
2487 case XML_ATTRIBUTE_ID:
2488 case XML_ATTRIBUTE_NMTOKENS:
2489 case XML_ATTRIBUTE_ENUMERATION:
2490 case XML_ATTRIBUTE_NMTOKEN:
2491 case XML_ATTRIBUTE_CDATA:
2492 break;
2493 case XML_ATTRIBUTE_ENTITY: {
2494 xmlEntityPtr ent;
2495
2496 ent = xmlGetDocEntity(doc, value);
2497 if (ent == NULL) {
2498 VERROR(ctxt->userData,
2499 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2500 name, value);
2501 ret = 0;
2502 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2503 VERROR(ctxt->userData,
2504 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2505 name, value);
2506 ret = 0;
2507 }
2508 break;
2509 }
2510 case XML_ATTRIBUTE_ENTITIES: {
2511 xmlChar *dup, *nam = NULL, *cur, save;
2512 xmlEntityPtr ent;
2513
2514 dup = xmlStrdup(value);
2515 if (dup == NULL)
2516 return(0);
2517 cur = dup;
2518 while (*cur != 0) {
2519 nam = cur;
2520 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2521 save = *cur;
2522 *cur = 0;
2523 ent = xmlGetDocEntity(doc, nam);
2524 if (ent == NULL) {
2525 VERROR(ctxt->userData,
2526 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2527 name, nam);
2528 ret = 0;
2529 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2530 VERROR(ctxt->userData,
2531 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2532 name, nam);
2533 ret = 0;
2534 }
2535 if (save == 0)
2536 break;
2537 *cur = save;
2538 while (IS_BLANK(*cur)) cur++;
2539 }
2540 xmlFree(dup);
2541 break;
2542 }
2543 case XML_ATTRIBUTE_NOTATION: {
2544 xmlNotationPtr nota;
2545
2546 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2547 if ((nota == NULL) && (doc->extSubset != NULL))
2548 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2549
2550 if (nota == NULL) {
2551 VERROR(ctxt->userData,
2552 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2553 name, value);
2554 ret = 0;
2555 }
2556 break;
2557 }
2558 }
2559 return(ret);
2560}
2561
2562/**
2563 * xmlValidNormalizeAttributeValue:
2564 * @doc: the document
2565 * @elem: the parent
2566 * @name: the attribute name
2567 * @value: the attribute value
2568 *
2569 * Does the validation related extra step of the normalization of attribute
2570 * values:
2571 *
2572 * If the declared value is not CDATA, then the XML processor must further
2573 * process the normalized attribute value by discarding any leading and
2574 * trailing space (#x20) characters, and by replacing sequences of space
2575 * (#x20) characters by single space (#x20) character.
2576 *
2577 * returns a new normalized string if normalization is needed, NULL otherwise
2578 * the caller must free the returned value.
2579 */
2580
2581xmlChar *
2582xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
2583 const xmlChar *name, const xmlChar *value) {
2584 xmlChar *ret, *dst;
2585 const xmlChar *src;
2586 xmlAttributePtr attrDecl = NULL;
2587
2588 if (doc == NULL) return(NULL);
2589 if (elem == NULL) return(NULL);
2590 if (name == NULL) return(NULL);
2591 if (value == NULL) return(NULL);
2592
2593 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2594 xmlChar qname[500];
2595#ifdef HAVE_SNPRINTF
2596 snprintf((char *) qname, sizeof(qname), "%s:%s",
2597 elem->ns->prefix, elem->name);
2598#else
2599 sprintf((char *) qname, "%s:%s", elem->ns->prefix, elem->name);
2600#endif
2601 qname[sizeof(qname) - 1] = 0;
2602 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
2603 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2604 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
2605 }
2606 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
2607 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2608 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
2609
2610 if (attrDecl == NULL)
2611 return(NULL);
2612 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
2613 return(NULL);
2614
2615 ret = xmlStrdup(value);
2616 if (ret == NULL)
2617 return(NULL);
2618 src = value;
2619 dst = ret;
2620 while (*src == 0x20) src++;
2621 while (*src != 0) {
2622 if (*src == 0x20) {
2623 while (*src == 0x20) src++;
2624 if (*src != 0)
2625 *dst++ = 0x20;
2626 } else {
2627 *dst++ = *src++;
2628 }
2629 }
2630 *dst = 0;
2631 return(ret);
2632}
2633
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002634static void
Owen Taylor3473f882001-02-23 17:55:21 +00002635xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002636 const xmlChar* name UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002637 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
2638}
2639
2640/**
2641 * xmlValidateAttributeDecl:
2642 * @ctxt: the validation context
2643 * @doc: a document instance
2644 * @attr: an attribute definition
2645 *
2646 * Try to validate a single attribute definition
2647 * basically it does the following checks as described by the
2648 * XML-1.0 recommendation:
2649 * - [ VC: Attribute Default Legal ]
2650 * - [ VC: Enumeration ]
2651 * - [ VC: ID Attribute Default ]
2652 *
2653 * The ID/IDREF uniqueness and matching are done separately
2654 *
2655 * returns 1 if valid or 0 otherwise
2656 */
2657
2658int
2659xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2660 xmlAttributePtr attr) {
2661 int ret = 1;
2662 int val;
2663 CHECK_DTD;
2664 if(attr == NULL) return(1);
2665
2666 /* Attribute Default Legal */
2667 /* Enumeration */
2668 if (attr->defaultValue != NULL) {
2669 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
2670 if (val == 0) {
2671 VERROR(ctxt->userData,
2672 "Syntax of default value for attribute %s on %s is not valid\n",
2673 attr->name, attr->elem);
2674 }
2675 ret &= val;
2676 }
2677
2678 /* ID Attribute Default */
2679 if ((attr->atype == XML_ATTRIBUTE_ID)&&
2680 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
2681 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
2682 VERROR(ctxt->userData,
2683 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
2684 attr->name, attr->elem);
2685 ret = 0;
2686 }
2687
2688 /* One ID per Element Type */
2689 if (attr->atype == XML_ATTRIBUTE_ID) {
2690 int nbId;
2691
2692 /* the trick is taht we parse DtD as their own internal subset */
2693 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
2694 attr->elem);
2695 if (elem != NULL) {
2696 nbId = xmlScanIDAttributeDecl(NULL, elem);
2697 } else {
2698 xmlAttributeTablePtr table;
2699
2700 /*
2701 * The attribute may be declared in the internal subset and the
2702 * element in the external subset.
2703 */
2704 nbId = 0;
2705 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
2706 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
2707 xmlValidateAttributeIdCallback, &nbId);
2708 }
2709 if (nbId > 1) {
2710 VERROR(ctxt->userData,
2711 "Element %s has %d ID attribute defined in the internal subset : %s\n",
2712 attr->elem, nbId, attr->name);
2713 } else if (doc->extSubset != NULL) {
2714 int extId = 0;
2715 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
2716 if (elem != NULL) {
2717 extId = xmlScanIDAttributeDecl(NULL, elem);
2718 }
2719 if (extId > 1) {
2720 VERROR(ctxt->userData,
2721 "Element %s has %d ID attribute defined in the external subset : %s\n",
2722 attr->elem, extId, attr->name);
2723 } else if (extId + nbId > 1) {
2724 VERROR(ctxt->userData,
2725"Element %s has ID attributes defined in the internal and external subset : %s\n",
2726 attr->elem, attr->name);
2727 }
2728 }
2729 }
2730
2731 /* Validity Constraint: Enumeration */
2732 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
2733 xmlEnumerationPtr tree = attr->tree;
2734 while (tree != NULL) {
2735 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
2736 tree = tree->next;
2737 }
2738 if (tree == NULL) {
2739 VERROR(ctxt->userData,
2740"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
2741 attr->defaultValue, attr->name, attr->elem);
2742 ret = 0;
2743 }
2744 }
2745
2746 return(ret);
2747}
2748
2749/**
2750 * xmlValidateElementDecl:
2751 * @ctxt: the validation context
2752 * @doc: a document instance
2753 * @elem: an element definition
2754 *
2755 * Try to validate a single element definition
2756 * basically it does the following checks as described by the
2757 * XML-1.0 recommendation:
2758 * - [ VC: One ID per Element Type ]
2759 * - [ VC: No Duplicate Types ]
2760 * - [ VC: Unique Element Type Declaration ]
2761 *
2762 * returns 1 if valid or 0 otherwise
2763 */
2764
2765int
2766xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2767 xmlElementPtr elem) {
2768 int ret = 1;
2769 xmlElementPtr tst;
2770
2771 CHECK_DTD;
2772
2773 if (elem == NULL) return(1);
2774
2775 /* No Duplicate Types */
2776 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
2777 xmlElementContentPtr cur, next;
2778 const xmlChar *name;
2779
2780 cur = elem->content;
2781 while (cur != NULL) {
2782 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
2783 if (cur->c1 == NULL) break;
2784 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
2785 name = cur->c1->name;
2786 next = cur->c2;
2787 while (next != NULL) {
2788 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
2789 if (xmlStrEqual(next->name, name)) {
2790 VERROR(ctxt->userData,
2791 "Definition of %s has duplicate references of %s\n",
2792 elem->name, name);
2793 ret = 0;
2794 }
2795 break;
2796 }
2797 if (next->c1 == NULL) break;
2798 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
2799 if (xmlStrEqual(next->c1->name, name)) {
2800 VERROR(ctxt->userData,
2801 "Definition of %s has duplicate references of %s\n",
2802 elem->name, name);
2803 ret = 0;
2804 }
2805 next = next->c2;
2806 }
2807 }
2808 cur = cur->c2;
2809 }
2810 }
2811
2812 /* VC: Unique Element Type Declaration */
2813 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
2814 if ((tst != NULL ) && (tst != elem)) {
2815 VERROR(ctxt->userData, "Redefinition of element %s\n",
2816 elem->name);
2817 ret = 0;
2818 }
2819 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
2820 if ((tst != NULL ) && (tst != elem)) {
2821 VERROR(ctxt->userData, "Redefinition of element %s\n",
2822 elem->name);
2823 ret = 0;
2824 }
2825
2826 /* One ID per Element Type */
2827 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
2828 ret = 0;
2829 }
2830 return(ret);
2831}
2832
2833/**
2834 * xmlValidateOneAttribute:
2835 * @ctxt: the validation context
2836 * @doc: a document instance
2837 * @elem: an element instance
2838 * @attr: an attribute instance
2839 * @value: the attribute value (without entities processing)
2840 *
2841 * Try to validate a single attribute for an element
2842 * basically it does the following checks as described by the
2843 * XML-1.0 recommendation:
2844 * - [ VC: Attribute Value Type ]
2845 * - [ VC: Fixed Attribute Default ]
2846 * - [ VC: Entity Name ]
2847 * - [ VC: Name Token ]
2848 * - [ VC: ID ]
2849 * - [ VC: IDREF ]
2850 * - [ VC: Entity Name ]
2851 * - [ VC: Notation Attributes ]
2852 *
2853 * The ID/IDREF uniqueness and matching are done separately
2854 *
2855 * returns 1 if valid or 0 otherwise
2856 */
2857
2858int
2859xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2860 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
2861 /* xmlElementPtr elemDecl; */
2862 xmlAttributePtr attrDecl = NULL;
2863 int val;
2864 int ret = 1;
2865
2866 CHECK_DTD;
2867 if ((elem == NULL) || (elem->name == NULL)) return(0);
2868 if ((attr == NULL) || (attr->name == NULL)) return(0);
2869
2870 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2871 xmlChar qname[500];
2872#ifdef HAVE_SNPRINTF
2873 snprintf((char *) qname, sizeof(qname), "%s:%s",
2874 elem->ns->prefix, elem->name);
2875#else
2876 sprintf((char *) qname, "%s:%s", elem->ns->prefix, elem->name);
2877#endif
2878 qname[sizeof(qname) - 1] = 0;
2879 if (attr->ns != NULL) {
2880 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
2881 attr->name, attr->ns->prefix);
2882 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2883 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
2884 attr->name, attr->ns->prefix);
2885 } else {
2886 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
2887 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2888 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2889 qname, attr->name);
2890 }
2891 }
2892 if (attrDecl == NULL) {
2893 if (attr->ns != NULL) {
2894 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
2895 attr->name, attr->ns->prefix);
2896 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2897 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
2898 attr->name, attr->ns->prefix);
2899 } else {
2900 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
2901 elem->name, attr->name);
2902 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2903 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2904 elem->name, attr->name);
2905 }
2906 }
2907
2908
2909 /* Validity Constraint: Attribute Value Type */
2910 if (attrDecl == NULL) {
2911 VERROR(ctxt->userData,
2912 "No declaration for attribute %s on element %s\n",
2913 attr->name, elem->name);
2914 return(0);
2915 }
2916 attr->atype = attrDecl->atype;
2917
2918 val = xmlValidateAttributeValue(attrDecl->atype, value);
2919 if (val == 0) {
2920 VERROR(ctxt->userData,
2921 "Syntax of value for attribute %s on %s is not valid\n",
2922 attr->name, elem->name);
2923 ret = 0;
2924 }
2925
2926 /* Validity constraint: Fixed Attribute Default */
2927 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
2928 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
2929 VERROR(ctxt->userData,
2930 "Value for attribute %s on %s is differnt from default \"%s\"\n",
2931 attr->name, elem->name, attrDecl->defaultValue);
2932 ret = 0;
2933 }
2934 }
2935
2936 /* Validity Constraint: ID uniqueness */
2937 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
2938 if (xmlAddID(ctxt, doc, value, attr) == NULL)
2939 ret = 0;
2940 }
2941
2942 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
2943 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
2944 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
2945 ret = 0;
2946 }
2947
2948 /* Validity Constraint: Notation Attributes */
2949 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
2950 xmlEnumerationPtr tree = attrDecl->tree;
2951 xmlNotationPtr nota;
2952
2953 /* First check that the given NOTATION was declared */
2954 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2955 if (nota == NULL)
2956 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2957
2958 if (nota == NULL) {
2959 VERROR(ctxt->userData,
2960 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
2961 value, attr->name, elem->name);
2962 ret = 0;
2963 }
2964
2965 /* Second, verify that it's among the list */
2966 while (tree != NULL) {
2967 if (xmlStrEqual(tree->name, value)) break;
2968 tree = tree->next;
2969 }
2970 if (tree == NULL) {
2971 VERROR(ctxt->userData,
2972"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
2973 value, attr->name, elem->name);
2974 ret = 0;
2975 }
2976 }
2977
2978 /* Validity Constraint: Enumeration */
2979 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
2980 xmlEnumerationPtr tree = attrDecl->tree;
2981 while (tree != NULL) {
2982 if (xmlStrEqual(tree->name, value)) break;
2983 tree = tree->next;
2984 }
2985 if (tree == NULL) {
2986 VERROR(ctxt->userData,
2987 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
2988 value, attr->name, elem->name);
2989 ret = 0;
2990 }
2991 }
2992
2993 /* Fixed Attribute Default */
2994 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
2995 (!xmlStrEqual(attrDecl->defaultValue, value))) {
2996 VERROR(ctxt->userData,
2997 "Value for attribute %s on %s must be \"%s\"\n",
2998 attr->name, elem->name, attrDecl->defaultValue);
2999 ret = 0;
3000 }
3001
3002 /* Extra check for the attribute value */
3003 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3004 attrDecl->atype, value);
3005
3006 return(ret);
3007}
3008
3009/* Find the next XML_ELEMENT_NODE, subject to the content constraints.
3010 * Return -1 if we found something unexpected, or 1 otherwise.
3011 */
3012
3013static int
3014xmlValidateFindNextElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
3015 xmlElementContentPtr cont)
3016{
Daniel Veillarde356c282001-03-10 12:32:04 +00003017 DEBUG_VALID_MSG("skipping to next element");
3018 while (*child && (*child)->type != XML_ELEMENT_NODE) {
3019 switch ((*child)->type) {
3020 /*
3021 * If there is an entity declared and it's not empty
3022 * Push the current node on the stack and process with the
3023 * entity content.
3024 */
3025 case XML_ENTITY_REF_NODE:
3026 if (((*child)->children != NULL) &&
3027 ((*child)->children->children != NULL)) {
3028 nodeVPush(ctxt, *child);
3029 *child = (*child)->children->children;
3030 continue;
3031 }
3032 break;
Owen Taylor3473f882001-02-23 17:55:21 +00003033
Daniel Veillarde356c282001-03-10 12:32:04 +00003034 /* These things are ignored (skipped) during validation. */
3035 case XML_PI_NODE:
3036 case XML_COMMENT_NODE:
3037 case XML_XINCLUDE_START:
3038 case XML_XINCLUDE_END:
3039 break;
Owen Taylor3473f882001-02-23 17:55:21 +00003040
Daniel Veillarde356c282001-03-10 12:32:04 +00003041 case XML_TEXT_NODE:
3042 if (xmlIsBlankNode(*child)
3043 && (cont->type == XML_ELEMENT_CONTENT_ELEMENT
3044 || cont->type == XML_ELEMENT_CONTENT_SEQ
3045 || cont->type == XML_ELEMENT_CONTENT_OR))
3046 break;
3047 DEBUG_VALID_MSG("failed non-blank");
3048 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003049
Daniel Veillarde356c282001-03-10 12:32:04 +00003050 default:
3051 DEBUG_VALID_MSG("failed unknown type");
3052 return(-1);
3053 }
3054 *child = (*child)->next;
Owen Taylor3473f882001-02-23 17:55:21 +00003055 }
Daniel Veillarde356c282001-03-10 12:32:04 +00003056#ifdef DEBUG_VALID_ALGO
3057 if (*child != NULL) {
3058 DEBUG_VALID_MSG((*child)->name);
3059 }
3060 DEBUG_VALID_MSG("found ...");
3061#endif
Owen Taylor3473f882001-02-23 17:55:21 +00003062
Daniel Veillarde356c282001-03-10 12:32:04 +00003063 return(1);
Owen Taylor3473f882001-02-23 17:55:21 +00003064}
3065
3066int xmlValidateElementTypeElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
3067 xmlElementContentPtr cont);
3068
3069/**
3070 * xmlValidateElementTypeExpr:
3071 * @ctxt: the validation context
3072 * @child: pointer to the child list
3073 * @cont: pointer to the content declaration
3074 *
3075 * Try to validate the content of an element of type element
3076 * but don't handle the occurence factor
3077 *
3078 * returns 1 if valid or 0 and -1 if PCDATA stuff is found,
3079 * also update child value in-situ.
3080 */
3081
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003082static int
Owen Taylor3473f882001-02-23 17:55:21 +00003083xmlValidateElementTypeExpr(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
3084 xmlElementContentPtr cont) {
3085 xmlNodePtr cur;
3086 int ret = 1;
3087
3088 if (cont == NULL) return(-1);
3089 DEBUG_VALID_STATE(*child, cont)
3090 ret = xmlValidateFindNextElement(ctxt, child, cont);
3091 if (ret < 0)
3092 return(-1);
3093 DEBUG_VALID_STATE(*child, cont)
3094 switch (cont->type) {
3095 case XML_ELEMENT_CONTENT_PCDATA:
3096 if (*child == NULL) return(0);
Daniel Veillarde356c282001-03-10 12:32:04 +00003097 if ((*child)->type == XML_TEXT_NODE) {
3098 DEBUG_VALID_MSG("pcdata found");
3099 return(1);
3100 }
Owen Taylor3473f882001-02-23 17:55:21 +00003101 return(0);
3102 case XML_ELEMENT_CONTENT_ELEMENT:
3103 if (*child == NULL) return(0);
3104 ret = (xmlStrEqual((*child)->name, cont->name));
3105 if (ret == 1) {
Daniel Veillarde356c282001-03-10 12:32:04 +00003106 DEBUG_VALID_MSG("element found, skip to next");
Owen Taylor3473f882001-02-23 17:55:21 +00003107 while ((*child)->next == NULL) {
3108 if (((*child)->parent != NULL) &&
3109 ((*child)->parent->type == XML_ENTITY_DECL)) {
3110 *child = nodeVPop(ctxt);
3111 } else
3112 break;
3113 }
3114 *child = (*child)->next;
3115 }
3116 return(ret);
3117 case XML_ELEMENT_CONTENT_OR:
3118 cur = *child;
3119 ret = xmlValidateElementTypeElement(ctxt, child, cont->c1);
3120 if (ret == -1) return(-1);
3121 if (ret == 1) {
Daniel Veillarde356c282001-03-10 12:32:04 +00003122 DEBUG_VALID_MSG("or succeeded first branch");
3123 return(1);
Owen Taylor3473f882001-02-23 17:55:21 +00003124 }
3125 /* rollback and retry the other path */
3126 *child = cur;
3127 ret = xmlValidateElementTypeElement(ctxt, child, cont->c2);
3128 if (ret == -1) return(-1);
3129 if (ret == 0) {
Daniel Veillarde356c282001-03-10 12:32:04 +00003130 DEBUG_VALID_MSG("or failed both branches");
Owen Taylor3473f882001-02-23 17:55:21 +00003131 *child = cur;
3132 return(0);
3133 }
Daniel Veillarde356c282001-03-10 12:32:04 +00003134 DEBUG_VALID_MSG("or succeeded second branch");
Owen Taylor3473f882001-02-23 17:55:21 +00003135 return(1);
3136 case XML_ELEMENT_CONTENT_SEQ:
3137 cur = *child;
3138 ret = xmlValidateElementTypeElement(ctxt, child, cont->c1);
3139 if (ret == -1) return(-1);
3140 if (ret == 0) {
Daniel Veillarde356c282001-03-10 12:32:04 +00003141 DEBUG_VALID_MSG("sequence failed");
Owen Taylor3473f882001-02-23 17:55:21 +00003142 *child = cur;
3143 return(0);
3144 }
3145 ret = xmlValidateElementTypeElement(ctxt, child, cont->c2);
3146 if (ret == -1) return(-1);
3147 if (ret == 0) {
3148 *child = cur;
3149 return(0);
3150 }
Daniel Veillarde356c282001-03-10 12:32:04 +00003151 DEBUG_VALID_MSG("sequence succeeded");
Owen Taylor3473f882001-02-23 17:55:21 +00003152 return(1);
3153 }
3154 return(ret);
3155}
3156
3157/**
3158 * xmlValidateElementTypeElement:
3159 * @ctxt: the validation context
3160 * @child: pointer to the child list
3161 * @cont: pointer to the content declaration
3162 *
3163 * Try to validate the content of an element of type element
3164 * yeah, Yet Another Regexp Implementation, and recursive
3165 *
3166 * returns 1 if valid or 0 and -1 if PCDATA stuff is found,
3167 * also update child and content values in-situ.
3168 */
3169
3170int
3171xmlValidateElementTypeElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
3172 xmlElementContentPtr cont) {
3173 xmlNodePtr cur;
3174 int ret;
3175
3176 if (cont == NULL) return(-1);
3177
3178 DEBUG_VALID_STATE(*child, cont)
3179 ret = xmlValidateFindNextElement(ctxt, child, cont);
3180 if (ret < 0)
3181 return(-1);
3182 DEBUG_VALID_STATE(*child, cont)
3183 cur = *child;
3184 ret = xmlValidateElementTypeExpr(ctxt, child, cont);
3185 if (ret == -1) return(-1);
3186 switch (cont->ocur) {
3187 case XML_ELEMENT_CONTENT_ONCE:
3188 if (ret == 1) {
Daniel Veillarde356c282001-03-10 12:32:04 +00003189 DEBUG_VALID_MSG("once found, skip to next");
Owen Taylor3473f882001-02-23 17:55:21 +00003190 /* skip ignorable elems */
3191 while ((*child != NULL) &&
3192 ((*child)->type == XML_PI_NODE
3193 || (*child)->type == XML_COMMENT_NODE
3194 || (*child)->type == XML_XINCLUDE_START
3195 || (*child)->type == XML_XINCLUDE_END)) {
3196 while ((*child)->next == NULL) {
3197 if (((*child)->parent != NULL) &&
3198 ((*child)->parent->type == XML_ENTITY_REF_NODE)) {
3199 *child = (*child)->parent;
3200 } else
3201 break;
3202 }
3203 *child = (*child)->next;
3204 }
3205 return(1);
3206 }
3207 *child = cur;
3208 return(0);
3209 case XML_ELEMENT_CONTENT_OPT:
3210 if (ret == 0) {
3211 *child = cur;
3212 return(1);
3213 }
Daniel Veillarde356c282001-03-10 12:32:04 +00003214 if (ret == 1) {
3215 DEBUG_VALID_MSG("optional found, skip to next");
3216 /* skip ignorable elems */
3217 while ((*child != NULL) &&
3218 ((*child)->type == XML_PI_NODE
3219 || (*child)->type == XML_COMMENT_NODE
3220 || (*child)->type == XML_XINCLUDE_START
3221 || (*child)->type == XML_XINCLUDE_END)) {
3222 while ((*child)->next == NULL) {
3223 if (((*child)->parent != NULL) &&
3224 ((*child)->parent->type == XML_ENTITY_REF_NODE)) {
3225 *child = (*child)->parent;
3226 } else
3227 break;
3228 }
3229 *child = (*child)->next;
3230 }
3231 return(1);
3232 }
Owen Taylor3473f882001-02-23 17:55:21 +00003233 break;
3234 case XML_ELEMENT_CONTENT_MULT:
3235 if (ret == 0) {
3236 *child = cur;
3237 break;
3238 }
3239 /* no break on purpose */
3240 case XML_ELEMENT_CONTENT_PLUS:
3241 if (ret == 0) {
3242 *child = cur;
3243 return(0);
3244 }
Daniel Veillarde356c282001-03-10 12:32:04 +00003245 DEBUG_VALID_MSG("mult/plus found");
Owen Taylor3473f882001-02-23 17:55:21 +00003246 if (ret == -1) return(-1);
3247 cur = *child;
3248 do {
3249 if (*child == NULL)
3250 break; /* while */
3251 if ((*child)->type == XML_TEXT_NODE
3252 && xmlIsBlankNode(*child)) {
3253 *child = (*child)->next;
3254 continue;
3255 }
3256 ret = xmlValidateElementTypeExpr(ctxt, child, cont);
3257 if (ret == 1)
3258 cur = *child;
3259 } while (ret == 1);
3260 if (ret == -1) return(-1);
3261 *child = cur;
3262 break;
3263 }
Daniel Veillarde356c282001-03-10 12:32:04 +00003264 if (ret == -1) return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00003265
Daniel Veillarde356c282001-03-10 12:32:04 +00003266 return(xmlValidateFindNextElement(ctxt, child, cont));
Owen Taylor3473f882001-02-23 17:55:21 +00003267}
3268
3269/**
3270 * xmlSprintfElementChilds:
3271 * @buf: an output buffer
3272 * @content: An element
3273 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3274 *
3275 * This will dump the list of childs to the buffer
3276 * Intended just for the debug routine
3277 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003278static void
Owen Taylor3473f882001-02-23 17:55:21 +00003279xmlSprintfElementChilds(char *buf, xmlNodePtr node, int glob) {
3280 xmlNodePtr cur;
3281
3282 if (node == NULL) return;
3283 if (glob) strcat(buf, "(");
3284 cur = node->children;
3285 while (cur != NULL) {
3286 switch (cur->type) {
3287 case XML_ELEMENT_NODE:
3288 strcat(buf, (char *) cur->name);
3289 if (cur->next != NULL)
3290 strcat(buf, " ");
3291 break;
3292 case XML_TEXT_NODE:
3293 if (xmlIsBlankNode(cur))
3294 break;
3295 case XML_CDATA_SECTION_NODE:
3296 case XML_ENTITY_REF_NODE:
3297 strcat(buf, "CDATA");
3298 if (cur->next != NULL)
3299 strcat(buf, " ");
3300 break;
3301 case XML_ATTRIBUTE_NODE:
3302 case XML_DOCUMENT_NODE:
3303#ifdef LIBXML_SGML_ENABLED
3304 case XML_SGML_DOCUMENT_NODE:
3305#endif
3306 case XML_HTML_DOCUMENT_NODE:
3307 case XML_DOCUMENT_TYPE_NODE:
3308 case XML_DOCUMENT_FRAG_NODE:
3309 case XML_NOTATION_NODE:
3310 case XML_NAMESPACE_DECL:
3311 strcat(buf, "???");
3312 if (cur->next != NULL)
3313 strcat(buf, " ");
3314 break;
3315 case XML_ENTITY_NODE:
3316 case XML_PI_NODE:
3317 case XML_DTD_NODE:
3318 case XML_COMMENT_NODE:
3319 case XML_ELEMENT_DECL:
3320 case XML_ATTRIBUTE_DECL:
3321 case XML_ENTITY_DECL:
3322 case XML_XINCLUDE_START:
3323 case XML_XINCLUDE_END:
3324 break;
3325 }
3326 cur = cur->next;
3327 }
3328 if (glob) strcat(buf, ")");
3329}
3330
3331
3332/**
3333 * xmlValidateOneElement:
3334 * @ctxt: the validation context
3335 * @doc: a document instance
3336 * @elem: an element instance
3337 *
3338 * Try to validate a single element and it's attributes,
3339 * basically it does the following checks as described by the
3340 * XML-1.0 recommendation:
3341 * - [ VC: Element Valid ]
3342 * - [ VC: Required Attribute ]
3343 * Then call xmlValidateOneAttribute() for each attribute present.
3344 *
3345 * The ID/IDREF checkings are done separately
3346 *
3347 * returns 1 if valid or 0 otherwise
3348 */
3349
3350int
3351xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3352 xmlNodePtr elem) {
3353 xmlElementPtr elemDecl = NULL;
3354 xmlElementContentPtr cont;
3355 xmlAttributePtr attr;
3356 xmlNodePtr child;
3357 int ret = 1;
3358 const xmlChar *name;
3359
3360 CHECK_DTD;
3361
3362 if (elem == NULL) return(0);
3363 if (elem->type == XML_TEXT_NODE) {
3364 }
3365 switch (elem->type) {
3366 case XML_ATTRIBUTE_NODE:
3367 VERROR(ctxt->userData,
3368 "Attribute element not expected here\n");
3369 return(0);
3370 case XML_TEXT_NODE:
3371 if (elem->children != NULL) {
3372 VERROR(ctxt->userData, "Text element has childs !\n");
3373 return(0);
3374 }
3375 if (elem->properties != NULL) {
3376 VERROR(ctxt->userData, "Text element has attributes !\n");
3377 return(0);
3378 }
3379 if (elem->ns != NULL) {
3380 VERROR(ctxt->userData, "Text element has namespace !\n");
3381 return(0);
3382 }
3383 if (elem->nsDef != NULL) {
3384 VERROR(ctxt->userData,
3385 "Text element carries namespace definitions !\n");
3386 return(0);
3387 }
3388 if (elem->content == NULL) {
3389 VERROR(ctxt->userData,
3390 "Text element has no content !\n");
3391 return(0);
3392 }
3393 return(1);
3394 case XML_XINCLUDE_START:
3395 case XML_XINCLUDE_END:
3396 return(1);
3397 case XML_CDATA_SECTION_NODE:
3398 case XML_ENTITY_REF_NODE:
3399 case XML_PI_NODE:
3400 case XML_COMMENT_NODE:
3401 return(1);
3402 case XML_ENTITY_NODE:
3403 VERROR(ctxt->userData,
3404 "Entity element not expected here\n");
3405 return(0);
3406 case XML_NOTATION_NODE:
3407 VERROR(ctxt->userData,
3408 "Notation element not expected here\n");
3409 return(0);
3410 case XML_DOCUMENT_NODE:
3411 case XML_DOCUMENT_TYPE_NODE:
3412 case XML_DOCUMENT_FRAG_NODE:
3413 VERROR(ctxt->userData,
3414 "Document element not expected here\n");
3415 return(0);
3416 case XML_HTML_DOCUMENT_NODE:
3417 VERROR(ctxt->userData,
3418 "\n");
3419 return(0);
3420 case XML_ELEMENT_NODE:
3421 break;
3422 default:
3423 VERROR(ctxt->userData,
3424 "unknown element type %d\n", elem->type);
3425 return(0);
3426 }
3427 if (elem->name == NULL) return(0);
3428
3429 /*
3430 * Fetch the declaration for the qualified name
3431 */
3432 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3433 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
3434 elem->name, elem->ns->prefix);
3435 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3436 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
3437 elem->name, elem->ns->prefix);
3438 }
3439
3440 /*
3441 * Fetch the declaration for the non qualified name
3442 */
3443 if (elemDecl == NULL) {
3444 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
3445 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3446 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
3447 }
3448 if (elemDecl == NULL) {
3449 VERROR(ctxt->userData, "No declaration for element %s\n",
3450 elem->name);
3451 return(0);
3452 }
3453
3454 /* Check taht the element content matches the definition */
3455 switch (elemDecl->etype) {
3456 case XML_ELEMENT_TYPE_EMPTY:
3457 if (elem->children != NULL) {
3458 VERROR(ctxt->userData,
3459 "Element %s was declared EMPTY this one has content\n",
3460 elem->name);
3461 ret = 0;
3462 }
3463 break;
3464 case XML_ELEMENT_TYPE_ANY:
3465 /* I don't think anything is required then */
3466 break;
3467 case XML_ELEMENT_TYPE_MIXED:
3468 /* Hum, this start to get messy */
3469 child = elem->children;
3470 while (child != NULL) {
3471 if (child->type == XML_ELEMENT_NODE) {
3472 name = child->name;
3473 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
3474 xmlChar qname[500];
3475#ifdef HAVE_SNPRINTF
3476 snprintf((char *) qname, sizeof(qname), "%s:%s",
3477 child->ns->prefix, child->name);
3478#else
3479 sprintf((char *) qname, "%s:%s",
3480 child->ns->prefix, child->name);
3481#endif
3482 qname[sizeof(qname) - 1] = 0;
3483 cont = elemDecl->content;
3484 while (cont != NULL) {
3485 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
3486 if (xmlStrEqual(cont->name, qname)) break;
3487 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
3488 (cont->c1 != NULL) &&
3489 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
3490 if (xmlStrEqual(cont->c1->name, qname)) break;
3491 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
3492 (cont->c1 == NULL) ||
3493 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
3494 /* Internal error !!! */
3495 xmlGenericError(xmlGenericErrorContext,
3496 "Internal: MIXED struct bad\n");
3497 break;
3498 }
3499 cont = cont->c2;
3500 }
3501 if (cont != NULL)
3502 goto child_ok;
3503 }
3504 cont = elemDecl->content;
3505 while (cont != NULL) {
3506 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
3507 if (xmlStrEqual(cont->name, name)) break;
3508 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
3509 (cont->c1 != NULL) &&
3510 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
3511 if (xmlStrEqual(cont->c1->name, name)) break;
3512 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
3513 (cont->c1 == NULL) ||
3514 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
3515 /* Internal error !!! */
3516 xmlGenericError(xmlGenericErrorContext,
3517 "Internal: MIXED struct bad\n");
3518 break;
3519 }
3520 cont = cont->c2;
3521 }
3522 if (cont == NULL) {
3523 VERROR(ctxt->userData,
3524 "Element %s is not declared in %s list of possible childs\n",
3525 name, elem->name);
3526 ret = 0;
3527 }
3528 }
3529child_ok:
3530 child = child->next;
3531 }
3532 break;
3533 case XML_ELEMENT_TYPE_ELEMENT:
3534 child = elem->children;
3535 cont = elemDecl->content;
3536 ret = xmlValidateElementTypeElement(ctxt, &child, cont);
3537 while ((child != NULL) && (child->type == XML_TEXT_NODE) &&
3538 (xmlIsBlankNode(child))) {
3539 child = child->next;
3540 continue;
3541 }
3542 if ((ret == 0) || (child != NULL)) {
3543 char expr[1000];
3544 char list[2000];
3545
3546 expr[0] = 0;
3547 xmlSprintfElementContent(expr, cont, 1);
3548 list[0] = 0;
3549 xmlSprintfElementChilds(list, elem, 1);
3550
3551 VERROR(ctxt->userData,
3552 "Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
3553 elem->name, expr, list);
3554 ret = 0;
3555 }
3556 break;
3557 }
3558
3559 /* [ VC: Required Attribute ] */
3560 attr = elemDecl->attributes;
3561 while (attr != NULL) {
3562 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
3563 xmlAttrPtr attrib;
3564 int qualified = -1;
3565
3566 attrib = elem->properties;
3567 while (attrib != NULL) {
3568 if (xmlStrEqual(attrib->name, attr->name)) {
3569 if (attr->prefix != NULL) {
3570 xmlNsPtr nameSpace = attrib->ns;
3571
3572 if (nameSpace == NULL)
3573 nameSpace = elem->ns;
3574 /*
3575 * qualified names handling is problematic, having a
3576 * different prefix should be possible but DTDs don't
3577 * allow to define the URI instead of the prefix :-(
3578 */
3579 if (nameSpace == NULL) {
3580 if (qualified < 0)
3581 qualified = 0;
3582 } else if (!xmlStrEqual(nameSpace->prefix, attr->prefix)) {
3583 if (qualified < 1)
3584 qualified = 1;
3585 } else
3586 goto found;
3587 } else {
3588 /*
3589 * We should allow applications to define namespaces
3590 * for their application even if the DTD doesn't
3591 * carry one, otherwise, basically we would always
3592 * break.
3593 */
3594 goto found;
3595 }
3596 }
3597 attrib = attrib->next;
3598 }
3599 if (qualified == -1) {
3600 if (attr->prefix == NULL) {
3601 VERROR(ctxt->userData,
3602 "Element %s doesn't carry attribute %s\n",
3603 elem->name, attr->name);
3604 ret = 0;
3605 } else {
3606 VERROR(ctxt->userData,
3607 "Element %s doesn't carry attribute %s:%s\n",
3608 elem->name, attr->prefix,attr->name);
3609 ret = 0;
3610 }
3611 } else if (qualified == 0) {
3612 VWARNING(ctxt->userData,
3613 "Element %s required attribute %s:%s has no prefix\n",
3614 elem->name, attr->prefix,attr->name);
3615 } else if (qualified == 1) {
3616 VWARNING(ctxt->userData,
3617 "Element %s required attribute %s:%s has different prefix\n",
3618 elem->name, attr->prefix,attr->name);
3619 }
3620 }
3621found:
3622 attr = attr->nexth;
3623 }
3624 return(ret);
3625}
3626
3627/**
3628 * xmlValidateRoot:
3629 * @ctxt: the validation context
3630 * @doc: a document instance
3631 *
3632 * Try to validate a the root element
3633 * basically it does the following check as described by the
3634 * XML-1.0 recommendation:
3635 * - [ VC: Root Element Type ]
3636 * it doesn't try to recurse or apply other check to the element
3637 *
3638 * returns 1 if valid or 0 otherwise
3639 */
3640
3641int
3642xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
3643 xmlNodePtr root;
3644 if (doc == NULL) return(0);
3645
3646 root = xmlDocGetRootElement(doc);
3647 if ((root == NULL) || (root->name == NULL)) {
3648 VERROR(ctxt->userData, "Not valid: no root element\n");
3649 return(0);
3650 }
3651
3652 /*
3653 * When doing post validation against a separate DTD, those may
3654 * no internal subset has been generated
3655 */
3656 if ((doc->intSubset != NULL) &&
3657 (doc->intSubset->name != NULL)) {
3658 /*
3659 * Check first the document root against the NQName
3660 */
3661 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
3662 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
3663 xmlChar qname[500];
3664#ifdef HAVE_SNPRINTF
3665 snprintf((char *) qname, sizeof(qname), "%s:%s",
3666 root->ns->prefix, root->name);
3667#else
3668 sprintf((char *) qname, "%s:%s", root->ns->prefix, root->name);
3669#endif
3670 qname[sizeof(qname) - 1] = 0;
3671 if (xmlStrEqual(doc->intSubset->name, qname))
3672 goto name_ok;
3673 }
3674 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
3675 (xmlStrEqual(root->name, BAD_CAST "html")))
3676 goto name_ok;
3677 VERROR(ctxt->userData,
3678 "Not valid: root and DtD name do not match '%s' and '%s'\n",
3679 root->name, doc->intSubset->name);
3680 return(0);
3681
3682 }
3683 }
3684name_ok:
3685 return(1);
3686}
3687
3688
3689/**
3690 * xmlValidateElement:
3691 * @ctxt: the validation context
3692 * @doc: a document instance
3693 * @elem: an element instance
3694 *
3695 * Try to validate the subtree under an element
3696 *
3697 * returns 1 if valid or 0 otherwise
3698 */
3699
3700int
3701xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
3702 xmlNodePtr child;
3703 xmlAttrPtr attr;
3704 xmlChar *value;
3705 int ret = 1;
3706
3707 if (elem == NULL) return(0);
3708
3709 /*
3710 * XInclude elements were added after parsing in the infoset,
3711 * they don't really mean anything validation wise.
3712 */
3713 if ((elem->type == XML_XINCLUDE_START) ||
3714 (elem->type == XML_XINCLUDE_END))
3715 return(1);
3716
3717 CHECK_DTD;
3718
3719 ret &= xmlValidateOneElement(ctxt, doc, elem);
3720 attr = elem->properties;
3721 while(attr != NULL) {
3722 value = xmlNodeListGetString(doc, attr->children, 0);
3723 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
3724 if (value != NULL)
3725 xmlFree(value);
3726 attr= attr->next;
3727 }
3728 child = elem->children;
3729 while (child != NULL) {
3730 ret &= xmlValidateElement(ctxt, doc, child);
3731 child = child->next;
3732 }
3733
3734 return(ret);
3735}
3736
Daniel Veillard8730c562001-02-26 10:49:57 +00003737/**
3738 * xmlValidateRef:
3739 * @ref: A reference to be validated
3740 * @ctxt: Validation context
3741 * @name: Name of ID we are searching for
3742 *
3743 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003744static void
Daniel Veillard8730c562001-02-26 10:49:57 +00003745xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00003746 const xmlChar *name) {
3747 xmlAttrPtr id;
3748 xmlAttrPtr attr;
3749
3750 if (ref == NULL)
3751 return;
3752 attr = ref->attr;
3753 if (attr == NULL)
3754 return;
3755 if (attr->atype == XML_ATTRIBUTE_IDREF) {
3756 id = xmlGetID(ctxt->doc, name);
3757 if (id == NULL) {
3758 VERROR(ctxt->userData,
3759 "IDREF attribute %s reference an unknown ID \"%s\"\n",
3760 attr->name, name);
3761 ctxt->valid = 0;
3762 }
3763 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
3764 xmlChar *dup, *str = NULL, *cur, save;
3765
3766 dup = xmlStrdup(name);
3767 if (dup == NULL) {
3768 ctxt->valid = 0;
3769 return;
3770 }
3771 cur = dup;
3772 while (*cur != 0) {
3773 str = cur;
3774 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3775 save = *cur;
3776 *cur = 0;
3777 id = xmlGetID(ctxt->doc, str);
3778 if (id == NULL) {
3779 VERROR(ctxt->userData,
3780 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
3781 attr->name, str);
3782 ctxt->valid = 0;
3783 }
3784 if (save == 0)
3785 break;
3786 *cur = save;
3787 while (IS_BLANK(*cur)) cur++;
3788 }
3789 xmlFree(dup);
3790 }
3791}
3792
3793/**
Daniel Veillard8730c562001-02-26 10:49:57 +00003794 * xmlWalkValidateList:
3795 * @data: Contents of current link
3796 * @user: Value supplied by the user
3797 *
3798 * Return 0 to abort the walk or 1 to continue
3799 */
3800static int
3801xmlWalkValidateList(const void *data, const void *user)
3802{
3803 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
3804 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
3805 return 1;
3806}
3807
3808/**
3809 * xmlValidateCheckRefCallback:
3810 * @ref_list: List of references
3811 * @ctxt: Validation context
3812 * @name: Name of ID we are searching for
3813 *
3814 */
3815static void
3816xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
3817 const xmlChar *name) {
3818 xmlValidateMemo memo;
3819
3820 if (ref_list == NULL)
3821 return;
3822 memo.ctxt = ctxt;
3823 memo.name = name;
3824
3825 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
3826
3827}
3828
3829/**
Owen Taylor3473f882001-02-23 17:55:21 +00003830 * xmlValidateDocumentFinal:
3831 * @ctxt: the validation context
3832 * @doc: a document instance
3833 *
3834 * Does the final step for the document validation once all the
3835 * incremental validation steps have been completed
3836 *
3837 * basically it does the following checks described by the XML Rec
3838 *
3839 *
3840 * returns 1 if valid or 0 otherwise
3841 */
3842
3843int
3844xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
3845 xmlRefTablePtr table;
3846
3847 if (doc == NULL) {
3848 xmlGenericError(xmlGenericErrorContext,
3849 "xmlValidateDocumentFinal: doc == NULL\n");
3850 return(0);
3851 }
3852
3853 /*
3854 * Check all the NOTATION/NOTATIONS attributes
3855 */
3856 /*
3857 * Check all the ENTITY/ENTITIES attributes definition for validity
3858 */
3859 /*
3860 * Check all the IDREF/IDREFS attributes definition for validity
3861 */
3862 table = (xmlRefTablePtr) doc->refs;
3863 ctxt->doc = doc;
3864 ctxt->valid = 1;
3865 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
3866 return(ctxt->valid);
3867}
3868
3869/**
3870 * xmlValidateDtd:
3871 * @ctxt: the validation context
3872 * @doc: a document instance
3873 * @dtd: a dtd instance
3874 *
3875 * Try to validate the document against the dtd instance
3876 *
3877 * basically it does check all the definitions in the DtD.
3878 *
3879 * returns 1 if valid or 0 otherwise
3880 */
3881
3882int
3883xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
3884 int ret;
3885 xmlDtdPtr oldExt;
3886 xmlNodePtr root;
3887
3888 if (dtd == NULL) return(0);
3889 if (doc == NULL) return(0);
3890 oldExt = doc->extSubset;
3891 doc->extSubset = dtd;
3892 ret = xmlValidateRoot(ctxt, doc);
3893 if (ret == 0) {
3894 doc->extSubset = oldExt;
3895 return(ret);
3896 }
3897 if (doc->ids != NULL) {
3898 xmlFreeIDTable(doc->ids);
3899 doc->ids = NULL;
3900 }
3901 if (doc->refs != NULL) {
3902 xmlFreeRefTable(doc->refs);
3903 doc->refs = NULL;
3904 }
3905 root = xmlDocGetRootElement(doc);
3906 ret = xmlValidateElement(ctxt, doc, root);
3907 ret &= xmlValidateDocumentFinal(ctxt, doc);
3908 doc->extSubset = oldExt;
3909 return(ret);
3910}
3911
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003912static void
Owen Taylor3473f882001-02-23 17:55:21 +00003913xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003914 const xmlChar *name UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003915 if (cur == NULL)
3916 return;
3917 switch (cur->atype) {
3918 case XML_ATTRIBUTE_CDATA:
3919 case XML_ATTRIBUTE_ID:
3920 case XML_ATTRIBUTE_IDREF :
3921 case XML_ATTRIBUTE_IDREFS:
3922 case XML_ATTRIBUTE_NMTOKEN:
3923 case XML_ATTRIBUTE_NMTOKENS:
3924 case XML_ATTRIBUTE_ENUMERATION:
3925 break;
3926 case XML_ATTRIBUTE_ENTITY:
3927 case XML_ATTRIBUTE_ENTITIES:
3928 case XML_ATTRIBUTE_NOTATION:
3929 if (cur->defaultValue != NULL) {
3930 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
3931 cur->name, cur->atype, cur->defaultValue);
3932 }
3933 if (cur->tree != NULL) {
3934 xmlEnumerationPtr tree = cur->tree;
3935 while (tree != NULL) {
3936 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
3937 cur->name, cur->atype, tree->name);
3938 tree = tree->next;
3939 }
3940 }
3941 }
3942}
3943
3944/**
3945 * xmlValidateDtdFinal:
3946 * @ctxt: the validation context
3947 * @doc: a document instance
3948 *
3949 * Does the final step for the dtds validation once all the
3950 * subsets have been parsed
3951 *
3952 * basically it does the following checks described by the XML Rec
3953 * - check that ENTITY and ENTITIES type attributes default or
3954 * possible values matches one of the defined entities.
3955 * - check that NOTATION type attributes default or
3956 * possible values matches one of the defined notations.
3957 *
3958 * returns 1 if valid or 0 otherwise
3959 */
3960
3961int
3962xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
3963 int ret = 1;
3964 xmlDtdPtr dtd;
3965 xmlAttributeTablePtr table;
3966
3967 if (doc == NULL) return(0);
3968 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
3969 return(0);
3970 ctxt->doc = doc;
3971 ctxt->valid = ret;
3972 dtd = doc->intSubset;
3973 if ((dtd != NULL) && (dtd->attributes != NULL)) {
3974 table = (xmlAttributeTablePtr) dtd->attributes;
3975 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
3976 }
3977 dtd = doc->extSubset;
3978 if ((dtd != NULL) && (dtd->attributes != NULL)) {
3979 table = (xmlAttributeTablePtr) dtd->attributes;
3980 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
3981 }
3982 return(ctxt->valid);
3983}
3984
3985/**
3986 * xmlValidateDocument:
3987 * @ctxt: the validation context
3988 * @doc: a document instance
3989 *
3990 * Try to validate the document instance
3991 *
3992 * basically it does the all the checks described by the XML Rec
3993 * i.e. validates the internal and external subset (if present)
3994 * and validate the document tree.
3995 *
3996 * returns 1 if valid or 0 otherwise
3997 */
3998
3999int
4000xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4001 int ret;
4002 xmlNodePtr root;
4003
4004 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4005 return(0);
4006 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4007 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4008 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4009 doc->intSubset->SystemID);
4010 if (doc->extSubset == NULL) {
4011 if (doc->intSubset->SystemID != NULL) {
4012 VERROR(ctxt->userData,
4013 "Could not load the external subset \"%s\"\n",
4014 doc->intSubset->SystemID);
4015 } else {
4016 VERROR(ctxt->userData,
4017 "Could not load the external subset \"%s\"\n",
4018 doc->intSubset->ExternalID);
4019 }
4020 return(0);
4021 }
4022 }
4023
4024 if (doc->ids != NULL) {
4025 xmlFreeIDTable(doc->ids);
4026 doc->ids = NULL;
4027 }
4028 if (doc->refs != NULL) {
4029 xmlFreeRefTable(doc->refs);
4030 doc->refs = NULL;
4031 }
4032 ret = xmlValidateDtdFinal(ctxt, doc);
4033 if (!xmlValidateRoot(ctxt, doc)) return(0);
4034
4035 root = xmlDocGetRootElement(doc);
4036 ret &= xmlValidateElement(ctxt, doc, root);
4037 ret &= xmlValidateDocumentFinal(ctxt, doc);
4038 return(ret);
4039}
4040
4041
4042/************************************************************************
4043 * *
4044 * Routines for dynamic validation editing *
4045 * *
4046 ************************************************************************/
4047
4048/**
4049 * xmlValidGetPotentialChildren:
4050 * @ctree: an element content tree
4051 * @list: an array to store the list of child names
4052 * @len: a pointer to the number of element in the list
4053 * @max: the size of the array
4054 *
4055 * Build/extend a list of potential children allowed by the content tree
4056 *
4057 * returns the number of element in the list, or -1 in case of error.
4058 */
4059
4060int
4061xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4062 int *len, int max) {
4063 int i;
4064
4065 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4066 return(-1);
4067 if (*len >= max) return(*len);
4068
4069 switch (ctree->type) {
4070 case XML_ELEMENT_CONTENT_PCDATA:
4071 for (i = 0; i < *len;i++)
4072 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4073 list[(*len)++] = BAD_CAST "#PCDATA";
4074 break;
4075 case XML_ELEMENT_CONTENT_ELEMENT:
4076 for (i = 0; i < *len;i++)
4077 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4078 list[(*len)++] = ctree->name;
4079 break;
4080 case XML_ELEMENT_CONTENT_SEQ:
4081 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4082 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4083 break;
4084 case XML_ELEMENT_CONTENT_OR:
4085 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4086 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4087 break;
4088 }
4089
4090 return(*len);
4091}
4092
4093/**
4094 * xmlValidGetValidElements:
4095 * @prev: an element to insert after
4096 * @next: an element to insert next
4097 * @list: an array to store the list of child names
4098 * @max: the size of the array
4099 *
4100 * This function returns the list of authorized children to insert
4101 * within an existing tree while respecting the validity constraints
4102 * forced by the Dtd. The insertion point is defined using @prev and
4103 * @next in the following ways:
4104 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4105 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4106 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4107 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4108 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4109 *
4110 * pointers to the element names are inserted at the beginning of the array
4111 * and do not need to be freed.
4112 *
4113 * returns the number of element in the list, or -1 in case of error. If
4114 * the function returns the value @max the caller is invited to grow the
4115 * receiving array and retry.
4116 */
4117
4118int
4119xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4120 int max) {
4121 int nb_valid_elements = 0;
4122 const xmlChar *elements[256];
4123 int nb_elements = 0, i;
4124
4125 xmlNode *ref_node;
4126 xmlNode *parent;
4127 xmlNode *test_node;
4128
4129 xmlNode *prev_next;
4130 xmlNode *next_prev;
4131 xmlNode *parent_childs;
4132 xmlNode *parent_last;
4133
4134 xmlElement *element_desc;
4135
4136 if (prev == NULL && next == NULL)
4137 return(-1);
4138
4139 if (list == NULL) return(-1);
4140 if (max <= 0) return(-1);
4141
4142 nb_valid_elements = 0;
4143 ref_node = prev ? prev : next;
4144 parent = ref_node->parent;
4145
4146 /*
4147 * Retrieves the parent element declaration
4148 */
4149 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
4150 parent->name);
4151 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
4152 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
4153 parent->name);
4154 if (element_desc == NULL) return(-1);
4155
4156 /*
4157 * Do a backup of the current tree structure
4158 */
4159 prev_next = prev ? prev->next : NULL;
4160 next_prev = next ? next->prev : NULL;
4161 parent_childs = parent->children;
4162 parent_last = parent->last;
4163
4164 /*
4165 * Creates a dummy node and insert it into the tree
4166 */
4167 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
4168 test_node->doc = ref_node->doc;
4169 test_node->parent = parent;
4170 test_node->prev = prev;
4171 test_node->next = next;
4172
4173 if (prev) prev->next = test_node;
4174 else parent->children = test_node;
4175
4176 if (next) next->prev = test_node;
4177 else parent->last = test_node;
4178
4179 /*
4180 * Insert each potential child node and check if the parent is
4181 * still valid
4182 */
4183 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
4184 elements, &nb_elements, 256);
4185
4186 for (i = 0;i < nb_elements;i++) {
4187 test_node->name = elements[i];
4188 if (xmlValidateOneElement(NULL, parent->doc, parent)) {
4189 int j;
4190
4191 for (j = 0; j < nb_valid_elements;j++)
4192 if (xmlStrEqual(elements[i], list[j])) break;
4193 list[nb_valid_elements++] = elements[i];
4194 if (nb_valid_elements >= max) break;
4195 }
4196 }
4197
4198 /*
4199 * Restore the tree structure
4200 */
4201 if (prev) prev->next = prev_next;
4202 if (next) next->prev = next_prev;
4203 parent->children = parent_childs;
4204 parent->last = parent_last;
4205
4206 return(nb_valid_elements);
4207}