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