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