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