blob: 7393d1e16aa846fa95ff18ec0c5bbcfc1ac8a1f8 [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) {
Daniel Veillard37721922001-05-04 15:21:12 +00001904 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
1905 if (ref == NULL) return;
1906 if (ref->value != NULL)
1907 xmlFree((xmlChar *)ref->value);
1908 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00001909}
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) {
Daniel Veillard37721922001-05-04 15:21:12 +00001919 if (list_ref == NULL) return;
1920 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00001921}
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{
Daniel Veillard37721922001-05-04 15:21:12 +00001933 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
1934 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
1935 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00001936
Daniel Veillard37721922001-05-04 15:21:12 +00001937 if (attr0 == attr1) { /* Matched: remove and terminate walk */
1938 xmlListRemoveFirst(ref_list, (void *)data);
1939 return 0;
1940 }
1941 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00001942}
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) {
Daniel Veillard37721922001-05-04 15:21:12 +00001958 xmlRefPtr ret;
1959 xmlRefTablePtr table;
1960 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00001961
Daniel Veillard37721922001-05-04 15:21:12 +00001962 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 }
Owen Taylor3473f882001-02-23 17:55:21 +00001977
Daniel Veillard37721922001-05-04 15:21:12 +00001978 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001979 * Create the Ref table if needed.
1980 */
Daniel Veillard37721922001-05-04 15:21:12 +00001981 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 }
Owen Taylor3473f882001-02-23 17:55:21 +00001989
Daniel Veillard37721922001-05-04 15:21:12 +00001990 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
1991 if (ret == NULL) {
1992 xmlGenericError(xmlGenericErrorContext,
1993 "xmlAddRef: out of memory\n");
1994 return(NULL);
1995 }
Owen Taylor3473f882001-02-23 17:55:21 +00001996
Daniel Veillard37721922001-05-04 15:21:12 +00001997 /*
Owen Taylor3473f882001-02-23 17:55:21 +00001998 * fill the structure.
1999 */
Daniel Veillard37721922001-05-04 15:21:12 +00002000 ret->value = xmlStrdup(value);
2001 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002002
Daniel Veillard37721922001-05-04 15:21:12 +00002003 /* 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 */
Owen Taylor3473f882001-02-23 17:55:21 +00002009
Daniel Veillard37721922001-05-04 15:21:12 +00002010 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);
Owen Taylor3473f882001-02-23 17:55:21 +00002025}
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) {
Daniel Veillard37721922001-05-04 15:21:12 +00002035 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002036}
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) {
Daniel Veillard37721922001-05-04 15:21:12 +00002052 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;
Owen Taylor3473f882001-02-23 17:55:21 +00002059
Daniel Veillard37721922001-05-04 15:21:12 +00002060 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2061 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2062 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2063 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002064
Daniel Veillard37721922001-05-04 15:21:12 +00002065 if ((attrDecl != NULL) &&
2066 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2067 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2068 return(1);
2069 }
2070 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002071}
2072
2073/**
2074 * xmlRemoveRef
2075 * @doc: the document
2076 * @attr: the attribute
2077 *
2078 * Remove the given attribute from the Ref table maintained internally.
2079 *
2080 * Returns -1 if the lookup failed and 0 otherwise
2081 */
2082int
2083xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002084 xmlListPtr ref_list;
2085 xmlRefTablePtr table;
2086 xmlChar *ID;
2087 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002088
Daniel Veillard37721922001-05-04 15:21:12 +00002089 if (doc == NULL) return(-1);
2090 if (attr == NULL) return(-1);
2091 table = (xmlRefTablePtr) doc->refs;
2092 if (table == NULL)
2093 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002094
Daniel Veillard37721922001-05-04 15:21:12 +00002095 if (attr == NULL)
2096 return(-1);
2097 ID = xmlNodeListGetString(doc, attr->children, 1);
2098 if (ID == NULL)
2099 return(-1);
2100 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002101
Daniel Veillard37721922001-05-04 15:21:12 +00002102 if(ref_list == NULL) {
2103 xmlFree(ID);
2104 return (-1);
2105 }
2106 /* At this point, ref_list refers to a list of references which
2107 * have the same key as the supplied attr. Our list of references
2108 * is ordered by reference address and we don't have that information
2109 * here to use when removing. We'll have to walk the list and
2110 * check for a matching attribute, when we find one stop the walk
2111 * and remove the entry.
2112 * The list is ordered by reference, so that means we don't have the
2113 * key. Passing the list and the reference to the walker means we
2114 * will have enough data to be able to remove the entry.
2115 */
2116 target.l = ref_list;
2117 target.ap = attr;
2118
2119 /* Remove the supplied attr from our list */
2120 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002121
Daniel Veillard37721922001-05-04 15:21:12 +00002122 /*If the list is empty then remove the list entry in the hash */
2123 if (xmlListEmpty(ref_list))
2124 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2125 xmlFreeRefList);
2126 xmlFree(ID);
2127 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002128}
2129
2130/**
2131 * xmlGetRefs:
2132 * @doc: pointer to the document
2133 * @ID: the ID value
2134 *
2135 * Find the set of references for the supplied ID.
2136 *
2137 * Returns NULL if not found, otherwise node set for the ID.
2138 */
2139xmlListPtr
2140xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002141 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002142
Daniel Veillard37721922001-05-04 15:21:12 +00002143 if (doc == NULL) {
2144 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: doc == NULL\n");
2145 return(NULL);
2146 }
Owen Taylor3473f882001-02-23 17:55:21 +00002147
Daniel Veillard37721922001-05-04 15:21:12 +00002148 if (ID == NULL) {
2149 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: ID == NULL\n");
2150 return(NULL);
2151 }
Owen Taylor3473f882001-02-23 17:55:21 +00002152
Daniel Veillard37721922001-05-04 15:21:12 +00002153 table = (xmlRefTablePtr) doc->refs;
2154 if (table == NULL)
2155 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002156
Daniel Veillard37721922001-05-04 15:21:12 +00002157 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002158}
2159
2160/************************************************************************
2161 * *
2162 * Routines for validity checking *
2163 * *
2164 ************************************************************************/
2165
2166/**
2167 * xmlGetDtdElementDesc:
2168 * @dtd: a pointer to the DtD to search
2169 * @name: the element name
2170 *
2171 * Search the Dtd for the description of this element
2172 *
2173 * returns the xmlElementPtr if found or NULL
2174 */
2175
2176xmlElementPtr
2177xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2178 xmlElementTablePtr table;
2179 xmlElementPtr cur;
2180 xmlChar *uqname = NULL, *prefix = NULL;
2181
2182 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002183 if (dtd->elements == NULL)
2184 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002185 table = (xmlElementTablePtr) dtd->elements;
2186
2187 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002188 if (uqname != NULL)
2189 name = uqname;
2190 cur = xmlHashLookup2(table, name, prefix);
2191 if (prefix != NULL) xmlFree(prefix);
2192 if (uqname != NULL) xmlFree(uqname);
2193 return(cur);
2194}
2195/**
2196 * xmlGetDtdElementDesc2:
2197 * @dtd: a pointer to the DtD to search
2198 * @name: the element name
2199 * @create: create an empty description if not found
2200 *
2201 * Search the Dtd for the description of this element
2202 *
2203 * returns the xmlElementPtr if found or NULL
2204 */
2205
2206xmlElementPtr
2207xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2208 xmlElementTablePtr table;
2209 xmlElementPtr cur;
2210 xmlChar *uqname = NULL, *prefix = NULL;
2211
2212 if (dtd == NULL) return(NULL);
2213 if (dtd->elements == NULL) {
2214 if (!create)
2215 return(NULL);
2216 /*
2217 * Create the Element table if needed.
2218 */
2219 table = (xmlElementTablePtr) dtd->elements;
2220 if (table == NULL) {
2221 table = xmlCreateElementTable();
2222 dtd->elements = (void *) table;
2223 }
2224 if (table == NULL) {
2225 xmlGenericError(xmlGenericErrorContext,
2226 "xmlGetDtdElementDesc: Table creation failed!\n");
2227 return(NULL);
2228 }
2229 }
2230 table = (xmlElementTablePtr) dtd->elements;
2231
2232 uqname = xmlSplitQName2(name, &prefix);
2233 if (uqname != NULL)
2234 name = uqname;
2235 cur = xmlHashLookup2(table, name, prefix);
2236 if ((cur == NULL) && (create)) {
2237 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2238 if (cur == NULL) {
2239 xmlGenericError(xmlGenericErrorContext,
2240 "xmlGetDtdElementDesc: out of memory\n");
2241 return(NULL);
2242 }
2243 memset(cur, 0, sizeof(xmlElement));
2244 cur->type = XML_ELEMENT_DECL;
2245
2246 /*
2247 * fill the structure.
2248 */
2249 cur->name = xmlStrdup(name);
2250 cur->prefix = xmlStrdup(prefix);
2251 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2252
2253 xmlHashAddEntry2(table, name, prefix, cur);
2254 }
2255 if (prefix != NULL) xmlFree(prefix);
2256 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002257 return(cur);
2258}
2259
2260/**
2261 * xmlGetDtdQElementDesc:
2262 * @dtd: a pointer to the DtD to search
2263 * @name: the element name
2264 * @prefix: the element namespace prefix
2265 *
2266 * Search the Dtd for the description of this element
2267 *
2268 * returns the xmlElementPtr if found or NULL
2269 */
2270
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002271static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002272xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2273 const xmlChar *prefix) {
2274 xmlElementTablePtr table;
2275
2276 if (dtd == NULL) return(NULL);
2277 if (dtd->elements == NULL) return(NULL);
2278 table = (xmlElementTablePtr) dtd->elements;
2279
2280 return(xmlHashLookup2(table, name, prefix));
2281}
2282
2283/**
2284 * xmlGetDtdAttrDesc:
2285 * @dtd: a pointer to the DtD to search
2286 * @elem: the element name
2287 * @name: the attribute name
2288 *
2289 * Search the Dtd for the description of this attribute on
2290 * this element.
2291 *
2292 * returns the xmlAttributePtr if found or NULL
2293 */
2294
2295xmlAttributePtr
2296xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2297 xmlAttributeTablePtr table;
2298 xmlAttributePtr cur;
2299 xmlChar *uqname = NULL, *prefix = NULL;
2300
2301 if (dtd == NULL) return(NULL);
2302 if (dtd->attributes == NULL) return(NULL);
2303
2304 table = (xmlAttributeTablePtr) dtd->attributes;
2305 if (table == NULL)
2306 return(NULL);
2307
2308 uqname = xmlSplitQName2(name, &prefix);
2309
2310 if (uqname != NULL) {
2311 cur = xmlHashLookup3(table, uqname, prefix, elem);
2312 if (prefix != NULL) xmlFree(prefix);
2313 if (uqname != NULL) xmlFree(uqname);
2314 } else
2315 cur = xmlHashLookup3(table, name, NULL, elem);
2316 return(cur);
2317}
2318
2319/**
2320 * xmlGetDtdQAttrDesc:
2321 * @dtd: a pointer to the DtD to search
2322 * @elem: the element name
2323 * @name: the attribute name
2324 * @prefix: the attribute namespace prefix
2325 *
2326 * Search the Dtd for the description of this qualified attribute on
2327 * this element.
2328 *
2329 * returns the xmlAttributePtr if found or NULL
2330 */
2331
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002332static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002333xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2334 const xmlChar *prefix) {
2335 xmlAttributeTablePtr table;
2336
2337 if (dtd == NULL) return(NULL);
2338 if (dtd->attributes == NULL) return(NULL);
2339 table = (xmlAttributeTablePtr) dtd->attributes;
2340
2341 return(xmlHashLookup3(table, name, prefix, elem));
2342}
2343
2344/**
2345 * xmlGetDtdNotationDesc:
2346 * @dtd: a pointer to the DtD to search
2347 * @name: the notation name
2348 *
2349 * Search the Dtd for the description of this notation
2350 *
2351 * returns the xmlNotationPtr if found or NULL
2352 */
2353
2354xmlNotationPtr
2355xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2356 xmlNotationTablePtr table;
2357
2358 if (dtd == NULL) return(NULL);
2359 if (dtd->notations == NULL) return(NULL);
2360 table = (xmlNotationTablePtr) dtd->notations;
2361
2362 return(xmlHashLookup(table, name));
2363}
2364
2365/**
2366 * xmlValidateNotationUse:
2367 * @ctxt: the validation context
2368 * @doc: the document
2369 * @notationName: the notation name to check
2370 *
2371 * Validate that the given mame match a notation declaration.
2372 * - [ VC: Notation Declared ]
2373 *
2374 * returns 1 if valid or 0 otherwise
2375 */
2376
2377int
2378xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2379 const xmlChar *notationName) {
2380 xmlNotationPtr notaDecl;
2381 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2382
2383 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2384 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2385 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2386
2387 if (notaDecl == NULL) {
2388 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2389 notationName);
2390 return(0);
2391 }
2392 return(1);
2393}
2394
2395/**
2396 * xmlIsMixedElement
2397 * @doc: the document
2398 * @name: the element name
2399 *
2400 * Search in the DtDs whether an element accept Mixed content (or ANY)
2401 * basically if it is supposed to accept text childs
2402 *
2403 * returns 0 if no, 1 if yes, and -1 if no element description is available
2404 */
2405
2406int
2407xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2408 xmlElementPtr elemDecl;
2409
2410 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2411
2412 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2413 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2414 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2415 if (elemDecl == NULL) return(-1);
2416 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002417 case XML_ELEMENT_TYPE_UNDEFINED:
2418 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002419 case XML_ELEMENT_TYPE_ELEMENT:
2420 return(0);
2421 case XML_ELEMENT_TYPE_EMPTY:
2422 /*
2423 * return 1 for EMPTY since we want VC error to pop up
2424 * on <empty> </empty> for example
2425 */
2426 case XML_ELEMENT_TYPE_ANY:
2427 case XML_ELEMENT_TYPE_MIXED:
2428 return(1);
2429 }
2430 return(1);
2431}
2432
2433/**
2434 * xmlValidateNameValue:
2435 * @value: an Name value
2436 *
2437 * Validate that the given value match Name production
2438 *
2439 * returns 1 if valid or 0 otherwise
2440 */
2441
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002442static int
Owen Taylor3473f882001-02-23 17:55:21 +00002443xmlValidateNameValue(const xmlChar *value) {
2444 const xmlChar *cur;
2445
2446 if (value == NULL) return(0);
2447 cur = value;
2448
2449 if (!IS_LETTER(*cur) && (*cur != '_') &&
2450 (*cur != ':')) {
2451 return(0);
2452 }
2453
2454 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2455 (*cur == '.') || (*cur == '-') ||
2456 (*cur == '_') || (*cur == ':') ||
2457 (IS_COMBINING(*cur)) ||
2458 (IS_EXTENDER(*cur)))
2459 cur++;
2460
2461 if (*cur != 0) return(0);
2462
2463 return(1);
2464}
2465
2466/**
2467 * xmlValidateNamesValue:
2468 * @value: an Names value
2469 *
2470 * Validate that the given value match Names production
2471 *
2472 * returns 1 if valid or 0 otherwise
2473 */
2474
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002475static int
Owen Taylor3473f882001-02-23 17:55:21 +00002476xmlValidateNamesValue(const xmlChar *value) {
2477 const xmlChar *cur;
2478
2479 if (value == NULL) return(0);
2480 cur = value;
2481
2482 if (!IS_LETTER(*cur) && (*cur != '_') &&
2483 (*cur != ':')) {
2484 return(0);
2485 }
2486
2487 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2488 (*cur == '.') || (*cur == '-') ||
2489 (*cur == '_') || (*cur == ':') ||
2490 (IS_COMBINING(*cur)) ||
2491 (IS_EXTENDER(*cur)))
2492 cur++;
2493
2494 while (IS_BLANK(*cur)) {
2495 while (IS_BLANK(*cur)) cur++;
2496
2497 if (!IS_LETTER(*cur) && (*cur != '_') &&
2498 (*cur != ':')) {
2499 return(0);
2500 }
2501
2502 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2503 (*cur == '.') || (*cur == '-') ||
2504 (*cur == '_') || (*cur == ':') ||
2505 (IS_COMBINING(*cur)) ||
2506 (IS_EXTENDER(*cur)))
2507 cur++;
2508 }
2509
2510 if (*cur != 0) return(0);
2511
2512 return(1);
2513}
2514
2515/**
2516 * xmlValidateNmtokenValue:
2517 * @value: an Mntoken value
2518 *
2519 * Validate that the given value match Nmtoken production
2520 *
2521 * [ VC: Name Token ]
2522 *
2523 * returns 1 if valid or 0 otherwise
2524 */
2525
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002526static int
Owen Taylor3473f882001-02-23 17:55:21 +00002527xmlValidateNmtokenValue(const xmlChar *value) {
2528 const xmlChar *cur;
2529
2530 if (value == NULL) return(0);
2531 cur = value;
2532
2533 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2534 (*cur != '.') && (*cur != '-') &&
2535 (*cur != '_') && (*cur != ':') &&
2536 (!IS_COMBINING(*cur)) &&
2537 (!IS_EXTENDER(*cur)))
2538 return(0);
2539
2540 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2541 (*cur == '.') || (*cur == '-') ||
2542 (*cur == '_') || (*cur == ':') ||
2543 (IS_COMBINING(*cur)) ||
2544 (IS_EXTENDER(*cur)))
2545 cur++;
2546
2547 if (*cur != 0) return(0);
2548
2549 return(1);
2550}
2551
2552/**
2553 * xmlValidateNmtokensValue:
2554 * @value: an Mntokens value
2555 *
2556 * Validate that the given value match Nmtokens production
2557 *
2558 * [ VC: Name Token ]
2559 *
2560 * returns 1 if valid or 0 otherwise
2561 */
2562
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002563static int
Owen Taylor3473f882001-02-23 17:55:21 +00002564xmlValidateNmtokensValue(const xmlChar *value) {
2565 const xmlChar *cur;
2566
2567 if (value == NULL) return(0);
2568 cur = value;
2569
2570 while (IS_BLANK(*cur)) cur++;
2571 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2572 (*cur != '.') && (*cur != '-') &&
2573 (*cur != '_') && (*cur != ':') &&
2574 (!IS_COMBINING(*cur)) &&
2575 (!IS_EXTENDER(*cur)))
2576 return(0);
2577
2578 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2579 (*cur == '.') || (*cur == '-') ||
2580 (*cur == '_') || (*cur == ':') ||
2581 (IS_COMBINING(*cur)) ||
2582 (IS_EXTENDER(*cur)))
2583 cur++;
2584
2585 while (IS_BLANK(*cur)) {
2586 while (IS_BLANK(*cur)) cur++;
2587 if (*cur == 0) return(1);
2588
2589 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2590 (*cur != '.') && (*cur != '-') &&
2591 (*cur != '_') && (*cur != ':') &&
2592 (!IS_COMBINING(*cur)) &&
2593 (!IS_EXTENDER(*cur)))
2594 return(0);
2595
2596 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2597 (*cur == '.') || (*cur == '-') ||
2598 (*cur == '_') || (*cur == ':') ||
2599 (IS_COMBINING(*cur)) ||
2600 (IS_EXTENDER(*cur)))
2601 cur++;
2602 }
2603
2604 if (*cur != 0) return(0);
2605
2606 return(1);
2607}
2608
2609/**
2610 * xmlValidateNotationDecl:
2611 * @ctxt: the validation context
2612 * @doc: a document instance
2613 * @nota: a notation definition
2614 *
2615 * Try to validate a single notation definition
2616 * basically it does the following checks as described by the
2617 * XML-1.0 recommendation:
2618 * - it seems that no validity constraing exist on notation declarations
2619 * But this function get called anyway ...
2620 *
2621 * returns 1 if valid or 0 otherwise
2622 */
2623
2624int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002625xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2626 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002627 int ret = 1;
2628
2629 return(ret);
2630}
2631
2632/**
2633 * xmlValidateAttributeValue:
2634 * @type: an attribute type
2635 * @value: an attribute value
2636 *
2637 * Validate that the given attribute value match the proper production
2638 *
2639 * [ VC: ID ]
2640 * Values of type ID must match the Name production....
2641 *
2642 * [ VC: IDREF ]
2643 * Values of type IDREF must match the Name production, and values
2644 * of type IDREFS must match Names ...
2645 *
2646 * [ VC: Entity Name ]
2647 * Values of type ENTITY must match the Name production, values
2648 * of type ENTITIES must match Names ...
2649 *
2650 * [ VC: Name Token ]
2651 * Values of type NMTOKEN must match the Nmtoken production; values
2652 * of type NMTOKENS must match Nmtokens.
2653 *
2654 * returns 1 if valid or 0 otherwise
2655 */
2656
2657int
2658xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2659 switch (type) {
2660 case XML_ATTRIBUTE_ENTITIES:
2661 case XML_ATTRIBUTE_IDREFS:
2662 return(xmlValidateNamesValue(value));
2663 case XML_ATTRIBUTE_ENTITY:
2664 case XML_ATTRIBUTE_IDREF:
2665 case XML_ATTRIBUTE_ID:
2666 case XML_ATTRIBUTE_NOTATION:
2667 return(xmlValidateNameValue(value));
2668 case XML_ATTRIBUTE_NMTOKENS:
2669 case XML_ATTRIBUTE_ENUMERATION:
2670 return(xmlValidateNmtokensValue(value));
2671 case XML_ATTRIBUTE_NMTOKEN:
2672 return(xmlValidateNmtokenValue(value));
2673 case XML_ATTRIBUTE_CDATA:
2674 break;
2675 }
2676 return(1);
2677}
2678
2679/**
2680 * xmlValidateAttributeValue2:
2681 * @ctxt: the validation context
2682 * @doc: the document
2683 * @name: the attribute name (used for error reporting only)
2684 * @type: the attribute type
2685 * @value: the attribute value
2686 *
2687 * Validate that the given attribute value match a given type.
2688 * This typically cannot be done before having finished parsing
2689 * the subsets.
2690 *
2691 * [ VC: IDREF ]
2692 * Values of type IDREF must match one of the declared IDs
2693 * Values of type IDREFS must match a sequence of the declared IDs
2694 * each Name must match the value of an ID attribute on some element
2695 * in the XML document; i.e. IDREF values must match the value of
2696 * some ID attribute
2697 *
2698 * [ VC: Entity Name ]
2699 * Values of type ENTITY must match one declared entity
2700 * Values of type ENTITIES must match a sequence of declared entities
2701 *
2702 * [ VC: Notation Attributes ]
2703 * all notation names in the declaration must be declared.
2704 *
2705 * returns 1 if valid or 0 otherwise
2706 */
2707
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002708static int
Owen Taylor3473f882001-02-23 17:55:21 +00002709xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2710 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2711 int ret = 1;
2712 switch (type) {
2713 case XML_ATTRIBUTE_IDREFS:
2714 case XML_ATTRIBUTE_IDREF:
2715 case XML_ATTRIBUTE_ID:
2716 case XML_ATTRIBUTE_NMTOKENS:
2717 case XML_ATTRIBUTE_ENUMERATION:
2718 case XML_ATTRIBUTE_NMTOKEN:
2719 case XML_ATTRIBUTE_CDATA:
2720 break;
2721 case XML_ATTRIBUTE_ENTITY: {
2722 xmlEntityPtr ent;
2723
2724 ent = xmlGetDocEntity(doc, value);
2725 if (ent == NULL) {
2726 VERROR(ctxt->userData,
2727 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2728 name, value);
2729 ret = 0;
2730 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2731 VERROR(ctxt->userData,
2732 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2733 name, value);
2734 ret = 0;
2735 }
2736 break;
2737 }
2738 case XML_ATTRIBUTE_ENTITIES: {
2739 xmlChar *dup, *nam = NULL, *cur, save;
2740 xmlEntityPtr ent;
2741
2742 dup = xmlStrdup(value);
2743 if (dup == NULL)
2744 return(0);
2745 cur = dup;
2746 while (*cur != 0) {
2747 nam = cur;
2748 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2749 save = *cur;
2750 *cur = 0;
2751 ent = xmlGetDocEntity(doc, nam);
2752 if (ent == NULL) {
2753 VERROR(ctxt->userData,
2754 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2755 name, nam);
2756 ret = 0;
2757 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2758 VERROR(ctxt->userData,
2759 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2760 name, nam);
2761 ret = 0;
2762 }
2763 if (save == 0)
2764 break;
2765 *cur = save;
2766 while (IS_BLANK(*cur)) cur++;
2767 }
2768 xmlFree(dup);
2769 break;
2770 }
2771 case XML_ATTRIBUTE_NOTATION: {
2772 xmlNotationPtr nota;
2773
2774 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2775 if ((nota == NULL) && (doc->extSubset != NULL))
2776 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2777
2778 if (nota == NULL) {
2779 VERROR(ctxt->userData,
2780 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2781 name, value);
2782 ret = 0;
2783 }
2784 break;
2785 }
2786 }
2787 return(ret);
2788}
2789
2790/**
2791 * xmlValidNormalizeAttributeValue:
2792 * @doc: the document
2793 * @elem: the parent
2794 * @name: the attribute name
2795 * @value: the attribute value
2796 *
2797 * Does the validation related extra step of the normalization of attribute
2798 * values:
2799 *
2800 * If the declared value is not CDATA, then the XML processor must further
2801 * process the normalized attribute value by discarding any leading and
2802 * trailing space (#x20) characters, and by replacing sequences of space
2803 * (#x20) characters by single space (#x20) character.
2804 *
2805 * returns a new normalized string if normalization is needed, NULL otherwise
2806 * the caller must free the returned value.
2807 */
2808
2809xmlChar *
2810xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
2811 const xmlChar *name, const xmlChar *value) {
2812 xmlChar *ret, *dst;
2813 const xmlChar *src;
2814 xmlAttributePtr attrDecl = NULL;
2815
2816 if (doc == NULL) return(NULL);
2817 if (elem == NULL) return(NULL);
2818 if (name == NULL) return(NULL);
2819 if (value == NULL) return(NULL);
2820
2821 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2822 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00002823 snprintf((char *) qname, sizeof(qname), "%s:%s",
2824 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002825 qname[sizeof(qname) - 1] = 0;
2826 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
2827 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2828 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
2829 }
2830 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
2831 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2832 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
2833
2834 if (attrDecl == NULL)
2835 return(NULL);
2836 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
2837 return(NULL);
2838
2839 ret = xmlStrdup(value);
2840 if (ret == NULL)
2841 return(NULL);
2842 src = value;
2843 dst = ret;
2844 while (*src == 0x20) src++;
2845 while (*src != 0) {
2846 if (*src == 0x20) {
2847 while (*src == 0x20) src++;
2848 if (*src != 0)
2849 *dst++ = 0x20;
2850 } else {
2851 *dst++ = *src++;
2852 }
2853 }
2854 *dst = 0;
2855 return(ret);
2856}
2857
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002858static void
Owen Taylor3473f882001-02-23 17:55:21 +00002859xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002860 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002861 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
2862}
2863
2864/**
2865 * xmlValidateAttributeDecl:
2866 * @ctxt: the validation context
2867 * @doc: a document instance
2868 * @attr: an attribute definition
2869 *
2870 * Try to validate a single attribute definition
2871 * basically it does the following checks as described by the
2872 * XML-1.0 recommendation:
2873 * - [ VC: Attribute Default Legal ]
2874 * - [ VC: Enumeration ]
2875 * - [ VC: ID Attribute Default ]
2876 *
2877 * The ID/IDREF uniqueness and matching are done separately
2878 *
2879 * returns 1 if valid or 0 otherwise
2880 */
2881
2882int
2883xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2884 xmlAttributePtr attr) {
2885 int ret = 1;
2886 int val;
2887 CHECK_DTD;
2888 if(attr == NULL) return(1);
2889
2890 /* Attribute Default Legal */
2891 /* Enumeration */
2892 if (attr->defaultValue != NULL) {
2893 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
2894 if (val == 0) {
2895 VERROR(ctxt->userData,
2896 "Syntax of default value for attribute %s on %s is not valid\n",
2897 attr->name, attr->elem);
2898 }
2899 ret &= val;
2900 }
2901
2902 /* ID Attribute Default */
2903 if ((attr->atype == XML_ATTRIBUTE_ID)&&
2904 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
2905 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
2906 VERROR(ctxt->userData,
2907 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
2908 attr->name, attr->elem);
2909 ret = 0;
2910 }
2911
2912 /* One ID per Element Type */
2913 if (attr->atype == XML_ATTRIBUTE_ID) {
2914 int nbId;
2915
2916 /* the trick is taht we parse DtD as their own internal subset */
2917 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
2918 attr->elem);
2919 if (elem != NULL) {
2920 nbId = xmlScanIDAttributeDecl(NULL, elem);
2921 } else {
2922 xmlAttributeTablePtr table;
2923
2924 /*
2925 * The attribute may be declared in the internal subset and the
2926 * element in the external subset.
2927 */
2928 nbId = 0;
2929 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
2930 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
2931 xmlValidateAttributeIdCallback, &nbId);
2932 }
2933 if (nbId > 1) {
2934 VERROR(ctxt->userData,
2935 "Element %s has %d ID attribute defined in the internal subset : %s\n",
2936 attr->elem, nbId, attr->name);
2937 } else if (doc->extSubset != NULL) {
2938 int extId = 0;
2939 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
2940 if (elem != NULL) {
2941 extId = xmlScanIDAttributeDecl(NULL, elem);
2942 }
2943 if (extId > 1) {
2944 VERROR(ctxt->userData,
2945 "Element %s has %d ID attribute defined in the external subset : %s\n",
2946 attr->elem, extId, attr->name);
2947 } else if (extId + nbId > 1) {
2948 VERROR(ctxt->userData,
2949"Element %s has ID attributes defined in the internal and external subset : %s\n",
2950 attr->elem, attr->name);
2951 }
2952 }
2953 }
2954
2955 /* Validity Constraint: Enumeration */
2956 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
2957 xmlEnumerationPtr tree = attr->tree;
2958 while (tree != NULL) {
2959 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
2960 tree = tree->next;
2961 }
2962 if (tree == NULL) {
2963 VERROR(ctxt->userData,
2964"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
2965 attr->defaultValue, attr->name, attr->elem);
2966 ret = 0;
2967 }
2968 }
2969
2970 return(ret);
2971}
2972
2973/**
2974 * xmlValidateElementDecl:
2975 * @ctxt: the validation context
2976 * @doc: a document instance
2977 * @elem: an element definition
2978 *
2979 * Try to validate a single element definition
2980 * basically it does the following checks as described by the
2981 * XML-1.0 recommendation:
2982 * - [ VC: One ID per Element Type ]
2983 * - [ VC: No Duplicate Types ]
2984 * - [ VC: Unique Element Type Declaration ]
2985 *
2986 * returns 1 if valid or 0 otherwise
2987 */
2988
2989int
2990xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2991 xmlElementPtr elem) {
2992 int ret = 1;
2993 xmlElementPtr tst;
2994
2995 CHECK_DTD;
2996
2997 if (elem == NULL) return(1);
2998
2999 /* No Duplicate Types */
3000 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3001 xmlElementContentPtr cur, next;
3002 const xmlChar *name;
3003
3004 cur = elem->content;
3005 while (cur != NULL) {
3006 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3007 if (cur->c1 == NULL) break;
3008 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3009 name = cur->c1->name;
3010 next = cur->c2;
3011 while (next != NULL) {
3012 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3013 if (xmlStrEqual(next->name, name)) {
3014 VERROR(ctxt->userData,
3015 "Definition of %s has duplicate references of %s\n",
3016 elem->name, name);
3017 ret = 0;
3018 }
3019 break;
3020 }
3021 if (next->c1 == NULL) break;
3022 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3023 if (xmlStrEqual(next->c1->name, name)) {
3024 VERROR(ctxt->userData,
3025 "Definition of %s has duplicate references of %s\n",
3026 elem->name, name);
3027 ret = 0;
3028 }
3029 next = next->c2;
3030 }
3031 }
3032 cur = cur->c2;
3033 }
3034 }
3035
3036 /* VC: Unique Element Type Declaration */
3037 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003038 if ((tst != NULL ) && (tst != elem) &&
3039 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003040 VERROR(ctxt->userData, "Redefinition of element %s\n",
3041 elem->name);
3042 ret = 0;
3043 }
3044 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003045 if ((tst != NULL ) && (tst != elem) &&
3046 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003047 VERROR(ctxt->userData, "Redefinition of element %s\n",
3048 elem->name);
3049 ret = 0;
3050 }
3051
Daniel Veillarda10efa82001-04-18 13:09:01 +00003052 /* One ID per Element Type
3053 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003054 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3055 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003056 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003057 return(ret);
3058}
3059
3060/**
3061 * xmlValidateOneAttribute:
3062 * @ctxt: the validation context
3063 * @doc: a document instance
3064 * @elem: an element instance
3065 * @attr: an attribute instance
3066 * @value: the attribute value (without entities processing)
3067 *
3068 * Try to validate a single attribute for an element
3069 * basically it does the following checks as described by the
3070 * XML-1.0 recommendation:
3071 * - [ VC: Attribute Value Type ]
3072 * - [ VC: Fixed Attribute Default ]
3073 * - [ VC: Entity Name ]
3074 * - [ VC: Name Token ]
3075 * - [ VC: ID ]
3076 * - [ VC: IDREF ]
3077 * - [ VC: Entity Name ]
3078 * - [ VC: Notation Attributes ]
3079 *
3080 * The ID/IDREF uniqueness and matching are done separately
3081 *
3082 * returns 1 if valid or 0 otherwise
3083 */
3084
3085int
3086xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3087 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3088 /* xmlElementPtr elemDecl; */
3089 xmlAttributePtr attrDecl = NULL;
3090 int val;
3091 int ret = 1;
3092
3093 CHECK_DTD;
3094 if ((elem == NULL) || (elem->name == NULL)) return(0);
3095 if ((attr == NULL) || (attr->name == NULL)) return(0);
3096
3097 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3098 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003099 snprintf((char *) qname, sizeof(qname), "%s:%s",
3100 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003101 qname[sizeof(qname) - 1] = 0;
3102 if (attr->ns != NULL) {
3103 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3104 attr->name, attr->ns->prefix);
3105 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3106 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3107 attr->name, attr->ns->prefix);
3108 } else {
3109 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3110 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3111 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3112 qname, attr->name);
3113 }
3114 }
3115 if (attrDecl == NULL) {
3116 if (attr->ns != NULL) {
3117 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3118 attr->name, attr->ns->prefix);
3119 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3120 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3121 attr->name, attr->ns->prefix);
3122 } else {
3123 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3124 elem->name, attr->name);
3125 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3126 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3127 elem->name, attr->name);
3128 }
3129 }
3130
3131
3132 /* Validity Constraint: Attribute Value Type */
3133 if (attrDecl == NULL) {
3134 VERROR(ctxt->userData,
3135 "No declaration for attribute %s on element %s\n",
3136 attr->name, elem->name);
3137 return(0);
3138 }
3139 attr->atype = attrDecl->atype;
3140
3141 val = xmlValidateAttributeValue(attrDecl->atype, value);
3142 if (val == 0) {
3143 VERROR(ctxt->userData,
3144 "Syntax of value for attribute %s on %s is not valid\n",
3145 attr->name, elem->name);
3146 ret = 0;
3147 }
3148
3149 /* Validity constraint: Fixed Attribute Default */
3150 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3151 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3152 VERROR(ctxt->userData,
3153 "Value for attribute %s on %s is differnt from default \"%s\"\n",
3154 attr->name, elem->name, attrDecl->defaultValue);
3155 ret = 0;
3156 }
3157 }
3158
3159 /* Validity Constraint: ID uniqueness */
3160 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3161 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3162 ret = 0;
3163 }
3164
3165 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3166 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3167 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3168 ret = 0;
3169 }
3170
3171 /* Validity Constraint: Notation Attributes */
3172 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3173 xmlEnumerationPtr tree = attrDecl->tree;
3174 xmlNotationPtr nota;
3175
3176 /* First check that the given NOTATION was declared */
3177 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3178 if (nota == NULL)
3179 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3180
3181 if (nota == NULL) {
3182 VERROR(ctxt->userData,
3183 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
3184 value, attr->name, elem->name);
3185 ret = 0;
3186 }
3187
3188 /* Second, verify that it's among the list */
3189 while (tree != NULL) {
3190 if (xmlStrEqual(tree->name, value)) break;
3191 tree = tree->next;
3192 }
3193 if (tree == NULL) {
3194 VERROR(ctxt->userData,
3195"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
3196 value, attr->name, elem->name);
3197 ret = 0;
3198 }
3199 }
3200
3201 /* Validity Constraint: Enumeration */
3202 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3203 xmlEnumerationPtr tree = attrDecl->tree;
3204 while (tree != NULL) {
3205 if (xmlStrEqual(tree->name, value)) break;
3206 tree = tree->next;
3207 }
3208 if (tree == NULL) {
3209 VERROR(ctxt->userData,
3210 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3211 value, attr->name, elem->name);
3212 ret = 0;
3213 }
3214 }
3215
3216 /* Fixed Attribute Default */
3217 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3218 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3219 VERROR(ctxt->userData,
3220 "Value for attribute %s on %s must be \"%s\"\n",
3221 attr->name, elem->name, attrDecl->defaultValue);
3222 ret = 0;
3223 }
3224
3225 /* Extra check for the attribute value */
3226 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3227 attrDecl->atype, value);
3228
3229 return(ret);
3230}
3231
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003232/**
3233 * xmlValidateSkipIgnorable:
3234 * @ctxt: the validation context
3235 * @child: the child list
3236 *
3237 * Skip ignorable elements w.r.t. the validation process
3238 *
3239 * returns the first element to consider for validation of the content model
3240 */
3241
3242static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003243xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003244 while (child != NULL) {
3245 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003246 /* These things are ignored (skipped) during validation. */
3247 case XML_PI_NODE:
3248 case XML_COMMENT_NODE:
3249 case XML_XINCLUDE_START:
3250 case XML_XINCLUDE_END:
3251 child = child->next;
3252 break;
3253 case XML_TEXT_NODE:
3254 if (xmlIsBlankNode(child))
3255 child = child->next;
3256 else
3257 return(child);
3258 break;
3259 /* keep current node */
3260 default:
3261 return(child);
3262 }
3263 }
3264 return(child);
3265}
3266
3267/**
3268 * xmlValidateElementType:
3269 * @ctxt: the validation context
3270 *
3271 * Try to validate the content model of an element internal function
3272 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003273 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3274 * reference is found and -3 if the validation succeeded but
3275 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003276 */
3277
3278static int
3279xmlValidateElementType(xmlValidCtxtPtr ctxt) {
3280 int ret = -1;
3281 int consumed = 1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003282 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003283
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003284 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003285 if ((NODE == NULL) && (CONT == NULL))
3286 return(1);
3287 if ((NODE == NULL) &&
3288 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3289 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3290 return(1);
3291 }
3292 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003293 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003294 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003295
3296 /*
3297 * We arrive here when more states need to be examined
3298 */
3299cont:
3300
3301 /*
3302 * We just recovered from a rollback generated by a possible
3303 * epsilon transition, go directly to the analysis phase
3304 */
3305 if (STATE == ROLLBACK_PARENT) {
3306 DEBUG_VALID_MSG("restaured parent branch");
3307 DEBUG_VALID_STATE(NODE, CONT)
3308 ret = 1;
3309 consumed = 0;
3310 goto analyze;
3311 }
3312
3313 DEBUG_VALID_STATE(NODE, CONT)
3314 /*
3315 * we may have to save a backup state here. This is the equivalent
3316 * of handling epsilon transition in NFAs.
3317 */
3318 if ((consumed) && (CONT != NULL) &&
3319 (CONT->parent != NULL) &&
3320 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003321 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
3322 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003323 DEBUG_VALID_MSG("saving parent branch");
3324 vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
3325 }
3326
3327
3328 /*
3329 * Check first if the content matches
3330 */
3331 switch (CONT->type) {
3332 case XML_ELEMENT_CONTENT_PCDATA:
3333 if (NODE == NULL) {
3334 DEBUG_VALID_MSG("pcdata failed no node");
3335 ret = 0;
3336 break;
3337 }
3338 if (NODE->type == XML_TEXT_NODE) {
3339 DEBUG_VALID_MSG("pcdata found, skip to next");
3340 /*
3341 * go to next element in the content model
3342 * skipping ignorable elems
3343 */
3344 do {
3345 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003346 NODE = xmlValidateSkipIgnorable(NODE);
3347 if ((NODE != NULL) &&
3348 (NODE->type == XML_ENTITY_REF_NODE))
3349 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003350 } while ((NODE != NULL) &&
3351 ((NODE->type != XML_ELEMENT_NODE) &&
3352 (NODE->type != XML_TEXT_NODE)));
3353 consumed = 1;
3354 ret = 1;
3355 break;
3356 } else {
3357 DEBUG_VALID_MSG("pcdata failed");
3358 ret = 0;
3359 break;
3360 }
3361 break;
3362 case XML_ELEMENT_CONTENT_ELEMENT:
3363 if (NODE == NULL) {
3364 DEBUG_VALID_MSG("element failed no node");
3365 ret = 0;
3366 break;
3367 }
3368 ret = (xmlStrEqual(NODE->name, CONT->name));
3369 if (ret == 1) {
3370 DEBUG_VALID_MSG("element found, skip to next");
3371 /*
3372 * go to next element in the content model
3373 * skipping ignorable elems
3374 */
3375 do {
3376 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003377 NODE = xmlValidateSkipIgnorable(NODE);
3378 if ((NODE != NULL) &&
3379 (NODE->type == XML_ENTITY_REF_NODE))
3380 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003381 } while ((NODE != NULL) &&
3382 ((NODE->type != XML_ELEMENT_NODE) &&
3383 (NODE->type != XML_TEXT_NODE)));
3384 consumed = 1;
3385 } else {
3386 DEBUG_VALID_MSG("element failed");
3387 ret = 0;
3388 break;
3389 }
3390 break;
3391 case XML_ELEMENT_CONTENT_OR:
3392 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003393 * Small optimization.
3394 */
3395 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3396 if ((NODE == NULL) ||
3397 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3398 DEPTH++;
3399 CONT = CONT->c2;
3400 goto cont;
3401 }
3402 }
3403
3404 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003405 * save the second branch 'or' branch
3406 */
3407 DEBUG_VALID_MSG("saving 'or' branch");
3408 vstateVPush(ctxt, CONT->c2, NODE, DEPTH + 1, OCCURS, ROLLBACK_OR);
3409
3410 DEPTH++;
3411 CONT = CONT->c1;
3412 goto cont;
3413 case XML_ELEMENT_CONTENT_SEQ:
3414 DEPTH++;
3415 CONT = CONT->c1;
3416 goto cont;
3417 }
3418
3419 /*
3420 * At this point handle going up in the tree
3421 */
3422 if (ret == -1) {
3423 DEBUG_VALID_MSG("error found returning");
3424 return(ret);
3425 }
3426analyze:
3427 while (CONT != NULL) {
3428 /*
3429 * First do the analysis depending on the occurence model at
3430 * this level.
3431 */
3432 if (ret == 0) {
3433 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003434 xmlNodePtr cur;
3435
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003436 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003437 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003438 DEBUG_VALID_MSG("Once branch failed, rollback");
3439 if (vstateVPop(ctxt) < 0 ) {
3440 DEBUG_VALID_MSG("exhaustion, failed");
3441 return(0);
3442 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003443 if (cur != ctxt->vstate->node)
3444 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003445 consumed = 0;
3446 goto cont;
3447 case XML_ELEMENT_CONTENT_PLUS:
3448 if (OCCURENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003449 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003450 DEBUG_VALID_MSG("Plus branch failed, rollback");
3451 if (vstateVPop(ctxt) < 0 ) {
3452 DEBUG_VALID_MSG("exhaustion, failed");
3453 return(0);
3454 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003455 if (cur != ctxt->vstate->node)
3456 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003457 consumed = 0;
3458 goto cont;
3459 }
3460 DEBUG_VALID_MSG("Plus branch found");
3461 ret = 1;
3462 break;
3463 case XML_ELEMENT_CONTENT_MULT:
3464#ifdef DEBUG_VALID_ALGO
3465 if (OCCURENCE == 0) {
3466 DEBUG_VALID_MSG("Mult branch failed");
3467 } else {
3468 DEBUG_VALID_MSG("Mult branch found");
3469 }
3470#endif
3471 ret = 1;
3472 break;
3473 case XML_ELEMENT_CONTENT_OPT:
3474 DEBUG_VALID_MSG("Option branch failed");
3475 ret = 1;
3476 break;
3477 }
3478 } else {
3479 switch (CONT->ocur) {
3480 case XML_ELEMENT_CONTENT_OPT:
3481 DEBUG_VALID_MSG("Option branch succeeded");
3482 ret = 1;
3483 break;
3484 case XML_ELEMENT_CONTENT_ONCE:
3485 DEBUG_VALID_MSG("Once branch succeeded");
3486 ret = 1;
3487 break;
3488 case XML_ELEMENT_CONTENT_PLUS:
3489 if (STATE == ROLLBACK_PARENT) {
3490 DEBUG_VALID_MSG("Plus branch rollback");
3491 ret = 1;
3492 break;
3493 }
3494 if (NODE == NULL) {
3495 DEBUG_VALID_MSG("Plus branch exhausted");
3496 ret = 1;
3497 break;
3498 }
3499 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
3500 SET_OCCURENCE;
3501 goto cont;
3502 case XML_ELEMENT_CONTENT_MULT:
3503 if (STATE == ROLLBACK_PARENT) {
3504 DEBUG_VALID_MSG("Mult branch rollback");
3505 ret = 1;
3506 break;
3507 }
3508 if (NODE == NULL) {
3509 DEBUG_VALID_MSG("Mult branch exhausted");
3510 ret = 1;
3511 break;
3512 }
3513 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
3514 SET_OCCURENCE;
3515 goto cont;
3516 }
3517 }
3518 STATE = 0;
3519
3520 /*
3521 * Then act accordingly at the parent level
3522 */
3523 RESET_OCCURENCE;
3524 if (CONT->parent == NULL)
3525 break;
3526
3527 switch (CONT->parent->type) {
3528 case XML_ELEMENT_CONTENT_PCDATA:
3529 DEBUG_VALID_MSG("Error: parent pcdata");
3530 return(-1);
3531 case XML_ELEMENT_CONTENT_ELEMENT:
3532 DEBUG_VALID_MSG("Error: parent element");
3533 return(-1);
3534 case XML_ELEMENT_CONTENT_OR:
3535 if (ret == 1) {
3536 DEBUG_VALID_MSG("Or succeeded");
3537 CONT = CONT->parent;
3538 DEPTH--;
3539 } else {
3540 DEBUG_VALID_MSG("Or failed");
3541 CONT = CONT->parent;
3542 DEPTH--;
3543 }
3544 break;
3545 case XML_ELEMENT_CONTENT_SEQ:
3546 if (ret == 0) {
3547 DEBUG_VALID_MSG("Sequence failed");
3548 CONT = CONT->parent;
3549 DEPTH--;
3550 } else if (CONT == CONT->parent->c1) {
3551 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3552 CONT = CONT->parent->c2;
3553 goto cont;
3554 } else {
3555 DEBUG_VALID_MSG("Sequence succeeded");
3556 CONT = CONT->parent;
3557 DEPTH--;
3558 }
3559 }
3560 }
3561 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003562 xmlNodePtr cur;
3563
3564 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003565 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3566 if (vstateVPop(ctxt) < 0 ) {
3567 DEBUG_VALID_MSG("exhaustion, failed");
3568 return(0);
3569 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003570 if (cur != ctxt->vstate->node)
3571 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003572 consumed = 0;
3573 goto cont;
3574 }
3575 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003576 xmlNodePtr cur;
3577
3578 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003579 DEBUG_VALID_MSG("Failure, rollback");
3580 if (vstateVPop(ctxt) < 0 ) {
3581 DEBUG_VALID_MSG("exhaustion, failed");
3582 return(0);
3583 }
3584 consumed = 0;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003585 if (cur != ctxt->vstate->node)
3586 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003587 goto cont;
3588 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003589 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003590}
3591
3592/**
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003593 * xmlSprintfElements:
3594 * @buf: an output buffer
3595 * @content: An element
3596 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3597 *
3598 * This will dump the list of elements to the buffer
3599 * Intended just for the debug routine
3600 */
3601static void
3602xmlSprintfElements(char *buf, xmlNodePtr node, int glob) {
3603 xmlNodePtr cur;
3604
3605 if (node == NULL) return;
3606 if (glob) strcat(buf, "(");
3607 cur = node;
3608 while (cur != NULL) {
3609 switch (cur->type) {
3610 case XML_ELEMENT_NODE:
3611 strcat(buf, (char *) cur->name);
3612 if (cur->next != NULL)
3613 strcat(buf, " ");
3614 break;
3615 case XML_TEXT_NODE:
3616 if (xmlIsBlankNode(cur))
3617 break;
3618 case XML_CDATA_SECTION_NODE:
3619 case XML_ENTITY_REF_NODE:
3620 strcat(buf, "CDATA");
3621 if (cur->next != NULL)
3622 strcat(buf, " ");
3623 break;
3624 case XML_ATTRIBUTE_NODE:
3625 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003626#ifdef LIBXML_DOCB_ENABLED
3627 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003628#endif
3629 case XML_HTML_DOCUMENT_NODE:
3630 case XML_DOCUMENT_TYPE_NODE:
3631 case XML_DOCUMENT_FRAG_NODE:
3632 case XML_NOTATION_NODE:
3633 case XML_NAMESPACE_DECL:
3634 strcat(buf, "???");
3635 if (cur->next != NULL)
3636 strcat(buf, " ");
3637 break;
3638 case XML_ENTITY_NODE:
3639 case XML_PI_NODE:
3640 case XML_DTD_NODE:
3641 case XML_COMMENT_NODE:
3642 case XML_ELEMENT_DECL:
3643 case XML_ATTRIBUTE_DECL:
3644 case XML_ENTITY_DECL:
3645 case XML_XINCLUDE_START:
3646 case XML_XINCLUDE_END:
3647 break;
3648 }
3649 cur = cur->next;
3650 }
3651 if (glob) strcat(buf, ")");
3652}
3653
3654/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003655 * xmlValidateElementContent:
3656 * @ctxt: the validation context
3657 * @child: the child list
3658 * @cont: pointer to the content declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003659 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003660 *
3661 * Try to validate the content model of an element
3662 *
3663 * returns 1 if valid or 0 if not and -1 in case of error
3664 */
3665
3666static int
3667xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillard7533cc82001-04-24 15:52:00 +00003668 xmlElementContentPtr cont, int warn, const xmlChar *name) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003669 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003670 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003671
3672 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003673 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003674 */
3675 ctxt->vstateMax = 8;
3676 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
3677 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
3678 if (ctxt->vstateTab == NULL) {
3679 xmlGenericError(xmlGenericErrorContext,
3680 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003681 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003682 }
3683 /*
3684 * The first entry in the stack is reserved to the current state
3685 */
Daniel Veillard61b33d52001-04-24 13:55:12 +00003686 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003687 ctxt->vstate = &ctxt->vstateTab[0];
3688 ctxt->vstateNr = 1;
3689 CONT = cont;
3690 NODE = child;
3691 DEPTH = 0;
3692 OCCURS = 0;
3693 STATE = 0;
3694 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003695 if ((ret == -3) && (warn)) {
3696 VWARNING(ctxt->userData,
3697 "Element %s content model is ambiguous\n", name);
3698 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003699 /*
3700 * An entities reference appeared at this level.
3701 * Buid a minimal representation of this node content
3702 * sufficient to run the validation process on it
3703 */
3704 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003705 cur = child;
3706 while (cur != NULL) {
3707 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003708 case XML_ENTITY_REF_NODE:
3709 /*
3710 * Push the current node to be able to roll back
3711 * and process within the entity
3712 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003713 if ((cur->children != NULL) &&
3714 (cur->children->children != NULL)) {
3715 nodeVPush(ctxt, cur);
3716 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003717 continue;
3718 }
3719 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003720 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003721 break;
3722 case XML_ELEMENT_NODE:
3723 /*
3724 * Allocate a new node and minimally fills in
3725 * what's required
3726 */
3727 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
3728 if (tmp == NULL) {
3729 xmlGenericError(xmlGenericErrorContext,
3730 "xmlValidateElementContent : malloc failed\n");
3731 xmlFreeNodeList(repl);
3732 ret = -1;
3733 goto done;
3734 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003735 tmp->type = cur->type;
3736 tmp->name = cur->name;
3737 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003738 tmp->next = NULL;
3739 if (repl == NULL)
3740 repl = last = tmp;
3741 else {
3742 last->next = tmp;
3743 last = tmp;
3744 }
3745 break;
3746 default:
3747 break;
3748 }
3749 /*
3750 * Switch to next element
3751 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003752 cur = cur->next;
3753 while (cur == NULL) {
3754 cur = nodeVPop(ctxt);
3755 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003756 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003757 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003758 }
3759 }
3760
3761 /*
3762 * Relaunch the validation
3763 */
3764 ctxt->vstate = &ctxt->vstateTab[0];
3765 ctxt->vstateNr = 1;
3766 CONT = cont;
3767 NODE = repl;
3768 DEPTH = 0;
3769 OCCURS = 0;
3770 STATE = 0;
3771 ret = xmlValidateElementType(ctxt);
3772 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003773 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003774 char expr[5000];
3775 char list[5000];
3776
3777 expr[0] = 0;
3778 xmlSprintfElementContent(expr, cont, 1);
3779 list[0] = 0;
3780 if (repl != NULL)
3781 xmlSprintfElements(list, repl, 1);
3782 else
3783 xmlSprintfElements(list, child, 1);
3784
Daniel Veillard7533cc82001-04-24 15:52:00 +00003785 if (name != NULL) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003786 VERROR(ctxt->userData,
3787 "Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
Daniel Veillard7533cc82001-04-24 15:52:00 +00003788 name, expr, list);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003789 } else {
3790 VERROR(ctxt->userData,
3791 "Element content doesn't follow the Dtd\nExpecting %s, got %s\n",
3792 expr, list);
3793 }
3794 ret = 0;
3795 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003796 if (ret == -3)
3797 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003798
3799
3800done:
3801 /*
3802 * Deallocate the copy if done, and free up the validation stack
3803 */
3804 while (repl != NULL) {
3805 tmp = repl->next;
3806 xmlFree(repl);
3807 repl = tmp;
3808 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003809 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003810 if (ctxt->vstateTab != NULL) {
3811 xmlFree(ctxt->vstateTab);
3812 ctxt->vstateTab = NULL;
3813 }
3814 ctxt->nodeMax = 0;
3815 if (ctxt->nodeTab != NULL) {
3816 xmlFree(ctxt->nodeTab);
3817 ctxt->nodeTab = NULL;
3818 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003819 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003820
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003821}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003822
Owen Taylor3473f882001-02-23 17:55:21 +00003823/**
Owen Taylor3473f882001-02-23 17:55:21 +00003824 * xmlValidateOneElement:
3825 * @ctxt: the validation context
3826 * @doc: a document instance
3827 * @elem: an element instance
3828 *
3829 * Try to validate a single element and it's attributes,
3830 * basically it does the following checks as described by the
3831 * XML-1.0 recommendation:
3832 * - [ VC: Element Valid ]
3833 * - [ VC: Required Attribute ]
3834 * Then call xmlValidateOneAttribute() for each attribute present.
3835 *
3836 * The ID/IDREF checkings are done separately
3837 *
3838 * returns 1 if valid or 0 otherwise
3839 */
3840
3841int
3842xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3843 xmlNodePtr elem) {
3844 xmlElementPtr elemDecl = NULL;
3845 xmlElementContentPtr cont;
3846 xmlAttributePtr attr;
3847 xmlNodePtr child;
3848 int ret = 1;
3849 const xmlChar *name;
3850
3851 CHECK_DTD;
3852
3853 if (elem == NULL) return(0);
3854 if (elem->type == XML_TEXT_NODE) {
3855 }
3856 switch (elem->type) {
3857 case XML_ATTRIBUTE_NODE:
3858 VERROR(ctxt->userData,
3859 "Attribute element not expected here\n");
3860 return(0);
3861 case XML_TEXT_NODE:
3862 if (elem->children != NULL) {
3863 VERROR(ctxt->userData, "Text element has childs !\n");
3864 return(0);
3865 }
3866 if (elem->properties != NULL) {
3867 VERROR(ctxt->userData, "Text element has attributes !\n");
3868 return(0);
3869 }
3870 if (elem->ns != NULL) {
3871 VERROR(ctxt->userData, "Text element has namespace !\n");
3872 return(0);
3873 }
3874 if (elem->nsDef != NULL) {
3875 VERROR(ctxt->userData,
3876 "Text element carries namespace definitions !\n");
3877 return(0);
3878 }
3879 if (elem->content == NULL) {
3880 VERROR(ctxt->userData,
3881 "Text element has no content !\n");
3882 return(0);
3883 }
3884 return(1);
3885 case XML_XINCLUDE_START:
3886 case XML_XINCLUDE_END:
3887 return(1);
3888 case XML_CDATA_SECTION_NODE:
3889 case XML_ENTITY_REF_NODE:
3890 case XML_PI_NODE:
3891 case XML_COMMENT_NODE:
3892 return(1);
3893 case XML_ENTITY_NODE:
3894 VERROR(ctxt->userData,
3895 "Entity element not expected here\n");
3896 return(0);
3897 case XML_NOTATION_NODE:
3898 VERROR(ctxt->userData,
3899 "Notation element not expected here\n");
3900 return(0);
3901 case XML_DOCUMENT_NODE:
3902 case XML_DOCUMENT_TYPE_NODE:
3903 case XML_DOCUMENT_FRAG_NODE:
3904 VERROR(ctxt->userData,
3905 "Document element not expected here\n");
3906 return(0);
3907 case XML_HTML_DOCUMENT_NODE:
3908 VERROR(ctxt->userData,
3909 "\n");
3910 return(0);
3911 case XML_ELEMENT_NODE:
3912 break;
3913 default:
3914 VERROR(ctxt->userData,
3915 "unknown element type %d\n", elem->type);
3916 return(0);
3917 }
3918 if (elem->name == NULL) return(0);
3919
3920 /*
3921 * Fetch the declaration for the qualified name
3922 */
3923 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3924 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
3925 elem->name, elem->ns->prefix);
3926 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3927 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
3928 elem->name, elem->ns->prefix);
3929 }
3930
3931 /*
3932 * Fetch the declaration for the non qualified name
3933 */
3934 if (elemDecl == NULL) {
3935 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
3936 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3937 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
3938 }
3939 if (elemDecl == NULL) {
3940 VERROR(ctxt->userData, "No declaration for element %s\n",
3941 elem->name);
3942 return(0);
3943 }
3944
3945 /* Check taht the element content matches the definition */
3946 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003947 case XML_ELEMENT_TYPE_UNDEFINED:
3948 VERROR(ctxt->userData, "No declaration for element %s\n",
3949 elem->name);
3950 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003951 case XML_ELEMENT_TYPE_EMPTY:
3952 if (elem->children != NULL) {
3953 VERROR(ctxt->userData,
3954 "Element %s was declared EMPTY this one has content\n",
3955 elem->name);
3956 ret = 0;
3957 }
3958 break;
3959 case XML_ELEMENT_TYPE_ANY:
3960 /* I don't think anything is required then */
3961 break;
3962 case XML_ELEMENT_TYPE_MIXED:
3963 /* Hum, this start to get messy */
3964 child = elem->children;
3965 while (child != NULL) {
3966 if (child->type == XML_ELEMENT_NODE) {
3967 name = child->name;
3968 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
3969 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003970 snprintf((char *) qname, sizeof(qname), "%s:%s",
3971 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003972 qname[sizeof(qname) - 1] = 0;
3973 cont = elemDecl->content;
3974 while (cont != NULL) {
3975 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
3976 if (xmlStrEqual(cont->name, qname)) break;
3977 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
3978 (cont->c1 != NULL) &&
3979 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
3980 if (xmlStrEqual(cont->c1->name, qname)) break;
3981 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
3982 (cont->c1 == NULL) ||
3983 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
3984 /* Internal error !!! */
3985 xmlGenericError(xmlGenericErrorContext,
3986 "Internal: MIXED struct bad\n");
3987 break;
3988 }
3989 cont = cont->c2;
3990 }
3991 if (cont != NULL)
3992 goto child_ok;
3993 }
3994 cont = elemDecl->content;
3995 while (cont != NULL) {
3996 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
3997 if (xmlStrEqual(cont->name, name)) break;
3998 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
3999 (cont->c1 != NULL) &&
4000 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4001 if (xmlStrEqual(cont->c1->name, name)) break;
4002 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4003 (cont->c1 == NULL) ||
4004 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4005 /* Internal error !!! */
4006 xmlGenericError(xmlGenericErrorContext,
4007 "Internal: MIXED struct bad\n");
4008 break;
4009 }
4010 cont = cont->c2;
4011 }
4012 if (cont == NULL) {
4013 VERROR(ctxt->userData,
4014 "Element %s is not declared in %s list of possible childs\n",
4015 name, elem->name);
4016 ret = 0;
4017 }
4018 }
4019child_ok:
4020 child = child->next;
4021 }
4022 break;
4023 case XML_ELEMENT_TYPE_ELEMENT:
4024 child = elem->children;
4025 cont = elemDecl->content;
Daniel Veillard7533cc82001-04-24 15:52:00 +00004026 ret = xmlValidateElementContent(ctxt, child, cont, 1, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004027 break;
4028 }
4029
4030 /* [ VC: Required Attribute ] */
4031 attr = elemDecl->attributes;
4032 while (attr != NULL) {
4033 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
4034 xmlAttrPtr attrib;
4035 int qualified = -1;
4036
4037 attrib = elem->properties;
4038 while (attrib != NULL) {
4039 if (xmlStrEqual(attrib->name, attr->name)) {
4040 if (attr->prefix != NULL) {
4041 xmlNsPtr nameSpace = attrib->ns;
4042
4043 if (nameSpace == NULL)
4044 nameSpace = elem->ns;
4045 /*
4046 * qualified names handling is problematic, having a
4047 * different prefix should be possible but DTDs don't
4048 * allow to define the URI instead of the prefix :-(
4049 */
4050 if (nameSpace == NULL) {
4051 if (qualified < 0)
4052 qualified = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004053 } else if (!xmlStrEqual(nameSpace->prefix,
4054 attr->prefix)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004055 if (qualified < 1)
4056 qualified = 1;
4057 } else
4058 goto found;
4059 } else {
4060 /*
4061 * We should allow applications to define namespaces
4062 * for their application even if the DTD doesn't
4063 * carry one, otherwise, basically we would always
4064 * break.
4065 */
4066 goto found;
4067 }
4068 }
4069 attrib = attrib->next;
4070 }
4071 if (qualified == -1) {
4072 if (attr->prefix == NULL) {
4073 VERROR(ctxt->userData,
4074 "Element %s doesn't carry attribute %s\n",
4075 elem->name, attr->name);
4076 ret = 0;
4077 } else {
4078 VERROR(ctxt->userData,
4079 "Element %s doesn't carry attribute %s:%s\n",
4080 elem->name, attr->prefix,attr->name);
4081 ret = 0;
4082 }
4083 } else if (qualified == 0) {
4084 VWARNING(ctxt->userData,
4085 "Element %s required attribute %s:%s has no prefix\n",
4086 elem->name, attr->prefix,attr->name);
4087 } else if (qualified == 1) {
4088 VWARNING(ctxt->userData,
4089 "Element %s required attribute %s:%s has different prefix\n",
4090 elem->name, attr->prefix,attr->name);
4091 }
4092 }
4093found:
4094 attr = attr->nexth;
4095 }
4096 return(ret);
4097}
4098
4099/**
4100 * xmlValidateRoot:
4101 * @ctxt: the validation context
4102 * @doc: a document instance
4103 *
4104 * Try to validate a the root element
4105 * basically it does the following check as described by the
4106 * XML-1.0 recommendation:
4107 * - [ VC: Root Element Type ]
4108 * it doesn't try to recurse or apply other check to the element
4109 *
4110 * returns 1 if valid or 0 otherwise
4111 */
4112
4113int
4114xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4115 xmlNodePtr root;
4116 if (doc == NULL) return(0);
4117
4118 root = xmlDocGetRootElement(doc);
4119 if ((root == NULL) || (root->name == NULL)) {
4120 VERROR(ctxt->userData, "Not valid: no root element\n");
4121 return(0);
4122 }
4123
4124 /*
4125 * When doing post validation against a separate DTD, those may
4126 * no internal subset has been generated
4127 */
4128 if ((doc->intSubset != NULL) &&
4129 (doc->intSubset->name != NULL)) {
4130 /*
4131 * Check first the document root against the NQName
4132 */
4133 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4134 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4135 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004136 snprintf((char *) qname, sizeof(qname), "%s:%s",
4137 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004138 qname[sizeof(qname) - 1] = 0;
4139 if (xmlStrEqual(doc->intSubset->name, qname))
4140 goto name_ok;
4141 }
4142 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4143 (xmlStrEqual(root->name, BAD_CAST "html")))
4144 goto name_ok;
4145 VERROR(ctxt->userData,
4146 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4147 root->name, doc->intSubset->name);
4148 return(0);
4149
4150 }
4151 }
4152name_ok:
4153 return(1);
4154}
4155
4156
4157/**
4158 * xmlValidateElement:
4159 * @ctxt: the validation context
4160 * @doc: a document instance
4161 * @elem: an element instance
4162 *
4163 * Try to validate the subtree under an element
4164 *
4165 * returns 1 if valid or 0 otherwise
4166 */
4167
4168int
4169xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4170 xmlNodePtr child;
4171 xmlAttrPtr attr;
4172 xmlChar *value;
4173 int ret = 1;
4174
4175 if (elem == NULL) return(0);
4176
4177 /*
4178 * XInclude elements were added after parsing in the infoset,
4179 * they don't really mean anything validation wise.
4180 */
4181 if ((elem->type == XML_XINCLUDE_START) ||
4182 (elem->type == XML_XINCLUDE_END))
4183 return(1);
4184
4185 CHECK_DTD;
4186
4187 ret &= xmlValidateOneElement(ctxt, doc, elem);
4188 attr = elem->properties;
4189 while(attr != NULL) {
4190 value = xmlNodeListGetString(doc, attr->children, 0);
4191 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4192 if (value != NULL)
4193 xmlFree(value);
4194 attr= attr->next;
4195 }
4196 child = elem->children;
4197 while (child != NULL) {
4198 ret &= xmlValidateElement(ctxt, doc, child);
4199 child = child->next;
4200 }
4201
4202 return(ret);
4203}
4204
Daniel Veillard8730c562001-02-26 10:49:57 +00004205/**
4206 * xmlValidateRef:
4207 * @ref: A reference to be validated
4208 * @ctxt: Validation context
4209 * @name: Name of ID we are searching for
4210 *
4211 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004212static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004213xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004214 const xmlChar *name) {
4215 xmlAttrPtr id;
4216 xmlAttrPtr attr;
4217
4218 if (ref == NULL)
4219 return;
4220 attr = ref->attr;
4221 if (attr == NULL)
4222 return;
4223 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4224 id = xmlGetID(ctxt->doc, name);
4225 if (id == NULL) {
4226 VERROR(ctxt->userData,
4227 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4228 attr->name, name);
4229 ctxt->valid = 0;
4230 }
4231 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4232 xmlChar *dup, *str = NULL, *cur, save;
4233
4234 dup = xmlStrdup(name);
4235 if (dup == NULL) {
4236 ctxt->valid = 0;
4237 return;
4238 }
4239 cur = dup;
4240 while (*cur != 0) {
4241 str = cur;
4242 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4243 save = *cur;
4244 *cur = 0;
4245 id = xmlGetID(ctxt->doc, str);
4246 if (id == NULL) {
4247 VERROR(ctxt->userData,
4248 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4249 attr->name, str);
4250 ctxt->valid = 0;
4251 }
4252 if (save == 0)
4253 break;
4254 *cur = save;
4255 while (IS_BLANK(*cur)) cur++;
4256 }
4257 xmlFree(dup);
4258 }
4259}
4260
4261/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004262 * xmlWalkValidateList:
4263 * @data: Contents of current link
4264 * @user: Value supplied by the user
4265 *
4266 * Return 0 to abort the walk or 1 to continue
4267 */
4268static int
4269xmlWalkValidateList(const void *data, const void *user)
4270{
4271 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4272 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4273 return 1;
4274}
4275
4276/**
4277 * xmlValidateCheckRefCallback:
4278 * @ref_list: List of references
4279 * @ctxt: Validation context
4280 * @name: Name of ID we are searching for
4281 *
4282 */
4283static void
4284xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4285 const xmlChar *name) {
4286 xmlValidateMemo memo;
4287
4288 if (ref_list == NULL)
4289 return;
4290 memo.ctxt = ctxt;
4291 memo.name = name;
4292
4293 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4294
4295}
4296
4297/**
Owen Taylor3473f882001-02-23 17:55:21 +00004298 * xmlValidateDocumentFinal:
4299 * @ctxt: the validation context
4300 * @doc: a document instance
4301 *
4302 * Does the final step for the document validation once all the
4303 * incremental validation steps have been completed
4304 *
4305 * basically it does the following checks described by the XML Rec
4306 *
4307 *
4308 * returns 1 if valid or 0 otherwise
4309 */
4310
4311int
4312xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4313 xmlRefTablePtr table;
4314
4315 if (doc == NULL) {
4316 xmlGenericError(xmlGenericErrorContext,
4317 "xmlValidateDocumentFinal: doc == NULL\n");
4318 return(0);
4319 }
4320
4321 /*
4322 * Check all the NOTATION/NOTATIONS attributes
4323 */
4324 /*
4325 * Check all the ENTITY/ENTITIES attributes definition for validity
4326 */
4327 /*
4328 * Check all the IDREF/IDREFS attributes definition for validity
4329 */
4330 table = (xmlRefTablePtr) doc->refs;
4331 ctxt->doc = doc;
4332 ctxt->valid = 1;
4333 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4334 return(ctxt->valid);
4335}
4336
4337/**
4338 * xmlValidateDtd:
4339 * @ctxt: the validation context
4340 * @doc: a document instance
4341 * @dtd: a dtd instance
4342 *
4343 * Try to validate the document against the dtd instance
4344 *
4345 * basically it does check all the definitions in the DtD.
4346 *
4347 * returns 1 if valid or 0 otherwise
4348 */
4349
4350int
4351xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4352 int ret;
4353 xmlDtdPtr oldExt;
4354 xmlNodePtr root;
4355
4356 if (dtd == NULL) return(0);
4357 if (doc == NULL) return(0);
4358 oldExt = doc->extSubset;
4359 doc->extSubset = dtd;
4360 ret = xmlValidateRoot(ctxt, doc);
4361 if (ret == 0) {
4362 doc->extSubset = oldExt;
4363 return(ret);
4364 }
4365 if (doc->ids != NULL) {
4366 xmlFreeIDTable(doc->ids);
4367 doc->ids = NULL;
4368 }
4369 if (doc->refs != NULL) {
4370 xmlFreeRefTable(doc->refs);
4371 doc->refs = NULL;
4372 }
4373 root = xmlDocGetRootElement(doc);
4374 ret = xmlValidateElement(ctxt, doc, root);
4375 ret &= xmlValidateDocumentFinal(ctxt, doc);
4376 doc->extSubset = oldExt;
4377 return(ret);
4378}
4379
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004380static void
Owen Taylor3473f882001-02-23 17:55:21 +00004381xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004382 const xmlChar *name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004383 if (cur == NULL)
4384 return;
4385 switch (cur->atype) {
4386 case XML_ATTRIBUTE_CDATA:
4387 case XML_ATTRIBUTE_ID:
4388 case XML_ATTRIBUTE_IDREF :
4389 case XML_ATTRIBUTE_IDREFS:
4390 case XML_ATTRIBUTE_NMTOKEN:
4391 case XML_ATTRIBUTE_NMTOKENS:
4392 case XML_ATTRIBUTE_ENUMERATION:
4393 break;
4394 case XML_ATTRIBUTE_ENTITY:
4395 case XML_ATTRIBUTE_ENTITIES:
4396 case XML_ATTRIBUTE_NOTATION:
4397 if (cur->defaultValue != NULL) {
4398 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4399 cur->name, cur->atype, cur->defaultValue);
4400 }
4401 if (cur->tree != NULL) {
4402 xmlEnumerationPtr tree = cur->tree;
4403 while (tree != NULL) {
4404 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4405 cur->name, cur->atype, tree->name);
4406 tree = tree->next;
4407 }
4408 }
4409 }
4410}
4411
4412/**
4413 * xmlValidateDtdFinal:
4414 * @ctxt: the validation context
4415 * @doc: a document instance
4416 *
4417 * Does the final step for the dtds validation once all the
4418 * subsets have been parsed
4419 *
4420 * basically it does the following checks described by the XML Rec
4421 * - check that ENTITY and ENTITIES type attributes default or
4422 * possible values matches one of the defined entities.
4423 * - check that NOTATION type attributes default or
4424 * possible values matches one of the defined notations.
4425 *
4426 * returns 1 if valid or 0 otherwise
4427 */
4428
4429int
4430xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4431 int ret = 1;
4432 xmlDtdPtr dtd;
4433 xmlAttributeTablePtr table;
4434
4435 if (doc == NULL) return(0);
4436 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4437 return(0);
4438 ctxt->doc = doc;
4439 ctxt->valid = ret;
4440 dtd = doc->intSubset;
4441 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4442 table = (xmlAttributeTablePtr) dtd->attributes;
4443 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4444 }
4445 dtd = doc->extSubset;
4446 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4447 table = (xmlAttributeTablePtr) dtd->attributes;
4448 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4449 }
4450 return(ctxt->valid);
4451}
4452
4453/**
4454 * xmlValidateDocument:
4455 * @ctxt: the validation context
4456 * @doc: a document instance
4457 *
4458 * Try to validate the document instance
4459 *
4460 * basically it does the all the checks described by the XML Rec
4461 * i.e. validates the internal and external subset (if present)
4462 * and validate the document tree.
4463 *
4464 * returns 1 if valid or 0 otherwise
4465 */
4466
4467int
4468xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4469 int ret;
4470 xmlNodePtr root;
4471
4472 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4473 return(0);
4474 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4475 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4476 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4477 doc->intSubset->SystemID);
4478 if (doc->extSubset == NULL) {
4479 if (doc->intSubset->SystemID != NULL) {
4480 VERROR(ctxt->userData,
4481 "Could not load the external subset \"%s\"\n",
4482 doc->intSubset->SystemID);
4483 } else {
4484 VERROR(ctxt->userData,
4485 "Could not load the external subset \"%s\"\n",
4486 doc->intSubset->ExternalID);
4487 }
4488 return(0);
4489 }
4490 }
4491
4492 if (doc->ids != NULL) {
4493 xmlFreeIDTable(doc->ids);
4494 doc->ids = NULL;
4495 }
4496 if (doc->refs != NULL) {
4497 xmlFreeRefTable(doc->refs);
4498 doc->refs = NULL;
4499 }
4500 ret = xmlValidateDtdFinal(ctxt, doc);
4501 if (!xmlValidateRoot(ctxt, doc)) return(0);
4502
4503 root = xmlDocGetRootElement(doc);
4504 ret &= xmlValidateElement(ctxt, doc, root);
4505 ret &= xmlValidateDocumentFinal(ctxt, doc);
4506 return(ret);
4507}
4508
4509
4510/************************************************************************
4511 * *
4512 * Routines for dynamic validation editing *
4513 * *
4514 ************************************************************************/
4515
4516/**
4517 * xmlValidGetPotentialChildren:
4518 * @ctree: an element content tree
4519 * @list: an array to store the list of child names
4520 * @len: a pointer to the number of element in the list
4521 * @max: the size of the array
4522 *
4523 * Build/extend a list of potential children allowed by the content tree
4524 *
4525 * returns the number of element in the list, or -1 in case of error.
4526 */
4527
4528int
4529xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4530 int *len, int max) {
4531 int i;
4532
4533 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4534 return(-1);
4535 if (*len >= max) return(*len);
4536
4537 switch (ctree->type) {
4538 case XML_ELEMENT_CONTENT_PCDATA:
4539 for (i = 0; i < *len;i++)
4540 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4541 list[(*len)++] = BAD_CAST "#PCDATA";
4542 break;
4543 case XML_ELEMENT_CONTENT_ELEMENT:
4544 for (i = 0; i < *len;i++)
4545 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4546 list[(*len)++] = ctree->name;
4547 break;
4548 case XML_ELEMENT_CONTENT_SEQ:
4549 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4550 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4551 break;
4552 case XML_ELEMENT_CONTENT_OR:
4553 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4554 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4555 break;
4556 }
4557
4558 return(*len);
4559}
4560
4561/**
4562 * xmlValidGetValidElements:
4563 * @prev: an element to insert after
4564 * @next: an element to insert next
4565 * @list: an array to store the list of child names
4566 * @max: the size of the array
4567 *
4568 * This function returns the list of authorized children to insert
4569 * within an existing tree while respecting the validity constraints
4570 * forced by the Dtd. The insertion point is defined using @prev and
4571 * @next in the following ways:
4572 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4573 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4574 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4575 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4576 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4577 *
4578 * pointers to the element names are inserted at the beginning of the array
4579 * and do not need to be freed.
4580 *
4581 * returns the number of element in the list, or -1 in case of error. If
4582 * the function returns the value @max the caller is invited to grow the
4583 * receiving array and retry.
4584 */
4585
4586int
4587xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4588 int max) {
4589 int nb_valid_elements = 0;
4590 const xmlChar *elements[256];
4591 int nb_elements = 0, i;
4592
4593 xmlNode *ref_node;
4594 xmlNode *parent;
4595 xmlNode *test_node;
4596
4597 xmlNode *prev_next;
4598 xmlNode *next_prev;
4599 xmlNode *parent_childs;
4600 xmlNode *parent_last;
4601
4602 xmlElement *element_desc;
4603
4604 if (prev == NULL && next == NULL)
4605 return(-1);
4606
4607 if (list == NULL) return(-1);
4608 if (max <= 0) return(-1);
4609
4610 nb_valid_elements = 0;
4611 ref_node = prev ? prev : next;
4612 parent = ref_node->parent;
4613
4614 /*
4615 * Retrieves the parent element declaration
4616 */
4617 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
4618 parent->name);
4619 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
4620 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
4621 parent->name);
4622 if (element_desc == NULL) return(-1);
4623
4624 /*
4625 * Do a backup of the current tree structure
4626 */
4627 prev_next = prev ? prev->next : NULL;
4628 next_prev = next ? next->prev : NULL;
4629 parent_childs = parent->children;
4630 parent_last = parent->last;
4631
4632 /*
4633 * Creates a dummy node and insert it into the tree
4634 */
4635 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
4636 test_node->doc = ref_node->doc;
4637 test_node->parent = parent;
4638 test_node->prev = prev;
4639 test_node->next = next;
4640
4641 if (prev) prev->next = test_node;
4642 else parent->children = test_node;
4643
4644 if (next) next->prev = test_node;
4645 else parent->last = test_node;
4646
4647 /*
4648 * Insert each potential child node and check if the parent is
4649 * still valid
4650 */
4651 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
4652 elements, &nb_elements, 256);
4653
4654 for (i = 0;i < nb_elements;i++) {
4655 test_node->name = elements[i];
4656 if (xmlValidateOneElement(NULL, parent->doc, parent)) {
4657 int j;
4658
4659 for (j = 0; j < nb_valid_elements;j++)
4660 if (xmlStrEqual(elements[i], list[j])) break;
4661 list[nb_valid_elements++] = elements[i];
4662 if (nb_valid_elements >= max) break;
4663 }
4664 }
4665
4666 /*
4667 * Restore the tree structure
4668 */
4669 if (prev) prev->next = prev_next;
4670 if (next) next->prev = next_prev;
4671 parent->children = parent_childs;
4672 parent->last = parent_last;
4673
4674 return(nb_valid_elements);
4675}