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