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