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