blob: a05a83c2c0395a2ed65aff840fefc12b8893e89f [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
Daniel Veillarde62d36c2001-05-15 08:53:16 +000026/* #define DEBUG_VALID_ALGO */
27
Owen Taylor3473f882001-02-23 17:55:21 +000028/*
29 * Generic function for accessing stacks in the Validity Context
30 */
31
32#define PUSH_AND_POP(scope, type, name) \
33scope int name##VPush(xmlValidCtxtPtr ctxt, type value) { \
Daniel Veillard34b1b3a2001-04-21 14:16:10 +000034 if (ctxt->name##Max <= 0) { \
35 ctxt->name##Max = 4; \
36 ctxt->name##Tab = (type *) xmlMalloc( \
37 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
38 if (ctxt->name##Tab == NULL) { \
39 xmlGenericError(xmlGenericErrorContext, \
40 "malloc failed !\n"); \
41 return(0); \
42 } \
43 } \
Owen Taylor3473f882001-02-23 17:55:21 +000044 if (ctxt->name##Nr >= ctxt->name##Max) { \
45 ctxt->name##Max *= 2; \
46 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
47 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
48 if (ctxt->name##Tab == NULL) { \
49 xmlGenericError(xmlGenericErrorContext, \
50 "realloc failed !\n"); \
51 return(0); \
52 } \
53 } \
54 ctxt->name##Tab[ctxt->name##Nr] = value; \
55 ctxt->name = value; \
56 return(ctxt->name##Nr++); \
57} \
58scope type name##VPop(xmlValidCtxtPtr ctxt) { \
59 type ret; \
60 if (ctxt->name##Nr <= 0) return(0); \
61 ctxt->name##Nr--; \
62 if (ctxt->name##Nr > 0) \
63 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
64 else \
65 ctxt->name = NULL; \
66 ret = ctxt->name##Tab[ctxt->name##Nr]; \
67 ctxt->name##Tab[ctxt->name##Nr] = 0; \
68 return(ret); \
69} \
70
Daniel Veillarddab4cb32001-04-20 13:03:48 +000071/*
72 * I will use a home made algorithm less complex and easier to
73 * debug/maintin than a generic NFA -> DFA state based algo. The
74 * only restriction is on the deepness of the tree limited by the
75 * size of the occurs bitfield
76 *
77 * this is the content of a saved state for rollbacks
78 */
79
80#define ROLLBACK_OR 0
81#define ROLLBACK_PARENT 1
82
83struct _xmlValidState {
84 xmlElementContentPtr cont; /* pointer to the content model subtree */
85 xmlNodePtr node; /* pointer to the current node in the list */
86 long occurs;/* bitfield for multiple occurences */
87 unsigned char depth; /* current depth in the overall tree */
88 unsigned char state; /* ROLLBACK_XXX */
89} _xmlValidState;
90
91#define MAX_DEPTH ((sizeof(_xmlValidState.occurs)) * 8)
92#define CONT ctxt->vstate->cont
93#define NODE ctxt->vstate->node
94#define DEPTH ctxt->vstate->depth
95#define OCCURS ctxt->vstate->occurs
96#define STATE ctxt->vstate->state
97
98#define OCCURENCE (ctxt->vstate->occurs & (1 << DEPTH))
99#define PARENT_OCCURENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
100
101#define SET_OCCURENCE ctxt->vstate->occurs |= (1 << DEPTH)
102#define RESET_OCCURENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
103
104static int
105vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont,
106 xmlNodePtr node, unsigned char depth, long occurs,
107 unsigned char state) {
108 if (ctxt->vstateNr >= ctxt->vstateMax) {
109 ctxt->vstateMax *= 2;
110 ctxt->vstateTab = (xmlValidState *) xmlRealloc(ctxt->vstateTab,
111 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
112 if (ctxt->vstateTab == NULL) {
113 xmlGenericError(xmlGenericErrorContext,
114 "realloc failed !n");
115 return(0);
116 }
Daniel Veillard06803992001-04-22 10:35:56 +0000117 ctxt->vstate = &ctxt->vstateTab[0];
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000118 }
119 ctxt->vstateTab[ctxt->vstateNr].cont = cont;
120 ctxt->vstateTab[ctxt->vstateNr].node = node;
121 ctxt->vstateTab[ctxt->vstateNr].depth = depth;
122 ctxt->vstateTab[ctxt->vstateNr].occurs = occurs;
123 ctxt->vstateTab[ctxt->vstateNr].state = state;
124 return(ctxt->vstateNr++);
125}
126
127static int
128vstateVPop(xmlValidCtxtPtr ctxt) {
129 if (ctxt->vstateNr <= 1) return(-1);
130 ctxt->vstateNr--;
131 ctxt->vstate = &ctxt->vstateTab[0];
132 ctxt->vstate->cont = ctxt->vstateTab[ctxt->vstateNr].cont;
133 ctxt->vstate->node = ctxt->vstateTab[ctxt->vstateNr].node;
134 ctxt->vstate->depth = ctxt->vstateTab[ctxt->vstateNr].depth;
135 ctxt->vstate->occurs = ctxt->vstateTab[ctxt->vstateNr].occurs;
136 ctxt->vstate->state = ctxt->vstateTab[ctxt->vstateNr].state;
137 return(ctxt->vstateNr);
138}
139
Owen Taylor3473f882001-02-23 17:55:21 +0000140PUSH_AND_POP(static, xmlNodePtr, node)
141
Owen Taylor3473f882001-02-23 17:55:21 +0000142#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;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003281 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003282
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003283 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003284 if ((NODE == NULL) && (CONT == NULL))
3285 return(1);
3286 if ((NODE == NULL) &&
3287 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3288 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3289 return(1);
3290 }
3291 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003292 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003293 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003294
3295 /*
3296 * We arrive here when more states need to be examined
3297 */
3298cont:
3299
3300 /*
3301 * We just recovered from a rollback generated by a possible
3302 * epsilon transition, go directly to the analysis phase
3303 */
3304 if (STATE == ROLLBACK_PARENT) {
3305 DEBUG_VALID_MSG("restaured parent branch");
3306 DEBUG_VALID_STATE(NODE, CONT)
3307 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003308 goto analyze;
3309 }
3310
3311 DEBUG_VALID_STATE(NODE, CONT)
3312 /*
3313 * we may have to save a backup state here. This is the equivalent
3314 * of handling epsilon transition in NFAs.
3315 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003316 if ((CONT != NULL) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003317 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003318 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
3319 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003320 DEBUG_VALID_MSG("saving parent branch");
3321 vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
3322 }
3323
3324
3325 /*
3326 * Check first if the content matches
3327 */
3328 switch (CONT->type) {
3329 case XML_ELEMENT_CONTENT_PCDATA:
3330 if (NODE == NULL) {
3331 DEBUG_VALID_MSG("pcdata failed no node");
3332 ret = 0;
3333 break;
3334 }
3335 if (NODE->type == XML_TEXT_NODE) {
3336 DEBUG_VALID_MSG("pcdata found, skip to next");
3337 /*
3338 * go to next element in the content model
3339 * skipping ignorable elems
3340 */
3341 do {
3342 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003343 NODE = xmlValidateSkipIgnorable(NODE);
3344 if ((NODE != NULL) &&
3345 (NODE->type == XML_ENTITY_REF_NODE))
3346 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003347 } while ((NODE != NULL) &&
3348 ((NODE->type != XML_ELEMENT_NODE) &&
3349 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003350 ret = 1;
3351 break;
3352 } else {
3353 DEBUG_VALID_MSG("pcdata failed");
3354 ret = 0;
3355 break;
3356 }
3357 break;
3358 case XML_ELEMENT_CONTENT_ELEMENT:
3359 if (NODE == NULL) {
3360 DEBUG_VALID_MSG("element failed no node");
3361 ret = 0;
3362 break;
3363 }
3364 ret = (xmlStrEqual(NODE->name, CONT->name));
3365 if (ret == 1) {
3366 DEBUG_VALID_MSG("element found, skip to next");
3367 /*
3368 * go to next element in the content model
3369 * skipping ignorable elems
3370 */
3371 do {
3372 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003373 NODE = xmlValidateSkipIgnorable(NODE);
3374 if ((NODE != NULL) &&
3375 (NODE->type == XML_ENTITY_REF_NODE))
3376 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003377 } while ((NODE != NULL) &&
3378 ((NODE->type != XML_ELEMENT_NODE) &&
3379 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003380 } else {
3381 DEBUG_VALID_MSG("element failed");
3382 ret = 0;
3383 break;
3384 }
3385 break;
3386 case XML_ELEMENT_CONTENT_OR:
3387 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003388 * Small optimization.
3389 */
3390 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3391 if ((NODE == NULL) ||
3392 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3393 DEPTH++;
3394 CONT = CONT->c2;
3395 goto cont;
3396 }
3397 }
3398
3399 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003400 * save the second branch 'or' branch
3401 */
3402 DEBUG_VALID_MSG("saving 'or' branch");
3403 vstateVPush(ctxt, CONT->c2, NODE, DEPTH + 1, OCCURS, ROLLBACK_OR);
3404
3405 DEPTH++;
3406 CONT = CONT->c1;
3407 goto cont;
3408 case XML_ELEMENT_CONTENT_SEQ:
3409 DEPTH++;
3410 CONT = CONT->c1;
3411 goto cont;
3412 }
3413
3414 /*
3415 * At this point handle going up in the tree
3416 */
3417 if (ret == -1) {
3418 DEBUG_VALID_MSG("error found returning");
3419 return(ret);
3420 }
3421analyze:
3422 while (CONT != NULL) {
3423 /*
3424 * First do the analysis depending on the occurence model at
3425 * this level.
3426 */
3427 if (ret == 0) {
3428 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003429 xmlNodePtr cur;
3430
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003431 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003432 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003433 DEBUG_VALID_MSG("Once branch failed, rollback");
3434 if (vstateVPop(ctxt) < 0 ) {
3435 DEBUG_VALID_MSG("exhaustion, failed");
3436 return(0);
3437 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003438 if (cur != ctxt->vstate->node)
3439 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003440 goto cont;
3441 case XML_ELEMENT_CONTENT_PLUS:
3442 if (OCCURENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003443 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003444 DEBUG_VALID_MSG("Plus branch failed, rollback");
3445 if (vstateVPop(ctxt) < 0 ) {
3446 DEBUG_VALID_MSG("exhaustion, failed");
3447 return(0);
3448 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003449 if (cur != ctxt->vstate->node)
3450 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003451 goto cont;
3452 }
3453 DEBUG_VALID_MSG("Plus branch found");
3454 ret = 1;
3455 break;
3456 case XML_ELEMENT_CONTENT_MULT:
3457#ifdef DEBUG_VALID_ALGO
3458 if (OCCURENCE == 0) {
3459 DEBUG_VALID_MSG("Mult branch failed");
3460 } else {
3461 DEBUG_VALID_MSG("Mult branch found");
3462 }
3463#endif
3464 ret = 1;
3465 break;
3466 case XML_ELEMENT_CONTENT_OPT:
3467 DEBUG_VALID_MSG("Option branch failed");
3468 ret = 1;
3469 break;
3470 }
3471 } else {
3472 switch (CONT->ocur) {
3473 case XML_ELEMENT_CONTENT_OPT:
3474 DEBUG_VALID_MSG("Option branch succeeded");
3475 ret = 1;
3476 break;
3477 case XML_ELEMENT_CONTENT_ONCE:
3478 DEBUG_VALID_MSG("Once branch succeeded");
3479 ret = 1;
3480 break;
3481 case XML_ELEMENT_CONTENT_PLUS:
3482 if (STATE == ROLLBACK_PARENT) {
3483 DEBUG_VALID_MSG("Plus branch rollback");
3484 ret = 1;
3485 break;
3486 }
3487 if (NODE == NULL) {
3488 DEBUG_VALID_MSG("Plus branch exhausted");
3489 ret = 1;
3490 break;
3491 }
3492 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
3493 SET_OCCURENCE;
3494 goto cont;
3495 case XML_ELEMENT_CONTENT_MULT:
3496 if (STATE == ROLLBACK_PARENT) {
3497 DEBUG_VALID_MSG("Mult branch rollback");
3498 ret = 1;
3499 break;
3500 }
3501 if (NODE == NULL) {
3502 DEBUG_VALID_MSG("Mult branch exhausted");
3503 ret = 1;
3504 break;
3505 }
3506 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
3507 SET_OCCURENCE;
3508 goto cont;
3509 }
3510 }
3511 STATE = 0;
3512
3513 /*
3514 * Then act accordingly at the parent level
3515 */
3516 RESET_OCCURENCE;
3517 if (CONT->parent == NULL)
3518 break;
3519
3520 switch (CONT->parent->type) {
3521 case XML_ELEMENT_CONTENT_PCDATA:
3522 DEBUG_VALID_MSG("Error: parent pcdata");
3523 return(-1);
3524 case XML_ELEMENT_CONTENT_ELEMENT:
3525 DEBUG_VALID_MSG("Error: parent element");
3526 return(-1);
3527 case XML_ELEMENT_CONTENT_OR:
3528 if (ret == 1) {
3529 DEBUG_VALID_MSG("Or succeeded");
3530 CONT = CONT->parent;
3531 DEPTH--;
3532 } else {
3533 DEBUG_VALID_MSG("Or failed");
3534 CONT = CONT->parent;
3535 DEPTH--;
3536 }
3537 break;
3538 case XML_ELEMENT_CONTENT_SEQ:
3539 if (ret == 0) {
3540 DEBUG_VALID_MSG("Sequence failed");
3541 CONT = CONT->parent;
3542 DEPTH--;
3543 } else if (CONT == CONT->parent->c1) {
3544 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3545 CONT = CONT->parent->c2;
3546 goto cont;
3547 } else {
3548 DEBUG_VALID_MSG("Sequence succeeded");
3549 CONT = CONT->parent;
3550 DEPTH--;
3551 }
3552 }
3553 }
3554 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003555 xmlNodePtr cur;
3556
3557 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003558 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3559 if (vstateVPop(ctxt) < 0 ) {
3560 DEBUG_VALID_MSG("exhaustion, failed");
3561 return(0);
3562 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003563 if (cur != ctxt->vstate->node)
3564 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003565 goto cont;
3566 }
3567 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003568 xmlNodePtr cur;
3569
3570 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003571 DEBUG_VALID_MSG("Failure, rollback");
3572 if (vstateVPop(ctxt) < 0 ) {
3573 DEBUG_VALID_MSG("exhaustion, failed");
3574 return(0);
3575 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003576 if (cur != ctxt->vstate->node)
3577 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003578 goto cont;
3579 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003580 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003581}
3582
3583/**
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003584 * xmlSprintfElements:
3585 * @buf: an output buffer
3586 * @content: An element
3587 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3588 *
3589 * This will dump the list of elements to the buffer
3590 * Intended just for the debug routine
3591 */
3592static void
3593xmlSprintfElements(char *buf, xmlNodePtr node, int glob) {
3594 xmlNodePtr cur;
3595
3596 if (node == NULL) return;
3597 if (glob) strcat(buf, "(");
3598 cur = node;
3599 while (cur != NULL) {
3600 switch (cur->type) {
3601 case XML_ELEMENT_NODE:
3602 strcat(buf, (char *) cur->name);
3603 if (cur->next != NULL)
3604 strcat(buf, " ");
3605 break;
3606 case XML_TEXT_NODE:
3607 if (xmlIsBlankNode(cur))
3608 break;
3609 case XML_CDATA_SECTION_NODE:
3610 case XML_ENTITY_REF_NODE:
3611 strcat(buf, "CDATA");
3612 if (cur->next != NULL)
3613 strcat(buf, " ");
3614 break;
3615 case XML_ATTRIBUTE_NODE:
3616 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003617#ifdef LIBXML_DOCB_ENABLED
3618 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003619#endif
3620 case XML_HTML_DOCUMENT_NODE:
3621 case XML_DOCUMENT_TYPE_NODE:
3622 case XML_DOCUMENT_FRAG_NODE:
3623 case XML_NOTATION_NODE:
3624 case XML_NAMESPACE_DECL:
3625 strcat(buf, "???");
3626 if (cur->next != NULL)
3627 strcat(buf, " ");
3628 break;
3629 case XML_ENTITY_NODE:
3630 case XML_PI_NODE:
3631 case XML_DTD_NODE:
3632 case XML_COMMENT_NODE:
3633 case XML_ELEMENT_DECL:
3634 case XML_ATTRIBUTE_DECL:
3635 case XML_ENTITY_DECL:
3636 case XML_XINCLUDE_START:
3637 case XML_XINCLUDE_END:
3638 break;
3639 }
3640 cur = cur->next;
3641 }
3642 if (glob) strcat(buf, ")");
3643}
3644
3645/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003646 * xmlValidateElementContent:
3647 * @ctxt: the validation context
3648 * @child: the child list
3649 * @cont: pointer to the content declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003650 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003651 *
3652 * Try to validate the content model of an element
3653 *
3654 * returns 1 if valid or 0 if not and -1 in case of error
3655 */
3656
3657static int
3658xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillard7533cc82001-04-24 15:52:00 +00003659 xmlElementContentPtr cont, int warn, const xmlChar *name) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003660 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003661 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003662
3663 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003664 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003665 */
3666 ctxt->vstateMax = 8;
3667 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
3668 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
3669 if (ctxt->vstateTab == NULL) {
3670 xmlGenericError(xmlGenericErrorContext,
3671 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003672 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003673 }
3674 /*
3675 * The first entry in the stack is reserved to the current state
3676 */
Daniel Veillard61b33d52001-04-24 13:55:12 +00003677 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003678 ctxt->vstate = &ctxt->vstateTab[0];
3679 ctxt->vstateNr = 1;
3680 CONT = cont;
3681 NODE = child;
3682 DEPTH = 0;
3683 OCCURS = 0;
3684 STATE = 0;
3685 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003686 if ((ret == -3) && (warn)) {
3687 VWARNING(ctxt->userData,
3688 "Element %s content model is ambiguous\n", name);
3689 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003690 /*
3691 * An entities reference appeared at this level.
3692 * Buid a minimal representation of this node content
3693 * sufficient to run the validation process on it
3694 */
3695 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003696 cur = child;
3697 while (cur != NULL) {
3698 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003699 case XML_ENTITY_REF_NODE:
3700 /*
3701 * Push the current node to be able to roll back
3702 * and process within the entity
3703 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003704 if ((cur->children != NULL) &&
3705 (cur->children->children != NULL)) {
3706 nodeVPush(ctxt, cur);
3707 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003708 continue;
3709 }
3710 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003711 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003712 break;
3713 case XML_ELEMENT_NODE:
3714 /*
3715 * Allocate a new node and minimally fills in
3716 * what's required
3717 */
3718 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
3719 if (tmp == NULL) {
3720 xmlGenericError(xmlGenericErrorContext,
3721 "xmlValidateElementContent : malloc failed\n");
3722 xmlFreeNodeList(repl);
3723 ret = -1;
3724 goto done;
3725 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003726 tmp->type = cur->type;
3727 tmp->name = cur->name;
3728 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003729 tmp->next = NULL;
3730 if (repl == NULL)
3731 repl = last = tmp;
3732 else {
3733 last->next = tmp;
3734 last = tmp;
3735 }
3736 break;
3737 default:
3738 break;
3739 }
3740 /*
3741 * Switch to next element
3742 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003743 cur = cur->next;
3744 while (cur == NULL) {
3745 cur = nodeVPop(ctxt);
3746 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003747 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003748 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003749 }
3750 }
3751
3752 /*
3753 * Relaunch the validation
3754 */
3755 ctxt->vstate = &ctxt->vstateTab[0];
3756 ctxt->vstateNr = 1;
3757 CONT = cont;
3758 NODE = repl;
3759 DEPTH = 0;
3760 OCCURS = 0;
3761 STATE = 0;
3762 ret = xmlValidateElementType(ctxt);
3763 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003764 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003765 char expr[5000];
3766 char list[5000];
3767
3768 expr[0] = 0;
3769 xmlSprintfElementContent(expr, cont, 1);
3770 list[0] = 0;
3771 if (repl != NULL)
3772 xmlSprintfElements(list, repl, 1);
3773 else
3774 xmlSprintfElements(list, child, 1);
3775
Daniel Veillard7533cc82001-04-24 15:52:00 +00003776 if (name != NULL) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003777 VERROR(ctxt->userData,
3778 "Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
Daniel Veillard7533cc82001-04-24 15:52:00 +00003779 name, expr, list);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003780 } else {
3781 VERROR(ctxt->userData,
3782 "Element content doesn't follow the Dtd\nExpecting %s, got %s\n",
3783 expr, list);
3784 }
3785 ret = 0;
3786 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003787 if (ret == -3)
3788 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003789
3790
3791done:
3792 /*
3793 * Deallocate the copy if done, and free up the validation stack
3794 */
3795 while (repl != NULL) {
3796 tmp = repl->next;
3797 xmlFree(repl);
3798 repl = tmp;
3799 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003800 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003801 if (ctxt->vstateTab != NULL) {
3802 xmlFree(ctxt->vstateTab);
3803 ctxt->vstateTab = NULL;
3804 }
3805 ctxt->nodeMax = 0;
3806 if (ctxt->nodeTab != NULL) {
3807 xmlFree(ctxt->nodeTab);
3808 ctxt->nodeTab = NULL;
3809 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003810 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003811
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003812}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003813
Owen Taylor3473f882001-02-23 17:55:21 +00003814/**
Owen Taylor3473f882001-02-23 17:55:21 +00003815 * xmlValidateOneElement:
3816 * @ctxt: the validation context
3817 * @doc: a document instance
3818 * @elem: an element instance
3819 *
3820 * Try to validate a single element and it's attributes,
3821 * basically it does the following checks as described by the
3822 * XML-1.0 recommendation:
3823 * - [ VC: Element Valid ]
3824 * - [ VC: Required Attribute ]
3825 * Then call xmlValidateOneAttribute() for each attribute present.
3826 *
3827 * The ID/IDREF checkings are done separately
3828 *
3829 * returns 1 if valid or 0 otherwise
3830 */
3831
3832int
3833xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3834 xmlNodePtr elem) {
3835 xmlElementPtr elemDecl = NULL;
3836 xmlElementContentPtr cont;
3837 xmlAttributePtr attr;
3838 xmlNodePtr child;
3839 int ret = 1;
3840 const xmlChar *name;
3841
3842 CHECK_DTD;
3843
3844 if (elem == NULL) return(0);
3845 if (elem->type == XML_TEXT_NODE) {
3846 }
3847 switch (elem->type) {
3848 case XML_ATTRIBUTE_NODE:
3849 VERROR(ctxt->userData,
3850 "Attribute element not expected here\n");
3851 return(0);
3852 case XML_TEXT_NODE:
3853 if (elem->children != NULL) {
3854 VERROR(ctxt->userData, "Text element has childs !\n");
3855 return(0);
3856 }
3857 if (elem->properties != NULL) {
3858 VERROR(ctxt->userData, "Text element has attributes !\n");
3859 return(0);
3860 }
3861 if (elem->ns != NULL) {
3862 VERROR(ctxt->userData, "Text element has namespace !\n");
3863 return(0);
3864 }
3865 if (elem->nsDef != NULL) {
3866 VERROR(ctxt->userData,
3867 "Text element carries namespace definitions !\n");
3868 return(0);
3869 }
3870 if (elem->content == NULL) {
3871 VERROR(ctxt->userData,
3872 "Text element has no content !\n");
3873 return(0);
3874 }
3875 return(1);
3876 case XML_XINCLUDE_START:
3877 case XML_XINCLUDE_END:
3878 return(1);
3879 case XML_CDATA_SECTION_NODE:
3880 case XML_ENTITY_REF_NODE:
3881 case XML_PI_NODE:
3882 case XML_COMMENT_NODE:
3883 return(1);
3884 case XML_ENTITY_NODE:
3885 VERROR(ctxt->userData,
3886 "Entity element not expected here\n");
3887 return(0);
3888 case XML_NOTATION_NODE:
3889 VERROR(ctxt->userData,
3890 "Notation element not expected here\n");
3891 return(0);
3892 case XML_DOCUMENT_NODE:
3893 case XML_DOCUMENT_TYPE_NODE:
3894 case XML_DOCUMENT_FRAG_NODE:
3895 VERROR(ctxt->userData,
3896 "Document element not expected here\n");
3897 return(0);
3898 case XML_HTML_DOCUMENT_NODE:
3899 VERROR(ctxt->userData,
3900 "\n");
3901 return(0);
3902 case XML_ELEMENT_NODE:
3903 break;
3904 default:
3905 VERROR(ctxt->userData,
3906 "unknown element type %d\n", elem->type);
3907 return(0);
3908 }
3909 if (elem->name == NULL) return(0);
3910
3911 /*
3912 * Fetch the declaration for the qualified name
3913 */
3914 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3915 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
3916 elem->name, elem->ns->prefix);
3917 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3918 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
3919 elem->name, elem->ns->prefix);
3920 }
3921
3922 /*
3923 * Fetch the declaration for the non qualified name
3924 */
3925 if (elemDecl == NULL) {
3926 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
3927 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3928 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
3929 }
3930 if (elemDecl == NULL) {
3931 VERROR(ctxt->userData, "No declaration for element %s\n",
3932 elem->name);
3933 return(0);
3934 }
3935
3936 /* Check taht the element content matches the definition */
3937 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00003938 case XML_ELEMENT_TYPE_UNDEFINED:
3939 VERROR(ctxt->userData, "No declaration for element %s\n",
3940 elem->name);
3941 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00003942 case XML_ELEMENT_TYPE_EMPTY:
3943 if (elem->children != NULL) {
3944 VERROR(ctxt->userData,
3945 "Element %s was declared EMPTY this one has content\n",
3946 elem->name);
3947 ret = 0;
3948 }
3949 break;
3950 case XML_ELEMENT_TYPE_ANY:
3951 /* I don't think anything is required then */
3952 break;
3953 case XML_ELEMENT_TYPE_MIXED:
3954 /* Hum, this start to get messy */
3955 child = elem->children;
3956 while (child != NULL) {
3957 if (child->type == XML_ELEMENT_NODE) {
3958 name = child->name;
3959 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
3960 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003961 snprintf((char *) qname, sizeof(qname), "%s:%s",
3962 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003963 qname[sizeof(qname) - 1] = 0;
3964 cont = elemDecl->content;
3965 while (cont != NULL) {
3966 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
3967 if (xmlStrEqual(cont->name, qname)) break;
3968 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
3969 (cont->c1 != NULL) &&
3970 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
3971 if (xmlStrEqual(cont->c1->name, qname)) break;
3972 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
3973 (cont->c1 == NULL) ||
3974 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
3975 /* Internal error !!! */
3976 xmlGenericError(xmlGenericErrorContext,
3977 "Internal: MIXED struct bad\n");
3978 break;
3979 }
3980 cont = cont->c2;
3981 }
3982 if (cont != NULL)
3983 goto child_ok;
3984 }
3985 cont = elemDecl->content;
3986 while (cont != NULL) {
3987 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
3988 if (xmlStrEqual(cont->name, name)) break;
3989 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
3990 (cont->c1 != NULL) &&
3991 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
3992 if (xmlStrEqual(cont->c1->name, name)) break;
3993 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
3994 (cont->c1 == NULL) ||
3995 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
3996 /* Internal error !!! */
3997 xmlGenericError(xmlGenericErrorContext,
3998 "Internal: MIXED struct bad\n");
3999 break;
4000 }
4001 cont = cont->c2;
4002 }
4003 if (cont == NULL) {
4004 VERROR(ctxt->userData,
4005 "Element %s is not declared in %s list of possible childs\n",
4006 name, elem->name);
4007 ret = 0;
4008 }
4009 }
4010child_ok:
4011 child = child->next;
4012 }
4013 break;
4014 case XML_ELEMENT_TYPE_ELEMENT:
4015 child = elem->children;
4016 cont = elemDecl->content;
Daniel Veillard7533cc82001-04-24 15:52:00 +00004017 ret = xmlValidateElementContent(ctxt, child, cont, 1, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004018 break;
4019 }
4020
4021 /* [ VC: Required Attribute ] */
4022 attr = elemDecl->attributes;
4023 while (attr != NULL) {
4024 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
4025 xmlAttrPtr attrib;
4026 int qualified = -1;
4027
4028 attrib = elem->properties;
4029 while (attrib != NULL) {
4030 if (xmlStrEqual(attrib->name, attr->name)) {
4031 if (attr->prefix != NULL) {
4032 xmlNsPtr nameSpace = attrib->ns;
4033
4034 if (nameSpace == NULL)
4035 nameSpace = elem->ns;
4036 /*
4037 * qualified names handling is problematic, having a
4038 * different prefix should be possible but DTDs don't
4039 * allow to define the URI instead of the prefix :-(
4040 */
4041 if (nameSpace == NULL) {
4042 if (qualified < 0)
4043 qualified = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004044 } else if (!xmlStrEqual(nameSpace->prefix,
4045 attr->prefix)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004046 if (qualified < 1)
4047 qualified = 1;
4048 } else
4049 goto found;
4050 } else {
4051 /*
4052 * We should allow applications to define namespaces
4053 * for their application even if the DTD doesn't
4054 * carry one, otherwise, basically we would always
4055 * break.
4056 */
4057 goto found;
4058 }
4059 }
4060 attrib = attrib->next;
4061 }
4062 if (qualified == -1) {
4063 if (attr->prefix == NULL) {
4064 VERROR(ctxt->userData,
4065 "Element %s doesn't carry attribute %s\n",
4066 elem->name, attr->name);
4067 ret = 0;
4068 } else {
4069 VERROR(ctxt->userData,
4070 "Element %s doesn't carry attribute %s:%s\n",
4071 elem->name, attr->prefix,attr->name);
4072 ret = 0;
4073 }
4074 } else if (qualified == 0) {
4075 VWARNING(ctxt->userData,
4076 "Element %s required attribute %s:%s has no prefix\n",
4077 elem->name, attr->prefix,attr->name);
4078 } else if (qualified == 1) {
4079 VWARNING(ctxt->userData,
4080 "Element %s required attribute %s:%s has different prefix\n",
4081 elem->name, attr->prefix,attr->name);
4082 }
4083 }
4084found:
4085 attr = attr->nexth;
4086 }
4087 return(ret);
4088}
4089
4090/**
4091 * xmlValidateRoot:
4092 * @ctxt: the validation context
4093 * @doc: a document instance
4094 *
4095 * Try to validate a the root element
4096 * basically it does the following check as described by the
4097 * XML-1.0 recommendation:
4098 * - [ VC: Root Element Type ]
4099 * it doesn't try to recurse or apply other check to the element
4100 *
4101 * returns 1 if valid or 0 otherwise
4102 */
4103
4104int
4105xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4106 xmlNodePtr root;
4107 if (doc == NULL) return(0);
4108
4109 root = xmlDocGetRootElement(doc);
4110 if ((root == NULL) || (root->name == NULL)) {
4111 VERROR(ctxt->userData, "Not valid: no root element\n");
4112 return(0);
4113 }
4114
4115 /*
4116 * When doing post validation against a separate DTD, those may
4117 * no internal subset has been generated
4118 */
4119 if ((doc->intSubset != NULL) &&
4120 (doc->intSubset->name != NULL)) {
4121 /*
4122 * Check first the document root against the NQName
4123 */
4124 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4125 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4126 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004127 snprintf((char *) qname, sizeof(qname), "%s:%s",
4128 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004129 qname[sizeof(qname) - 1] = 0;
4130 if (xmlStrEqual(doc->intSubset->name, qname))
4131 goto name_ok;
4132 }
4133 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4134 (xmlStrEqual(root->name, BAD_CAST "html")))
4135 goto name_ok;
4136 VERROR(ctxt->userData,
4137 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4138 root->name, doc->intSubset->name);
4139 return(0);
4140
4141 }
4142 }
4143name_ok:
4144 return(1);
4145}
4146
4147
4148/**
4149 * xmlValidateElement:
4150 * @ctxt: the validation context
4151 * @doc: a document instance
4152 * @elem: an element instance
4153 *
4154 * Try to validate the subtree under an element
4155 *
4156 * returns 1 if valid or 0 otherwise
4157 */
4158
4159int
4160xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4161 xmlNodePtr child;
4162 xmlAttrPtr attr;
4163 xmlChar *value;
4164 int ret = 1;
4165
4166 if (elem == NULL) return(0);
4167
4168 /*
4169 * XInclude elements were added after parsing in the infoset,
4170 * they don't really mean anything validation wise.
4171 */
4172 if ((elem->type == XML_XINCLUDE_START) ||
4173 (elem->type == XML_XINCLUDE_END))
4174 return(1);
4175
4176 CHECK_DTD;
4177
4178 ret &= xmlValidateOneElement(ctxt, doc, elem);
4179 attr = elem->properties;
4180 while(attr != NULL) {
4181 value = xmlNodeListGetString(doc, attr->children, 0);
4182 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4183 if (value != NULL)
4184 xmlFree(value);
4185 attr= attr->next;
4186 }
4187 child = elem->children;
4188 while (child != NULL) {
4189 ret &= xmlValidateElement(ctxt, doc, child);
4190 child = child->next;
4191 }
4192
4193 return(ret);
4194}
4195
Daniel Veillard8730c562001-02-26 10:49:57 +00004196/**
4197 * xmlValidateRef:
4198 * @ref: A reference to be validated
4199 * @ctxt: Validation context
4200 * @name: Name of ID we are searching for
4201 *
4202 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004203static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004204xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004205 const xmlChar *name) {
4206 xmlAttrPtr id;
4207 xmlAttrPtr attr;
4208
4209 if (ref == NULL)
4210 return;
4211 attr = ref->attr;
4212 if (attr == NULL)
4213 return;
4214 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4215 id = xmlGetID(ctxt->doc, name);
4216 if (id == NULL) {
4217 VERROR(ctxt->userData,
4218 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4219 attr->name, name);
4220 ctxt->valid = 0;
4221 }
4222 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4223 xmlChar *dup, *str = NULL, *cur, save;
4224
4225 dup = xmlStrdup(name);
4226 if (dup == NULL) {
4227 ctxt->valid = 0;
4228 return;
4229 }
4230 cur = dup;
4231 while (*cur != 0) {
4232 str = cur;
4233 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4234 save = *cur;
4235 *cur = 0;
4236 id = xmlGetID(ctxt->doc, str);
4237 if (id == NULL) {
4238 VERROR(ctxt->userData,
4239 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4240 attr->name, str);
4241 ctxt->valid = 0;
4242 }
4243 if (save == 0)
4244 break;
4245 *cur = save;
4246 while (IS_BLANK(*cur)) cur++;
4247 }
4248 xmlFree(dup);
4249 }
4250}
4251
4252/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004253 * xmlWalkValidateList:
4254 * @data: Contents of current link
4255 * @user: Value supplied by the user
4256 *
4257 * Return 0 to abort the walk or 1 to continue
4258 */
4259static int
4260xmlWalkValidateList(const void *data, const void *user)
4261{
4262 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4263 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4264 return 1;
4265}
4266
4267/**
4268 * xmlValidateCheckRefCallback:
4269 * @ref_list: List of references
4270 * @ctxt: Validation context
4271 * @name: Name of ID we are searching for
4272 *
4273 */
4274static void
4275xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4276 const xmlChar *name) {
4277 xmlValidateMemo memo;
4278
4279 if (ref_list == NULL)
4280 return;
4281 memo.ctxt = ctxt;
4282 memo.name = name;
4283
4284 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4285
4286}
4287
4288/**
Owen Taylor3473f882001-02-23 17:55:21 +00004289 * xmlValidateDocumentFinal:
4290 * @ctxt: the validation context
4291 * @doc: a document instance
4292 *
4293 * Does the final step for the document validation once all the
4294 * incremental validation steps have been completed
4295 *
4296 * basically it does the following checks described by the XML Rec
4297 *
4298 *
4299 * returns 1 if valid or 0 otherwise
4300 */
4301
4302int
4303xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4304 xmlRefTablePtr table;
4305
4306 if (doc == NULL) {
4307 xmlGenericError(xmlGenericErrorContext,
4308 "xmlValidateDocumentFinal: doc == NULL\n");
4309 return(0);
4310 }
4311
4312 /*
4313 * Check all the NOTATION/NOTATIONS attributes
4314 */
4315 /*
4316 * Check all the ENTITY/ENTITIES attributes definition for validity
4317 */
4318 /*
4319 * Check all the IDREF/IDREFS attributes definition for validity
4320 */
4321 table = (xmlRefTablePtr) doc->refs;
4322 ctxt->doc = doc;
4323 ctxt->valid = 1;
4324 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4325 return(ctxt->valid);
4326}
4327
4328/**
4329 * xmlValidateDtd:
4330 * @ctxt: the validation context
4331 * @doc: a document instance
4332 * @dtd: a dtd instance
4333 *
4334 * Try to validate the document against the dtd instance
4335 *
4336 * basically it does check all the definitions in the DtD.
4337 *
4338 * returns 1 if valid or 0 otherwise
4339 */
4340
4341int
4342xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4343 int ret;
4344 xmlDtdPtr oldExt;
4345 xmlNodePtr root;
4346
4347 if (dtd == NULL) return(0);
4348 if (doc == NULL) return(0);
4349 oldExt = doc->extSubset;
4350 doc->extSubset = dtd;
4351 ret = xmlValidateRoot(ctxt, doc);
4352 if (ret == 0) {
4353 doc->extSubset = oldExt;
4354 return(ret);
4355 }
4356 if (doc->ids != NULL) {
4357 xmlFreeIDTable(doc->ids);
4358 doc->ids = NULL;
4359 }
4360 if (doc->refs != NULL) {
4361 xmlFreeRefTable(doc->refs);
4362 doc->refs = NULL;
4363 }
4364 root = xmlDocGetRootElement(doc);
4365 ret = xmlValidateElement(ctxt, doc, root);
4366 ret &= xmlValidateDocumentFinal(ctxt, doc);
4367 doc->extSubset = oldExt;
4368 return(ret);
4369}
4370
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004371static void
Owen Taylor3473f882001-02-23 17:55:21 +00004372xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004373 const xmlChar *name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004374 if (cur == NULL)
4375 return;
4376 switch (cur->atype) {
4377 case XML_ATTRIBUTE_CDATA:
4378 case XML_ATTRIBUTE_ID:
4379 case XML_ATTRIBUTE_IDREF :
4380 case XML_ATTRIBUTE_IDREFS:
4381 case XML_ATTRIBUTE_NMTOKEN:
4382 case XML_ATTRIBUTE_NMTOKENS:
4383 case XML_ATTRIBUTE_ENUMERATION:
4384 break;
4385 case XML_ATTRIBUTE_ENTITY:
4386 case XML_ATTRIBUTE_ENTITIES:
4387 case XML_ATTRIBUTE_NOTATION:
4388 if (cur->defaultValue != NULL) {
4389 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4390 cur->name, cur->atype, cur->defaultValue);
4391 }
4392 if (cur->tree != NULL) {
4393 xmlEnumerationPtr tree = cur->tree;
4394 while (tree != NULL) {
4395 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4396 cur->name, cur->atype, tree->name);
4397 tree = tree->next;
4398 }
4399 }
4400 }
4401}
4402
4403/**
4404 * xmlValidateDtdFinal:
4405 * @ctxt: the validation context
4406 * @doc: a document instance
4407 *
4408 * Does the final step for the dtds validation once all the
4409 * subsets have been parsed
4410 *
4411 * basically it does the following checks described by the XML Rec
4412 * - check that ENTITY and ENTITIES type attributes default or
4413 * possible values matches one of the defined entities.
4414 * - check that NOTATION type attributes default or
4415 * possible values matches one of the defined notations.
4416 *
4417 * returns 1 if valid or 0 otherwise
4418 */
4419
4420int
4421xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4422 int ret = 1;
4423 xmlDtdPtr dtd;
4424 xmlAttributeTablePtr table;
4425
4426 if (doc == NULL) return(0);
4427 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4428 return(0);
4429 ctxt->doc = doc;
4430 ctxt->valid = ret;
4431 dtd = doc->intSubset;
4432 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4433 table = (xmlAttributeTablePtr) dtd->attributes;
4434 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4435 }
4436 dtd = doc->extSubset;
4437 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4438 table = (xmlAttributeTablePtr) dtd->attributes;
4439 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4440 }
4441 return(ctxt->valid);
4442}
4443
4444/**
4445 * xmlValidateDocument:
4446 * @ctxt: the validation context
4447 * @doc: a document instance
4448 *
4449 * Try to validate the document instance
4450 *
4451 * basically it does the all the checks described by the XML Rec
4452 * i.e. validates the internal and external subset (if present)
4453 * and validate the document tree.
4454 *
4455 * returns 1 if valid or 0 otherwise
4456 */
4457
4458int
4459xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4460 int ret;
4461 xmlNodePtr root;
4462
4463 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4464 return(0);
4465 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4466 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4467 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4468 doc->intSubset->SystemID);
4469 if (doc->extSubset == NULL) {
4470 if (doc->intSubset->SystemID != NULL) {
4471 VERROR(ctxt->userData,
4472 "Could not load the external subset \"%s\"\n",
4473 doc->intSubset->SystemID);
4474 } else {
4475 VERROR(ctxt->userData,
4476 "Could not load the external subset \"%s\"\n",
4477 doc->intSubset->ExternalID);
4478 }
4479 return(0);
4480 }
4481 }
4482
4483 if (doc->ids != NULL) {
4484 xmlFreeIDTable(doc->ids);
4485 doc->ids = NULL;
4486 }
4487 if (doc->refs != NULL) {
4488 xmlFreeRefTable(doc->refs);
4489 doc->refs = NULL;
4490 }
4491 ret = xmlValidateDtdFinal(ctxt, doc);
4492 if (!xmlValidateRoot(ctxt, doc)) return(0);
4493
4494 root = xmlDocGetRootElement(doc);
4495 ret &= xmlValidateElement(ctxt, doc, root);
4496 ret &= xmlValidateDocumentFinal(ctxt, doc);
4497 return(ret);
4498}
4499
4500
4501/************************************************************************
4502 * *
4503 * Routines for dynamic validation editing *
4504 * *
4505 ************************************************************************/
4506
4507/**
4508 * xmlValidGetPotentialChildren:
4509 * @ctree: an element content tree
4510 * @list: an array to store the list of child names
4511 * @len: a pointer to the number of element in the list
4512 * @max: the size of the array
4513 *
4514 * Build/extend a list of potential children allowed by the content tree
4515 *
4516 * returns the number of element in the list, or -1 in case of error.
4517 */
4518
4519int
4520xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4521 int *len, int max) {
4522 int i;
4523
4524 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4525 return(-1);
4526 if (*len >= max) return(*len);
4527
4528 switch (ctree->type) {
4529 case XML_ELEMENT_CONTENT_PCDATA:
4530 for (i = 0; i < *len;i++)
4531 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4532 list[(*len)++] = BAD_CAST "#PCDATA";
4533 break;
4534 case XML_ELEMENT_CONTENT_ELEMENT:
4535 for (i = 0; i < *len;i++)
4536 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4537 list[(*len)++] = ctree->name;
4538 break;
4539 case XML_ELEMENT_CONTENT_SEQ:
4540 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4541 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4542 break;
4543 case XML_ELEMENT_CONTENT_OR:
4544 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4545 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4546 break;
4547 }
4548
4549 return(*len);
4550}
4551
4552/**
4553 * xmlValidGetValidElements:
4554 * @prev: an element to insert after
4555 * @next: an element to insert next
4556 * @list: an array to store the list of child names
4557 * @max: the size of the array
4558 *
4559 * This function returns the list of authorized children to insert
4560 * within an existing tree while respecting the validity constraints
4561 * forced by the Dtd. The insertion point is defined using @prev and
4562 * @next in the following ways:
4563 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4564 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4565 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4566 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4567 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4568 *
4569 * pointers to the element names are inserted at the beginning of the array
4570 * and do not need to be freed.
4571 *
4572 * returns the number of element in the list, or -1 in case of error. If
4573 * the function returns the value @max the caller is invited to grow the
4574 * receiving array and retry.
4575 */
4576
4577int
4578xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4579 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004580 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00004581 int nb_valid_elements = 0;
4582 const xmlChar *elements[256];
4583 int nb_elements = 0, i;
4584
4585 xmlNode *ref_node;
4586 xmlNode *parent;
4587 xmlNode *test_node;
4588
4589 xmlNode *prev_next;
4590 xmlNode *next_prev;
4591 xmlNode *parent_childs;
4592 xmlNode *parent_last;
4593
4594 xmlElement *element_desc;
4595
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004596 vctxt.userData = NULL;
4597 vctxt.error = NULL;
4598 vctxt.warning = NULL;
4599
Owen Taylor3473f882001-02-23 17:55:21 +00004600 if (prev == NULL && next == NULL)
4601 return(-1);
4602
4603 if (list == NULL) return(-1);
4604 if (max <= 0) return(-1);
4605
4606 nb_valid_elements = 0;
4607 ref_node = prev ? prev : next;
4608 parent = ref_node->parent;
4609
4610 /*
4611 * Retrieves the parent element declaration
4612 */
4613 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
4614 parent->name);
4615 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
4616 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
4617 parent->name);
4618 if (element_desc == NULL) return(-1);
4619
4620 /*
4621 * Do a backup of the current tree structure
4622 */
4623 prev_next = prev ? prev->next : NULL;
4624 next_prev = next ? next->prev : NULL;
4625 parent_childs = parent->children;
4626 parent_last = parent->last;
4627
4628 /*
4629 * Creates a dummy node and insert it into the tree
4630 */
4631 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
4632 test_node->doc = ref_node->doc;
4633 test_node->parent = parent;
4634 test_node->prev = prev;
4635 test_node->next = next;
4636
4637 if (prev) prev->next = test_node;
4638 else parent->children = test_node;
4639
4640 if (next) next->prev = test_node;
4641 else parent->last = test_node;
4642
4643 /*
4644 * Insert each potential child node and check if the parent is
4645 * still valid
4646 */
4647 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
4648 elements, &nb_elements, 256);
4649
4650 for (i = 0;i < nb_elements;i++) {
4651 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004652 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004653 int j;
4654
4655 for (j = 0; j < nb_valid_elements;j++)
4656 if (xmlStrEqual(elements[i], list[j])) break;
4657 list[nb_valid_elements++] = elements[i];
4658 if (nb_valid_elements >= max) break;
4659 }
4660 }
4661
4662 /*
4663 * Restore the tree structure
4664 */
4665 if (prev) prev->next = prev_next;
4666 if (next) next->prev = next_prev;
4667 parent->children = parent_childs;
4668 parent->last = parent_last;
4669
4670 return(nb_valid_elements);
4671}