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