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