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