blob: a1bb5bac67a6db468f8a3c875d324db8a5f933b5 [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) { \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000037 if (ctxt->name##Max <= 0) { \
38 ctxt->name##Max = 4; \
39 ctxt->name##Tab = (type *) xmlMalloc( \
40 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
41 if (ctxt->name##Tab == NULL) { \
42 xmlGenericError(xmlGenericErrorContext, \
43 "malloc failed !\n"); \
44 return(0); \
45 } \
46 } \
Owen Taylor3473f882001-02-23 17:55:21 +000047 if (ctxt->name##Nr >= ctxt->name##Max) { \
48 ctxt->name##Max *= 2; \
49 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
50 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
51 if (ctxt->name##Tab == NULL) { \
52 xmlGenericError(xmlGenericErrorContext, \
53 "realloc failed !\n"); \
54 return(0); \
55 } \
56 } \
57 ctxt->name##Tab[ctxt->name##Nr] = value; \
58 ctxt->name = value; \
59 return(ctxt->name##Nr++); \
60} \
61scope type name##VPop(xmlValidCtxtPtr ctxt) { \
62 type ret; \
63 if (ctxt->name##Nr <= 0) return(0); \
64 ctxt->name##Nr--; \
65 if (ctxt->name##Nr > 0) \
66 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
67 else \
68 ctxt->name = NULL; \
69 ret = ctxt->name##Tab[ctxt->name##Nr]; \
70 ctxt->name##Tab[ctxt->name##Nr] = 0; \
71 return(ret); \
72} \
73
Daniel Veillarddab4cb32001-04-20 13:03:48 +000074/*
75 * I will use a home made algorithm less complex and easier to
76 * debug/maintin than a generic NFA -> DFA state based algo. The
77 * only restriction is on the deepness of the tree limited by the
78 * size of the occurs bitfield
79 *
80 * this is the content of a saved state for rollbacks
81 */
82
83#define ROLLBACK_OR 0
84#define ROLLBACK_PARENT 1
85
86struct _xmlValidState {
87 xmlElementContentPtr cont; /* pointer to the content model subtree */
88 xmlNodePtr node; /* pointer to the current node in the list */
89 long occurs;/* bitfield for multiple occurences */
90 unsigned char depth; /* current depth in the overall tree */
91 unsigned char state; /* ROLLBACK_XXX */
92} _xmlValidState;
93
94#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
95#define CONT ctxt->vstate->cont
96#define NODE ctxt->vstate->node
97#define DEPTH ctxt->vstate->depth
98#define OCCURS ctxt->vstate->occurs
99#define STATE ctxt->vstate->state
100
101#define OCCURENCE (ctxt->vstate->occurs & (1 << DEPTH))
102#define PARENT_OCCURENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
103
104#define SET_OCCURENCE ctxt->vstate->occurs |= (1 << DEPTH)
105#define RESET_OCCURENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
106
107static int
108vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
109 xmlNodePtr node, unsigned char depth, long occurs,
110 unsigned char state) {
111 if (ctxt->vstateNr >= ctxt->vstateMax) {
112 ctxt->vstateMax *= 2;
113 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
114 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
115 if (ctxt->vstateTab == NULL) {
116 xmlGenericError(xmlGenericErrorContext,
117 "realloc failed !n");
118 return(0);
119 }
120 }
121 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
122 ctxt->vstateTab[ctxt->vstateNr].node = node;
123 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
124 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
125 ctxt->vstateTab[ctxt->vstateNr].state = state;
126 return(ctxt->vstateNr++);
127}
128
129static int
130vstateVPop(xmlValidCtxtPtr ctxt) {
131 if (ctxt->vstateNr <= 1) return(-1);
132 ctxt->vstateNr--;
133 ctxt->vstate = &ctxt->vstateTab[0];
134 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
135 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
136 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
137 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
138 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
139 return(ctxt->vstateNr);
140}
141
Owen Taylor3473f882001-02-23 17:55:21 +0000142PUSH_AND_POP(static, xmlNodePtr, node)
143
144/* #define DEBUG_VALID_ALGO */
145
146#ifdef DEBUG_VALID_ALGO
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000147static void
148xmlValidPrintNode(xmlNodePtr cur) {
149 if (cur == NULL) {
150 xmlGenericError(xmlGenericErrorContext, "null");
151 return;
152 }
153 switch (cur->type) {
154 case XML_ELEMENT_NODE:
155 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
156 break;
157 case XML_TEXT_NODE:
158 xmlGenericError(xmlGenericErrorContext, "text ");
159 break;
160 case XML_CDATA_SECTION_NODE:
161 xmlGenericError(xmlGenericErrorContext, "cdata ");
162 break;
163 case XML_ENTITY_REF_NODE:
164 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
165 break;
166 case XML_PI_NODE:
167 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
168 break;
169 case XML_COMMENT_NODE:
170 xmlGenericError(xmlGenericErrorContext, "comment ");
171 break;
172 case XML_ATTRIBUTE_NODE:
173 xmlGenericError(xmlGenericErrorContext, "?attr? ");
174 break;
175 case XML_ENTITY_NODE:
176 xmlGenericError(xmlGenericErrorContext, "?ent? ");
177 break;
178 case XML_DOCUMENT_NODE:
179 xmlGenericError(xmlGenericErrorContext, "?doc? ");
180 break;
181 case XML_DOCUMENT_TYPE_NODE:
182 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
183 break;
184 case XML_DOCUMENT_FRAG_NODE:
185 xmlGenericError(xmlGenericErrorContext, "?frag? ");
186 break;
187 case XML_NOTATION_NODE:
188 xmlGenericError(xmlGenericErrorContext, "?nota? ");
189 break;
190 case XML_HTML_DOCUMENT_NODE:
191 xmlGenericError(xmlGenericErrorContext, "?html? ");
192 break;
193 case XML_DTD_NODE:
194 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
195 break;
196 case XML_ELEMENT_DECL:
197 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
198 break;
199 case XML_ATTRIBUTE_DECL:
200 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
201 break;
202 case XML_ENTITY_DECL:
203 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
204 break;
205 case XML_NAMESPACE_DECL:
206 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
207 break;
208 case XML_XINCLUDE_START:
209 xmlGenericError(xmlGenericErrorContext, "incstart ");
210 break;
211 case XML_XINCLUDE_END:
212 xmlGenericError(xmlGenericErrorContext, "incend ");
213 break;
214 }
215}
216
217static void
218xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000219 if (cur == NULL)
220 xmlGenericError(xmlGenericErrorContext, "null ");
221 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000222 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000223 cur = cur->next;
224 }
225}
226
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000227static void
228xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000229 char expr[1000];
230
231 expr[0] = 0;
232 xmlGenericError(xmlGenericErrorContext, "valid: ");
233 xmlValidPrintNodeList(cur);
234 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000235 xmlSprintfElementContent(expr, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000236 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
237}
238
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000239static void
240xmlValidDebugState(xmlValidStatePtr state) {
241 xmlGenericError(xmlGenericErrorContext, "(");
242 if (state->cont == NULL)
243 xmlGenericError(xmlGenericErrorContext, "null,");
244 else
245 switch (state->cont->type) {
246 case XML_ELEMENT_CONTENT_PCDATA:
247 xmlGenericError(xmlGenericErrorContext, "pcdata,");
248 break;
249 case XML_ELEMENT_CONTENT_ELEMENT:
250 xmlGenericError(xmlGenericErrorContext, "%s,",
251 state->cont->name);
252 break;
253 case XML_ELEMENT_CONTENT_SEQ:
254 xmlGenericError(xmlGenericErrorContext, "seq,");
255 break;
256 case XML_ELEMENT_CONTENT_OR:
257 xmlGenericError(xmlGenericErrorContext, "or,");
258 break;
259 }
260 xmlValidPrintNode(state->node);
261 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
262 state->depth, state->occurs, state->state);
263}
264
265static void
266xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
267 int i, j;
268
269 xmlGenericError(xmlGenericErrorContext, "state: ");
270 xmlValidDebugState(ctxt->vstate);
271 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
272 ctxt->vstateNr - 1);
273 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
274 xmlValidDebugState(&ctxt->vstateTab[j]);
275 xmlGenericError(xmlGenericErrorContext, "\n");
276}
277
278/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000279#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000280 *****/
281
282#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000283#define DEBUG_VALID_MSG(m) \
284 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
285
Owen Taylor3473f882001-02-23 17:55:21 +0000286#else
287#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000288#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000289#endif
290
291/* TODO: use hash table for accesses to elem and attribute dedinitions */
292
293#define VERROR \
294 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
295
296#define VWARNING \
297 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
298
299#define CHECK_DTD \
300 if (doc == NULL) return(0); \
301 else if ((doc->intSubset == NULL) && \
302 (doc->extSubset == NULL)) return(0)
303
304xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000305static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
306 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000307xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
308
309/************************************************************************
310 * *
311 * QName handling helper *
312 * *
313 ************************************************************************/
314
315/**
316 * xmlSplitQName2:
317 * @name: an XML parser context
318 * @prefix: a xmlChar **
319 *
320 * parse an XML qualified name string
321 *
322 * [NS 5] QName ::= (Prefix ':')? LocalPart
323 *
324 * [NS 6] Prefix ::= NCName
325 *
326 * [NS 7] LocalPart ::= NCName
327 *
328 * Returns NULL if not a QName, otherwise the local part, and prefix
329 * is updated to get the Prefix if any.
330 */
331
332xmlChar *
333xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
334 int len = 0;
335 xmlChar *ret = NULL;
336
337 *prefix = NULL;
338
339 /* xml: prefix is not really a namespace */
340 if ((name[0] == 'x') && (name[1] == 'm') &&
341 (name[2] == 'l') && (name[3] == ':'))
342 return(NULL);
343
344 /* nasty but valid */
345 if (name[0] == ':')
346 return(NULL);
347
348 /*
349 * we are not trying to validate but just to cut, and yes it will
350 * work even if this is as set of UTF-8 encoded chars
351 */
352 while ((name[len] != 0) && (name[len] != ':'))
353 len++;
354
355 if (name[len] == 0)
356 return(NULL);
357
358 *prefix = xmlStrndup(name, len);
359 ret = xmlStrdup(&name[len + 1]);
360
361 return(ret);
362}
363
364/****************************************************************
365 * *
366 * Util functions for data allocation/deallocation *
367 * *
368 ****************************************************************/
369
370/**
371 * xmlNewElementContent:
372 * @name: the subelement name or NULL
373 * @type: the type of element content decl
374 *
375 * Allocate an element content structure.
376 *
377 * Returns NULL if not, othervise the new element content structure
378 */
379xmlElementContentPtr
380xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
381 xmlElementContentPtr ret;
382
383 switch(type) {
384 case XML_ELEMENT_CONTENT_ELEMENT:
385 if (name == NULL) {
386 xmlGenericError(xmlGenericErrorContext,
387 "xmlNewElementContent : name == NULL !\n");
388 }
389 break;
390 case XML_ELEMENT_CONTENT_PCDATA:
391 case XML_ELEMENT_CONTENT_SEQ:
392 case XML_ELEMENT_CONTENT_OR:
393 if (name != NULL) {
394 xmlGenericError(xmlGenericErrorContext,
395 "xmlNewElementContent : name != NULL !\n");
396 }
397 break;
398 default:
399 xmlGenericError(xmlGenericErrorContext,
400 "xmlNewElementContent: unknown type %d\n", type);
401 return(NULL);
402 }
403 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
404 if (ret == NULL) {
405 xmlGenericError(xmlGenericErrorContext,
406 "xmlNewElementContent : out of memory!\n");
407 return(NULL);
408 }
409 ret->type = type;
410 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
411 if (name != NULL)
412 ret->name = xmlStrdup(name);
413 else
414 ret->name = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000415 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000416 return(ret);
417}
418
419/**
420 * xmlCopyElementContent:
421 * @content: An element content pointer.
422 *
423 * Build a copy of an element content description.
424 *
425 * Returns the new xmlElementContentPtr or NULL in case of error.
426 */
427xmlElementContentPtr
428xmlCopyElementContent(xmlElementContentPtr cur) {
429 xmlElementContentPtr ret;
430
431 if (cur == NULL) return(NULL);
432 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
433 if (ret == NULL) {
434 xmlGenericError(xmlGenericErrorContext,
435 "xmlCopyElementContent : out of memory\n");
436 return(NULL);
437 }
438 ret->ocur = cur->ocur;
439 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000440 if (ret->c1 != NULL)
441 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000442 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000443 if (ret->c2 != NULL)
444 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000445 return(ret);
446}
447
448/**
449 * xmlFreeElementContent:
450 * @cur: the element content tree to free
451 *
452 * Free an element content structure. This is a recursive call !
453 */
454void
455xmlFreeElementContent(xmlElementContentPtr cur) {
456 if (cur == NULL) return;
457 switch (cur->type) {
458 case XML_ELEMENT_CONTENT_PCDATA:
459 case XML_ELEMENT_CONTENT_ELEMENT:
460 case XML_ELEMENT_CONTENT_SEQ:
461 case XML_ELEMENT_CONTENT_OR:
462 break;
463 default:
464 xmlGenericError(xmlGenericErrorContext,
465 "xmlFreeElementContent : type %d\n", cur->type);
466 return;
467 }
468 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
469 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
470 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +0000471 xmlFree(cur);
472}
473
474/**
475 * xmlDumpElementContent:
476 * @buf: An XML buffer
477 * @content: An element table
478 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
479 *
480 * This will dump the content of the element table as an XML DTD definition
481 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000482static void
Owen Taylor3473f882001-02-23 17:55:21 +0000483xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
484 if (content == NULL) return;
485
486 if (glob) xmlBufferWriteChar(buf, "(");
487 switch (content->type) {
488 case XML_ELEMENT_CONTENT_PCDATA:
489 xmlBufferWriteChar(buf, "#PCDATA");
490 break;
491 case XML_ELEMENT_CONTENT_ELEMENT:
492 xmlBufferWriteCHAR(buf, content->name);
493 break;
494 case XML_ELEMENT_CONTENT_SEQ:
495 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
496 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
497 xmlDumpElementContent(buf, content->c1, 1);
498 else
499 xmlDumpElementContent(buf, content->c1, 0);
500 xmlBufferWriteChar(buf, " , ");
501 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
502 xmlDumpElementContent(buf, content->c2, 1);
503 else
504 xmlDumpElementContent(buf, content->c2, 0);
505 break;
506 case XML_ELEMENT_CONTENT_OR:
507 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
508 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
509 xmlDumpElementContent(buf, content->c1, 1);
510 else
511 xmlDumpElementContent(buf, content->c1, 0);
512 xmlBufferWriteChar(buf, " | ");
513 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
514 xmlDumpElementContent(buf, content->c2, 1);
515 else
516 xmlDumpElementContent(buf, content->c2, 0);
517 break;
518 default:
519 xmlGenericError(xmlGenericErrorContext,
520 "xmlDumpElementContent: unknown type %d\n",
521 content->type);
522 }
523 if (glob)
524 xmlBufferWriteChar(buf, ")");
525 switch (content->ocur) {
526 case XML_ELEMENT_CONTENT_ONCE:
527 break;
528 case XML_ELEMENT_CONTENT_OPT:
529 xmlBufferWriteChar(buf, "?");
530 break;
531 case XML_ELEMENT_CONTENT_MULT:
532 xmlBufferWriteChar(buf, "*");
533 break;
534 case XML_ELEMENT_CONTENT_PLUS:
535 xmlBufferWriteChar(buf, "+");
536 break;
537 }
538}
539
540/**
541 * xmlSprintfElementContent:
542 * @buf: an output buffer
543 * @content: An element table
544 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
545 *
546 * This will dump the content of the element content definition
547 * Intended just for the debug routine
548 */
549void
550xmlSprintfElementContent(char *buf, xmlElementContentPtr content, int glob) {
551 if (content == NULL) return;
552 if (glob) strcat(buf, "(");
553 switch (content->type) {
554 case XML_ELEMENT_CONTENT_PCDATA:
555 strcat(buf, "#PCDATA");
556 break;
557 case XML_ELEMENT_CONTENT_ELEMENT:
558 strcat(buf, (char *) content->name);
559 break;
560 case XML_ELEMENT_CONTENT_SEQ:
561 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
562 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
563 xmlSprintfElementContent(buf, content->c1, 1);
564 else
565 xmlSprintfElementContent(buf, content->c1, 0);
566 strcat(buf, " , ");
567 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
568 xmlSprintfElementContent(buf, content->c2, 1);
569 else
570 xmlSprintfElementContent(buf, content->c2, 0);
571 break;
572 case XML_ELEMENT_CONTENT_OR:
573 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
574 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
575 xmlSprintfElementContent(buf, content->c1, 1);
576 else
577 xmlSprintfElementContent(buf, content->c1, 0);
578 strcat(buf, " | ");
579 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
580 xmlSprintfElementContent(buf, content->c2, 1);
581 else
582 xmlSprintfElementContent(buf, content->c2, 0);
583 break;
584 }
585 if (glob)
586 strcat(buf, ")");
587 switch (content->ocur) {
588 case XML_ELEMENT_CONTENT_ONCE:
589 break;
590 case XML_ELEMENT_CONTENT_OPT:
591 strcat(buf, "?");
592 break;
593 case XML_ELEMENT_CONTENT_MULT:
594 strcat(buf, "*");
595 break;
596 case XML_ELEMENT_CONTENT_PLUS:
597 strcat(buf, "+");
598 break;
599 }
600}
601
602/****************************************************************
603 * *
604 * Registration of DTD declarations *
605 * *
606 ****************************************************************/
607
608/**
609 * xmlCreateElementTable:
610 *
611 * create and initialize an empty element hash table.
612 *
613 * Returns the xmlElementTablePtr just created or NULL in case of error.
614 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000615static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000616xmlCreateElementTable(void) {
617 return(xmlHashCreate(0));
618}
619
620/**
621 * xmlFreeElement:
622 * @elem: An element
623 *
624 * Deallocate the memory used by an element definition
625 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000626static void
Owen Taylor3473f882001-02-23 17:55:21 +0000627xmlFreeElement(xmlElementPtr elem) {
628 if (elem == NULL) return;
629 xmlUnlinkNode((xmlNodePtr) elem);
630 xmlFreeElementContent(elem->content);
631 if (elem->name != NULL)
632 xmlFree((xmlChar *) elem->name);
633 if (elem->prefix != NULL)
634 xmlFree((xmlChar *) elem->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000635 xmlFree(elem);
636}
637
638
639/**
640 * xmlAddElementDecl:
641 * @ctxt: the validation context
642 * @dtd: pointer to the DTD
643 * @name: the entity name
644 * @type: the element type
645 * @content: the element content tree or NULL
646 *
647 * Register a new element declaration
648 *
649 * Returns NULL if not, othervise the entity
650 */
651xmlElementPtr
652xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
653 xmlElementTypeVal type,
654 xmlElementContentPtr content) {
655 xmlElementPtr ret;
656 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000657 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000658 xmlChar *ns, *uqname;
659
660 if (dtd == NULL) {
661 xmlGenericError(xmlGenericErrorContext,
662 "xmlAddElementDecl: dtd == NULL\n");
663 return(NULL);
664 }
665 if (name == NULL) {
666 xmlGenericError(xmlGenericErrorContext,
667 "xmlAddElementDecl: name == NULL\n");
668 return(NULL);
669 }
670 switch (type) {
671 case XML_ELEMENT_TYPE_EMPTY:
672 if (content != NULL) {
673 xmlGenericError(xmlGenericErrorContext,
674 "xmlAddElementDecl: content != NULL for EMPTY\n");
675 return(NULL);
676 }
677 break;
678 case XML_ELEMENT_TYPE_ANY:
679 if (content != NULL) {
680 xmlGenericError(xmlGenericErrorContext,
681 "xmlAddElementDecl: content != NULL for ANY\n");
682 return(NULL);
683 }
684 break;
685 case XML_ELEMENT_TYPE_MIXED:
686 if (content == NULL) {
687 xmlGenericError(xmlGenericErrorContext,
688 "xmlAddElementDecl: content == NULL for MIXED\n");
689 return(NULL);
690 }
691 break;
692 case XML_ELEMENT_TYPE_ELEMENT:
693 if (content == NULL) {
694 xmlGenericError(xmlGenericErrorContext,
695 "xmlAddElementDecl: content == NULL for ELEMENT\n");
696 return(NULL);
697 }
698 break;
699 default:
700 xmlGenericError(xmlGenericErrorContext,
701 "xmlAddElementDecl: unknown type %d\n", type);
702 return(NULL);
703 }
704
705 /*
706 * check if name is a QName
707 */
708 uqname = xmlSplitQName2(name, &ns);
709 if (uqname != NULL)
710 name = uqname;
711
712 /*
713 * Create the Element table if needed.
714 */
715 table = (xmlElementTablePtr) dtd->elements;
716 if (table == NULL) {
717 table = xmlCreateElementTable();
718 dtd->elements = (void *) table;
719 }
720 if (table == NULL) {
721 xmlGenericError(xmlGenericErrorContext,
722 "xmlAddElementDecl: Table creation failed!\n");
723 return(NULL);
724 }
725
Daniel Veillarda10efa82001-04-18 13:09:01 +0000726 /*
727 * lookup old attributes inserted on an undefined element in the
728 * internal subset.
729 */
730 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
731 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
732 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
733 oldAttributes = ret->attributes;
734 ret->attributes = NULL;
735 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
736 xmlFreeElement(ret);
737 }
Owen Taylor3473f882001-02-23 17:55:21 +0000738 }
Owen Taylor3473f882001-02-23 17:55:21 +0000739
740 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +0000741 * The element may already be present if one of its attribute
742 * was registered first
743 */
744 ret = xmlHashLookup2(table, name, ns);
745 if (ret != NULL) {
746 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
747 /*
748 * The element is already defined in this Dtd.
749 */
750 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
751 if (uqname != NULL)
752 xmlFree(uqname);
753 return(NULL);
754 }
755 } else {
756 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
757 if (ret == NULL) {
758 xmlGenericError(xmlGenericErrorContext,
759 "xmlAddElementDecl: out of memory\n");
760 return(NULL);
761 }
762 memset(ret, 0, sizeof(xmlElement));
763 ret->type = XML_ELEMENT_DECL;
764
765 /*
766 * fill the structure.
767 */
768 ret->name = xmlStrdup(name);
769 ret->prefix = ns;
770
771 /*
772 * Validity Check:
773 * Insertion must not fail
774 */
775 if (xmlHashAddEntry2(table, name, ns, ret)) {
776 /*
777 * The element is already defined in this Dtd.
778 */
779 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
780 xmlFreeElement(ret);
781 if (uqname != NULL)
782 xmlFree(uqname);
783 return(NULL);
784 }
785 }
786
787 /*
788 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +0000789 */
790 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +0000791 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000792 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +0000793
794 /*
795 * Link it to the Dtd
796 */
797 ret->parent = dtd;
798 ret->doc = dtd->doc;
799 if (dtd->last == NULL) {
800 dtd->children = dtd->last = (xmlNodePtr) ret;
801 } else {
802 dtd->last->next = (xmlNodePtr) ret;
803 ret->prev = dtd->last;
804 dtd->last = (xmlNodePtr) ret;
805 }
806 if (uqname != NULL)
807 xmlFree(uqname);
808 return(ret);
809}
810
811/**
812 * xmlFreeElementTable:
813 * @table: An element table
814 *
815 * Deallocate the memory used by an element hash table.
816 */
817void
818xmlFreeElementTable(xmlElementTablePtr table) {
819 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
820}
821
822/**
823 * xmlCopyElement:
824 * @elem: An element
825 *
826 * Build a copy of an element.
827 *
828 * Returns the new xmlElementPtr or NULL in case of error.
829 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000830static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000831xmlCopyElement(xmlElementPtr elem) {
832 xmlElementPtr cur;
833
834 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
835 if (cur == NULL) {
836 xmlGenericError(xmlGenericErrorContext,
837 "xmlCopyElement: out of memory !\n");
838 return(NULL);
839 }
840 memset(cur, 0, sizeof(xmlElement));
841 cur->type = XML_ELEMENT_DECL;
842 cur->etype = elem->etype;
843 if (elem->name != NULL)
844 cur->name = xmlStrdup(elem->name);
845 else
846 cur->name = NULL;
847 if (elem->prefix != NULL)
848 cur->prefix = xmlStrdup(elem->prefix);
849 else
850 cur->prefix = NULL;
851 cur->content = xmlCopyElementContent(elem->content);
852 /* TODO : rebuild the attribute list on the copy */
853 cur->attributes = NULL;
854 return(cur);
855}
856
857/**
858 * xmlCopyElementTable:
859 * @table: An element table
860 *
861 * Build a copy of an element table.
862 *
863 * Returns the new xmlElementTablePtr or NULL in case of error.
864 */
865xmlElementTablePtr
866xmlCopyElementTable(xmlElementTablePtr table) {
867 return((xmlElementTablePtr) xmlHashCopy(table,
868 (xmlHashCopier) xmlCopyElement));
869}
870
871/**
872 * xmlDumpElementDecl:
873 * @buf: the XML buffer output
874 * @elem: An element table
875 *
876 * This will dump the content of the element declaration as an XML
877 * DTD definition
878 */
879void
880xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
881 switch (elem->etype) {
882 case XML_ELEMENT_TYPE_EMPTY:
883 xmlBufferWriteChar(buf, "<!ELEMENT ");
884 xmlBufferWriteCHAR(buf, elem->name);
885 xmlBufferWriteChar(buf, " EMPTY>\n");
886 break;
887 case XML_ELEMENT_TYPE_ANY:
888 xmlBufferWriteChar(buf, "<!ELEMENT ");
889 xmlBufferWriteCHAR(buf, elem->name);
890 xmlBufferWriteChar(buf, " ANY>\n");
891 break;
892 case XML_ELEMENT_TYPE_MIXED:
893 xmlBufferWriteChar(buf, "<!ELEMENT ");
894 xmlBufferWriteCHAR(buf, elem->name);
895 xmlBufferWriteChar(buf, " ");
896 xmlDumpElementContent(buf, elem->content, 1);
897 xmlBufferWriteChar(buf, ">\n");
898 break;
899 case XML_ELEMENT_TYPE_ELEMENT:
900 xmlBufferWriteChar(buf, "<!ELEMENT ");
901 xmlBufferWriteCHAR(buf, elem->name);
902 xmlBufferWriteChar(buf, " ");
903 xmlDumpElementContent(buf, elem->content, 1);
904 xmlBufferWriteChar(buf, ">\n");
905 break;
906 default:
907 xmlGenericError(xmlGenericErrorContext,
908 "xmlDumpElementDecl: internal: unknown type %d\n",
909 elem->etype);
910 }
911}
912
913/**
914 * xmlDumpElementTable:
915 * @buf: the XML buffer output
916 * @table: An element table
917 *
918 * This will dump the content of the element table as an XML DTD definition
919 */
920void
921xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
922 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
923}
924
925/**
926 * xmlCreateEnumeration:
927 * @name: the enumeration name or NULL
928 *
929 * create and initialize an enumeration attribute node.
930 *
931 * Returns the xmlEnumerationPtr just created or NULL in case
932 * of error.
933 */
934xmlEnumerationPtr
935xmlCreateEnumeration(xmlChar *name) {
936 xmlEnumerationPtr ret;
937
938 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
939 if (ret == NULL) {
940 xmlGenericError(xmlGenericErrorContext,
941 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
942 (long)sizeof(xmlEnumeration));
943 return(NULL);
944 }
945 memset(ret, 0, sizeof(xmlEnumeration));
946
947 if (name != NULL)
948 ret->name = xmlStrdup(name);
949 return(ret);
950}
951
952/**
953 * xmlFreeEnumeration:
954 * @cur: the tree to free.
955 *
956 * free an enumeration attribute node (recursive).
957 */
958void
959xmlFreeEnumeration(xmlEnumerationPtr cur) {
960 if (cur == NULL) return;
961
962 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
963
964 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +0000965 xmlFree(cur);
966}
967
968/**
969 * xmlCopyEnumeration:
970 * @cur: the tree to copy.
971 *
972 * Copy an enumeration attribute node (recursive).
973 *
974 * Returns the xmlEnumerationPtr just created or NULL in case
975 * of error.
976 */
977xmlEnumerationPtr
978xmlCopyEnumeration(xmlEnumerationPtr cur) {
979 xmlEnumerationPtr ret;
980
981 if (cur == NULL) return(NULL);
982 ret = xmlCreateEnumeration((xmlChar *) cur->name);
983
984 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
985 else ret->next = NULL;
986
987 return(ret);
988}
989
990/**
991 * xmlDumpEnumeration:
992 * @buf: the XML buffer output
993 * @enum: An enumeration
994 *
995 * This will dump the content of the enumeration
996 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000997static void
Owen Taylor3473f882001-02-23 17:55:21 +0000998xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
999 if (cur == NULL) return;
1000
1001 xmlBufferWriteCHAR(buf, cur->name);
1002 if (cur->next == NULL)
1003 xmlBufferWriteChar(buf, ")");
1004 else {
1005 xmlBufferWriteChar(buf, " | ");
1006 xmlDumpEnumeration(buf, cur->next);
1007 }
1008}
1009
1010/**
1011 * xmlCreateAttributeTable:
1012 *
1013 * create and initialize an empty attribute hash table.
1014 *
1015 * Returns the xmlAttributeTablePtr just created or NULL in case
1016 * of error.
1017 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001018static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001019xmlCreateAttributeTable(void) {
1020 return(xmlHashCreate(0));
1021}
1022
1023/**
1024 * xmlScanAttributeDeclCallback:
1025 * @attr: the attribute decl
1026 * @list: the list to update
1027 *
1028 * Callback called by xmlScanAttributeDecl when a new attribute
1029 * has to be entered in the list.
1030 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001031static void
Owen Taylor3473f882001-02-23 17:55:21 +00001032xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001033 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001034 attr->nexth = *list;
1035 *list = attr;
1036}
1037
1038/**
1039 * xmlScanAttributeDecl:
1040 * @dtd: pointer to the DTD
1041 * @elem: the element name
1042 *
1043 * When inserting a new element scan the DtD for existing attributes
1044 * for taht element and initialize the Attribute chain
1045 *
1046 * Returns the pointer to the first attribute decl in the chain,
1047 * possibly NULL.
1048 */
1049xmlAttributePtr
1050xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1051 xmlAttributePtr ret = NULL;
1052 xmlAttributeTablePtr table;
1053
1054 if (dtd == NULL) {
1055 xmlGenericError(xmlGenericErrorContext,
1056 "xmlScanAttributeDecl: dtd == NULL\n");
1057 return(NULL);
1058 }
1059 if (elem == NULL) {
1060 xmlGenericError(xmlGenericErrorContext,
1061 "xmlScanAttributeDecl: elem == NULL\n");
1062 return(NULL);
1063 }
1064 table = (xmlAttributeTablePtr) dtd->attributes;
1065 if (table == NULL)
1066 return(NULL);
1067
1068 /* WRONG !!! */
1069 xmlHashScan3(table, NULL, NULL, elem,
1070 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1071 return(ret);
1072}
1073
1074/**
1075 * xmlScanIDAttributeDecl:
1076 * @ctxt: the validation context
1077 * @elem: the element name
1078 *
1079 * Verify that the element don't have too many ID attributes
1080 * declared.
1081 *
1082 * Returns the number of ID attributes found.
1083 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001084static int
Owen Taylor3473f882001-02-23 17:55:21 +00001085xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1086 xmlAttributePtr cur;
1087 int ret = 0;
1088
1089 if (elem == NULL) return(0);
1090 cur = elem->attributes;
1091 while (cur != NULL) {
1092 if (cur->atype == XML_ATTRIBUTE_ID) {
1093 ret ++;
1094 if (ret > 1)
1095 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001096 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001097 elem->name, cur->name);
1098 }
1099 cur = cur->nexth;
1100 }
1101 return(ret);
1102}
1103
1104/**
1105 * xmlFreeAttribute:
1106 * @elem: An attribute
1107 *
1108 * Deallocate the memory used by an attribute definition
1109 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001110static void
Owen Taylor3473f882001-02-23 17:55:21 +00001111xmlFreeAttribute(xmlAttributePtr attr) {
1112 if (attr == NULL) return;
1113 xmlUnlinkNode((xmlNodePtr) attr);
1114 if (attr->tree != NULL)
1115 xmlFreeEnumeration(attr->tree);
1116 if (attr->elem != NULL)
1117 xmlFree((xmlChar *) attr->elem);
1118 if (attr->name != NULL)
1119 xmlFree((xmlChar *) attr->name);
1120 if (attr->defaultValue != NULL)
1121 xmlFree((xmlChar *) attr->defaultValue);
1122 if (attr->prefix != NULL)
1123 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001124 xmlFree(attr);
1125}
1126
1127
1128/**
1129 * xmlAddAttributeDecl:
1130 * @ctxt: the validation context
1131 * @dtd: pointer to the DTD
1132 * @elem: the element name
1133 * @name: the attribute name
1134 * @ns: the attribute namespace prefix
1135 * @type: the attribute type
1136 * @def: the attribute default type
1137 * @defaultValue: the attribute default value
1138 * @tree: if it's an enumeration, the associated list
1139 *
1140 * Register a new attribute declaration
1141 * Note that @tree becomes the ownership of the DTD
1142 *
1143 * Returns NULL if not new, othervise the attribute decl
1144 */
1145xmlAttributePtr
1146xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1147 const xmlChar *name, const xmlChar *ns,
1148 xmlAttributeType type, xmlAttributeDefault def,
1149 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1150 xmlAttributePtr ret;
1151 xmlAttributeTablePtr table;
1152 xmlElementPtr elemDef;
1153
1154 if (dtd == NULL) {
1155 xmlGenericError(xmlGenericErrorContext,
1156 "xmlAddAttributeDecl: dtd == NULL\n");
1157 xmlFreeEnumeration(tree);
1158 return(NULL);
1159 }
1160 if (name == NULL) {
1161 xmlGenericError(xmlGenericErrorContext,
1162 "xmlAddAttributeDecl: name == NULL\n");
1163 xmlFreeEnumeration(tree);
1164 return(NULL);
1165 }
1166 if (elem == NULL) {
1167 xmlGenericError(xmlGenericErrorContext,
1168 "xmlAddAttributeDecl: elem == NULL\n");
1169 xmlFreeEnumeration(tree);
1170 return(NULL);
1171 }
1172 /*
1173 * Check the type and possibly the default value.
1174 */
1175 switch (type) {
1176 case XML_ATTRIBUTE_CDATA:
1177 break;
1178 case XML_ATTRIBUTE_ID:
1179 break;
1180 case XML_ATTRIBUTE_IDREF:
1181 break;
1182 case XML_ATTRIBUTE_IDREFS:
1183 break;
1184 case XML_ATTRIBUTE_ENTITY:
1185 break;
1186 case XML_ATTRIBUTE_ENTITIES:
1187 break;
1188 case XML_ATTRIBUTE_NMTOKEN:
1189 break;
1190 case XML_ATTRIBUTE_NMTOKENS:
1191 break;
1192 case XML_ATTRIBUTE_ENUMERATION:
1193 break;
1194 case XML_ATTRIBUTE_NOTATION:
1195 break;
1196 default:
1197 xmlGenericError(xmlGenericErrorContext,
1198 "xmlAddAttributeDecl: unknown type %d\n", type);
1199 xmlFreeEnumeration(tree);
1200 return(NULL);
1201 }
1202 if ((defaultValue != NULL) &&
1203 (!xmlValidateAttributeValue(type, defaultValue))) {
1204 VERROR(ctxt->userData, "Attribute %s on %s: invalid default value\n",
1205 elem, name, defaultValue);
1206 defaultValue = NULL;
1207 }
1208
1209 /*
1210 * Create the Attribute table if needed.
1211 */
1212 table = (xmlAttributeTablePtr) dtd->attributes;
1213 if (table == NULL) {
1214 table = xmlCreateAttributeTable();
1215 dtd->attributes = (void *) table;
1216 }
1217 if (table == NULL) {
1218 xmlGenericError(xmlGenericErrorContext,
1219 "xmlAddAttributeDecl: Table creation failed!\n");
1220 return(NULL);
1221 }
1222
1223
1224 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1225 if (ret == NULL) {
1226 xmlGenericError(xmlGenericErrorContext,
1227 "xmlAddAttributeDecl: out of memory\n");
1228 return(NULL);
1229 }
1230 memset(ret, 0, sizeof(xmlAttribute));
1231 ret->type = XML_ATTRIBUTE_DECL;
1232
1233 /*
1234 * fill the structure.
1235 */
1236 ret->atype = type;
1237 ret->name = xmlStrdup(name);
1238 ret->prefix = xmlStrdup(ns);
1239 ret->elem = xmlStrdup(elem);
1240 ret->def = def;
1241 ret->tree = tree;
1242 if (defaultValue != NULL)
1243 ret->defaultValue = xmlStrdup(defaultValue);
1244
1245 /*
1246 * Validity Check:
1247 * Search the DTD for previous declarations of the ATTLIST
1248 */
1249 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1250 /*
1251 * The attribute is already defined in this Dtd.
1252 */
1253 VWARNING(ctxt->userData,
1254 "Attribute %s on %s: already defined\n",
1255 name, elem);
1256 xmlFreeAttribute(ret);
1257 return(NULL);
1258 }
1259
1260 /*
1261 * Validity Check:
1262 * Multiple ID per element
1263 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001264 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001265 if (elemDef != NULL) {
1266 if ((type == XML_ATTRIBUTE_ID) &&
1267 (xmlScanIDAttributeDecl(NULL, elemDef) != 0))
1268 VERROR(ctxt->userData,
1269 "Element %s has too may ID attributes defined : %s\n",
1270 elem, name);
1271 ret->nexth = elemDef->attributes;
1272 elemDef->attributes = ret;
1273 }
1274
1275 /*
1276 * Link it to the Dtd
1277 */
1278 ret->parent = dtd;
1279 ret->doc = dtd->doc;
1280 if (dtd->last == NULL) {
1281 dtd->children = dtd->last = (xmlNodePtr) ret;
1282 } else {
1283 dtd->last->next = (xmlNodePtr) ret;
1284 ret->prev = dtd->last;
1285 dtd->last = (xmlNodePtr) ret;
1286 }
1287 return(ret);
1288}
1289
1290/**
1291 * xmlFreeAttributeTable:
1292 * @table: An attribute table
1293 *
1294 * Deallocate the memory used by an entities hash table.
1295 */
1296void
1297xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1298 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1299}
1300
1301/**
1302 * xmlCopyAttribute:
1303 * @attr: An attribute
1304 *
1305 * Build a copy of an attribute.
1306 *
1307 * Returns the new xmlAttributePtr or NULL in case of error.
1308 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001309static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001310xmlCopyAttribute(xmlAttributePtr attr) {
1311 xmlAttributePtr cur;
1312
1313 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1314 if (cur == NULL) {
1315 xmlGenericError(xmlGenericErrorContext,
1316 "xmlCopyAttribute: out of memory !\n");
1317 return(NULL);
1318 }
1319 memset(cur, 0, sizeof(xmlAttribute));
1320 cur->atype = attr->atype;
1321 cur->def = attr->def;
1322 cur->tree = xmlCopyEnumeration(attr->tree);
1323 if (attr->elem != NULL)
1324 cur->elem = xmlStrdup(attr->elem);
1325 if (attr->name != NULL)
1326 cur->name = xmlStrdup(attr->name);
1327 if (attr->defaultValue != NULL)
1328 cur->defaultValue = xmlStrdup(attr->defaultValue);
1329 return(cur);
1330}
1331
1332/**
1333 * xmlCopyAttributeTable:
1334 * @table: An attribute table
1335 *
1336 * Build a copy of an attribute table.
1337 *
1338 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1339 */
1340xmlAttributeTablePtr
1341xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1342 return((xmlAttributeTablePtr) xmlHashCopy(table,
1343 (xmlHashCopier) xmlCopyAttribute));
1344}
1345
1346/**
1347 * xmlDumpAttributeDecl:
1348 * @buf: the XML buffer output
1349 * @attr: An attribute declaration
1350 *
1351 * This will dump the content of the attribute declaration as an XML
1352 * DTD definition
1353 */
1354void
1355xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1356 xmlBufferWriteChar(buf, "<!ATTLIST ");
1357 xmlBufferWriteCHAR(buf, attr->elem);
1358 xmlBufferWriteChar(buf, " ");
1359 if (attr->prefix != NULL) {
1360 xmlBufferWriteCHAR(buf, attr->prefix);
1361 xmlBufferWriteChar(buf, ":");
1362 }
1363 xmlBufferWriteCHAR(buf, attr->name);
1364 switch (attr->atype) {
1365 case XML_ATTRIBUTE_CDATA:
1366 xmlBufferWriteChar(buf, " CDATA");
1367 break;
1368 case XML_ATTRIBUTE_ID:
1369 xmlBufferWriteChar(buf, " ID");
1370 break;
1371 case XML_ATTRIBUTE_IDREF:
1372 xmlBufferWriteChar(buf, " IDREF");
1373 break;
1374 case XML_ATTRIBUTE_IDREFS:
1375 xmlBufferWriteChar(buf, " IDREFS");
1376 break;
1377 case XML_ATTRIBUTE_ENTITY:
1378 xmlBufferWriteChar(buf, " ENTITY");
1379 break;
1380 case XML_ATTRIBUTE_ENTITIES:
1381 xmlBufferWriteChar(buf, " ENTITIES");
1382 break;
1383 case XML_ATTRIBUTE_NMTOKEN:
1384 xmlBufferWriteChar(buf, " NMTOKEN");
1385 break;
1386 case XML_ATTRIBUTE_NMTOKENS:
1387 xmlBufferWriteChar(buf, " NMTOKENS");
1388 break;
1389 case XML_ATTRIBUTE_ENUMERATION:
1390 xmlBufferWriteChar(buf, " (");
1391 xmlDumpEnumeration(buf, attr->tree);
1392 break;
1393 case XML_ATTRIBUTE_NOTATION:
1394 xmlBufferWriteChar(buf, " NOTATION (");
1395 xmlDumpEnumeration(buf, attr->tree);
1396 break;
1397 default:
1398 xmlGenericError(xmlGenericErrorContext,
1399 "xmlDumpAttributeTable: internal: unknown type %d\n",
1400 attr->atype);
1401 }
1402 switch (attr->def) {
1403 case XML_ATTRIBUTE_NONE:
1404 break;
1405 case XML_ATTRIBUTE_REQUIRED:
1406 xmlBufferWriteChar(buf, " #REQUIRED");
1407 break;
1408 case XML_ATTRIBUTE_IMPLIED:
1409 xmlBufferWriteChar(buf, " #IMPLIED");
1410 break;
1411 case XML_ATTRIBUTE_FIXED:
1412 xmlBufferWriteChar(buf, " #FIXED");
1413 break;
1414 default:
1415 xmlGenericError(xmlGenericErrorContext,
1416 "xmlDumpAttributeTable: internal: unknown default %d\n",
1417 attr->def);
1418 }
1419 if (attr->defaultValue != NULL) {
1420 xmlBufferWriteChar(buf, " ");
1421 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1422 }
1423 xmlBufferWriteChar(buf, ">\n");
1424}
1425
1426/**
1427 * xmlDumpAttributeTable:
1428 * @buf: the XML buffer output
1429 * @table: An attribute table
1430 *
1431 * This will dump the content of the attribute table as an XML DTD definition
1432 */
1433void
1434xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1435 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1436}
1437
1438/************************************************************************
1439 * *
1440 * NOTATIONs *
1441 * *
1442 ************************************************************************/
1443/**
1444 * xmlCreateNotationTable:
1445 *
1446 * create and initialize an empty notation hash table.
1447 *
1448 * Returns the xmlNotationTablePtr just created or NULL in case
1449 * of error.
1450 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001451static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001452xmlCreateNotationTable(void) {
1453 return(xmlHashCreate(0));
1454}
1455
1456/**
1457 * xmlFreeNotation:
1458 * @not: A notation
1459 *
1460 * Deallocate the memory used by an notation definition
1461 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001462static void
Owen Taylor3473f882001-02-23 17:55:21 +00001463xmlFreeNotation(xmlNotationPtr nota) {
1464 if (nota == NULL) return;
1465 if (nota->name != NULL)
1466 xmlFree((xmlChar *) nota->name);
1467 if (nota->PublicID != NULL)
1468 xmlFree((xmlChar *) nota->PublicID);
1469 if (nota->SystemID != NULL)
1470 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001471 xmlFree(nota);
1472}
1473
1474
1475/**
1476 * xmlAddNotationDecl:
1477 * @dtd: pointer to the DTD
1478 * @ctxt: the validation context
1479 * @name: the entity name
1480 * @PublicID: the public identifier or NULL
1481 * @SystemID: the system identifier or NULL
1482 *
1483 * Register a new notation declaration
1484 *
1485 * Returns NULL if not, othervise the entity
1486 */
1487xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001488xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001489 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001490 const xmlChar *PublicID, const xmlChar *SystemID) {
1491 xmlNotationPtr ret;
1492 xmlNotationTablePtr table;
1493
1494 if (dtd == NULL) {
1495 xmlGenericError(xmlGenericErrorContext,
1496 "xmlAddNotationDecl: dtd == NULL\n");
1497 return(NULL);
1498 }
1499 if (name == NULL) {
1500 xmlGenericError(xmlGenericErrorContext,
1501 "xmlAddNotationDecl: name == NULL\n");
1502 return(NULL);
1503 }
1504 if ((PublicID == NULL) && (SystemID == NULL)) {
1505 xmlGenericError(xmlGenericErrorContext,
1506 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
1507 }
1508
1509 /*
1510 * Create the Notation table if needed.
1511 */
1512 table = (xmlNotationTablePtr) dtd->notations;
1513 if (table == NULL)
1514 dtd->notations = table = xmlCreateNotationTable();
1515 if (table == NULL) {
1516 xmlGenericError(xmlGenericErrorContext,
1517 "xmlAddNotationDecl: Table creation failed!\n");
1518 return(NULL);
1519 }
1520
1521 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1522 if (ret == NULL) {
1523 xmlGenericError(xmlGenericErrorContext,
1524 "xmlAddNotationDecl: out of memory\n");
1525 return(NULL);
1526 }
1527 memset(ret, 0, sizeof(xmlNotation));
1528
1529 /*
1530 * fill the structure.
1531 */
1532 ret->name = xmlStrdup(name);
1533 if (SystemID != NULL)
1534 ret->SystemID = xmlStrdup(SystemID);
1535 if (PublicID != NULL)
1536 ret->PublicID = xmlStrdup(PublicID);
1537
1538 /*
1539 * Validity Check:
1540 * Check the DTD for previous declarations of the ATTLIST
1541 */
1542 if (xmlHashAddEntry(table, name, ret)) {
1543 xmlGenericError(xmlGenericErrorContext,
1544 "xmlAddNotationDecl: %s already defined\n", name);
1545 xmlFreeNotation(ret);
1546 return(NULL);
1547 }
1548 return(ret);
1549}
1550
1551/**
1552 * xmlFreeNotationTable:
1553 * @table: An notation table
1554 *
1555 * Deallocate the memory used by an entities hash table.
1556 */
1557void
1558xmlFreeNotationTable(xmlNotationTablePtr table) {
1559 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1560}
1561
1562/**
1563 * xmlCopyNotation:
1564 * @nota: A notation
1565 *
1566 * Build a copy of a notation.
1567 *
1568 * Returns the new xmlNotationPtr or NULL in case of error.
1569 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001570static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001571xmlCopyNotation(xmlNotationPtr nota) {
1572 xmlNotationPtr cur;
1573
1574 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1575 if (cur == NULL) {
1576 xmlGenericError(xmlGenericErrorContext,
1577 "xmlCopyNotation: out of memory !\n");
1578 return(NULL);
1579 }
1580 if (nota->name != NULL)
1581 cur->name = xmlStrdup(nota->name);
1582 else
1583 cur->name = NULL;
1584 if (nota->PublicID != NULL)
1585 cur->PublicID = xmlStrdup(nota->PublicID);
1586 else
1587 cur->PublicID = NULL;
1588 if (nota->SystemID != NULL)
1589 cur->SystemID = xmlStrdup(nota->SystemID);
1590 else
1591 cur->SystemID = NULL;
1592 return(cur);
1593}
1594
1595/**
1596 * xmlCopyNotationTable:
1597 * @table: A notation table
1598 *
1599 * Build a copy of a notation table.
1600 *
1601 * Returns the new xmlNotationTablePtr or NULL in case of error.
1602 */
1603xmlNotationTablePtr
1604xmlCopyNotationTable(xmlNotationTablePtr table) {
1605 return((xmlNotationTablePtr) xmlHashCopy(table,
1606 (xmlHashCopier) xmlCopyNotation));
1607}
1608
1609/**
1610 * xmlDumpNotationDecl:
1611 * @buf: the XML buffer output
1612 * @nota: A notation declaration
1613 *
1614 * This will dump the content the notation declaration as an XML DTD definition
1615 */
1616void
1617xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1618 xmlBufferWriteChar(buf, "<!NOTATION ");
1619 xmlBufferWriteCHAR(buf, nota->name);
1620 if (nota->PublicID != NULL) {
1621 xmlBufferWriteChar(buf, " PUBLIC ");
1622 xmlBufferWriteQuotedString(buf, nota->PublicID);
1623 if (nota->SystemID != NULL) {
1624 xmlBufferWriteChar(buf, " ");
1625 xmlBufferWriteCHAR(buf, nota->SystemID);
1626 }
1627 } else {
1628 xmlBufferWriteChar(buf, " SYSTEM ");
1629 xmlBufferWriteCHAR(buf, nota->SystemID);
1630 }
1631 xmlBufferWriteChar(buf, " >\n");
1632}
1633
1634/**
1635 * xmlDumpNotationTable:
1636 * @buf: the XML buffer output
1637 * @table: A notation table
1638 *
1639 * This will dump the content of the notation table as an XML DTD definition
1640 */
1641void
1642xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1643 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1644}
1645
1646/************************************************************************
1647 * *
1648 * IDs *
1649 * *
1650 ************************************************************************/
1651/**
1652 * xmlCreateIDTable:
1653 *
1654 * create and initialize an empty id hash table.
1655 *
1656 * Returns the xmlIDTablePtr just created or NULL in case
1657 * of error.
1658 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001659static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001660xmlCreateIDTable(void) {
1661 return(xmlHashCreate(0));
1662}
1663
1664/**
1665 * xmlFreeID:
1666 * @not: A id
1667 *
1668 * Deallocate the memory used by an id definition
1669 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001670static void
Owen Taylor3473f882001-02-23 17:55:21 +00001671xmlFreeID(xmlIDPtr id) {
1672 if (id == NULL) return;
1673 if (id->value != NULL)
1674 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001675 xmlFree(id);
1676}
1677
1678/**
1679 * xmlAddID:
1680 * @ctxt: the validation context
1681 * @doc: pointer to the document
1682 * @value: the value name
1683 * @attr: the attribute holding the ID
1684 *
1685 * Register a new id declaration
1686 *
1687 * Returns NULL if not, othervise the new xmlIDPtr
1688 */
1689xmlIDPtr
1690xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1691 xmlAttrPtr attr) {
1692 xmlIDPtr ret;
1693 xmlIDTablePtr table;
1694
1695 if (doc == NULL) {
1696 xmlGenericError(xmlGenericErrorContext,
1697 "xmlAddIDDecl: doc == NULL\n");
1698 return(NULL);
1699 }
1700 if (value == NULL) {
1701 xmlGenericError(xmlGenericErrorContext,
1702 "xmlAddIDDecl: value == NULL\n");
1703 return(NULL);
1704 }
1705 if (attr == NULL) {
1706 xmlGenericError(xmlGenericErrorContext,
1707 "xmlAddIDDecl: attr == NULL\n");
1708 return(NULL);
1709 }
1710
1711 /*
1712 * Create the ID table if needed.
1713 */
1714 table = (xmlIDTablePtr) doc->ids;
1715 if (table == NULL)
1716 doc->ids = table = xmlCreateIDTable();
1717 if (table == NULL) {
1718 xmlGenericError(xmlGenericErrorContext,
1719 "xmlAddID: Table creation failed!\n");
1720 return(NULL);
1721 }
1722
1723 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1724 if (ret == NULL) {
1725 xmlGenericError(xmlGenericErrorContext,
1726 "xmlAddID: out of memory\n");
1727 return(NULL);
1728 }
1729
1730 /*
1731 * fill the structure.
1732 */
1733 ret->value = xmlStrdup(value);
1734 ret->attr = attr;
1735
1736 if (xmlHashAddEntry(table, value, ret) < 0) {
1737 /*
1738 * The id is already defined in this Dtd.
1739 */
1740 VERROR(ctxt->userData, "ID %s already defined\n", value);
1741 xmlFreeID(ret);
1742 return(NULL);
1743 }
1744 return(ret);
1745}
1746
1747/**
1748 * xmlFreeIDTable:
1749 * @table: An id table
1750 *
1751 * Deallocate the memory used by an ID hash table.
1752 */
1753void
1754xmlFreeIDTable(xmlIDTablePtr table) {
1755 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1756}
1757
1758/**
1759 * xmlIsID:
1760 * @doc: the document
1761 * @elem: the element carrying the attribute
1762 * @attr: the attribute
1763 *
1764 * Determine whether an attribute is of type ID. In case we have Dtd(s)
1765 * then this is simple, otherwise we use an heuristic: name ID (upper
1766 * or lowercase).
1767 *
1768 * Returns 0 or 1 depending on the lookup result
1769 */
1770int
1771xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1772 if (doc == NULL) return(0);
1773 if (attr == NULL) return(0);
1774 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1775 return(0);
1776 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1777 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1778 (xmlStrEqual(BAD_CAST "name", attr->name)))
1779 return(1);
1780 return(0);
1781 } else {
1782 xmlAttributePtr attrDecl;
1783
1784 if (elem == NULL) return(0);
1785 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1786 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1787 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1788 attr->name);
1789
1790 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1791 return(1);
1792 }
1793 return(0);
1794}
1795
1796/**
1797 * xmlRemoveID
1798 * @doc: the document
1799 * @attr: the attribute
1800 *
1801 * Remove the given attribute from the ID table maintained internally.
1802 *
1803 * Returns -1 if the lookup failed and 0 otherwise
1804 */
1805int
1806xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1807 xmlAttrPtr cur;
1808 xmlIDTablePtr table;
1809 xmlChar *ID;
1810
1811 if (doc == NULL) return(-1);
1812 if (attr == NULL) return(-1);
1813 table = (xmlIDTablePtr) doc->ids;
1814 if (table == NULL)
1815 return(-1);
1816
1817 if (attr == NULL)
1818 return(-1);
1819 ID = xmlNodeListGetString(doc, attr->children, 1);
1820 if (ID == NULL)
1821 return(-1);
1822 cur = xmlHashLookup(table, ID);
1823 if (cur != attr) {
1824 xmlFree(ID);
1825 return(-1);
1826 }
1827 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1828 xmlFree(ID);
1829 return(0);
1830}
1831
1832/**
1833 * xmlGetID:
1834 * @doc: pointer to the document
1835 * @ID: the ID value
1836 *
1837 * Search the attribute declaring the given ID
1838 *
1839 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1840 */
1841xmlAttrPtr
1842xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
1843 xmlIDTablePtr table;
1844 xmlIDPtr id;
1845
1846 if (doc == NULL) {
1847 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
1848 return(NULL);
1849 }
1850
1851 if (ID == NULL) {
1852 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
1853 return(NULL);
1854 }
1855
1856 table = (xmlIDTablePtr) doc->ids;
1857 if (table == NULL)
1858 return(NULL);
1859
1860 id = xmlHashLookup(table, ID);
1861 if (id == NULL)
1862 return(NULL);
1863 return(id->attr);
1864}
1865
1866/************************************************************************
1867 * *
1868 * Refs *
1869 * *
1870 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00001871typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00001872{
1873 xmlListPtr l;
1874 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00001875} xmlRemoveMemo;
1876
1877typedef xmlRemoveMemo *xmlRemoveMemoPtr;
1878
1879typedef struct xmlValidateMemo_t
1880{
1881 xmlValidCtxtPtr ctxt;
1882 const xmlChar *name;
1883} xmlValidateMemo;
1884
1885typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00001886
1887/**
1888 * xmlCreateRefTable:
1889 *
1890 * create and initialize an empty ref hash table.
1891 *
1892 * Returns the xmlRefTablePtr just created or NULL in case
1893 * of error.
1894 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001895static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001896xmlCreateRefTable(void) {
1897 return(xmlHashCreate(0));
1898}
1899
1900/**
1901 * xmlFreeRef:
1902 * @lk: A list link
1903 *
1904 * Deallocate the memory used by a ref definition
1905 */
1906static void
1907xmlFreeRef(xmlLinkPtr lk) {
1908 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
1909 if (ref == NULL) return;
1910 if (ref->value != NULL)
1911 xmlFree((xmlChar *)ref->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001912 xmlFree(ref);
1913}
1914
1915/**
1916 * xmlFreeRefList:
1917 * @list_ref: A list of references.
1918 *
1919 * Deallocate the memory used by a list of references
1920 */
1921static void
1922xmlFreeRefList(xmlListPtr list_ref) {
1923 if (list_ref == NULL) return;
1924 xmlListDelete(list_ref);
1925}
1926
1927/**
1928 * xmlWalkRemoveRef:
1929 * @data: Contents of current link
1930 * @user: Value supplied by the user
1931 *
1932 * Return 0 to abort the walk or 1 to continue
1933 */
1934static int
1935xmlWalkRemoveRef(const void *data, const void *user)
1936{
1937 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
Daniel Veillard8730c562001-02-26 10:49:57 +00001938 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
1939 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00001940
1941 if (attr0 == attr1) { /* Matched: remove and terminate walk */
1942 xmlListRemoveFirst(ref_list, (void *)data);
1943 return 0;
1944 }
1945 return 1;
1946}
1947
1948/**
1949 * xmlAddRef:
1950 * @ctxt: the validation context
1951 * @doc: pointer to the document
1952 * @value: the value name
1953 * @attr: the attribute holding the Ref
1954 *
1955 * Register a new ref declaration
1956 *
1957 * Returns NULL if not, othervise the new xmlRefPtr
1958 */
1959xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001960xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00001961 xmlAttrPtr attr) {
1962 xmlRefPtr ret;
1963 xmlRefTablePtr table;
1964 xmlListPtr ref_list;
1965
1966 if (doc == NULL) {
1967 xmlGenericError(xmlGenericErrorContext,
1968 "xmlAddRefDecl: doc == NULL\n");
1969 return(NULL);
1970 }
1971 if (value == NULL) {
1972 xmlGenericError(xmlGenericErrorContext,
1973 "xmlAddRefDecl: value == NULL\n");
1974 return(NULL);
1975 }
1976 if (attr == NULL) {
1977 xmlGenericError(xmlGenericErrorContext,
1978 "xmlAddRefDecl: attr == NULL\n");
1979 return(NULL);
1980 }
1981
1982 /*
1983 * Create the Ref table if needed.
1984 */
1985 table = (xmlRefTablePtr) doc->refs;
1986 if (table == NULL)
1987 doc->refs = table = xmlCreateRefTable();
1988 if (table == NULL) {
1989 xmlGenericError(xmlGenericErrorContext,
1990 "xmlAddRef: Table creation failed!\n");
1991 return(NULL);
1992 }
1993
1994 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
1995 if (ret == NULL) {
1996 xmlGenericError(xmlGenericErrorContext,
1997 "xmlAddRef: out of memory\n");
1998 return(NULL);
1999 }
2000
2001 /*
2002 * fill the structure.
2003 */
2004 ret->value = xmlStrdup(value);
2005 ret->attr = attr;
2006
2007 /* To add a reference :-
2008 * References are maintained as a list of references,
2009 * Lookup the entry, if no entry create new nodelist
2010 * Add the owning node to the NodeList
2011 * Return the ref
2012 */
2013
2014 if(NULL == (ref_list = xmlHashLookup(table, value))) {
2015 if(NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2016 xmlGenericError(xmlGenericErrorContext,
2017 "xmlAddRef: Reference list creation failed!\n");
2018 return(NULL);
2019 }
2020 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2021 xmlListDelete(ref_list);
2022 xmlGenericError(xmlGenericErrorContext,
2023 "xmlAddRef: Reference list insertion failed!\n");
2024 return(NULL);
2025 }
2026 }
2027 xmlListInsert(ref_list, ret);
2028 return(ret);
2029}
2030
2031/**
2032 * xmlFreeRefTable:
2033 * @table: An ref table
2034 *
2035 * Deallocate the memory used by an Ref hash table.
2036 */
2037void
2038xmlFreeRefTable(xmlRefTablePtr table) {
2039 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
2040}
2041
2042/**
2043 * xmlIsRef:
2044 * @doc: the document
2045 * @elem: the element carrying the attribute
2046 * @attr: the attribute
2047 *
2048 * Determine whether an attribute is of type Ref. In case we have Dtd(s)
2049 * then this is simple, otherwise we use an heuristic: name Ref (upper
2050 * or lowercase).
2051 *
2052 * Returns 0 or 1 depending on the lookup result
2053 */
2054int
2055xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
2056 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2057 return(0);
2058 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2059 /* TODO @@@ */
2060 return(0);
2061 } else {
2062 xmlAttributePtr attrDecl;
2063
2064 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2065 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2066 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
2067 attr->name);
2068
2069 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_IDREF))
2070 return(1);
2071 }
2072 return(0);
2073}
2074
2075/**
2076 * xmlRemoveRef
2077 * @doc: the document
2078 * @attr: the attribute
2079 *
2080 * Remove the given attribute from the Ref table maintained internally.
2081 *
2082 * Returns -1 if the lookup failed and 0 otherwise
2083 */
2084int
2085xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
2086 xmlListPtr ref_list;
2087 xmlRefTablePtr table;
2088 xmlChar *ID;
Daniel Veillard8730c562001-02-26 10:49:57 +00002089 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002090
2091 if (doc == NULL) return(-1);
2092 if (attr == NULL) return(-1);
2093 table = (xmlRefTablePtr) doc->refs;
2094 if (table == NULL)
2095 return(-1);
2096
2097 if (attr == NULL)
2098 return(-1);
2099 ID = xmlNodeListGetString(doc, attr->children, 1);
2100 if (ID == NULL)
2101 return(-1);
2102 ref_list = xmlHashLookup(table, ID);
2103
2104 if(ref_list == NULL) {
2105 xmlFree(ID);
2106 return (-1);
2107 }
2108 /* At this point, ref_list refers to a list of references which
2109 * have the same key as the supplied attr. Our list of references
2110 * is ordered by reference address and we don't have that information
2111 * here to use when removing. We'll have to walk the list and
2112 * check for a matching attribute, when we find one stop the walk
2113 * and remove the entry.
2114 * The list is ordered by reference, so that means we don't have the
2115 * key. Passing the list and the reference to the walker means we
2116 * will have enough data to be able to remove the entry.
2117 */
2118 target.l = ref_list;
2119 target.ap = attr;
2120
2121 /* Remove the supplied attr from our list */
2122 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
2123
2124 /*If the list is empty then remove the list entry in the hash */
2125 if (xmlListEmpty(ref_list))
2126 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2127 xmlFreeRefList);
2128 xmlFree(ID);
2129 return(0);
2130}
2131
2132/**
2133 * xmlGetRefs:
2134 * @doc: pointer to the document
2135 * @ID: the ID value
2136 *
2137 * Find the set of references for the supplied ID.
2138 *
2139 * Returns NULL if not found, otherwise node set for the ID.
2140 */
2141xmlListPtr
2142xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
2143 xmlRefTablePtr table;
2144
2145 if (doc == NULL) {
2146 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: doc == NULL\n");
2147 return(NULL);
2148 }
2149
2150 if (ID == NULL) {
2151 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: ID == NULL\n");
2152 return(NULL);
2153 }
2154
2155 table = (xmlRefTablePtr) doc->refs;
2156 if (table == NULL)
2157 return(NULL);
2158
2159 return (xmlHashLookup(table, ID));
2160}
2161
2162/************************************************************************
2163 * *
2164 * Routines for validity checking *
2165 * *
2166 ************************************************************************/
2167
2168/**
2169 * xmlGetDtdElementDesc:
2170 * @dtd: a pointer to the DtD to search
2171 * @name: the element name
2172 *
2173 * Search the Dtd for the description of this element
2174 *
2175 * returns the xmlElementPtr if found or NULL
2176 */
2177
2178xmlElementPtr
2179xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2180 xmlElementTablePtr table;
2181 xmlElementPtr cur;
2182 xmlChar *uqname = NULL, *prefix = NULL;
2183
2184 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002185 if (dtd->elements == NULL)
2186 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002187 table = (xmlElementTablePtr) dtd->elements;
2188
2189 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002190 if (uqname != NULL)
2191 name = uqname;
2192 cur = xmlHashLookup2(table, name, prefix);
2193 if (prefix != NULL) xmlFree(prefix);
2194 if (uqname != NULL) xmlFree(uqname);
2195 return(cur);
2196}
2197/**
2198 * xmlGetDtdElementDesc2:
2199 * @dtd: a pointer to the DtD to search
2200 * @name: the element name
2201 * @create: create an empty description if not found
2202 *
2203 * Search the Dtd for the description of this element
2204 *
2205 * returns the xmlElementPtr if found or NULL
2206 */
2207
2208xmlElementPtr
2209xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2210 xmlElementTablePtr table;
2211 xmlElementPtr cur;
2212 xmlChar *uqname = NULL, *prefix = NULL;
2213
2214 if (dtd == NULL) return(NULL);
2215 if (dtd->elements == NULL) {
2216 if (!create)
2217 return(NULL);
2218 /*
2219 * Create the Element table if needed.
2220 */
2221 table = (xmlElementTablePtr) dtd->elements;
2222 if (table == NULL) {
2223 table = xmlCreateElementTable();
2224 dtd->elements = (void *) table;
2225 }
2226 if (table == NULL) {
2227 xmlGenericError(xmlGenericErrorContext,
2228 "xmlGetDtdElementDesc: Table creation failed!\n");
2229 return(NULL);
2230 }
2231 }
2232 table = (xmlElementTablePtr) dtd->elements;
2233
2234 uqname = xmlSplitQName2(name, &prefix);
2235 if (uqname != NULL)
2236 name = uqname;
2237 cur = xmlHashLookup2(table, name, prefix);
2238 if ((cur == NULL) && (create)) {
2239 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2240 if (cur == NULL) {
2241 xmlGenericError(xmlGenericErrorContext,
2242 "xmlGetDtdElementDesc: out of memory\n");
2243 return(NULL);
2244 }
2245 memset(cur, 0, sizeof(xmlElement));
2246 cur->type = XML_ELEMENT_DECL;
2247
2248 /*
2249 * fill the structure.
2250 */
2251 cur->name = xmlStrdup(name);
2252 cur->prefix = xmlStrdup(prefix);
2253 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2254
2255 xmlHashAddEntry2(table, name, prefix, cur);
2256 }
2257 if (prefix != NULL) xmlFree(prefix);
2258 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002259 return(cur);
2260}
2261
2262/**
2263 * xmlGetDtdQElementDesc:
2264 * @dtd: a pointer to the DtD to search
2265 * @name: the element name
2266 * @prefix: the element namespace prefix
2267 *
2268 * Search the Dtd for the description of this element
2269 *
2270 * returns the xmlElementPtr if found or NULL
2271 */
2272
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002273static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002274xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2275 const xmlChar *prefix) {
2276 xmlElementTablePtr table;
2277
2278 if (dtd == NULL) return(NULL);
2279 if (dtd->elements == NULL) return(NULL);
2280 table = (xmlElementTablePtr) dtd->elements;
2281
2282 return(xmlHashLookup2(table, name, prefix));
2283}
2284
2285/**
2286 * xmlGetDtdAttrDesc:
2287 * @dtd: a pointer to the DtD to search
2288 * @elem: the element name
2289 * @name: the attribute name
2290 *
2291 * Search the Dtd for the description of this attribute on
2292 * this element.
2293 *
2294 * returns the xmlAttributePtr if found or NULL
2295 */
2296
2297xmlAttributePtr
2298xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2299 xmlAttributeTablePtr table;
2300 xmlAttributePtr cur;
2301 xmlChar *uqname = NULL, *prefix = NULL;
2302
2303 if (dtd == NULL) return(NULL);
2304 if (dtd->attributes == NULL) return(NULL);
2305
2306 table = (xmlAttributeTablePtr) dtd->attributes;
2307 if (table == NULL)
2308 return(NULL);
2309
2310 uqname = xmlSplitQName2(name, &prefix);
2311
2312 if (uqname != NULL) {
2313 cur = xmlHashLookup3(table, uqname, prefix, elem);
2314 if (prefix != NULL) xmlFree(prefix);
2315 if (uqname != NULL) xmlFree(uqname);
2316 } else
2317 cur = xmlHashLookup3(table, name, NULL, elem);
2318 return(cur);
2319}
2320
2321/**
2322 * xmlGetDtdQAttrDesc:
2323 * @dtd: a pointer to the DtD to search
2324 * @elem: the element name
2325 * @name: the attribute name
2326 * @prefix: the attribute namespace prefix
2327 *
2328 * Search the Dtd for the description of this qualified attribute on
2329 * this element.
2330 *
2331 * returns the xmlAttributePtr if found or NULL
2332 */
2333
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002334static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002335xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2336 const xmlChar *prefix) {
2337 xmlAttributeTablePtr table;
2338
2339 if (dtd == NULL) return(NULL);
2340 if (dtd->attributes == NULL) return(NULL);
2341 table = (xmlAttributeTablePtr) dtd->attributes;
2342
2343 return(xmlHashLookup3(table, name, prefix, elem));
2344}
2345
2346/**
2347 * xmlGetDtdNotationDesc:
2348 * @dtd: a pointer to the DtD to search
2349 * @name: the notation name
2350 *
2351 * Search the Dtd for the description of this notation
2352 *
2353 * returns the xmlNotationPtr if found or NULL
2354 */
2355
2356xmlNotationPtr
2357xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2358 xmlNotationTablePtr table;
2359
2360 if (dtd == NULL) return(NULL);
2361 if (dtd->notations == NULL) return(NULL);
2362 table = (xmlNotationTablePtr) dtd->notations;
2363
2364 return(xmlHashLookup(table, name));
2365}
2366
2367/**
2368 * xmlValidateNotationUse:
2369 * @ctxt: the validation context
2370 * @doc: the document
2371 * @notationName: the notation name to check
2372 *
2373 * Validate that the given mame match a notation declaration.
2374 * - [ VC: Notation Declared ]
2375 *
2376 * returns 1 if valid or 0 otherwise
2377 */
2378
2379int
2380xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2381 const xmlChar *notationName) {
2382 xmlNotationPtr notaDecl;
2383 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2384
2385 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2386 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2387 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2388
2389 if (notaDecl == NULL) {
2390 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2391 notationName);
2392 return(0);
2393 }
2394 return(1);
2395}
2396
2397/**
2398 * xmlIsMixedElement
2399 * @doc: the document
2400 * @name: the element name
2401 *
2402 * Search in the DtDs whether an element accept Mixed content (or ANY)
2403 * basically if it is supposed to accept text childs
2404 *
2405 * returns 0 if no, 1 if yes, and -1 if no element description is available
2406 */
2407
2408int
2409xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2410 xmlElementPtr elemDecl;
2411
2412 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2413
2414 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2415 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2416 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2417 if (elemDecl == NULL) return(-1);
2418 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002419 case XML_ELEMENT_TYPE_UNDEFINED:
2420 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002421 case XML_ELEMENT_TYPE_ELEMENT:
2422 return(0);
2423 case XML_ELEMENT_TYPE_EMPTY:
2424 /*
2425 * return 1 for EMPTY since we want VC error to pop up
2426 * on <empty> </empty> for example
2427 */
2428 case XML_ELEMENT_TYPE_ANY:
2429 case XML_ELEMENT_TYPE_MIXED:
2430 return(1);
2431 }
2432 return(1);
2433}
2434
2435/**
2436 * xmlValidateNameValue:
2437 * @value: an Name value
2438 *
2439 * Validate that the given value match Name production
2440 *
2441 * returns 1 if valid or 0 otherwise
2442 */
2443
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002444static int
Owen Taylor3473f882001-02-23 17:55:21 +00002445xmlValidateNameValue(const xmlChar *value) {
2446 const xmlChar *cur;
2447
2448 if (value == NULL) return(0);
2449 cur = value;
2450
2451 if (!IS_LETTER(*cur) && (*cur != '_') &&
2452 (*cur != ':')) {
2453 return(0);
2454 }
2455
2456 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2457 (*cur == '.') || (*cur == '-') ||
2458 (*cur == '_') || (*cur == ':') ||
2459 (IS_COMBINING(*cur)) ||
2460 (IS_EXTENDER(*cur)))
2461 cur++;
2462
2463 if (*cur != 0) return(0);
2464
2465 return(1);
2466}
2467
2468/**
2469 * xmlValidateNamesValue:
2470 * @value: an Names value
2471 *
2472 * Validate that the given value match Names production
2473 *
2474 * returns 1 if valid or 0 otherwise
2475 */
2476
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002477static int
Owen Taylor3473f882001-02-23 17:55:21 +00002478xmlValidateNamesValue(const xmlChar *value) {
2479 const xmlChar *cur;
2480
2481 if (value == NULL) return(0);
2482 cur = value;
2483
2484 if (!IS_LETTER(*cur) && (*cur != '_') &&
2485 (*cur != ':')) {
2486 return(0);
2487 }
2488
2489 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2490 (*cur == '.') || (*cur == '-') ||
2491 (*cur == '_') || (*cur == ':') ||
2492 (IS_COMBINING(*cur)) ||
2493 (IS_EXTENDER(*cur)))
2494 cur++;
2495
2496 while (IS_BLANK(*cur)) {
2497 while (IS_BLANK(*cur)) cur++;
2498
2499 if (!IS_LETTER(*cur) && (*cur != '_') &&
2500 (*cur != ':')) {
2501 return(0);
2502 }
2503
2504 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2505 (*cur == '.') || (*cur == '-') ||
2506 (*cur == '_') || (*cur == ':') ||
2507 (IS_COMBINING(*cur)) ||
2508 (IS_EXTENDER(*cur)))
2509 cur++;
2510 }
2511
2512 if (*cur != 0) return(0);
2513
2514 return(1);
2515}
2516
2517/**
2518 * xmlValidateNmtokenValue:
2519 * @value: an Mntoken value
2520 *
2521 * Validate that the given value match Nmtoken production
2522 *
2523 * [ VC: Name Token ]
2524 *
2525 * returns 1 if valid or 0 otherwise
2526 */
2527
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002528static int
Owen Taylor3473f882001-02-23 17:55:21 +00002529xmlValidateNmtokenValue(const xmlChar *value) {
2530 const xmlChar *cur;
2531
2532 if (value == NULL) return(0);
2533 cur = value;
2534
2535 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2536 (*cur != '.') && (*cur != '-') &&
2537 (*cur != '_') && (*cur != ':') &&
2538 (!IS_COMBINING(*cur)) &&
2539 (!IS_EXTENDER(*cur)))
2540 return(0);
2541
2542 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2543 (*cur == '.') || (*cur == '-') ||
2544 (*cur == '_') || (*cur == ':') ||
2545 (IS_COMBINING(*cur)) ||
2546 (IS_EXTENDER(*cur)))
2547 cur++;
2548
2549 if (*cur != 0) return(0);
2550
2551 return(1);
2552}
2553
2554/**
2555 * xmlValidateNmtokensValue:
2556 * @value: an Mntokens value
2557 *
2558 * Validate that the given value match Nmtokens production
2559 *
2560 * [ VC: Name Token ]
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 +00002566xmlValidateNmtokensValue(const xmlChar *value) {
2567 const xmlChar *cur;
2568
2569 if (value == NULL) return(0);
2570 cur = value;
2571
2572 while (IS_BLANK(*cur)) cur++;
2573 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2574 (*cur != '.') && (*cur != '-') &&
2575 (*cur != '_') && (*cur != ':') &&
2576 (!IS_COMBINING(*cur)) &&
2577 (!IS_EXTENDER(*cur)))
2578 return(0);
2579
2580 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2581 (*cur == '.') || (*cur == '-') ||
2582 (*cur == '_') || (*cur == ':') ||
2583 (IS_COMBINING(*cur)) ||
2584 (IS_EXTENDER(*cur)))
2585 cur++;
2586
2587 while (IS_BLANK(*cur)) {
2588 while (IS_BLANK(*cur)) cur++;
2589 if (*cur == 0) return(1);
2590
2591 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2592 (*cur != '.') && (*cur != '-') &&
2593 (*cur != '_') && (*cur != ':') &&
2594 (!IS_COMBINING(*cur)) &&
2595 (!IS_EXTENDER(*cur)))
2596 return(0);
2597
2598 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2599 (*cur == '.') || (*cur == '-') ||
2600 (*cur == '_') || (*cur == ':') ||
2601 (IS_COMBINING(*cur)) ||
2602 (IS_EXTENDER(*cur)))
2603 cur++;
2604 }
2605
2606 if (*cur != 0) return(0);
2607
2608 return(1);
2609}
2610
2611/**
2612 * xmlValidateNotationDecl:
2613 * @ctxt: the validation context
2614 * @doc: a document instance
2615 * @nota: a notation definition
2616 *
2617 * Try to validate a single notation definition
2618 * basically it does the following checks as described by the
2619 * XML-1.0 recommendation:
2620 * - it seems that no validity constraing exist on notation declarations
2621 * But this function get called anyway ...
2622 *
2623 * returns 1 if valid or 0 otherwise
2624 */
2625
2626int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002627xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2628 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002629 int ret = 1;
2630
2631 return(ret);
2632}
2633
2634/**
2635 * xmlValidateAttributeValue:
2636 * @type: an attribute type
2637 * @value: an attribute value
2638 *
2639 * Validate that the given attribute value match the proper production
2640 *
2641 * [ VC: ID ]
2642 * Values of type ID must match the Name production....
2643 *
2644 * [ VC: IDREF ]
2645 * Values of type IDREF must match the Name production, and values
2646 * of type IDREFS must match Names ...
2647 *
2648 * [ VC: Entity Name ]
2649 * Values of type ENTITY must match the Name production, values
2650 * of type ENTITIES must match Names ...
2651 *
2652 * [ VC: Name Token ]
2653 * Values of type NMTOKEN must match the Nmtoken production; values
2654 * of type NMTOKENS must match Nmtokens.
2655 *
2656 * returns 1 if valid or 0 otherwise
2657 */
2658
2659int
2660xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2661 switch (type) {
2662 case XML_ATTRIBUTE_ENTITIES:
2663 case XML_ATTRIBUTE_IDREFS:
2664 return(xmlValidateNamesValue(value));
2665 case XML_ATTRIBUTE_ENTITY:
2666 case XML_ATTRIBUTE_IDREF:
2667 case XML_ATTRIBUTE_ID:
2668 case XML_ATTRIBUTE_NOTATION:
2669 return(xmlValidateNameValue(value));
2670 case XML_ATTRIBUTE_NMTOKENS:
2671 case XML_ATTRIBUTE_ENUMERATION:
2672 return(xmlValidateNmtokensValue(value));
2673 case XML_ATTRIBUTE_NMTOKEN:
2674 return(xmlValidateNmtokenValue(value));
2675 case XML_ATTRIBUTE_CDATA:
2676 break;
2677 }
2678 return(1);
2679}
2680
2681/**
2682 * xmlValidateAttributeValue2:
2683 * @ctxt: the validation context
2684 * @doc: the document
2685 * @name: the attribute name (used for error reporting only)
2686 * @type: the attribute type
2687 * @value: the attribute value
2688 *
2689 * Validate that the given attribute value match a given type.
2690 * This typically cannot be done before having finished parsing
2691 * the subsets.
2692 *
2693 * [ VC: IDREF ]
2694 * Values of type IDREF must match one of the declared IDs
2695 * Values of type IDREFS must match a sequence of the declared IDs
2696 * each Name must match the value of an ID attribute on some element
2697 * in the XML document; i.e. IDREF values must match the value of
2698 * some ID attribute
2699 *
2700 * [ VC: Entity Name ]
2701 * Values of type ENTITY must match one declared entity
2702 * Values of type ENTITIES must match a sequence of declared entities
2703 *
2704 * [ VC: Notation Attributes ]
2705 * all notation names in the declaration must be declared.
2706 *
2707 * returns 1 if valid or 0 otherwise
2708 */
2709
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002710static int
Owen Taylor3473f882001-02-23 17:55:21 +00002711xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2712 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2713 int ret = 1;
2714 switch (type) {
2715 case XML_ATTRIBUTE_IDREFS:
2716 case XML_ATTRIBUTE_IDREF:
2717 case XML_ATTRIBUTE_ID:
2718 case XML_ATTRIBUTE_NMTOKENS:
2719 case XML_ATTRIBUTE_ENUMERATION:
2720 case XML_ATTRIBUTE_NMTOKEN:
2721 case XML_ATTRIBUTE_CDATA:
2722 break;
2723 case XML_ATTRIBUTE_ENTITY: {
2724 xmlEntityPtr ent;
2725
2726 ent = xmlGetDocEntity(doc, value);
2727 if (ent == NULL) {
2728 VERROR(ctxt->userData,
2729 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2730 name, value);
2731 ret = 0;
2732 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2733 VERROR(ctxt->userData,
2734 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2735 name, value);
2736 ret = 0;
2737 }
2738 break;
2739 }
2740 case XML_ATTRIBUTE_ENTITIES: {
2741 xmlChar *dup, *nam = NULL, *cur, save;
2742 xmlEntityPtr ent;
2743
2744 dup = xmlStrdup(value);
2745 if (dup == NULL)
2746 return(0);
2747 cur = dup;
2748 while (*cur != 0) {
2749 nam = cur;
2750 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2751 save = *cur;
2752 *cur = 0;
2753 ent = xmlGetDocEntity(doc, nam);
2754 if (ent == NULL) {
2755 VERROR(ctxt->userData,
2756 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2757 name, nam);
2758 ret = 0;
2759 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2760 VERROR(ctxt->userData,
2761 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2762 name, nam);
2763 ret = 0;
2764 }
2765 if (save == 0)
2766 break;
2767 *cur = save;
2768 while (IS_BLANK(*cur)) cur++;
2769 }
2770 xmlFree(dup);
2771 break;
2772 }
2773 case XML_ATTRIBUTE_NOTATION: {
2774 xmlNotationPtr nota;
2775
2776 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2777 if ((nota == NULL) && (doc->extSubset != NULL))
2778 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2779
2780 if (nota == NULL) {
2781 VERROR(ctxt->userData,
2782 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2783 name, value);
2784 ret = 0;
2785 }
2786 break;
2787 }
2788 }
2789 return(ret);
2790}
2791
2792/**
2793 * xmlValidNormalizeAttributeValue:
2794 * @doc: the document
2795 * @elem: the parent
2796 * @name: the attribute name
2797 * @value: the attribute value
2798 *
2799 * Does the validation related extra step of the normalization of attribute
2800 * values:
2801 *
2802 * If the declared value is not CDATA, then the XML processor must further
2803 * process the normalized attribute value by discarding any leading and
2804 * trailing space (#x20) characters, and by replacing sequences of space
2805 * (#x20) characters by single space (#x20) character.
2806 *
2807 * returns a new normalized string if normalization is needed, NULL otherwise
2808 * the caller must free the returned value.
2809 */
2810
2811xmlChar *
2812xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
2813 const xmlChar *name, const xmlChar *value) {
2814 xmlChar *ret, *dst;
2815 const xmlChar *src;
2816 xmlAttributePtr attrDecl = NULL;
2817
2818 if (doc == NULL) return(NULL);
2819 if (elem == NULL) return(NULL);
2820 if (name == NULL) return(NULL);
2821 if (value == NULL) return(NULL);
2822
2823 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2824 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00002825 snprintf((char *) qname, sizeof(qname), "%s:%s",
2826 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002827 qname[sizeof(qname) - 1] = 0;
2828 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
2829 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2830 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
2831 }
2832 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
2833 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2834 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
2835
2836 if (attrDecl == NULL)
2837 return(NULL);
2838 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
2839 return(NULL);
2840
2841 ret = xmlStrdup(value);
2842 if (ret == NULL)
2843 return(NULL);
2844 src = value;
2845 dst = ret;
2846 while (*src == 0x20) src++;
2847 while (*src != 0) {
2848 if (*src == 0x20) {
2849 while (*src == 0x20) src++;
2850 if (*src != 0)
2851 *dst++ = 0x20;
2852 } else {
2853 *dst++ = *src++;
2854 }
2855 }
2856 *dst = 0;
2857 return(ret);
2858}
2859
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002860static void
Owen Taylor3473f882001-02-23 17:55:21 +00002861xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002862 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002863 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
2864}
2865
2866/**
2867 * xmlValidateAttributeDecl:
2868 * @ctxt: the validation context
2869 * @doc: a document instance
2870 * @attr: an attribute definition
2871 *
2872 * Try to validate a single attribute definition
2873 * basically it does the following checks as described by the
2874 * XML-1.0 recommendation:
2875 * - [ VC: Attribute Default Legal ]
2876 * - [ VC: Enumeration ]
2877 * - [ VC: ID Attribute Default ]
2878 *
2879 * The ID/IDREF uniqueness and matching are done separately
2880 *
2881 * returns 1 if valid or 0 otherwise
2882 */
2883
2884int
2885xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2886 xmlAttributePtr attr) {
2887 int ret = 1;
2888 int val;
2889 CHECK_DTD;
2890 if(attr == NULL) return(1);
2891
2892 /* Attribute Default Legal */
2893 /* Enumeration */
2894 if (attr->defaultValue != NULL) {
2895 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
2896 if (val == 0) {
2897 VERROR(ctxt->userData,
2898 "Syntax of default value for attribute %s on %s is not valid\n",
2899 attr->name, attr->elem);
2900 }
2901 ret &= val;
2902 }
2903
2904 /* ID Attribute Default */
2905 if ((attr->atype == XML_ATTRIBUTE_ID)&&
2906 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
2907 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
2908 VERROR(ctxt->userData,
2909 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
2910 attr->name, attr->elem);
2911 ret = 0;
2912 }
2913
2914 /* One ID per Element Type */
2915 if (attr->atype == XML_ATTRIBUTE_ID) {
2916 int nbId;
2917
2918 /* the trick is taht we parse DtD as their own internal subset */
2919 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
2920 attr->elem);
2921 if (elem != NULL) {
2922 nbId = xmlScanIDAttributeDecl(NULL, elem);
2923 } else {
2924 xmlAttributeTablePtr table;
2925
2926 /*
2927 * The attribute may be declared in the internal subset and the
2928 * element in the external subset.
2929 */
2930 nbId = 0;
2931 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
2932 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
2933 xmlValidateAttributeIdCallback, &nbId);
2934 }
2935 if (nbId > 1) {
2936 VERROR(ctxt->userData,
2937 "Element %s has %d ID attribute defined in the internal subset : %s\n",
2938 attr->elem, nbId, attr->name);
2939 } else if (doc->extSubset != NULL) {
2940 int extId = 0;
2941 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
2942 if (elem != NULL) {
2943 extId = xmlScanIDAttributeDecl(NULL, elem);
2944 }
2945 if (extId > 1) {
2946 VERROR(ctxt->userData,
2947 "Element %s has %d ID attribute defined in the external subset : %s\n",
2948 attr->elem, extId, attr->name);
2949 } else if (extId + nbId > 1) {
2950 VERROR(ctxt->userData,
2951"Element %s has ID attributes defined in the internal and external subset : %s\n",
2952 attr->elem, attr->name);
2953 }
2954 }
2955 }
2956
2957 /* Validity Constraint: Enumeration */
2958 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
2959 xmlEnumerationPtr tree = attr->tree;
2960 while (tree != NULL) {
2961 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
2962 tree = tree->next;
2963 }
2964 if (tree == NULL) {
2965 VERROR(ctxt->userData,
2966"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
2967 attr->defaultValue, attr->name, attr->elem);
2968 ret = 0;
2969 }
2970 }
2971
2972 return(ret);
2973}
2974
2975/**
2976 * xmlValidateElementDecl:
2977 * @ctxt: the validation context
2978 * @doc: a document instance
2979 * @elem: an element definition
2980 *
2981 * Try to validate a single element definition
2982 * basically it does the following checks as described by the
2983 * XML-1.0 recommendation:
2984 * - [ VC: One ID per Element Type ]
2985 * - [ VC: No Duplicate Types ]
2986 * - [ VC: Unique Element Type Declaration ]
2987 *
2988 * returns 1 if valid or 0 otherwise
2989 */
2990
2991int
2992xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2993 xmlElementPtr elem) {
2994 int ret = 1;
2995 xmlElementPtr tst;
2996
2997 CHECK_DTD;
2998
2999 if (elem == NULL) return(1);
3000
3001 /* No Duplicate Types */
3002 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3003 xmlElementContentPtr cur, next;
3004 const xmlChar *name;
3005
3006 cur = elem->content;
3007 while (cur != NULL) {
3008 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3009 if (cur->c1 == NULL) break;
3010 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3011 name = cur->c1->name;
3012 next = cur->c2;
3013 while (next != NULL) {
3014 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3015 if (xmlStrEqual(next->name, name)) {
3016 VERROR(ctxt->userData,
3017 "Definition of %s has duplicate references of %s\n",
3018 elem->name, name);
3019 ret = 0;
3020 }
3021 break;
3022 }
3023 if (next->c1 == NULL) break;
3024 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3025 if (xmlStrEqual(next->c1->name, name)) {
3026 VERROR(ctxt->userData,
3027 "Definition of %s has duplicate references of %s\n",
3028 elem->name, name);
3029 ret = 0;
3030 }
3031 next = next->c2;
3032 }
3033 }
3034 cur = cur->c2;
3035 }
3036 }
3037
3038 /* VC: Unique Element Type Declaration */
3039 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003040 if ((tst != NULL ) && (tst != elem) &&
3041 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003042 VERROR(ctxt->userData, "Redefinition of element %s\n",
3043 elem->name);
3044 ret = 0;
3045 }
3046 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003047 if ((tst != NULL ) && (tst != elem) &&
3048 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003049 VERROR(ctxt->userData, "Redefinition of element %s\n",
3050 elem->name);
3051 ret = 0;
3052 }
3053
Daniel Veillarda10efa82001-04-18 13:09:01 +00003054 /* One ID per Element Type
3055 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003056 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3057 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003058 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003059 return(ret);
3060}
3061
3062/**
3063 * xmlValidateOneAttribute:
3064 * @ctxt: the validation context
3065 * @doc: a document instance
3066 * @elem: an element instance
3067 * @attr: an attribute instance
3068 * @value: the attribute value (without entities processing)
3069 *
3070 * Try to validate a single attribute for an element
3071 * basically it does the following checks as described by the
3072 * XML-1.0 recommendation:
3073 * - [ VC: Attribute Value Type ]
3074 * - [ VC: Fixed Attribute Default ]
3075 * - [ VC: Entity Name ]
3076 * - [ VC: Name Token ]
3077 * - [ VC: ID ]
3078 * - [ VC: IDREF ]
3079 * - [ VC: Entity Name ]
3080 * - [ VC: Notation Attributes ]
3081 *
3082 * The ID/IDREF uniqueness and matching are done separately
3083 *
3084 * returns 1 if valid or 0 otherwise
3085 */
3086
3087int
3088xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3089 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3090 /* xmlElementPtr elemDecl; */
3091 xmlAttributePtr attrDecl = NULL;
3092 int val;
3093 int ret = 1;
3094
3095 CHECK_DTD;
3096 if ((elem == NULL) || (elem->name == NULL)) return(0);
3097 if ((attr == NULL) || (attr->name == NULL)) return(0);
3098
3099 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3100 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003101 snprintf((char *) qname, sizeof(qname), "%s:%s",
3102 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003103 qname[sizeof(qname) - 1] = 0;
3104 if (attr->ns != NULL) {
3105 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3106 attr->name, attr->ns->prefix);
3107 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3108 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3109 attr->name, attr->ns->prefix);
3110 } else {
3111 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3112 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3113 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3114 qname, attr->name);
3115 }
3116 }
3117 if (attrDecl == NULL) {
3118 if (attr->ns != NULL) {
3119 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3120 attr->name, attr->ns->prefix);
3121 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3122 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3123 attr->name, attr->ns->prefix);
3124 } else {
3125 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3126 elem->name, attr->name);
3127 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3128 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3129 elem->name, attr->name);
3130 }
3131 }
3132
3133
3134 /* Validity Constraint: Attribute Value Type */
3135 if (attrDecl == NULL) {
3136 VERROR(ctxt->userData,
3137 "No declaration for attribute %s on element %s\n",
3138 attr->name, elem->name);
3139 return(0);
3140 }
3141 attr->atype = attrDecl->atype;
3142
3143 val = xmlValidateAttributeValue(attrDecl->atype, value);
3144 if (val == 0) {
3145 VERROR(ctxt->userData,
3146 "Syntax of value for attribute %s on %s is not valid\n",
3147 attr->name, elem->name);
3148 ret = 0;
3149 }
3150
3151 /* Validity constraint: Fixed Attribute Default */
3152 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3153 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3154 VERROR(ctxt->userData,
3155 "Value for attribute %s on %s is differnt from default \"%s\"\n",
3156 attr->name, elem->name, attrDecl->defaultValue);
3157 ret = 0;
3158 }
3159 }
3160
3161 /* Validity Constraint: ID uniqueness */
3162 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3163 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3164 ret = 0;
3165 }
3166
3167 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3168 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3169 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3170 ret = 0;
3171 }
3172
3173 /* Validity Constraint: Notation Attributes */
3174 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3175 xmlEnumerationPtr tree = attrDecl->tree;
3176 xmlNotationPtr nota;
3177
3178 /* First check that the given NOTATION was declared */
3179 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3180 if (nota == NULL)
3181 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3182
3183 if (nota == NULL) {
3184 VERROR(ctxt->userData,
3185 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
3186 value, attr->name, elem->name);
3187 ret = 0;
3188 }
3189
3190 /* Second, verify that it's among the list */
3191 while (tree != NULL) {
3192 if (xmlStrEqual(tree->name, value)) break;
3193 tree = tree->next;
3194 }
3195 if (tree == NULL) {
3196 VERROR(ctxt->userData,
3197"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
3198 value, attr->name, elem->name);
3199 ret = 0;
3200 }
3201 }
3202
3203 /* Validity Constraint: Enumeration */
3204 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3205 xmlEnumerationPtr tree = attrDecl->tree;
3206 while (tree != NULL) {
3207 if (xmlStrEqual(tree->name, value)) break;
3208 tree = tree->next;
3209 }
3210 if (tree == NULL) {
3211 VERROR(ctxt->userData,
3212 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3213 value, attr->name, elem->name);
3214 ret = 0;
3215 }
3216 }
3217
3218 /* Fixed Attribute Default */
3219 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3220 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3221 VERROR(ctxt->userData,
3222 "Value for attribute %s on %s must be \"%s\"\n",
3223 attr->name, elem->name, attrDecl->defaultValue);
3224 ret = 0;
3225 }
3226
3227 /* Extra check for the attribute value */
3228 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3229 attrDecl->atype, value);
3230
3231 return(ret);
3232}
3233
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003234/**
3235 * xmlValidateSkipIgnorable:
3236 * @ctxt: the validation context
3237 * @child: the child list
3238 *
3239 * Skip ignorable elements w.r.t. the validation process
3240 *
3241 * returns the first element to consider for validation of the content model
3242 */
3243
3244static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003245xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003246 while (child != NULL) {
3247 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003248 /* These things are ignored (skipped) during validation. */
3249 case XML_PI_NODE:
3250 case XML_COMMENT_NODE:
3251 case XML_XINCLUDE_START:
3252 case XML_XINCLUDE_END:
3253 child = child->next;
3254 break;
3255 case XML_TEXT_NODE:
3256 if (xmlIsBlankNode(child))
3257 child = child->next;
3258 else
3259 return(child);
3260 break;
3261 /* keep current node */
3262 default:
3263 return(child);
3264 }
3265 }
3266 return(child);
3267}
3268
3269/**
3270 * xmlValidateElementType:
3271 * @ctxt: the validation context
3272 *
3273 * Try to validate the content model of an element internal function
3274 *
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003275 * returns 1 if valid or 0 ,-1 in case of error, and -2 if an entity
3276 * reference is found.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003277 */
3278
3279static int
3280xmlValidateElementType(xmlValidCtxtPtr ctxt) {
3281 int ret = -1;
3282 int consumed = 1;
3283
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003284 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003285 if ((NODE == NULL) && (CONT == NULL))
3286 return(1);
3287 if ((NODE == NULL) &&
3288 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3289 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3290 return(1);
3291 }
3292 if (CONT == NULL) return(-1);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003293 if (NODE->type == XML_ENTITY_REF_NODE)
3294 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003295
3296 /*
3297 * We arrive here when more states need to be examined
3298 */
3299cont:
3300
3301 /*
3302 * We just recovered from a rollback generated by a possible
3303 * epsilon transition, go directly to the analysis phase
3304 */
3305 if (STATE == ROLLBACK_PARENT) {
3306 DEBUG_VALID_MSG("restaured parent branch");
3307 DEBUG_VALID_STATE(NODE, CONT)
3308 ret = 1;
3309 consumed = 0;
3310 goto analyze;
3311 }
3312
3313 DEBUG_VALID_STATE(NODE, CONT)
3314 /*
3315 * we may have to save a backup state here. This is the equivalent
3316 * of handling epsilon transition in NFAs.
3317 */
3318 if ((consumed) && (CONT != NULL) &&
3319 (CONT->parent != NULL) &&
3320 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003321 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
3322 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003323 DEBUG_VALID_MSG("saving parent branch");
3324 vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
3325 }
3326
3327
3328 /*
3329 * Check first if the content matches
3330 */
3331 switch (CONT->type) {
3332 case XML_ELEMENT_CONTENT_PCDATA:
3333 if (NODE == NULL) {
3334 DEBUG_VALID_MSG("pcdata failed no node");
3335 ret = 0;
3336 break;
3337 }
3338 if (NODE->type == XML_TEXT_NODE) {
3339 DEBUG_VALID_MSG("pcdata found, skip to next");
3340 /*
3341 * go to next element in the content model
3342 * skipping ignorable elems
3343 */
3344 do {
3345 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003346 NODE = xmlValidateSkipIgnorable(NODE);
3347 if ((NODE != NULL) &&
3348 (NODE->type == XML_ENTITY_REF_NODE))
3349 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003350 } while ((NODE != NULL) &&
3351 ((NODE->type != XML_ELEMENT_NODE) &&
3352 (NODE->type != XML_TEXT_NODE)));
3353 consumed = 1;
3354 ret = 1;
3355 break;
3356 } else {
3357 DEBUG_VALID_MSG("pcdata failed");
3358 ret = 0;
3359 break;
3360 }
3361 break;
3362 case XML_ELEMENT_CONTENT_ELEMENT:
3363 if (NODE == NULL) {
3364 DEBUG_VALID_MSG("element failed no node");
3365 ret = 0;
3366 break;
3367 }
3368 ret = (xmlStrEqual(NODE->name, CONT->name));
3369 if (ret == 1) {
3370 DEBUG_VALID_MSG("element found, skip to next");
3371 /*
3372 * go to next element in the content model
3373 * skipping ignorable elems
3374 */
3375 do {
3376 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003377 NODE = xmlValidateSkipIgnorable(NODE);
3378 if ((NODE != NULL) &&
3379 (NODE->type == XML_ENTITY_REF_NODE))
3380 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003381 } while ((NODE != NULL) &&
3382 ((NODE->type != XML_ELEMENT_NODE) &&
3383 (NODE->type != XML_TEXT_NODE)));
3384 consumed = 1;
3385 } else {
3386 DEBUG_VALID_MSG("element failed");
3387 ret = 0;
3388 break;
3389 }
3390 break;
3391 case XML_ELEMENT_CONTENT_OR:
3392 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003393 * Small optimization.
3394 */
3395 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3396 if ((NODE == NULL) ||
3397 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3398 DEPTH++;
3399 CONT = CONT->c2;
3400 goto cont;
3401 }
3402 }
3403
3404 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003405 * save the second branch 'or' branch
3406 */
3407 DEBUG_VALID_MSG("saving 'or' branch");
3408 vstateVPush(ctxt, CONT->c2, NODE, DEPTH + 1, OCCURS, ROLLBACK_OR);
3409
3410 DEPTH++;
3411 CONT = CONT->c1;
3412 goto cont;
3413 case XML_ELEMENT_CONTENT_SEQ:
3414 DEPTH++;
3415 CONT = CONT->c1;
3416 goto cont;
3417 }
3418
3419 /*
3420 * At this point handle going up in the tree
3421 */
3422 if (ret == -1) {
3423 DEBUG_VALID_MSG("error found returning");
3424 return(ret);
3425 }
3426analyze:
3427 while (CONT != NULL) {
3428 /*
3429 * First do the analysis depending on the occurence model at
3430 * this level.
3431 */
3432 if (ret == 0) {
3433 switch (CONT->ocur) {
3434 case XML_ELEMENT_CONTENT_ONCE:
3435 DEBUG_VALID_MSG("Once branch failed, rollback");
3436 if (vstateVPop(ctxt) < 0 ) {
3437 DEBUG_VALID_MSG("exhaustion, failed");
3438 return(0);
3439 }
3440 consumed = 0;
3441 goto cont;
3442 case XML_ELEMENT_CONTENT_PLUS:
3443 if (OCCURENCE == 0) {
3444 DEBUG_VALID_MSG("Plus branch failed, rollback");
3445 if (vstateVPop(ctxt) < 0 ) {
3446 DEBUG_VALID_MSG("exhaustion, failed");
3447 return(0);
3448 }
3449 consumed = 0;
3450 goto cont;
3451 }
3452 DEBUG_VALID_MSG("Plus branch found");
3453 ret = 1;
3454 break;
3455 case XML_ELEMENT_CONTENT_MULT:
3456#ifdef DEBUG_VALID_ALGO
3457 if (OCCURENCE == 0) {
3458 DEBUG_VALID_MSG("Mult branch failed");
3459 } else {
3460 DEBUG_VALID_MSG("Mult branch found");
3461 }
3462#endif
3463 ret = 1;
3464 break;
3465 case XML_ELEMENT_CONTENT_OPT:
3466 DEBUG_VALID_MSG("Option branch failed");
3467 ret = 1;
3468 break;
3469 }
3470 } else {
3471 switch (CONT->ocur) {
3472 case XML_ELEMENT_CONTENT_OPT:
3473 DEBUG_VALID_MSG("Option branch succeeded");
3474 ret = 1;
3475 break;
3476 case XML_ELEMENT_CONTENT_ONCE:
3477 DEBUG_VALID_MSG("Once branch succeeded");
3478 ret = 1;
3479 break;
3480 case XML_ELEMENT_CONTENT_PLUS:
3481 if (STATE == ROLLBACK_PARENT) {
3482 DEBUG_VALID_MSG("Plus branch rollback");
3483 ret = 1;
3484 break;
3485 }
3486 if (NODE == NULL) {
3487 DEBUG_VALID_MSG("Plus branch exhausted");
3488 ret = 1;
3489 break;
3490 }
3491 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
3492 SET_OCCURENCE;
3493 goto cont;
3494 case XML_ELEMENT_CONTENT_MULT:
3495 if (STATE == ROLLBACK_PARENT) {
3496 DEBUG_VALID_MSG("Mult branch rollback");
3497 ret = 1;
3498 break;
3499 }
3500 if (NODE == NULL) {
3501 DEBUG_VALID_MSG("Mult branch exhausted");
3502 ret = 1;
3503 break;
3504 }
3505 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
3506 SET_OCCURENCE;
3507 goto cont;
3508 }
3509 }
3510 STATE = 0;
3511
3512 /*
3513 * Then act accordingly at the parent level
3514 */
3515 RESET_OCCURENCE;
3516 if (CONT->parent == NULL)
3517 break;
3518
3519 switch (CONT->parent->type) {
3520 case XML_ELEMENT_CONTENT_PCDATA:
3521 DEBUG_VALID_MSG("Error: parent pcdata");
3522 return(-1);
3523 case XML_ELEMENT_CONTENT_ELEMENT:
3524 DEBUG_VALID_MSG("Error: parent element");
3525 return(-1);
3526 case XML_ELEMENT_CONTENT_OR:
3527 if (ret == 1) {
3528 DEBUG_VALID_MSG("Or succeeded");
3529 CONT = CONT->parent;
3530 DEPTH--;
3531 } else {
3532 DEBUG_VALID_MSG("Or failed");
3533 CONT = CONT->parent;
3534 DEPTH--;
3535 }
3536 break;
3537 case XML_ELEMENT_CONTENT_SEQ:
3538 if (ret == 0) {
3539 DEBUG_VALID_MSG("Sequence failed");
3540 CONT = CONT->parent;
3541 DEPTH--;
3542 } else if (CONT == CONT->parent->c1) {
3543 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3544 CONT = CONT->parent->c2;
3545 goto cont;
3546 } else {
3547 DEBUG_VALID_MSG("Sequence succeeded");
3548 CONT = CONT->parent;
3549 DEPTH--;
3550 }
3551 }
3552 }
3553 if (NODE != NULL) {
3554 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3555 if (vstateVPop(ctxt) < 0 ) {
3556 DEBUG_VALID_MSG("exhaustion, failed");
3557 return(0);
3558 }
3559 consumed = 0;
3560 goto cont;
3561 }
3562 if (ret == 0) {
3563 DEBUG_VALID_MSG("Failure, rollback");
3564 if (vstateVPop(ctxt) < 0 ) {
3565 DEBUG_VALID_MSG("exhaustion, failed");
3566 return(0);
3567 }
3568 consumed = 0;
3569 goto cont;
3570 }
3571 return(1);
3572}
3573
3574/**
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003575 * xmlSprintfElements:
3576 * @buf: an output buffer
3577 * @content: An element
3578 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3579 *
3580 * This will dump the list of elements to the buffer
3581 * Intended just for the debug routine
3582 */
3583static void
3584xmlSprintfElements(char *buf, xmlNodePtr node, int glob) {
3585 xmlNodePtr cur;
3586
3587 if (node == NULL) return;
3588 if (glob) strcat(buf, "(");
3589 cur = node;
3590 while (cur != NULL) {
3591 switch (cur->type) {
3592 case XML_ELEMENT_NODE:
3593 strcat(buf, (char *) cur->name);
3594 if (cur->next != NULL)
3595 strcat(buf, " ");
3596 break;
3597 case XML_TEXT_NODE:
3598 if (xmlIsBlankNode(cur))
3599 break;
3600 case XML_CDATA_SECTION_NODE:
3601 case XML_ENTITY_REF_NODE:
3602 strcat(buf, "CDATA");
3603 if (cur->next != NULL)
3604 strcat(buf, " ");
3605 break;
3606 case XML_ATTRIBUTE_NODE:
3607 case XML_DOCUMENT_NODE:
3608#ifdef LIBXML_SGML_ENABLED
3609 case XML_SGML_DOCUMENT_NODE:
3610#endif
3611 case XML_HTML_DOCUMENT_NODE:
3612 case XML_DOCUMENT_TYPE_NODE:
3613 case XML_DOCUMENT_FRAG_NODE:
3614 case XML_NOTATION_NODE:
3615 case XML_NAMESPACE_DECL:
3616 strcat(buf, "???");
3617 if (cur->next != NULL)
3618 strcat(buf, " ");
3619 break;
3620 case XML_ENTITY_NODE:
3621 case XML_PI_NODE:
3622 case XML_DTD_NODE:
3623 case XML_COMMENT_NODE:
3624 case XML_ELEMENT_DECL:
3625 case XML_ATTRIBUTE_DECL:
3626 case XML_ENTITY_DECL:
3627 case XML_XINCLUDE_START:
3628 case XML_XINCLUDE_END:
3629 break;
3630 }
3631 cur = cur->next;
3632 }
3633 if (glob) strcat(buf, ")");
3634}
3635
3636/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003637 * xmlValidateElementContent:
3638 * @ctxt: the validation context
3639 * @child: the child list
3640 * @cont: pointer to the content declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003641 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003642 *
3643 * Try to validate the content model of an element
3644 *
3645 * returns 1 if valid or 0 if not and -1 in case of error
3646 */
3647
3648static int
3649xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003650 xmlElementContentPtr cont, int warn) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003651 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003652 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003653
3654 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003655 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003656 */
3657 ctxt->vstateMax = 8;
3658 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
3659 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
3660 if (ctxt->vstateTab == NULL) {
3661 xmlGenericError(xmlGenericErrorContext,
3662 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003663 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003664 }
3665 /*
3666 * The first entry in the stack is reserved to the current state
3667 */
3668 ctxt->vstate = &ctxt->vstateTab[0];
3669 ctxt->vstateNr = 1;
3670 CONT = cont;
3671 NODE = child;
3672 DEPTH = 0;
3673 OCCURS = 0;
3674 STATE = 0;
3675 ret = xmlValidateElementType(ctxt);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003676 if (ret == -2) {
3677 /*
3678 * An entities reference appeared at this level.
3679 * Buid a minimal representation of this node content
3680 * sufficient to run the validation process on it
3681 */
3682 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003683 cur = child;
3684 while (cur != NULL) {
3685 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003686 case XML_ENTITY_REF_NODE:
3687 /*
3688 * Push the current node to be able to roll back
3689 * and process within the entity
3690 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003691 if ((cur->children != NULL) &&
3692 (cur->children->children != NULL)) {
3693 nodeVPush(ctxt, cur);
3694 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003695 continue;
3696 }
3697 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003698 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003699 break;
3700 case XML_ELEMENT_NODE:
3701 /*
3702 * Allocate a new node and minimally fills in
3703 * what's required
3704 */
3705 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
3706 if (tmp == NULL) {
3707 xmlGenericError(xmlGenericErrorContext,
3708 "xmlValidateElementContent : malloc failed\n");
3709 xmlFreeNodeList(repl);
3710 ret = -1;
3711 goto done;
3712 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003713 tmp->type = cur->type;
3714 tmp->name = cur->name;
3715 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003716 tmp->next = NULL;
3717 if (repl == NULL)
3718 repl = last = tmp;
3719 else {
3720 last->next = tmp;
3721 last = tmp;
3722 }
3723 break;
3724 default:
3725 break;
3726 }
3727 /*
3728 * Switch to next element
3729 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003730 cur = cur->next;
3731 while (cur == NULL) {
3732 cur = nodeVPop(ctxt);
3733 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003734 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003735 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003736 }
3737 }
3738
3739 /*
3740 * Relaunch the validation
3741 */
3742 ctxt->vstate = &ctxt->vstateTab[0];
3743 ctxt->vstateNr = 1;
3744 CONT = cont;
3745 NODE = repl;
3746 DEPTH = 0;
3747 OCCURS = 0;
3748 STATE = 0;
3749 ret = xmlValidateElementType(ctxt);
3750 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003751 if ((warn) && (ret != 1)) {
3752 char expr[5000];
3753 char list[5000];
3754
3755 expr[0] = 0;
3756 xmlSprintfElementContent(expr, cont, 1);
3757 list[0] = 0;
3758 if (repl != NULL)
3759 xmlSprintfElements(list, repl, 1);
3760 else
3761 xmlSprintfElements(list, child, 1);
3762
3763 if ((child->parent != NULL) && (child->parent->name != NULL)) {
3764 VERROR(ctxt->userData,
3765 "Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
3766 child->parent->name, expr, list);
3767 } else {
3768 VERROR(ctxt->userData,
3769 "Element content doesn't follow the Dtd\nExpecting %s, got %s\n",
3770 expr, list);
3771 }
3772 ret = 0;
3773 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003774
3775
3776done:
3777 /*
3778 * Deallocate the copy if done, and free up the validation stack
3779 */
3780 while (repl != NULL) {
3781 tmp = repl->next;
3782 xmlFree(repl);
3783 repl = tmp;
3784 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003785 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003786 if (ctxt->vstateTab != NULL) {
3787 xmlFree(ctxt->vstateTab);
3788 ctxt->vstateTab = NULL;
3789 }
3790 ctxt->nodeMax = 0;
3791 if (ctxt->nodeTab != NULL) {
3792 xmlFree(ctxt->nodeTab);
3793 ctxt->nodeTab = NULL;
3794 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003795 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003796
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003797}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003798
Owen Taylor3473f882001-02-23 17:55:21 +00003799/**
3800 * xmlSprintfElementChilds:
3801 * @buf: an output buffer
3802 * @content: An element
3803 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3804 *
3805 * This will dump the list of childs to the buffer
3806 * Intended just for the debug routine
3807 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003808static void
Owen Taylor3473f882001-02-23 17:55:21 +00003809xmlSprintfElementChilds(char *buf, xmlNodePtr node, int glob) {
3810 xmlNodePtr cur;
3811
3812 if (node == NULL) return;
3813 if (glob) strcat(buf, "(");
3814 cur = node->children;
3815 while (cur != NULL) {
3816 switch (cur->type) {
3817 case XML_ELEMENT_NODE:
3818 strcat(buf, (char *) cur->name);
3819 if (cur->next != NULL)
3820 strcat(buf, " ");
3821 break;
3822 case XML_TEXT_NODE:
3823 if (xmlIsBlankNode(cur))
3824 break;
3825 case XML_CDATA_SECTION_NODE:
3826 case XML_ENTITY_REF_NODE:
3827 strcat(buf, "CDATA");
3828 if (cur->next != NULL)
3829 strcat(buf, " ");
3830 break;
3831 case XML_ATTRIBUTE_NODE:
3832 case XML_DOCUMENT_NODE:
3833#ifdef LIBXML_SGML_ENABLED
3834 case XML_SGML_DOCUMENT_NODE:
3835#endif
3836 case XML_HTML_DOCUMENT_NODE:
3837 case XML_DOCUMENT_TYPE_NODE:
3838 case XML_DOCUMENT_FRAG_NODE:
3839 case XML_NOTATION_NODE:
3840 case XML_NAMESPACE_DECL:
3841 strcat(buf, "???");
3842 if (cur->next != NULL)
3843 strcat(buf, " ");
3844 break;
3845 case XML_ENTITY_NODE:
3846 case XML_PI_NODE:
3847 case XML_DTD_NODE:
3848 case XML_COMMENT_NODE:
3849 case XML_ELEMENT_DECL:
3850 case XML_ATTRIBUTE_DECL:
3851 case XML_ENTITY_DECL:
3852 case XML_XINCLUDE_START:
3853 case XML_XINCLUDE_END:
3854 break;
3855 }
3856 cur = cur->next;
3857 }
3858 if (glob) strcat(buf, ")");
3859}
3860
3861
3862/**
3863 * xmlValidateOneElement:
3864 * @ctxt: the validation context
3865 * @doc: a document instance
3866 * @elem: an element instance
3867 *
3868 * Try to validate a single element and it's attributes,
3869 * basically it does the following checks as described by the
3870 * XML-1.0 recommendation:
3871 * - [ VC: Element Valid ]
3872 * - [ VC: Required Attribute ]
3873 * Then call xmlValidateOneAttribute() for each attribute present.
3874 *
3875 * The ID/IDREF checkings are done separately
3876 *
3877 * returns 1 if valid or 0 otherwise
3878 */
3879
3880int
3881xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3882 xmlNodePtr elem) {
3883 xmlElementPtr elemDecl = NULL;
3884 xmlElementContentPtr cont;
3885 xmlAttributePtr attr;
3886 xmlNodePtr child;
3887 int ret = 1;
3888 const xmlChar *name;
3889
3890 CHECK_DTD;
3891
3892 if (elem == NULL) return(0);
3893 if (elem->type == XML_TEXT_NODE) {
3894 }
3895 switch (elem->type) {
3896 case XML_ATTRIBUTE_NODE:
3897 VERROR(ctxt->userData,
3898 "Attribute element not expected here\n");
3899 return(0);
3900 case XML_TEXT_NODE:
3901 if (elem->children != NULL) {
3902 VERROR(ctxt->userData, "Text element has childs !\n");
3903 return(0);
3904 }
3905 if (elem->properties != NULL) {
3906 VERROR(ctxt->userData, "Text element has attributes !\n");
3907 return(0);
3908 }
3909 if (elem->ns != NULL) {
3910 VERROR(ctxt->userData, "Text element has namespace !\n");
3911 return(0);
3912 }
3913 if (elem->nsDef != NULL) {
3914 VERROR(ctxt->userData,
3915 "Text element carries namespace definitions !\n");
3916 return(0);
3917 }
3918 if (elem->content == NULL) {
3919 VERROR(ctxt->userData,
3920 "Text element has no content !\n");
3921 return(0);
3922 }
3923 return(1);
3924 case XML_XINCLUDE_START:
3925 case XML_XINCLUDE_END:
3926 return(1);
3927 case XML_CDATA_SECTION_NODE:
3928 case XML_ENTITY_REF_NODE:
3929 case XML_PI_NODE:
3930 case XML_COMMENT_NODE:
3931 return(1);
3932 case XML_ENTITY_NODE:
3933 VERROR(ctxt->userData,
3934 "Entity element not expected here\n");
3935 return(0);
3936 case XML_NOTATION_NODE:
3937 VERROR(ctxt->userData,
3938 "Notation element not expected here\n");
3939 return(0);
3940 case XML_DOCUMENT_NODE:
3941 case XML_DOCUMENT_TYPE_NODE:
3942 case XML_DOCUMENT_FRAG_NODE:
3943 VERROR(ctxt->userData,
3944 "Document element not expected here\n");
3945 return(0);
3946 case XML_HTML_DOCUMENT_NODE:
3947 VERROR(ctxt->userData,
3948 "\n");
3949 return(0);
3950 case XML_ELEMENT_NODE:
3951 break;
3952 default:
3953 VERROR(ctxt->userData,
3954 "unknown element type %d\n", elem->type);
3955 return(0);
3956 }
3957 if (elem->name == NULL) return(0);
3958
3959 /*
3960 * Fetch the declaration for the qualified name
3961 */
3962 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3963 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
3964 elem->name, elem->ns->prefix);
3965 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3966 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
3967 elem->name, elem->ns->prefix);
3968 }
3969
3970 /*
3971 * Fetch the declaration for the non qualified name
3972 */
3973 if (elemDecl == NULL) {
3974 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
3975 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3976 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
3977 }
3978 if (elemDecl == NULL) {
3979 VERROR(ctxt->userData, "No declaration for element %s\n",
3980 elem->name);
3981 return(0);
3982 }
3983
3984 /* Check taht the element content matches the definition */
3985 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003986 case XML_ELEMENT_TYPE_UNDEFINED:
3987 VERROR(ctxt->userData, "No declaration for element %s\n",
3988 elem->name);
3989 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003990 case XML_ELEMENT_TYPE_EMPTY:
3991 if (elem->children != NULL) {
3992 VERROR(ctxt->userData,
3993 "Element %s was declared EMPTY this one has content\n",
3994 elem->name);
3995 ret = 0;
3996 }
3997 break;
3998 case XML_ELEMENT_TYPE_ANY:
3999 /* I don't think anything is required then */
4000 break;
4001 case XML_ELEMENT_TYPE_MIXED:
4002 /* Hum, this start to get messy */
4003 child = elem->children;
4004 while (child != NULL) {
4005 if (child->type == XML_ELEMENT_NODE) {
4006 name = child->name;
4007 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4008 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004009 snprintf((char *) qname, sizeof(qname), "%s:%s",
4010 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004011 qname[sizeof(qname) - 1] = 0;
4012 cont = elemDecl->content;
4013 while (cont != NULL) {
4014 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4015 if (xmlStrEqual(cont->name, qname)) break;
4016 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4017 (cont->c1 != NULL) &&
4018 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4019 if (xmlStrEqual(cont->c1->name, qname)) break;
4020 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4021 (cont->c1 == NULL) ||
4022 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4023 /* Internal error !!! */
4024 xmlGenericError(xmlGenericErrorContext,
4025 "Internal: MIXED struct bad\n");
4026 break;
4027 }
4028 cont = cont->c2;
4029 }
4030 if (cont != NULL)
4031 goto child_ok;
4032 }
4033 cont = elemDecl->content;
4034 while (cont != NULL) {
4035 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4036 if (xmlStrEqual(cont->name, name)) break;
4037 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4038 (cont->c1 != NULL) &&
4039 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4040 if (xmlStrEqual(cont->c1->name, name)) break;
4041 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4042 (cont->c1 == NULL) ||
4043 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4044 /* Internal error !!! */
4045 xmlGenericError(xmlGenericErrorContext,
4046 "Internal: MIXED struct bad\n");
4047 break;
4048 }
4049 cont = cont->c2;
4050 }
4051 if (cont == NULL) {
4052 VERROR(ctxt->userData,
4053 "Element %s is not declared in %s list of possible childs\n",
4054 name, elem->name);
4055 ret = 0;
4056 }
4057 }
4058child_ok:
4059 child = child->next;
4060 }
4061 break;
4062 case XML_ELEMENT_TYPE_ELEMENT:
4063 child = elem->children;
4064 cont = elemDecl->content;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004065 ret = xmlValidateElementContent(ctxt, child, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00004066 break;
4067 }
4068
4069 /* [ VC: Required Attribute ] */
4070 attr = elemDecl->attributes;
4071 while (attr != NULL) {
4072 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
4073 xmlAttrPtr attrib;
4074 int qualified = -1;
4075
4076 attrib = elem->properties;
4077 while (attrib != NULL) {
4078 if (xmlStrEqual(attrib->name, attr->name)) {
4079 if (attr->prefix != NULL) {
4080 xmlNsPtr nameSpace = attrib->ns;
4081
4082 if (nameSpace == NULL)
4083 nameSpace = elem->ns;
4084 /*
4085 * qualified names handling is problematic, having a
4086 * different prefix should be possible but DTDs don't
4087 * allow to define the URI instead of the prefix :-(
4088 */
4089 if (nameSpace == NULL) {
4090 if (qualified < 0)
4091 qualified = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004092 } else if (!xmlStrEqual(nameSpace->prefix,
4093 attr->prefix)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004094 if (qualified < 1)
4095 qualified = 1;
4096 } else
4097 goto found;
4098 } else {
4099 /*
4100 * We should allow applications to define namespaces
4101 * for their application even if the DTD doesn't
4102 * carry one, otherwise, basically we would always
4103 * break.
4104 */
4105 goto found;
4106 }
4107 }
4108 attrib = attrib->next;
4109 }
4110 if (qualified == -1) {
4111 if (attr->prefix == NULL) {
4112 VERROR(ctxt->userData,
4113 "Element %s doesn't carry attribute %s\n",
4114 elem->name, attr->name);
4115 ret = 0;
4116 } else {
4117 VERROR(ctxt->userData,
4118 "Element %s doesn't carry attribute %s:%s\n",
4119 elem->name, attr->prefix,attr->name);
4120 ret = 0;
4121 }
4122 } else if (qualified == 0) {
4123 VWARNING(ctxt->userData,
4124 "Element %s required attribute %s:%s has no prefix\n",
4125 elem->name, attr->prefix,attr->name);
4126 } else if (qualified == 1) {
4127 VWARNING(ctxt->userData,
4128 "Element %s required attribute %s:%s has different prefix\n",
4129 elem->name, attr->prefix,attr->name);
4130 }
4131 }
4132found:
4133 attr = attr->nexth;
4134 }
4135 return(ret);
4136}
4137
4138/**
4139 * xmlValidateRoot:
4140 * @ctxt: the validation context
4141 * @doc: a document instance
4142 *
4143 * Try to validate a the root element
4144 * basically it does the following check as described by the
4145 * XML-1.0 recommendation:
4146 * - [ VC: Root Element Type ]
4147 * it doesn't try to recurse or apply other check to the element
4148 *
4149 * returns 1 if valid or 0 otherwise
4150 */
4151
4152int
4153xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4154 xmlNodePtr root;
4155 if (doc == NULL) return(0);
4156
4157 root = xmlDocGetRootElement(doc);
4158 if ((root == NULL) || (root->name == NULL)) {
4159 VERROR(ctxt->userData, "Not valid: no root element\n");
4160 return(0);
4161 }
4162
4163 /*
4164 * When doing post validation against a separate DTD, those may
4165 * no internal subset has been generated
4166 */
4167 if ((doc->intSubset != NULL) &&
4168 (doc->intSubset->name != NULL)) {
4169 /*
4170 * Check first the document root against the NQName
4171 */
4172 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4173 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4174 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004175 snprintf((char *) qname, sizeof(qname), "%s:%s",
4176 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004177 qname[sizeof(qname) - 1] = 0;
4178 if (xmlStrEqual(doc->intSubset->name, qname))
4179 goto name_ok;
4180 }
4181 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4182 (xmlStrEqual(root->name, BAD_CAST "html")))
4183 goto name_ok;
4184 VERROR(ctxt->userData,
4185 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4186 root->name, doc->intSubset->name);
4187 return(0);
4188
4189 }
4190 }
4191name_ok:
4192 return(1);
4193}
4194
4195
4196/**
4197 * xmlValidateElement:
4198 * @ctxt: the validation context
4199 * @doc: a document instance
4200 * @elem: an element instance
4201 *
4202 * Try to validate the subtree under an element
4203 *
4204 * returns 1 if valid or 0 otherwise
4205 */
4206
4207int
4208xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4209 xmlNodePtr child;
4210 xmlAttrPtr attr;
4211 xmlChar *value;
4212 int ret = 1;
4213
4214 if (elem == NULL) return(0);
4215
4216 /*
4217 * XInclude elements were added after parsing in the infoset,
4218 * they don't really mean anything validation wise.
4219 */
4220 if ((elem->type == XML_XINCLUDE_START) ||
4221 (elem->type == XML_XINCLUDE_END))
4222 return(1);
4223
4224 CHECK_DTD;
4225
4226 ret &= xmlValidateOneElement(ctxt, doc, elem);
4227 attr = elem->properties;
4228 while(attr != NULL) {
4229 value = xmlNodeListGetString(doc, attr->children, 0);
4230 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4231 if (value != NULL)
4232 xmlFree(value);
4233 attr= attr->next;
4234 }
4235 child = elem->children;
4236 while (child != NULL) {
4237 ret &= xmlValidateElement(ctxt, doc, child);
4238 child = child->next;
4239 }
4240
4241 return(ret);
4242}
4243
Daniel Veillard8730c562001-02-26 10:49:57 +00004244/**
4245 * xmlValidateRef:
4246 * @ref: A reference to be validated
4247 * @ctxt: Validation context
4248 * @name: Name of ID we are searching for
4249 *
4250 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004251static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004252xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004253 const xmlChar *name) {
4254 xmlAttrPtr id;
4255 xmlAttrPtr attr;
4256
4257 if (ref == NULL)
4258 return;
4259 attr = ref->attr;
4260 if (attr == NULL)
4261 return;
4262 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4263 id = xmlGetID(ctxt->doc, name);
4264 if (id == NULL) {
4265 VERROR(ctxt->userData,
4266 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4267 attr->name, name);
4268 ctxt->valid = 0;
4269 }
4270 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4271 xmlChar *dup, *str = NULL, *cur, save;
4272
4273 dup = xmlStrdup(name);
4274 if (dup == NULL) {
4275 ctxt->valid = 0;
4276 return;
4277 }
4278 cur = dup;
4279 while (*cur != 0) {
4280 str = cur;
4281 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4282 save = *cur;
4283 *cur = 0;
4284 id = xmlGetID(ctxt->doc, str);
4285 if (id == NULL) {
4286 VERROR(ctxt->userData,
4287 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4288 attr->name, str);
4289 ctxt->valid = 0;
4290 }
4291 if (save == 0)
4292 break;
4293 *cur = save;
4294 while (IS_BLANK(*cur)) cur++;
4295 }
4296 xmlFree(dup);
4297 }
4298}
4299
4300/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004301 * xmlWalkValidateList:
4302 * @data: Contents of current link
4303 * @user: Value supplied by the user
4304 *
4305 * Return 0 to abort the walk or 1 to continue
4306 */
4307static int
4308xmlWalkValidateList(const void *data, const void *user)
4309{
4310 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4311 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4312 return 1;
4313}
4314
4315/**
4316 * xmlValidateCheckRefCallback:
4317 * @ref_list: List of references
4318 * @ctxt: Validation context
4319 * @name: Name of ID we are searching for
4320 *
4321 */
4322static void
4323xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4324 const xmlChar *name) {
4325 xmlValidateMemo memo;
4326
4327 if (ref_list == NULL)
4328 return;
4329 memo.ctxt = ctxt;
4330 memo.name = name;
4331
4332 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4333
4334}
4335
4336/**
Owen Taylor3473f882001-02-23 17:55:21 +00004337 * xmlValidateDocumentFinal:
4338 * @ctxt: the validation context
4339 * @doc: a document instance
4340 *
4341 * Does the final step for the document validation once all the
4342 * incremental validation steps have been completed
4343 *
4344 * basically it does the following checks described by the XML Rec
4345 *
4346 *
4347 * returns 1 if valid or 0 otherwise
4348 */
4349
4350int
4351xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4352 xmlRefTablePtr table;
4353
4354 if (doc == NULL) {
4355 xmlGenericError(xmlGenericErrorContext,
4356 "xmlValidateDocumentFinal: doc == NULL\n");
4357 return(0);
4358 }
4359
4360 /*
4361 * Check all the NOTATION/NOTATIONS attributes
4362 */
4363 /*
4364 * Check all the ENTITY/ENTITIES attributes definition for validity
4365 */
4366 /*
4367 * Check all the IDREF/IDREFS attributes definition for validity
4368 */
4369 table = (xmlRefTablePtr) doc->refs;
4370 ctxt->doc = doc;
4371 ctxt->valid = 1;
4372 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4373 return(ctxt->valid);
4374}
4375
4376/**
4377 * xmlValidateDtd:
4378 * @ctxt: the validation context
4379 * @doc: a document instance
4380 * @dtd: a dtd instance
4381 *
4382 * Try to validate the document against the dtd instance
4383 *
4384 * basically it does check all the definitions in the DtD.
4385 *
4386 * returns 1 if valid or 0 otherwise
4387 */
4388
4389int
4390xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4391 int ret;
4392 xmlDtdPtr oldExt;
4393 xmlNodePtr root;
4394
4395 if (dtd == NULL) return(0);
4396 if (doc == NULL) return(0);
4397 oldExt = doc->extSubset;
4398 doc->extSubset = dtd;
4399 ret = xmlValidateRoot(ctxt, doc);
4400 if (ret == 0) {
4401 doc->extSubset = oldExt;
4402 return(ret);
4403 }
4404 if (doc->ids != NULL) {
4405 xmlFreeIDTable(doc->ids);
4406 doc->ids = NULL;
4407 }
4408 if (doc->refs != NULL) {
4409 xmlFreeRefTable(doc->refs);
4410 doc->refs = NULL;
4411 }
4412 root = xmlDocGetRootElement(doc);
4413 ret = xmlValidateElement(ctxt, doc, root);
4414 ret &= xmlValidateDocumentFinal(ctxt, doc);
4415 doc->extSubset = oldExt;
4416 return(ret);
4417}
4418
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004419static void
Owen Taylor3473f882001-02-23 17:55:21 +00004420xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004421 const xmlChar *name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004422 if (cur == NULL)
4423 return;
4424 switch (cur->atype) {
4425 case XML_ATTRIBUTE_CDATA:
4426 case XML_ATTRIBUTE_ID:
4427 case XML_ATTRIBUTE_IDREF :
4428 case XML_ATTRIBUTE_IDREFS:
4429 case XML_ATTRIBUTE_NMTOKEN:
4430 case XML_ATTRIBUTE_NMTOKENS:
4431 case XML_ATTRIBUTE_ENUMERATION:
4432 break;
4433 case XML_ATTRIBUTE_ENTITY:
4434 case XML_ATTRIBUTE_ENTITIES:
4435 case XML_ATTRIBUTE_NOTATION:
4436 if (cur->defaultValue != NULL) {
4437 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4438 cur->name, cur->atype, cur->defaultValue);
4439 }
4440 if (cur->tree != NULL) {
4441 xmlEnumerationPtr tree = cur->tree;
4442 while (tree != NULL) {
4443 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4444 cur->name, cur->atype, tree->name);
4445 tree = tree->next;
4446 }
4447 }
4448 }
4449}
4450
4451/**
4452 * xmlValidateDtdFinal:
4453 * @ctxt: the validation context
4454 * @doc: a document instance
4455 *
4456 * Does the final step for the dtds validation once all the
4457 * subsets have been parsed
4458 *
4459 * basically it does the following checks described by the XML Rec
4460 * - check that ENTITY and ENTITIES type attributes default or
4461 * possible values matches one of the defined entities.
4462 * - check that NOTATION type attributes default or
4463 * possible values matches one of the defined notations.
4464 *
4465 * returns 1 if valid or 0 otherwise
4466 */
4467
4468int
4469xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4470 int ret = 1;
4471 xmlDtdPtr dtd;
4472 xmlAttributeTablePtr table;
4473
4474 if (doc == NULL) return(0);
4475 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4476 return(0);
4477 ctxt->doc = doc;
4478 ctxt->valid = ret;
4479 dtd = doc->intSubset;
4480 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4481 table = (xmlAttributeTablePtr) dtd->attributes;
4482 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4483 }
4484 dtd = doc->extSubset;
4485 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4486 table = (xmlAttributeTablePtr) dtd->attributes;
4487 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4488 }
4489 return(ctxt->valid);
4490}
4491
4492/**
4493 * xmlValidateDocument:
4494 * @ctxt: the validation context
4495 * @doc: a document instance
4496 *
4497 * Try to validate the document instance
4498 *
4499 * basically it does the all the checks described by the XML Rec
4500 * i.e. validates the internal and external subset (if present)
4501 * and validate the document tree.
4502 *
4503 * returns 1 if valid or 0 otherwise
4504 */
4505
4506int
4507xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4508 int ret;
4509 xmlNodePtr root;
4510
4511 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4512 return(0);
4513 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4514 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4515 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4516 doc->intSubset->SystemID);
4517 if (doc->extSubset == NULL) {
4518 if (doc->intSubset->SystemID != NULL) {
4519 VERROR(ctxt->userData,
4520 "Could not load the external subset \"%s\"\n",
4521 doc->intSubset->SystemID);
4522 } else {
4523 VERROR(ctxt->userData,
4524 "Could not load the external subset \"%s\"\n",
4525 doc->intSubset->ExternalID);
4526 }
4527 return(0);
4528 }
4529 }
4530
4531 if (doc->ids != NULL) {
4532 xmlFreeIDTable(doc->ids);
4533 doc->ids = NULL;
4534 }
4535 if (doc->refs != NULL) {
4536 xmlFreeRefTable(doc->refs);
4537 doc->refs = NULL;
4538 }
4539 ret = xmlValidateDtdFinal(ctxt, doc);
4540 if (!xmlValidateRoot(ctxt, doc)) return(0);
4541
4542 root = xmlDocGetRootElement(doc);
4543 ret &= xmlValidateElement(ctxt, doc, root);
4544 ret &= xmlValidateDocumentFinal(ctxt, doc);
4545 return(ret);
4546}
4547
4548
4549/************************************************************************
4550 * *
4551 * Routines for dynamic validation editing *
4552 * *
4553 ************************************************************************/
4554
4555/**
4556 * xmlValidGetPotentialChildren:
4557 * @ctree: an element content tree
4558 * @list: an array to store the list of child names
4559 * @len: a pointer to the number of element in the list
4560 * @max: the size of the array
4561 *
4562 * Build/extend a list of potential children allowed by the content tree
4563 *
4564 * returns the number of element in the list, or -1 in case of error.
4565 */
4566
4567int
4568xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4569 int *len, int max) {
4570 int i;
4571
4572 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4573 return(-1);
4574 if (*len >= max) return(*len);
4575
4576 switch (ctree->type) {
4577 case XML_ELEMENT_CONTENT_PCDATA:
4578 for (i = 0; i < *len;i++)
4579 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4580 list[(*len)++] = BAD_CAST "#PCDATA";
4581 break;
4582 case XML_ELEMENT_CONTENT_ELEMENT:
4583 for (i = 0; i < *len;i++)
4584 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4585 list[(*len)++] = ctree->name;
4586 break;
4587 case XML_ELEMENT_CONTENT_SEQ:
4588 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4589 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4590 break;
4591 case XML_ELEMENT_CONTENT_OR:
4592 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4593 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4594 break;
4595 }
4596
4597 return(*len);
4598}
4599
4600/**
4601 * xmlValidGetValidElements:
4602 * @prev: an element to insert after
4603 * @next: an element to insert next
4604 * @list: an array to store the list of child names
4605 * @max: the size of the array
4606 *
4607 * This function returns the list of authorized children to insert
4608 * within an existing tree while respecting the validity constraints
4609 * forced by the Dtd. The insertion point is defined using @prev and
4610 * @next in the following ways:
4611 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4612 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4613 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4614 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4615 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4616 *
4617 * pointers to the element names are inserted at the beginning of the array
4618 * and do not need to be freed.
4619 *
4620 * returns the number of element in the list, or -1 in case of error. If
4621 * the function returns the value @max the caller is invited to grow the
4622 * receiving array and retry.
4623 */
4624
4625int
4626xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4627 int max) {
4628 int nb_valid_elements = 0;
4629 const xmlChar *elements[256];
4630 int nb_elements = 0, i;
4631
4632 xmlNode *ref_node;
4633 xmlNode *parent;
4634 xmlNode *test_node;
4635
4636 xmlNode *prev_next;
4637 xmlNode *next_prev;
4638 xmlNode *parent_childs;
4639 xmlNode *parent_last;
4640
4641 xmlElement *element_desc;
4642
4643 if (prev == NULL && next == NULL)
4644 return(-1);
4645
4646 if (list == NULL) return(-1);
4647 if (max <= 0) return(-1);
4648
4649 nb_valid_elements = 0;
4650 ref_node = prev ? prev : next;
4651 parent = ref_node->parent;
4652
4653 /*
4654 * Retrieves the parent element declaration
4655 */
4656 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
4657 parent->name);
4658 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
4659 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
4660 parent->name);
4661 if (element_desc == NULL) return(-1);
4662
4663 /*
4664 * Do a backup of the current tree structure
4665 */
4666 prev_next = prev ? prev->next : NULL;
4667 next_prev = next ? next->prev : NULL;
4668 parent_childs = parent->children;
4669 parent_last = parent->last;
4670
4671 /*
4672 * Creates a dummy node and insert it into the tree
4673 */
4674 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
4675 test_node->doc = ref_node->doc;
4676 test_node->parent = parent;
4677 test_node->prev = prev;
4678 test_node->next = next;
4679
4680 if (prev) prev->next = test_node;
4681 else parent->children = test_node;
4682
4683 if (next) next->prev = test_node;
4684 else parent->last = test_node;
4685
4686 /*
4687 * Insert each potential child node and check if the parent is
4688 * still valid
4689 */
4690 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
4691 elements, &nb_elements, 256);
4692
4693 for (i = 0;i < nb_elements;i++) {
4694 test_node->name = elements[i];
4695 if (xmlValidateOneElement(NULL, parent->doc, parent)) {
4696 int j;
4697
4698 for (j = 0; j < nb_valid_elements;j++)
4699 if (xmlStrEqual(elements[i], list[j])) break;
4700 list[nb_valid_elements++] = elements[i];
4701 if (nb_valid_elements >= max) break;
4702 }
4703 }
4704
4705 /*
4706 * Restore the tree structure
4707 */
4708 if (prev) prev->next = prev_next;
4709 if (next) next->prev = next_prev;
4710 parent->children = parent_childs;
4711 parent->last = parent_last;
4712
4713 return(nb_valid_elements);
4714}