blob: c894ff68589cf2046afa84c5d4f8cb5f704b9b55 [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;
Daniel Veillardce2c2f02001-10-18 14:57:24 +0000202#ifdef LIBXML_DOCB_ENABLED
203 case XML_DOCB_DOCUMENT_NODE:
204 xmlGenericError(xmlGenericErrorContext, "?docb? ");
205 break;
206#endif
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000207 case XML_DTD_NODE:
208 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
209 break;
210 case XML_ELEMENT_DECL:
211 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
212 break;
213 case XML_ATTRIBUTE_DECL:
214 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
215 break;
216 case XML_ENTITY_DECL:
217 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
218 break;
219 case XML_NAMESPACE_DECL:
220 xmlGenericError(xmlGenericErrorContext, "?nsdecl? ");
221 break;
222 case XML_XINCLUDE_START:
223 xmlGenericError(xmlGenericErrorContext, "incstart ");
224 break;
225 case XML_XINCLUDE_END:
226 xmlGenericError(xmlGenericErrorContext, "incend ");
227 break;
228 }
229}
230
231static void
232xmlValidPrintNodeList(xmlNodePtr cur) {
Owen Taylor3473f882001-02-23 17:55:21 +0000233 if (cur == NULL)
234 xmlGenericError(xmlGenericErrorContext, "null ");
235 while (cur != NULL) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000236 xmlValidPrintNode(cur);
Owen Taylor3473f882001-02-23 17:55:21 +0000237 cur = cur->next;
238 }
239}
240
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000241static void
242xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
Owen Taylor3473f882001-02-23 17:55:21 +0000243 char expr[1000];
244
245 expr[0] = 0;
246 xmlGenericError(xmlGenericErrorContext, "valid: ");
247 xmlValidPrintNodeList(cur);
248 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardd3d06722001-08-15 12:06:36 +0000249 xmlSnprintfElementContent(expr, 5000, cont, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000250 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
251}
252
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000253static void
254xmlValidDebugState(xmlValidStatePtr state) {
255 xmlGenericError(xmlGenericErrorContext, "(");
256 if (state->cont == NULL)
257 xmlGenericError(xmlGenericErrorContext, "null,");
258 else
259 switch (state->cont->type) {
260 case XML_ELEMENT_CONTENT_PCDATA:
261 xmlGenericError(xmlGenericErrorContext, "pcdata,");
262 break;
263 case XML_ELEMENT_CONTENT_ELEMENT:
264 xmlGenericError(xmlGenericErrorContext, "%s,",
265 state->cont->name);
266 break;
267 case XML_ELEMENT_CONTENT_SEQ:
268 xmlGenericError(xmlGenericErrorContext, "seq,");
269 break;
270 case XML_ELEMENT_CONTENT_OR:
271 xmlGenericError(xmlGenericErrorContext, "or,");
272 break;
273 }
274 xmlValidPrintNode(state->node);
275 xmlGenericError(xmlGenericErrorContext, ",%d,%X,%d)",
276 state->depth, state->occurs, state->state);
277}
278
279static void
280xmlValidStateDebug(xmlValidCtxtPtr ctxt) {
281 int i, j;
282
283 xmlGenericError(xmlGenericErrorContext, "state: ");
284 xmlValidDebugState(ctxt->vstate);
285 xmlGenericError(xmlGenericErrorContext, " stack: %d ",
286 ctxt->vstateNr - 1);
287 for (i = 0, j = ctxt->vstateNr - 1;(i < 3) && (j > 0);i++,j--)
288 xmlValidDebugState(&ctxt->vstateTab[j]);
289 xmlGenericError(xmlGenericErrorContext, "\n");
290}
291
292/*****
Owen Taylor3473f882001-02-23 17:55:21 +0000293#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000294 *****/
295
296#define DEBUG_VALID_STATE(n,c) xmlValidStateDebug(ctxt);
Daniel Veillarde356c282001-03-10 12:32:04 +0000297#define DEBUG_VALID_MSG(m) \
298 xmlGenericError(xmlGenericErrorContext, "%s\n", m);
299
Owen Taylor3473f882001-02-23 17:55:21 +0000300#else
301#define DEBUG_VALID_STATE(n,c)
Daniel Veillarde356c282001-03-10 12:32:04 +0000302#define DEBUG_VALID_MSG(m)
Owen Taylor3473f882001-02-23 17:55:21 +0000303#endif
304
305/* TODO: use hash table for accesses to elem and attribute dedinitions */
306
307#define VERROR \
308 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
309
310#define VWARNING \
311 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
312
313#define CHECK_DTD \
314 if (doc == NULL) return(0); \
315 else if ((doc->intSubset == NULL) && \
316 (doc->extSubset == NULL)) return(0)
317
318xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000319static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
320 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000321xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
322
323/************************************************************************
324 * *
325 * QName handling helper *
326 * *
327 ************************************************************************/
328
329/**
330 * xmlSplitQName2:
331 * @name: an XML parser context
332 * @prefix: a xmlChar **
333 *
334 * parse an XML qualified name string
335 *
336 * [NS 5] QName ::= (Prefix ':')? LocalPart
337 *
338 * [NS 6] Prefix ::= NCName
339 *
340 * [NS 7] LocalPart ::= NCName
341 *
342 * Returns NULL if not a QName, otherwise the local part, and prefix
343 * is updated to get the Prefix if any.
344 */
345
346xmlChar *
347xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
348 int len = 0;
349 xmlChar *ret = NULL;
350
351 *prefix = NULL;
352
Daniel Veillardf4309d72001-10-02 09:28:58 +0000353#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000354 /* xml: prefix is not really a namespace */
355 if ((name[0] == 'x') && (name[1] == 'm') &&
356 (name[2] == 'l') && (name[3] == ':'))
357 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000358#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000359
360 /* nasty but valid */
361 if (name[0] == ':')
362 return(NULL);
363
364 /*
365 * we are not trying to validate but just to cut, and yes it will
366 * work even if this is as set of UTF-8 encoded chars
367 */
368 while ((name[len] != 0) && (name[len] != ':'))
369 len++;
370
371 if (name[len] == 0)
372 return(NULL);
373
374 *prefix = xmlStrndup(name, len);
375 ret = xmlStrdup(&name[len + 1]);
376
377 return(ret);
378}
379
380/****************************************************************
381 * *
382 * Util functions for data allocation/deallocation *
383 * *
384 ****************************************************************/
385
386/**
387 * xmlNewElementContent:
388 * @name: the subelement name or NULL
389 * @type: the type of element content decl
390 *
391 * Allocate an element content structure.
392 *
393 * Returns NULL if not, othervise the new element content structure
394 */
395xmlElementContentPtr
396xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
397 xmlElementContentPtr ret;
398
399 switch(type) {
400 case XML_ELEMENT_CONTENT_ELEMENT:
401 if (name == NULL) {
402 xmlGenericError(xmlGenericErrorContext,
403 "xmlNewElementContent : name == NULL !\n");
404 }
405 break;
406 case XML_ELEMENT_CONTENT_PCDATA:
407 case XML_ELEMENT_CONTENT_SEQ:
408 case XML_ELEMENT_CONTENT_OR:
409 if (name != NULL) {
410 xmlGenericError(xmlGenericErrorContext,
411 "xmlNewElementContent : name != NULL !\n");
412 }
413 break;
414 default:
415 xmlGenericError(xmlGenericErrorContext,
416 "xmlNewElementContent: unknown type %d\n", type);
417 return(NULL);
418 }
419 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
420 if (ret == NULL) {
421 xmlGenericError(xmlGenericErrorContext,
422 "xmlNewElementContent : out of memory!\n");
423 return(NULL);
424 }
425 ret->type = type;
426 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
427 if (name != NULL)
428 ret->name = xmlStrdup(name);
429 else
430 ret->name = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000431 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000432 return(ret);
433}
434
435/**
436 * xmlCopyElementContent:
437 * @content: An element content pointer.
438 *
439 * Build a copy of an element content description.
440 *
441 * Returns the new xmlElementContentPtr or NULL in case of error.
442 */
443xmlElementContentPtr
444xmlCopyElementContent(xmlElementContentPtr cur) {
445 xmlElementContentPtr ret;
446
447 if (cur == NULL) return(NULL);
448 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
449 if (ret == NULL) {
450 xmlGenericError(xmlGenericErrorContext,
451 "xmlCopyElementContent : out of memory\n");
452 return(NULL);
453 }
454 ret->ocur = cur->ocur;
455 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000456 if (ret->c1 != NULL)
457 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000458 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000459 if (ret->c2 != NULL)
460 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000461 return(ret);
462}
463
464/**
465 * xmlFreeElementContent:
466 * @cur: the element content tree to free
467 *
468 * Free an element content structure. This is a recursive call !
469 */
470void
471xmlFreeElementContent(xmlElementContentPtr cur) {
472 if (cur == NULL) return;
473 switch (cur->type) {
474 case XML_ELEMENT_CONTENT_PCDATA:
475 case XML_ELEMENT_CONTENT_ELEMENT:
476 case XML_ELEMENT_CONTENT_SEQ:
477 case XML_ELEMENT_CONTENT_OR:
478 break;
479 default:
480 xmlGenericError(xmlGenericErrorContext,
481 "xmlFreeElementContent : type %d\n", cur->type);
482 return;
483 }
484 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
485 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
486 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +0000487 xmlFree(cur);
488}
489
490/**
491 * xmlDumpElementContent:
492 * @buf: An XML buffer
493 * @content: An element table
494 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
495 *
496 * This will dump the content of the element table as an XML DTD definition
497 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000498static void
Owen Taylor3473f882001-02-23 17:55:21 +0000499xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
500 if (content == NULL) return;
501
502 if (glob) xmlBufferWriteChar(buf, "(");
503 switch (content->type) {
504 case XML_ELEMENT_CONTENT_PCDATA:
505 xmlBufferWriteChar(buf, "#PCDATA");
506 break;
507 case XML_ELEMENT_CONTENT_ELEMENT:
508 xmlBufferWriteCHAR(buf, content->name);
509 break;
510 case XML_ELEMENT_CONTENT_SEQ:
511 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
512 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
513 xmlDumpElementContent(buf, content->c1, 1);
514 else
515 xmlDumpElementContent(buf, content->c1, 0);
516 xmlBufferWriteChar(buf, " , ");
517 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
518 xmlDumpElementContent(buf, content->c2, 1);
519 else
520 xmlDumpElementContent(buf, content->c2, 0);
521 break;
522 case XML_ELEMENT_CONTENT_OR:
523 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
524 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
525 xmlDumpElementContent(buf, content->c1, 1);
526 else
527 xmlDumpElementContent(buf, content->c1, 0);
528 xmlBufferWriteChar(buf, " | ");
529 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
530 xmlDumpElementContent(buf, content->c2, 1);
531 else
532 xmlDumpElementContent(buf, content->c2, 0);
533 break;
534 default:
535 xmlGenericError(xmlGenericErrorContext,
536 "xmlDumpElementContent: unknown type %d\n",
537 content->type);
538 }
539 if (glob)
540 xmlBufferWriteChar(buf, ")");
541 switch (content->ocur) {
542 case XML_ELEMENT_CONTENT_ONCE:
543 break;
544 case XML_ELEMENT_CONTENT_OPT:
545 xmlBufferWriteChar(buf, "?");
546 break;
547 case XML_ELEMENT_CONTENT_MULT:
548 xmlBufferWriteChar(buf, "*");
549 break;
550 case XML_ELEMENT_CONTENT_PLUS:
551 xmlBufferWriteChar(buf, "+");
552 break;
553 }
554}
555
556/**
557 * xmlSprintfElementContent:
558 * @buf: an output buffer
559 * @content: An element table
560 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
561 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000562 * Deprecated, unsafe, use xmlSnprintfElementContent
563 */
564void
565xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
566 xmlElementContentPtr content ATTRIBUTE_UNUSED,
567 int glob ATTRIBUTE_UNUSED) {
568}
569
570/**
571 * xmlSnprintfElementContent:
572 * @buf: an output buffer
573 * @size: the buffer size
574 * @content: An element table
575 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
576 *
Owen Taylor3473f882001-02-23 17:55:21 +0000577 * This will dump the content of the element content definition
578 * Intended just for the debug routine
579 */
580void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000581xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
582 int len;
583
Owen Taylor3473f882001-02-23 17:55:21 +0000584 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000585 len = strlen(buf);
586 if (size - len < 50) {
587 if ((size - len > 4) && (buf[len - 1] != '.'))
588 strcat(buf, " ...");
589 return;
590 }
Owen Taylor3473f882001-02-23 17:55:21 +0000591 if (glob) strcat(buf, "(");
592 switch (content->type) {
593 case XML_ELEMENT_CONTENT_PCDATA:
594 strcat(buf, "#PCDATA");
595 break;
596 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardd3d06722001-08-15 12:06:36 +0000597 if (size - len < xmlStrlen(content->name + 10)) {
598 strcat(buf, " ...");
599 return;
600 }
Owen Taylor3473f882001-02-23 17:55:21 +0000601 strcat(buf, (char *) content->name);
602 break;
603 case XML_ELEMENT_CONTENT_SEQ:
604 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
605 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000606 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000607 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000608 xmlSnprintfElementContent(buf, size, content->c1, 0);
609 len = strlen(buf);
610 if (size - len < 50) {
611 if ((size - len > 4) && (buf[len - 1] != '.'))
612 strcat(buf, " ...");
613 return;
614 }
Owen Taylor3473f882001-02-23 17:55:21 +0000615 strcat(buf, " , ");
616 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000617 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000618 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000619 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000620 break;
621 case XML_ELEMENT_CONTENT_OR:
622 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
623 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000624 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000625 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000626 xmlSnprintfElementContent(buf, size, content->c1, 0);
627 len = strlen(buf);
628 if (size - len < 50) {
629 if ((size - len > 4) && (buf[len - 1] != '.'))
630 strcat(buf, " ...");
631 return;
632 }
Owen Taylor3473f882001-02-23 17:55:21 +0000633 strcat(buf, " | ");
634 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000635 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000636 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000637 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000638 break;
639 }
640 if (glob)
641 strcat(buf, ")");
642 switch (content->ocur) {
643 case XML_ELEMENT_CONTENT_ONCE:
644 break;
645 case XML_ELEMENT_CONTENT_OPT:
646 strcat(buf, "?");
647 break;
648 case XML_ELEMENT_CONTENT_MULT:
649 strcat(buf, "*");
650 break;
651 case XML_ELEMENT_CONTENT_PLUS:
652 strcat(buf, "+");
653 break;
654 }
655}
656
657/****************************************************************
658 * *
659 * Registration of DTD declarations *
660 * *
661 ****************************************************************/
662
663/**
664 * xmlCreateElementTable:
665 *
666 * create and initialize an empty element hash table.
667 *
668 * Returns the xmlElementTablePtr just created or NULL in case of error.
669 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000670static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000671xmlCreateElementTable(void) {
672 return(xmlHashCreate(0));
673}
674
675/**
676 * xmlFreeElement:
677 * @elem: An element
678 *
679 * Deallocate the memory used by an element definition
680 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000681static void
Owen Taylor3473f882001-02-23 17:55:21 +0000682xmlFreeElement(xmlElementPtr elem) {
683 if (elem == NULL) return;
684 xmlUnlinkNode((xmlNodePtr) elem);
685 xmlFreeElementContent(elem->content);
686 if (elem->name != NULL)
687 xmlFree((xmlChar *) elem->name);
688 if (elem->prefix != NULL)
689 xmlFree((xmlChar *) elem->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000690 xmlFree(elem);
691}
692
693
694/**
695 * xmlAddElementDecl:
696 * @ctxt: the validation context
697 * @dtd: pointer to the DTD
698 * @name: the entity name
699 * @type: the element type
700 * @content: the element content tree or NULL
701 *
702 * Register a new element declaration
703 *
704 * Returns NULL if not, othervise the entity
705 */
706xmlElementPtr
707xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
708 xmlElementTypeVal type,
709 xmlElementContentPtr content) {
710 xmlElementPtr ret;
711 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000712 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000713 xmlChar *ns, *uqname;
714
715 if (dtd == NULL) {
716 xmlGenericError(xmlGenericErrorContext,
717 "xmlAddElementDecl: dtd == NULL\n");
718 return(NULL);
719 }
720 if (name == NULL) {
721 xmlGenericError(xmlGenericErrorContext,
722 "xmlAddElementDecl: name == NULL\n");
723 return(NULL);
724 }
725 switch (type) {
726 case XML_ELEMENT_TYPE_EMPTY:
727 if (content != NULL) {
728 xmlGenericError(xmlGenericErrorContext,
729 "xmlAddElementDecl: content != NULL for EMPTY\n");
730 return(NULL);
731 }
732 break;
733 case XML_ELEMENT_TYPE_ANY:
734 if (content != NULL) {
735 xmlGenericError(xmlGenericErrorContext,
736 "xmlAddElementDecl: content != NULL for ANY\n");
737 return(NULL);
738 }
739 break;
740 case XML_ELEMENT_TYPE_MIXED:
741 if (content == NULL) {
742 xmlGenericError(xmlGenericErrorContext,
743 "xmlAddElementDecl: content == NULL for MIXED\n");
744 return(NULL);
745 }
746 break;
747 case XML_ELEMENT_TYPE_ELEMENT:
748 if (content == NULL) {
749 xmlGenericError(xmlGenericErrorContext,
750 "xmlAddElementDecl: content == NULL for ELEMENT\n");
751 return(NULL);
752 }
753 break;
754 default:
755 xmlGenericError(xmlGenericErrorContext,
756 "xmlAddElementDecl: unknown type %d\n", type);
757 return(NULL);
758 }
759
760 /*
761 * check if name is a QName
762 */
763 uqname = xmlSplitQName2(name, &ns);
764 if (uqname != NULL)
765 name = uqname;
766
767 /*
768 * Create the Element table if needed.
769 */
770 table = (xmlElementTablePtr) dtd->elements;
771 if (table == NULL) {
772 table = xmlCreateElementTable();
773 dtd->elements = (void *) table;
774 }
775 if (table == NULL) {
776 xmlGenericError(xmlGenericErrorContext,
777 "xmlAddElementDecl: Table creation failed!\n");
778 return(NULL);
779 }
780
Daniel Veillarda10efa82001-04-18 13:09:01 +0000781 /*
782 * lookup old attributes inserted on an undefined element in the
783 * internal subset.
784 */
785 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
786 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
787 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
788 oldAttributes = ret->attributes;
789 ret->attributes = NULL;
790 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
791 xmlFreeElement(ret);
792 }
Owen Taylor3473f882001-02-23 17:55:21 +0000793 }
Owen Taylor3473f882001-02-23 17:55:21 +0000794
795 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +0000796 * The element may already be present if one of its attribute
797 * was registered first
798 */
799 ret = xmlHashLookup2(table, name, ns);
800 if (ret != NULL) {
801 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
802 /*
803 * The element is already defined in this Dtd.
804 */
805 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
806 if (uqname != NULL)
807 xmlFree(uqname);
808 return(NULL);
809 }
810 } else {
811 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
812 if (ret == NULL) {
813 xmlGenericError(xmlGenericErrorContext,
814 "xmlAddElementDecl: out of memory\n");
815 return(NULL);
816 }
817 memset(ret, 0, sizeof(xmlElement));
818 ret->type = XML_ELEMENT_DECL;
819
820 /*
821 * fill the structure.
822 */
823 ret->name = xmlStrdup(name);
824 ret->prefix = ns;
825
826 /*
827 * Validity Check:
828 * Insertion must not fail
829 */
830 if (xmlHashAddEntry2(table, name, ns, ret)) {
831 /*
832 * The element is already defined in this Dtd.
833 */
834 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
835 xmlFreeElement(ret);
836 if (uqname != NULL)
837 xmlFree(uqname);
838 return(NULL);
839 }
840 }
841
842 /*
843 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +0000844 */
845 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +0000846 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000847 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +0000848
849 /*
850 * Link it to the Dtd
851 */
852 ret->parent = dtd;
853 ret->doc = dtd->doc;
854 if (dtd->last == NULL) {
855 dtd->children = dtd->last = (xmlNodePtr) ret;
856 } else {
857 dtd->last->next = (xmlNodePtr) ret;
858 ret->prev = dtd->last;
859 dtd->last = (xmlNodePtr) ret;
860 }
861 if (uqname != NULL)
862 xmlFree(uqname);
863 return(ret);
864}
865
866/**
867 * xmlFreeElementTable:
868 * @table: An element table
869 *
870 * Deallocate the memory used by an element hash table.
871 */
872void
873xmlFreeElementTable(xmlElementTablePtr table) {
874 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
875}
876
877/**
878 * xmlCopyElement:
879 * @elem: An element
880 *
881 * Build a copy of an element.
882 *
883 * Returns the new xmlElementPtr or NULL in case of error.
884 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000885static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000886xmlCopyElement(xmlElementPtr elem) {
887 xmlElementPtr cur;
888
889 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
890 if (cur == NULL) {
891 xmlGenericError(xmlGenericErrorContext,
892 "xmlCopyElement: out of memory !\n");
893 return(NULL);
894 }
895 memset(cur, 0, sizeof(xmlElement));
896 cur->type = XML_ELEMENT_DECL;
897 cur->etype = elem->etype;
898 if (elem->name != NULL)
899 cur->name = xmlStrdup(elem->name);
900 else
901 cur->name = NULL;
902 if (elem->prefix != NULL)
903 cur->prefix = xmlStrdup(elem->prefix);
904 else
905 cur->prefix = NULL;
906 cur->content = xmlCopyElementContent(elem->content);
907 /* TODO : rebuild the attribute list on the copy */
908 cur->attributes = NULL;
909 return(cur);
910}
911
912/**
913 * xmlCopyElementTable:
914 * @table: An element table
915 *
916 * Build a copy of an element table.
917 *
918 * Returns the new xmlElementTablePtr or NULL in case of error.
919 */
920xmlElementTablePtr
921xmlCopyElementTable(xmlElementTablePtr table) {
922 return((xmlElementTablePtr) xmlHashCopy(table,
923 (xmlHashCopier) xmlCopyElement));
924}
925
926/**
927 * xmlDumpElementDecl:
928 * @buf: the XML buffer output
929 * @elem: An element table
930 *
931 * This will dump the content of the element declaration as an XML
932 * DTD definition
933 */
934void
935xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
936 switch (elem->etype) {
937 case XML_ELEMENT_TYPE_EMPTY:
938 xmlBufferWriteChar(buf, "<!ELEMENT ");
939 xmlBufferWriteCHAR(buf, elem->name);
940 xmlBufferWriteChar(buf, " EMPTY>\n");
941 break;
942 case XML_ELEMENT_TYPE_ANY:
943 xmlBufferWriteChar(buf, "<!ELEMENT ");
944 xmlBufferWriteCHAR(buf, elem->name);
945 xmlBufferWriteChar(buf, " ANY>\n");
946 break;
947 case XML_ELEMENT_TYPE_MIXED:
948 xmlBufferWriteChar(buf, "<!ELEMENT ");
949 xmlBufferWriteCHAR(buf, elem->name);
950 xmlBufferWriteChar(buf, " ");
951 xmlDumpElementContent(buf, elem->content, 1);
952 xmlBufferWriteChar(buf, ">\n");
953 break;
954 case XML_ELEMENT_TYPE_ELEMENT:
955 xmlBufferWriteChar(buf, "<!ELEMENT ");
956 xmlBufferWriteCHAR(buf, elem->name);
957 xmlBufferWriteChar(buf, " ");
958 xmlDumpElementContent(buf, elem->content, 1);
959 xmlBufferWriteChar(buf, ">\n");
960 break;
961 default:
962 xmlGenericError(xmlGenericErrorContext,
963 "xmlDumpElementDecl: internal: unknown type %d\n",
964 elem->etype);
965 }
966}
967
968/**
969 * xmlDumpElementTable:
970 * @buf: the XML buffer output
971 * @table: An element table
972 *
973 * This will dump the content of the element table as an XML DTD definition
974 */
975void
976xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
977 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
978}
979
980/**
981 * xmlCreateEnumeration:
982 * @name: the enumeration name or NULL
983 *
984 * create and initialize an enumeration attribute node.
985 *
986 * Returns the xmlEnumerationPtr just created or NULL in case
987 * of error.
988 */
989xmlEnumerationPtr
990xmlCreateEnumeration(xmlChar *name) {
991 xmlEnumerationPtr ret;
992
993 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
994 if (ret == NULL) {
995 xmlGenericError(xmlGenericErrorContext,
996 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
997 (long)sizeof(xmlEnumeration));
998 return(NULL);
999 }
1000 memset(ret, 0, sizeof(xmlEnumeration));
1001
1002 if (name != NULL)
1003 ret->name = xmlStrdup(name);
1004 return(ret);
1005}
1006
1007/**
1008 * xmlFreeEnumeration:
1009 * @cur: the tree to free.
1010 *
1011 * free an enumeration attribute node (recursive).
1012 */
1013void
1014xmlFreeEnumeration(xmlEnumerationPtr cur) {
1015 if (cur == NULL) return;
1016
1017 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1018
1019 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001020 xmlFree(cur);
1021}
1022
1023/**
1024 * xmlCopyEnumeration:
1025 * @cur: the tree to copy.
1026 *
1027 * Copy an enumeration attribute node (recursive).
1028 *
1029 * Returns the xmlEnumerationPtr just created or NULL in case
1030 * of error.
1031 */
1032xmlEnumerationPtr
1033xmlCopyEnumeration(xmlEnumerationPtr cur) {
1034 xmlEnumerationPtr ret;
1035
1036 if (cur == NULL) return(NULL);
1037 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1038
1039 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1040 else ret->next = NULL;
1041
1042 return(ret);
1043}
1044
1045/**
1046 * xmlDumpEnumeration:
1047 * @buf: the XML buffer output
1048 * @enum: An enumeration
1049 *
1050 * This will dump the content of the enumeration
1051 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001052static void
Owen Taylor3473f882001-02-23 17:55:21 +00001053xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1054 if (cur == NULL) return;
1055
1056 xmlBufferWriteCHAR(buf, cur->name);
1057 if (cur->next == NULL)
1058 xmlBufferWriteChar(buf, ")");
1059 else {
1060 xmlBufferWriteChar(buf, " | ");
1061 xmlDumpEnumeration(buf, cur->next);
1062 }
1063}
1064
1065/**
1066 * xmlCreateAttributeTable:
1067 *
1068 * create and initialize an empty attribute hash table.
1069 *
1070 * Returns the xmlAttributeTablePtr just created or NULL in case
1071 * of error.
1072 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001073static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001074xmlCreateAttributeTable(void) {
1075 return(xmlHashCreate(0));
1076}
1077
1078/**
1079 * xmlScanAttributeDeclCallback:
1080 * @attr: the attribute decl
1081 * @list: the list to update
1082 *
1083 * Callback called by xmlScanAttributeDecl when a new attribute
1084 * has to be entered in the list.
1085 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001086static void
Owen Taylor3473f882001-02-23 17:55:21 +00001087xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001088 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001089 attr->nexth = *list;
1090 *list = attr;
1091}
1092
1093/**
1094 * xmlScanAttributeDecl:
1095 * @dtd: pointer to the DTD
1096 * @elem: the element name
1097 *
1098 * When inserting a new element scan the DtD for existing attributes
1099 * for taht element and initialize the Attribute chain
1100 *
1101 * Returns the pointer to the first attribute decl in the chain,
1102 * possibly NULL.
1103 */
1104xmlAttributePtr
1105xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1106 xmlAttributePtr ret = NULL;
1107 xmlAttributeTablePtr table;
1108
1109 if (dtd == NULL) {
1110 xmlGenericError(xmlGenericErrorContext,
1111 "xmlScanAttributeDecl: dtd == NULL\n");
1112 return(NULL);
1113 }
1114 if (elem == NULL) {
1115 xmlGenericError(xmlGenericErrorContext,
1116 "xmlScanAttributeDecl: elem == NULL\n");
1117 return(NULL);
1118 }
1119 table = (xmlAttributeTablePtr) dtd->attributes;
1120 if (table == NULL)
1121 return(NULL);
1122
1123 /* WRONG !!! */
1124 xmlHashScan3(table, NULL, NULL, elem,
1125 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1126 return(ret);
1127}
1128
1129/**
1130 * xmlScanIDAttributeDecl:
1131 * @ctxt: the validation context
1132 * @elem: the element name
1133 *
1134 * Verify that the element don't have too many ID attributes
1135 * declared.
1136 *
1137 * Returns the number of ID attributes found.
1138 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001139static int
Owen Taylor3473f882001-02-23 17:55:21 +00001140xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1141 xmlAttributePtr cur;
1142 int ret = 0;
1143
1144 if (elem == NULL) return(0);
1145 cur = elem->attributes;
1146 while (cur != NULL) {
1147 if (cur->atype == XML_ATTRIBUTE_ID) {
1148 ret ++;
1149 if (ret > 1)
1150 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001151 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001152 elem->name, cur->name);
1153 }
1154 cur = cur->nexth;
1155 }
1156 return(ret);
1157}
1158
1159/**
1160 * xmlFreeAttribute:
1161 * @elem: An attribute
1162 *
1163 * Deallocate the memory used by an attribute definition
1164 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001165static void
Owen Taylor3473f882001-02-23 17:55:21 +00001166xmlFreeAttribute(xmlAttributePtr attr) {
1167 if (attr == NULL) return;
1168 xmlUnlinkNode((xmlNodePtr) attr);
1169 if (attr->tree != NULL)
1170 xmlFreeEnumeration(attr->tree);
1171 if (attr->elem != NULL)
1172 xmlFree((xmlChar *) attr->elem);
1173 if (attr->name != NULL)
1174 xmlFree((xmlChar *) attr->name);
1175 if (attr->defaultValue != NULL)
1176 xmlFree((xmlChar *) attr->defaultValue);
1177 if (attr->prefix != NULL)
1178 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001179 xmlFree(attr);
1180}
1181
1182
1183/**
1184 * xmlAddAttributeDecl:
1185 * @ctxt: the validation context
1186 * @dtd: pointer to the DTD
1187 * @elem: the element name
1188 * @name: the attribute name
1189 * @ns: the attribute namespace prefix
1190 * @type: the attribute type
1191 * @def: the attribute default type
1192 * @defaultValue: the attribute default value
1193 * @tree: if it's an enumeration, the associated list
1194 *
1195 * Register a new attribute declaration
1196 * Note that @tree becomes the ownership of the DTD
1197 *
1198 * Returns NULL if not new, othervise the attribute decl
1199 */
1200xmlAttributePtr
1201xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1202 const xmlChar *name, const xmlChar *ns,
1203 xmlAttributeType type, xmlAttributeDefault def,
1204 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1205 xmlAttributePtr ret;
1206 xmlAttributeTablePtr table;
1207 xmlElementPtr elemDef;
1208
1209 if (dtd == NULL) {
1210 xmlGenericError(xmlGenericErrorContext,
1211 "xmlAddAttributeDecl: dtd == NULL\n");
1212 xmlFreeEnumeration(tree);
1213 return(NULL);
1214 }
1215 if (name == NULL) {
1216 xmlGenericError(xmlGenericErrorContext,
1217 "xmlAddAttributeDecl: name == NULL\n");
1218 xmlFreeEnumeration(tree);
1219 return(NULL);
1220 }
1221 if (elem == NULL) {
1222 xmlGenericError(xmlGenericErrorContext,
1223 "xmlAddAttributeDecl: elem == NULL\n");
1224 xmlFreeEnumeration(tree);
1225 return(NULL);
1226 }
1227 /*
1228 * Check the type and possibly the default value.
1229 */
1230 switch (type) {
1231 case XML_ATTRIBUTE_CDATA:
1232 break;
1233 case XML_ATTRIBUTE_ID:
1234 break;
1235 case XML_ATTRIBUTE_IDREF:
1236 break;
1237 case XML_ATTRIBUTE_IDREFS:
1238 break;
1239 case XML_ATTRIBUTE_ENTITY:
1240 break;
1241 case XML_ATTRIBUTE_ENTITIES:
1242 break;
1243 case XML_ATTRIBUTE_NMTOKEN:
1244 break;
1245 case XML_ATTRIBUTE_NMTOKENS:
1246 break;
1247 case XML_ATTRIBUTE_ENUMERATION:
1248 break;
1249 case XML_ATTRIBUTE_NOTATION:
1250 break;
1251 default:
1252 xmlGenericError(xmlGenericErrorContext,
1253 "xmlAddAttributeDecl: unknown type %d\n", type);
1254 xmlFreeEnumeration(tree);
1255 return(NULL);
1256 }
1257 if ((defaultValue != NULL) &&
1258 (!xmlValidateAttributeValue(type, defaultValue))) {
1259 VERROR(ctxt->userData, "Attribute %s on %s: invalid default value\n",
1260 elem, name, defaultValue);
1261 defaultValue = NULL;
1262 }
1263
1264 /*
1265 * Create the Attribute table if needed.
1266 */
1267 table = (xmlAttributeTablePtr) dtd->attributes;
1268 if (table == NULL) {
1269 table = xmlCreateAttributeTable();
1270 dtd->attributes = (void *) table;
1271 }
1272 if (table == NULL) {
1273 xmlGenericError(xmlGenericErrorContext,
1274 "xmlAddAttributeDecl: Table creation failed!\n");
1275 return(NULL);
1276 }
1277
1278
1279 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1280 if (ret == NULL) {
1281 xmlGenericError(xmlGenericErrorContext,
1282 "xmlAddAttributeDecl: out of memory\n");
1283 return(NULL);
1284 }
1285 memset(ret, 0, sizeof(xmlAttribute));
1286 ret->type = XML_ATTRIBUTE_DECL;
1287
1288 /*
1289 * fill the structure.
1290 */
1291 ret->atype = type;
1292 ret->name = xmlStrdup(name);
1293 ret->prefix = xmlStrdup(ns);
1294 ret->elem = xmlStrdup(elem);
1295 ret->def = def;
1296 ret->tree = tree;
1297 if (defaultValue != NULL)
1298 ret->defaultValue = xmlStrdup(defaultValue);
1299
1300 /*
1301 * Validity Check:
1302 * Search the DTD for previous declarations of the ATTLIST
1303 */
1304 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1305 /*
1306 * The attribute is already defined in this Dtd.
1307 */
1308 VWARNING(ctxt->userData,
1309 "Attribute %s on %s: already defined\n",
1310 name, elem);
1311 xmlFreeAttribute(ret);
1312 return(NULL);
1313 }
1314
1315 /*
1316 * Validity Check:
1317 * Multiple ID per element
1318 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001319 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001320 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001321
Owen Taylor3473f882001-02-23 17:55:21 +00001322 if ((type == XML_ATTRIBUTE_ID) &&
1323 (xmlScanIDAttributeDecl(NULL, elemDef) != 0))
1324 VERROR(ctxt->userData,
1325 "Element %s has too may ID attributes defined : %s\n",
1326 elem, name);
Daniel Veillard48da9102001-08-07 01:10:10 +00001327 /*
1328 * Insert namespace default def first they need to be
1329 * processed firt.
1330 */
1331 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1332 ((ret->prefix != NULL &&
1333 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1334 ret->nexth = elemDef->attributes;
1335 elemDef->attributes = ret;
1336 } else {
1337 xmlAttributePtr tmp = elemDef->attributes;
1338
1339 while ((tmp != NULL) &&
1340 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1341 ((ret->prefix != NULL &&
1342 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1343 if (tmp->nexth == NULL)
1344 break;
1345 tmp = tmp->nexth;
1346 }
1347 if (tmp != NULL) {
1348 ret->nexth = tmp->nexth;
1349 tmp->nexth = ret;
1350 } else {
1351 ret->nexth = elemDef->attributes;
1352 elemDef->attributes = ret;
1353 }
1354 }
Owen Taylor3473f882001-02-23 17:55:21 +00001355 }
1356
1357 /*
1358 * Link it to the Dtd
1359 */
1360 ret->parent = dtd;
1361 ret->doc = dtd->doc;
1362 if (dtd->last == NULL) {
1363 dtd->children = dtd->last = (xmlNodePtr) ret;
1364 } else {
1365 dtd->last->next = (xmlNodePtr) ret;
1366 ret->prev = dtd->last;
1367 dtd->last = (xmlNodePtr) ret;
1368 }
1369 return(ret);
1370}
1371
1372/**
1373 * xmlFreeAttributeTable:
1374 * @table: An attribute table
1375 *
1376 * Deallocate the memory used by an entities hash table.
1377 */
1378void
1379xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1380 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1381}
1382
1383/**
1384 * xmlCopyAttribute:
1385 * @attr: An attribute
1386 *
1387 * Build a copy of an attribute.
1388 *
1389 * Returns the new xmlAttributePtr or NULL in case of error.
1390 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001391static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001392xmlCopyAttribute(xmlAttributePtr attr) {
1393 xmlAttributePtr cur;
1394
1395 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1396 if (cur == NULL) {
1397 xmlGenericError(xmlGenericErrorContext,
1398 "xmlCopyAttribute: out of memory !\n");
1399 return(NULL);
1400 }
1401 memset(cur, 0, sizeof(xmlAttribute));
1402 cur->atype = attr->atype;
1403 cur->def = attr->def;
1404 cur->tree = xmlCopyEnumeration(attr->tree);
1405 if (attr->elem != NULL)
1406 cur->elem = xmlStrdup(attr->elem);
1407 if (attr->name != NULL)
1408 cur->name = xmlStrdup(attr->name);
1409 if (attr->defaultValue != NULL)
1410 cur->defaultValue = xmlStrdup(attr->defaultValue);
1411 return(cur);
1412}
1413
1414/**
1415 * xmlCopyAttributeTable:
1416 * @table: An attribute table
1417 *
1418 * Build a copy of an attribute table.
1419 *
1420 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1421 */
1422xmlAttributeTablePtr
1423xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1424 return((xmlAttributeTablePtr) xmlHashCopy(table,
1425 (xmlHashCopier) xmlCopyAttribute));
1426}
1427
1428/**
1429 * xmlDumpAttributeDecl:
1430 * @buf: the XML buffer output
1431 * @attr: An attribute declaration
1432 *
1433 * This will dump the content of the attribute declaration as an XML
1434 * DTD definition
1435 */
1436void
1437xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1438 xmlBufferWriteChar(buf, "<!ATTLIST ");
1439 xmlBufferWriteCHAR(buf, attr->elem);
1440 xmlBufferWriteChar(buf, " ");
1441 if (attr->prefix != NULL) {
1442 xmlBufferWriteCHAR(buf, attr->prefix);
1443 xmlBufferWriteChar(buf, ":");
1444 }
1445 xmlBufferWriteCHAR(buf, attr->name);
1446 switch (attr->atype) {
1447 case XML_ATTRIBUTE_CDATA:
1448 xmlBufferWriteChar(buf, " CDATA");
1449 break;
1450 case XML_ATTRIBUTE_ID:
1451 xmlBufferWriteChar(buf, " ID");
1452 break;
1453 case XML_ATTRIBUTE_IDREF:
1454 xmlBufferWriteChar(buf, " IDREF");
1455 break;
1456 case XML_ATTRIBUTE_IDREFS:
1457 xmlBufferWriteChar(buf, " IDREFS");
1458 break;
1459 case XML_ATTRIBUTE_ENTITY:
1460 xmlBufferWriteChar(buf, " ENTITY");
1461 break;
1462 case XML_ATTRIBUTE_ENTITIES:
1463 xmlBufferWriteChar(buf, " ENTITIES");
1464 break;
1465 case XML_ATTRIBUTE_NMTOKEN:
1466 xmlBufferWriteChar(buf, " NMTOKEN");
1467 break;
1468 case XML_ATTRIBUTE_NMTOKENS:
1469 xmlBufferWriteChar(buf, " NMTOKENS");
1470 break;
1471 case XML_ATTRIBUTE_ENUMERATION:
1472 xmlBufferWriteChar(buf, " (");
1473 xmlDumpEnumeration(buf, attr->tree);
1474 break;
1475 case XML_ATTRIBUTE_NOTATION:
1476 xmlBufferWriteChar(buf, " NOTATION (");
1477 xmlDumpEnumeration(buf, attr->tree);
1478 break;
1479 default:
1480 xmlGenericError(xmlGenericErrorContext,
1481 "xmlDumpAttributeTable: internal: unknown type %d\n",
1482 attr->atype);
1483 }
1484 switch (attr->def) {
1485 case XML_ATTRIBUTE_NONE:
1486 break;
1487 case XML_ATTRIBUTE_REQUIRED:
1488 xmlBufferWriteChar(buf, " #REQUIRED");
1489 break;
1490 case XML_ATTRIBUTE_IMPLIED:
1491 xmlBufferWriteChar(buf, " #IMPLIED");
1492 break;
1493 case XML_ATTRIBUTE_FIXED:
1494 xmlBufferWriteChar(buf, " #FIXED");
1495 break;
1496 default:
1497 xmlGenericError(xmlGenericErrorContext,
1498 "xmlDumpAttributeTable: internal: unknown default %d\n",
1499 attr->def);
1500 }
1501 if (attr->defaultValue != NULL) {
1502 xmlBufferWriteChar(buf, " ");
1503 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1504 }
1505 xmlBufferWriteChar(buf, ">\n");
1506}
1507
1508/**
1509 * xmlDumpAttributeTable:
1510 * @buf: the XML buffer output
1511 * @table: An attribute table
1512 *
1513 * This will dump the content of the attribute table as an XML DTD definition
1514 */
1515void
1516xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1517 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1518}
1519
1520/************************************************************************
1521 * *
1522 * NOTATIONs *
1523 * *
1524 ************************************************************************/
1525/**
1526 * xmlCreateNotationTable:
1527 *
1528 * create and initialize an empty notation hash table.
1529 *
1530 * Returns the xmlNotationTablePtr just created or NULL in case
1531 * of error.
1532 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001533static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001534xmlCreateNotationTable(void) {
1535 return(xmlHashCreate(0));
1536}
1537
1538/**
1539 * xmlFreeNotation:
1540 * @not: A notation
1541 *
1542 * Deallocate the memory used by an notation definition
1543 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001544static void
Owen Taylor3473f882001-02-23 17:55:21 +00001545xmlFreeNotation(xmlNotationPtr nota) {
1546 if (nota == NULL) return;
1547 if (nota->name != NULL)
1548 xmlFree((xmlChar *) nota->name);
1549 if (nota->PublicID != NULL)
1550 xmlFree((xmlChar *) nota->PublicID);
1551 if (nota->SystemID != NULL)
1552 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001553 xmlFree(nota);
1554}
1555
1556
1557/**
1558 * xmlAddNotationDecl:
1559 * @dtd: pointer to the DTD
1560 * @ctxt: the validation context
1561 * @name: the entity name
1562 * @PublicID: the public identifier or NULL
1563 * @SystemID: the system identifier or NULL
1564 *
1565 * Register a new notation declaration
1566 *
1567 * Returns NULL if not, othervise the entity
1568 */
1569xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001570xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001571 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001572 const xmlChar *PublicID, const xmlChar *SystemID) {
1573 xmlNotationPtr ret;
1574 xmlNotationTablePtr table;
1575
1576 if (dtd == NULL) {
1577 xmlGenericError(xmlGenericErrorContext,
1578 "xmlAddNotationDecl: dtd == NULL\n");
1579 return(NULL);
1580 }
1581 if (name == NULL) {
1582 xmlGenericError(xmlGenericErrorContext,
1583 "xmlAddNotationDecl: name == NULL\n");
1584 return(NULL);
1585 }
1586 if ((PublicID == NULL) && (SystemID == NULL)) {
1587 xmlGenericError(xmlGenericErrorContext,
1588 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
1589 }
1590
1591 /*
1592 * Create the Notation table if needed.
1593 */
1594 table = (xmlNotationTablePtr) dtd->notations;
1595 if (table == NULL)
1596 dtd->notations = table = xmlCreateNotationTable();
1597 if (table == NULL) {
1598 xmlGenericError(xmlGenericErrorContext,
1599 "xmlAddNotationDecl: Table creation failed!\n");
1600 return(NULL);
1601 }
1602
1603 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1604 if (ret == NULL) {
1605 xmlGenericError(xmlGenericErrorContext,
1606 "xmlAddNotationDecl: out of memory\n");
1607 return(NULL);
1608 }
1609 memset(ret, 0, sizeof(xmlNotation));
1610
1611 /*
1612 * fill the structure.
1613 */
1614 ret->name = xmlStrdup(name);
1615 if (SystemID != NULL)
1616 ret->SystemID = xmlStrdup(SystemID);
1617 if (PublicID != NULL)
1618 ret->PublicID = xmlStrdup(PublicID);
1619
1620 /*
1621 * Validity Check:
1622 * Check the DTD for previous declarations of the ATTLIST
1623 */
1624 if (xmlHashAddEntry(table, name, ret)) {
1625 xmlGenericError(xmlGenericErrorContext,
1626 "xmlAddNotationDecl: %s already defined\n", name);
1627 xmlFreeNotation(ret);
1628 return(NULL);
1629 }
1630 return(ret);
1631}
1632
1633/**
1634 * xmlFreeNotationTable:
1635 * @table: An notation table
1636 *
1637 * Deallocate the memory used by an entities hash table.
1638 */
1639void
1640xmlFreeNotationTable(xmlNotationTablePtr table) {
1641 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1642}
1643
1644/**
1645 * xmlCopyNotation:
1646 * @nota: A notation
1647 *
1648 * Build a copy of a notation.
1649 *
1650 * Returns the new xmlNotationPtr or NULL in case of error.
1651 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001652static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001653xmlCopyNotation(xmlNotationPtr nota) {
1654 xmlNotationPtr cur;
1655
1656 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1657 if (cur == NULL) {
1658 xmlGenericError(xmlGenericErrorContext,
1659 "xmlCopyNotation: out of memory !\n");
1660 return(NULL);
1661 }
1662 if (nota->name != NULL)
1663 cur->name = xmlStrdup(nota->name);
1664 else
1665 cur->name = NULL;
1666 if (nota->PublicID != NULL)
1667 cur->PublicID = xmlStrdup(nota->PublicID);
1668 else
1669 cur->PublicID = NULL;
1670 if (nota->SystemID != NULL)
1671 cur->SystemID = xmlStrdup(nota->SystemID);
1672 else
1673 cur->SystemID = NULL;
1674 return(cur);
1675}
1676
1677/**
1678 * xmlCopyNotationTable:
1679 * @table: A notation table
1680 *
1681 * Build a copy of a notation table.
1682 *
1683 * Returns the new xmlNotationTablePtr or NULL in case of error.
1684 */
1685xmlNotationTablePtr
1686xmlCopyNotationTable(xmlNotationTablePtr table) {
1687 return((xmlNotationTablePtr) xmlHashCopy(table,
1688 (xmlHashCopier) xmlCopyNotation));
1689}
1690
1691/**
1692 * xmlDumpNotationDecl:
1693 * @buf: the XML buffer output
1694 * @nota: A notation declaration
1695 *
1696 * This will dump the content the notation declaration as an XML DTD definition
1697 */
1698void
1699xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1700 xmlBufferWriteChar(buf, "<!NOTATION ");
1701 xmlBufferWriteCHAR(buf, nota->name);
1702 if (nota->PublicID != NULL) {
1703 xmlBufferWriteChar(buf, " PUBLIC ");
1704 xmlBufferWriteQuotedString(buf, nota->PublicID);
1705 if (nota->SystemID != NULL) {
1706 xmlBufferWriteChar(buf, " ");
1707 xmlBufferWriteCHAR(buf, nota->SystemID);
1708 }
1709 } else {
1710 xmlBufferWriteChar(buf, " SYSTEM ");
1711 xmlBufferWriteCHAR(buf, nota->SystemID);
1712 }
1713 xmlBufferWriteChar(buf, " >\n");
1714}
1715
1716/**
1717 * xmlDumpNotationTable:
1718 * @buf: the XML buffer output
1719 * @table: A notation table
1720 *
1721 * This will dump the content of the notation table as an XML DTD definition
1722 */
1723void
1724xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1725 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1726}
1727
1728/************************************************************************
1729 * *
1730 * IDs *
1731 * *
1732 ************************************************************************/
1733/**
1734 * xmlCreateIDTable:
1735 *
1736 * create and initialize an empty id hash table.
1737 *
1738 * Returns the xmlIDTablePtr just created or NULL in case
1739 * of error.
1740 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001741static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001742xmlCreateIDTable(void) {
1743 return(xmlHashCreate(0));
1744}
1745
1746/**
1747 * xmlFreeID:
1748 * @not: A id
1749 *
1750 * Deallocate the memory used by an id definition
1751 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001752static void
Owen Taylor3473f882001-02-23 17:55:21 +00001753xmlFreeID(xmlIDPtr id) {
1754 if (id == NULL) return;
1755 if (id->value != NULL)
1756 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001757 xmlFree(id);
1758}
1759
1760/**
1761 * xmlAddID:
1762 * @ctxt: the validation context
1763 * @doc: pointer to the document
1764 * @value: the value name
1765 * @attr: the attribute holding the ID
1766 *
1767 * Register a new id declaration
1768 *
1769 * Returns NULL if not, othervise the new xmlIDPtr
1770 */
1771xmlIDPtr
1772xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1773 xmlAttrPtr attr) {
1774 xmlIDPtr ret;
1775 xmlIDTablePtr table;
1776
1777 if (doc == NULL) {
1778 xmlGenericError(xmlGenericErrorContext,
1779 "xmlAddIDDecl: doc == NULL\n");
1780 return(NULL);
1781 }
1782 if (value == NULL) {
1783 xmlGenericError(xmlGenericErrorContext,
1784 "xmlAddIDDecl: value == NULL\n");
1785 return(NULL);
1786 }
1787 if (attr == NULL) {
1788 xmlGenericError(xmlGenericErrorContext,
1789 "xmlAddIDDecl: attr == NULL\n");
1790 return(NULL);
1791 }
1792
1793 /*
1794 * Create the ID table if needed.
1795 */
1796 table = (xmlIDTablePtr) doc->ids;
1797 if (table == NULL)
1798 doc->ids = table = xmlCreateIDTable();
1799 if (table == NULL) {
1800 xmlGenericError(xmlGenericErrorContext,
1801 "xmlAddID: Table creation failed!\n");
1802 return(NULL);
1803 }
1804
1805 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1806 if (ret == NULL) {
1807 xmlGenericError(xmlGenericErrorContext,
1808 "xmlAddID: out of memory\n");
1809 return(NULL);
1810 }
1811
1812 /*
1813 * fill the structure.
1814 */
1815 ret->value = xmlStrdup(value);
1816 ret->attr = attr;
1817
1818 if (xmlHashAddEntry(table, value, ret) < 0) {
1819 /*
1820 * The id is already defined in this Dtd.
1821 */
1822 VERROR(ctxt->userData, "ID %s already defined\n", value);
1823 xmlFreeID(ret);
1824 return(NULL);
1825 }
1826 return(ret);
1827}
1828
1829/**
1830 * xmlFreeIDTable:
1831 * @table: An id table
1832 *
1833 * Deallocate the memory used by an ID hash table.
1834 */
1835void
1836xmlFreeIDTable(xmlIDTablePtr table) {
1837 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1838}
1839
1840/**
1841 * xmlIsID:
1842 * @doc: the document
1843 * @elem: the element carrying the attribute
1844 * @attr: the attribute
1845 *
1846 * Determine whether an attribute is of type ID. In case we have Dtd(s)
1847 * then this is simple, otherwise we use an heuristic: name ID (upper
1848 * or lowercase).
1849 *
1850 * Returns 0 or 1 depending on the lookup result
1851 */
1852int
1853xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1854 if (doc == NULL) return(0);
1855 if (attr == NULL) return(0);
1856 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1857 return(0);
1858 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1859 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1860 (xmlStrEqual(BAD_CAST "name", attr->name)))
1861 return(1);
1862 return(0);
1863 } else {
1864 xmlAttributePtr attrDecl;
1865
1866 if (elem == NULL) return(0);
1867 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1868 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1869 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1870 attr->name);
1871
1872 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1873 return(1);
1874 }
1875 return(0);
1876}
1877
1878/**
1879 * xmlRemoveID
1880 * @doc: the document
1881 * @attr: the attribute
1882 *
1883 * Remove the given attribute from the ID table maintained internally.
1884 *
1885 * Returns -1 if the lookup failed and 0 otherwise
1886 */
1887int
1888xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1889 xmlAttrPtr cur;
1890 xmlIDTablePtr table;
1891 xmlChar *ID;
1892
1893 if (doc == NULL) return(-1);
1894 if (attr == NULL) return(-1);
1895 table = (xmlIDTablePtr) doc->ids;
1896 if (table == NULL)
1897 return(-1);
1898
1899 if (attr == NULL)
1900 return(-1);
1901 ID = xmlNodeListGetString(doc, attr->children, 1);
1902 if (ID == NULL)
1903 return(-1);
1904 cur = xmlHashLookup(table, ID);
1905 if (cur != attr) {
1906 xmlFree(ID);
1907 return(-1);
1908 }
1909 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1910 xmlFree(ID);
1911 return(0);
1912}
1913
1914/**
1915 * xmlGetID:
1916 * @doc: pointer to the document
1917 * @ID: the ID value
1918 *
1919 * Search the attribute declaring the given ID
1920 *
1921 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1922 */
1923xmlAttrPtr
1924xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
1925 xmlIDTablePtr table;
1926 xmlIDPtr id;
1927
1928 if (doc == NULL) {
1929 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
1930 return(NULL);
1931 }
1932
1933 if (ID == NULL) {
1934 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
1935 return(NULL);
1936 }
1937
1938 table = (xmlIDTablePtr) doc->ids;
1939 if (table == NULL)
1940 return(NULL);
1941
1942 id = xmlHashLookup(table, ID);
1943 if (id == NULL)
1944 return(NULL);
1945 return(id->attr);
1946}
1947
1948/************************************************************************
1949 * *
1950 * Refs *
1951 * *
1952 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00001953typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00001954{
1955 xmlListPtr l;
1956 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00001957} xmlRemoveMemo;
1958
1959typedef xmlRemoveMemo *xmlRemoveMemoPtr;
1960
1961typedef struct xmlValidateMemo_t
1962{
1963 xmlValidCtxtPtr ctxt;
1964 const xmlChar *name;
1965} xmlValidateMemo;
1966
1967typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00001968
1969/**
1970 * xmlCreateRefTable:
1971 *
1972 * create and initialize an empty ref hash table.
1973 *
1974 * Returns the xmlRefTablePtr just created or NULL in case
1975 * of error.
1976 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001977static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001978xmlCreateRefTable(void) {
1979 return(xmlHashCreate(0));
1980}
1981
1982/**
1983 * xmlFreeRef:
1984 * @lk: A list link
1985 *
1986 * Deallocate the memory used by a ref definition
1987 */
1988static void
1989xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00001990 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
1991 if (ref == NULL) return;
1992 if (ref->value != NULL)
1993 xmlFree((xmlChar *)ref->value);
1994 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00001995}
1996
1997/**
1998 * xmlFreeRefList:
1999 * @list_ref: A list of references.
2000 *
2001 * Deallocate the memory used by a list of references
2002 */
2003static void
2004xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002005 if (list_ref == NULL) return;
2006 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002007}
2008
2009/**
2010 * xmlWalkRemoveRef:
2011 * @data: Contents of current link
2012 * @user: Value supplied by the user
2013 *
2014 * Return 0 to abort the walk or 1 to continue
2015 */
2016static int
2017xmlWalkRemoveRef(const void *data, const void *user)
2018{
Daniel Veillard37721922001-05-04 15:21:12 +00002019 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2020 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2021 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002022
Daniel Veillard37721922001-05-04 15:21:12 +00002023 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2024 xmlListRemoveFirst(ref_list, (void *)data);
2025 return 0;
2026 }
2027 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002028}
2029
2030/**
2031 * xmlAddRef:
2032 * @ctxt: the validation context
2033 * @doc: pointer to the document
2034 * @value: the value name
2035 * @attr: the attribute holding the Ref
2036 *
2037 * Register a new ref declaration
2038 *
2039 * Returns NULL if not, othervise the new xmlRefPtr
2040 */
2041xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002042xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002043 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002044 xmlRefPtr ret;
2045 xmlRefTablePtr table;
2046 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002047
Daniel Veillard37721922001-05-04 15:21:12 +00002048 if (doc == NULL) {
2049 xmlGenericError(xmlGenericErrorContext,
2050 "xmlAddRefDecl: doc == NULL\n");
2051 return(NULL);
2052 }
2053 if (value == NULL) {
2054 xmlGenericError(xmlGenericErrorContext,
2055 "xmlAddRefDecl: value == NULL\n");
2056 return(NULL);
2057 }
2058 if (attr == NULL) {
2059 xmlGenericError(xmlGenericErrorContext,
2060 "xmlAddRefDecl: attr == NULL\n");
2061 return(NULL);
2062 }
Owen Taylor3473f882001-02-23 17:55:21 +00002063
Daniel Veillard37721922001-05-04 15:21:12 +00002064 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002065 * Create the Ref table if needed.
2066 */
Daniel Veillard37721922001-05-04 15:21:12 +00002067 table = (xmlRefTablePtr) doc->refs;
2068 if (table == NULL)
2069 doc->refs = table = xmlCreateRefTable();
2070 if (table == NULL) {
2071 xmlGenericError(xmlGenericErrorContext,
2072 "xmlAddRef: Table creation failed!\n");
2073 return(NULL);
2074 }
Owen Taylor3473f882001-02-23 17:55:21 +00002075
Daniel Veillard37721922001-05-04 15:21:12 +00002076 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2077 if (ret == NULL) {
2078 xmlGenericError(xmlGenericErrorContext,
2079 "xmlAddRef: out of memory\n");
2080 return(NULL);
2081 }
Owen Taylor3473f882001-02-23 17:55:21 +00002082
Daniel Veillard37721922001-05-04 15:21:12 +00002083 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002084 * fill the structure.
2085 */
Daniel Veillard37721922001-05-04 15:21:12 +00002086 ret->value = xmlStrdup(value);
2087 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002088
Daniel Veillard37721922001-05-04 15:21:12 +00002089 /* To add a reference :-
2090 * References are maintained as a list of references,
2091 * Lookup the entry, if no entry create new nodelist
2092 * Add the owning node to the NodeList
2093 * Return the ref
2094 */
Owen Taylor3473f882001-02-23 17:55:21 +00002095
Daniel Veillard37721922001-05-04 15:21:12 +00002096 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2097 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2098 xmlGenericError(xmlGenericErrorContext,
2099 "xmlAddRef: Reference list creation failed!\n");
2100 return(NULL);
2101 }
2102 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2103 xmlListDelete(ref_list);
2104 xmlGenericError(xmlGenericErrorContext,
2105 "xmlAddRef: Reference list insertion failed!\n");
2106 return(NULL);
2107 }
2108 }
2109 xmlListInsert(ref_list, ret);
2110 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002111}
2112
2113/**
2114 * xmlFreeRefTable:
2115 * @table: An ref table
2116 *
2117 * Deallocate the memory used by an Ref hash table.
2118 */
2119void
2120xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002121 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002122}
2123
2124/**
2125 * xmlIsRef:
2126 * @doc: the document
2127 * @elem: the element carrying the attribute
2128 * @attr: the attribute
2129 *
2130 * Determine whether an attribute is of type Ref. In case we have Dtd(s)
2131 * then this is simple, otherwise we use an heuristic: name Ref (upper
2132 * or lowercase).
2133 *
2134 * Returns 0 or 1 depending on the lookup result
2135 */
2136int
2137xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002138 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2139 return(0);
2140 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2141 /* TODO @@@ */
2142 return(0);
2143 } else {
2144 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002145
Daniel Veillard37721922001-05-04 15:21:12 +00002146 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2147 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2148 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2149 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002150
Daniel Veillard37721922001-05-04 15:21:12 +00002151 if ((attrDecl != NULL) &&
2152 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2153 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2154 return(1);
2155 }
2156 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002157}
2158
2159/**
2160 * xmlRemoveRef
2161 * @doc: the document
2162 * @attr: the attribute
2163 *
2164 * Remove the given attribute from the Ref table maintained internally.
2165 *
2166 * Returns -1 if the lookup failed and 0 otherwise
2167 */
2168int
2169xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002170 xmlListPtr ref_list;
2171 xmlRefTablePtr table;
2172 xmlChar *ID;
2173 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002174
Daniel Veillard37721922001-05-04 15:21:12 +00002175 if (doc == NULL) return(-1);
2176 if (attr == NULL) return(-1);
2177 table = (xmlRefTablePtr) doc->refs;
2178 if (table == NULL)
2179 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002180
Daniel Veillard37721922001-05-04 15:21:12 +00002181 if (attr == NULL)
2182 return(-1);
2183 ID = xmlNodeListGetString(doc, attr->children, 1);
2184 if (ID == NULL)
2185 return(-1);
2186 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002187
Daniel Veillard37721922001-05-04 15:21:12 +00002188 if(ref_list == NULL) {
2189 xmlFree(ID);
2190 return (-1);
2191 }
2192 /* At this point, ref_list refers to a list of references which
2193 * have the same key as the supplied attr. Our list of references
2194 * is ordered by reference address and we don't have that information
2195 * here to use when removing. We'll have to walk the list and
2196 * check for a matching attribute, when we find one stop the walk
2197 * and remove the entry.
2198 * The list is ordered by reference, so that means we don't have the
2199 * key. Passing the list and the reference to the walker means we
2200 * will have enough data to be able to remove the entry.
2201 */
2202 target.l = ref_list;
2203 target.ap = attr;
2204
2205 /* Remove the supplied attr from our list */
2206 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002207
Daniel Veillard37721922001-05-04 15:21:12 +00002208 /*If the list is empty then remove the list entry in the hash */
2209 if (xmlListEmpty(ref_list))
2210 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2211 xmlFreeRefList);
2212 xmlFree(ID);
2213 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002214}
2215
2216/**
2217 * xmlGetRefs:
2218 * @doc: pointer to the document
2219 * @ID: the ID value
2220 *
2221 * Find the set of references for the supplied ID.
2222 *
2223 * Returns NULL if not found, otherwise node set for the ID.
2224 */
2225xmlListPtr
2226xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002227 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002228
Daniel Veillard37721922001-05-04 15:21:12 +00002229 if (doc == NULL) {
2230 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: doc == NULL\n");
2231 return(NULL);
2232 }
Owen Taylor3473f882001-02-23 17:55:21 +00002233
Daniel Veillard37721922001-05-04 15:21:12 +00002234 if (ID == NULL) {
2235 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: ID == NULL\n");
2236 return(NULL);
2237 }
Owen Taylor3473f882001-02-23 17:55:21 +00002238
Daniel Veillard37721922001-05-04 15:21:12 +00002239 table = (xmlRefTablePtr) doc->refs;
2240 if (table == NULL)
2241 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002242
Daniel Veillard37721922001-05-04 15:21:12 +00002243 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002244}
2245
2246/************************************************************************
2247 * *
2248 * Routines for validity checking *
2249 * *
2250 ************************************************************************/
2251
2252/**
2253 * xmlGetDtdElementDesc:
2254 * @dtd: a pointer to the DtD to search
2255 * @name: the element name
2256 *
2257 * Search the Dtd for the description of this element
2258 *
2259 * returns the xmlElementPtr if found or NULL
2260 */
2261
2262xmlElementPtr
2263xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2264 xmlElementTablePtr table;
2265 xmlElementPtr cur;
2266 xmlChar *uqname = NULL, *prefix = NULL;
2267
2268 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002269 if (dtd->elements == NULL)
2270 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002271 table = (xmlElementTablePtr) dtd->elements;
2272
2273 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002274 if (uqname != NULL)
2275 name = uqname;
2276 cur = xmlHashLookup2(table, name, prefix);
2277 if (prefix != NULL) xmlFree(prefix);
2278 if (uqname != NULL) xmlFree(uqname);
2279 return(cur);
2280}
2281/**
2282 * xmlGetDtdElementDesc2:
2283 * @dtd: a pointer to the DtD to search
2284 * @name: the element name
2285 * @create: create an empty description if not found
2286 *
2287 * Search the Dtd for the description of this element
2288 *
2289 * returns the xmlElementPtr if found or NULL
2290 */
2291
2292xmlElementPtr
2293xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2294 xmlElementTablePtr table;
2295 xmlElementPtr cur;
2296 xmlChar *uqname = NULL, *prefix = NULL;
2297
2298 if (dtd == NULL) return(NULL);
2299 if (dtd->elements == NULL) {
2300 if (!create)
2301 return(NULL);
2302 /*
2303 * Create the Element table if needed.
2304 */
2305 table = (xmlElementTablePtr) dtd->elements;
2306 if (table == NULL) {
2307 table = xmlCreateElementTable();
2308 dtd->elements = (void *) table;
2309 }
2310 if (table == NULL) {
2311 xmlGenericError(xmlGenericErrorContext,
2312 "xmlGetDtdElementDesc: Table creation failed!\n");
2313 return(NULL);
2314 }
2315 }
2316 table = (xmlElementTablePtr) dtd->elements;
2317
2318 uqname = xmlSplitQName2(name, &prefix);
2319 if (uqname != NULL)
2320 name = uqname;
2321 cur = xmlHashLookup2(table, name, prefix);
2322 if ((cur == NULL) && (create)) {
2323 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2324 if (cur == NULL) {
2325 xmlGenericError(xmlGenericErrorContext,
2326 "xmlGetDtdElementDesc: out of memory\n");
2327 return(NULL);
2328 }
2329 memset(cur, 0, sizeof(xmlElement));
2330 cur->type = XML_ELEMENT_DECL;
2331
2332 /*
2333 * fill the structure.
2334 */
2335 cur->name = xmlStrdup(name);
2336 cur->prefix = xmlStrdup(prefix);
2337 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2338
2339 xmlHashAddEntry2(table, name, prefix, cur);
2340 }
2341 if (prefix != NULL) xmlFree(prefix);
2342 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002343 return(cur);
2344}
2345
2346/**
2347 * xmlGetDtdQElementDesc:
2348 * @dtd: a pointer to the DtD to search
2349 * @name: the element name
2350 * @prefix: the element namespace prefix
2351 *
2352 * Search the Dtd for the description of this element
2353 *
2354 * returns the xmlElementPtr if found or NULL
2355 */
2356
Daniel Veillard48da9102001-08-07 01:10:10 +00002357xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002358xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2359 const xmlChar *prefix) {
2360 xmlElementTablePtr table;
2361
2362 if (dtd == NULL) return(NULL);
2363 if (dtd->elements == NULL) return(NULL);
2364 table = (xmlElementTablePtr) dtd->elements;
2365
2366 return(xmlHashLookup2(table, name, prefix));
2367}
2368
2369/**
2370 * xmlGetDtdAttrDesc:
2371 * @dtd: a pointer to the DtD to search
2372 * @elem: the element name
2373 * @name: the attribute name
2374 *
2375 * Search the Dtd for the description of this attribute on
2376 * this element.
2377 *
2378 * returns the xmlAttributePtr if found or NULL
2379 */
2380
2381xmlAttributePtr
2382xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2383 xmlAttributeTablePtr table;
2384 xmlAttributePtr cur;
2385 xmlChar *uqname = NULL, *prefix = NULL;
2386
2387 if (dtd == NULL) return(NULL);
2388 if (dtd->attributes == NULL) return(NULL);
2389
2390 table = (xmlAttributeTablePtr) dtd->attributes;
2391 if (table == NULL)
2392 return(NULL);
2393
2394 uqname = xmlSplitQName2(name, &prefix);
2395
2396 if (uqname != NULL) {
2397 cur = xmlHashLookup3(table, uqname, prefix, elem);
2398 if (prefix != NULL) xmlFree(prefix);
2399 if (uqname != NULL) xmlFree(uqname);
2400 } else
2401 cur = xmlHashLookup3(table, name, NULL, elem);
2402 return(cur);
2403}
2404
2405/**
2406 * xmlGetDtdQAttrDesc:
2407 * @dtd: a pointer to the DtD to search
2408 * @elem: the element name
2409 * @name: the attribute name
2410 * @prefix: the attribute namespace prefix
2411 *
2412 * Search the Dtd for the description of this qualified attribute on
2413 * this element.
2414 *
2415 * returns the xmlAttributePtr if found or NULL
2416 */
2417
Daniel Veillard48da9102001-08-07 01:10:10 +00002418xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002419xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2420 const xmlChar *prefix) {
2421 xmlAttributeTablePtr table;
2422
2423 if (dtd == NULL) return(NULL);
2424 if (dtd->attributes == NULL) return(NULL);
2425 table = (xmlAttributeTablePtr) dtd->attributes;
2426
2427 return(xmlHashLookup3(table, name, prefix, elem));
2428}
2429
2430/**
2431 * xmlGetDtdNotationDesc:
2432 * @dtd: a pointer to the DtD to search
2433 * @name: the notation name
2434 *
2435 * Search the Dtd for the description of this notation
2436 *
2437 * returns the xmlNotationPtr if found or NULL
2438 */
2439
2440xmlNotationPtr
2441xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2442 xmlNotationTablePtr table;
2443
2444 if (dtd == NULL) return(NULL);
2445 if (dtd->notations == NULL) return(NULL);
2446 table = (xmlNotationTablePtr) dtd->notations;
2447
2448 return(xmlHashLookup(table, name));
2449}
2450
2451/**
2452 * xmlValidateNotationUse:
2453 * @ctxt: the validation context
2454 * @doc: the document
2455 * @notationName: the notation name to check
2456 *
2457 * Validate that the given mame match a notation declaration.
2458 * - [ VC: Notation Declared ]
2459 *
2460 * returns 1 if valid or 0 otherwise
2461 */
2462
2463int
2464xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2465 const xmlChar *notationName) {
2466 xmlNotationPtr notaDecl;
2467 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2468
2469 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2470 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2471 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2472
2473 if (notaDecl == NULL) {
2474 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2475 notationName);
2476 return(0);
2477 }
2478 return(1);
2479}
2480
2481/**
2482 * xmlIsMixedElement
2483 * @doc: the document
2484 * @name: the element name
2485 *
2486 * Search in the DtDs whether an element accept Mixed content (or ANY)
2487 * basically if it is supposed to accept text childs
2488 *
2489 * returns 0 if no, 1 if yes, and -1 if no element description is available
2490 */
2491
2492int
2493xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2494 xmlElementPtr elemDecl;
2495
2496 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2497
2498 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2499 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2500 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2501 if (elemDecl == NULL) return(-1);
2502 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002503 case XML_ELEMENT_TYPE_UNDEFINED:
2504 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002505 case XML_ELEMENT_TYPE_ELEMENT:
2506 return(0);
2507 case XML_ELEMENT_TYPE_EMPTY:
2508 /*
2509 * return 1 for EMPTY since we want VC error to pop up
2510 * on <empty> </empty> for example
2511 */
2512 case XML_ELEMENT_TYPE_ANY:
2513 case XML_ELEMENT_TYPE_MIXED:
2514 return(1);
2515 }
2516 return(1);
2517}
2518
2519/**
2520 * xmlValidateNameValue:
2521 * @value: an Name value
2522 *
2523 * Validate that the given value match Name production
2524 *
2525 * returns 1 if valid or 0 otherwise
2526 */
2527
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002528static int
Owen Taylor3473f882001-02-23 17:55:21 +00002529xmlValidateNameValue(const xmlChar *value) {
2530 const xmlChar *cur;
2531
2532 if (value == NULL) return(0);
2533 cur = value;
2534
2535 if (!IS_LETTER(*cur) && (*cur != '_') &&
2536 (*cur != ':')) {
2537 return(0);
2538 }
2539
2540 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2541 (*cur == '.') || (*cur == '-') ||
2542 (*cur == '_') || (*cur == ':') ||
2543 (IS_COMBINING(*cur)) ||
2544 (IS_EXTENDER(*cur)))
2545 cur++;
2546
2547 if (*cur != 0) return(0);
2548
2549 return(1);
2550}
2551
2552/**
2553 * xmlValidateNamesValue:
2554 * @value: an Names value
2555 *
2556 * Validate that the given value match Names production
2557 *
2558 * returns 1 if valid or 0 otherwise
2559 */
2560
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002561static int
Owen Taylor3473f882001-02-23 17:55:21 +00002562xmlValidateNamesValue(const xmlChar *value) {
2563 const xmlChar *cur;
2564
2565 if (value == NULL) return(0);
2566 cur = value;
2567
2568 if (!IS_LETTER(*cur) && (*cur != '_') &&
2569 (*cur != ':')) {
2570 return(0);
2571 }
2572
2573 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2574 (*cur == '.') || (*cur == '-') ||
2575 (*cur == '_') || (*cur == ':') ||
2576 (IS_COMBINING(*cur)) ||
2577 (IS_EXTENDER(*cur)))
2578 cur++;
2579
2580 while (IS_BLANK(*cur)) {
2581 while (IS_BLANK(*cur)) cur++;
2582
2583 if (!IS_LETTER(*cur) && (*cur != '_') &&
2584 (*cur != ':')) {
2585 return(0);
2586 }
2587
2588 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2589 (*cur == '.') || (*cur == '-') ||
2590 (*cur == '_') || (*cur == ':') ||
2591 (IS_COMBINING(*cur)) ||
2592 (IS_EXTENDER(*cur)))
2593 cur++;
2594 }
2595
2596 if (*cur != 0) return(0);
2597
2598 return(1);
2599}
2600
2601/**
2602 * xmlValidateNmtokenValue:
2603 * @value: an Mntoken value
2604 *
2605 * Validate that the given value match Nmtoken production
2606 *
2607 * [ VC: Name Token ]
2608 *
2609 * returns 1 if valid or 0 otherwise
2610 */
2611
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002612static int
Owen Taylor3473f882001-02-23 17:55:21 +00002613xmlValidateNmtokenValue(const xmlChar *value) {
2614 const xmlChar *cur;
2615
2616 if (value == NULL) return(0);
2617 cur = value;
2618
2619 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2620 (*cur != '.') && (*cur != '-') &&
2621 (*cur != '_') && (*cur != ':') &&
2622 (!IS_COMBINING(*cur)) &&
2623 (!IS_EXTENDER(*cur)))
2624 return(0);
2625
2626 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2627 (*cur == '.') || (*cur == '-') ||
2628 (*cur == '_') || (*cur == ':') ||
2629 (IS_COMBINING(*cur)) ||
2630 (IS_EXTENDER(*cur)))
2631 cur++;
2632
2633 if (*cur != 0) return(0);
2634
2635 return(1);
2636}
2637
2638/**
2639 * xmlValidateNmtokensValue:
2640 * @value: an Mntokens value
2641 *
2642 * Validate that the given value match Nmtokens production
2643 *
2644 * [ VC: Name Token ]
2645 *
2646 * returns 1 if valid or 0 otherwise
2647 */
2648
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002649static int
Owen Taylor3473f882001-02-23 17:55:21 +00002650xmlValidateNmtokensValue(const xmlChar *value) {
2651 const xmlChar *cur;
2652
2653 if (value == NULL) return(0);
2654 cur = value;
2655
2656 while (IS_BLANK(*cur)) cur++;
2657 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2658 (*cur != '.') && (*cur != '-') &&
2659 (*cur != '_') && (*cur != ':') &&
2660 (!IS_COMBINING(*cur)) &&
2661 (!IS_EXTENDER(*cur)))
2662 return(0);
2663
2664 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2665 (*cur == '.') || (*cur == '-') ||
2666 (*cur == '_') || (*cur == ':') ||
2667 (IS_COMBINING(*cur)) ||
2668 (IS_EXTENDER(*cur)))
2669 cur++;
2670
2671 while (IS_BLANK(*cur)) {
2672 while (IS_BLANK(*cur)) cur++;
2673 if (*cur == 0) return(1);
2674
2675 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2676 (*cur != '.') && (*cur != '-') &&
2677 (*cur != '_') && (*cur != ':') &&
2678 (!IS_COMBINING(*cur)) &&
2679 (!IS_EXTENDER(*cur)))
2680 return(0);
2681
2682 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2683 (*cur == '.') || (*cur == '-') ||
2684 (*cur == '_') || (*cur == ':') ||
2685 (IS_COMBINING(*cur)) ||
2686 (IS_EXTENDER(*cur)))
2687 cur++;
2688 }
2689
2690 if (*cur != 0) return(0);
2691
2692 return(1);
2693}
2694
2695/**
2696 * xmlValidateNotationDecl:
2697 * @ctxt: the validation context
2698 * @doc: a document instance
2699 * @nota: a notation definition
2700 *
2701 * Try to validate a single notation definition
2702 * basically it does the following checks as described by the
2703 * XML-1.0 recommendation:
2704 * - it seems that no validity constraing exist on notation declarations
2705 * But this function get called anyway ...
2706 *
2707 * returns 1 if valid or 0 otherwise
2708 */
2709
2710int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002711xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2712 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002713 int ret = 1;
2714
2715 return(ret);
2716}
2717
2718/**
2719 * xmlValidateAttributeValue:
2720 * @type: an attribute type
2721 * @value: an attribute value
2722 *
2723 * Validate that the given attribute value match the proper production
2724 *
2725 * [ VC: ID ]
2726 * Values of type ID must match the Name production....
2727 *
2728 * [ VC: IDREF ]
2729 * Values of type IDREF must match the Name production, and values
2730 * of type IDREFS must match Names ...
2731 *
2732 * [ VC: Entity Name ]
2733 * Values of type ENTITY must match the Name production, values
2734 * of type ENTITIES must match Names ...
2735 *
2736 * [ VC: Name Token ]
2737 * Values of type NMTOKEN must match the Nmtoken production; values
2738 * of type NMTOKENS must match Nmtokens.
2739 *
2740 * returns 1 if valid or 0 otherwise
2741 */
2742
2743int
2744xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2745 switch (type) {
2746 case XML_ATTRIBUTE_ENTITIES:
2747 case XML_ATTRIBUTE_IDREFS:
2748 return(xmlValidateNamesValue(value));
2749 case XML_ATTRIBUTE_ENTITY:
2750 case XML_ATTRIBUTE_IDREF:
2751 case XML_ATTRIBUTE_ID:
2752 case XML_ATTRIBUTE_NOTATION:
2753 return(xmlValidateNameValue(value));
2754 case XML_ATTRIBUTE_NMTOKENS:
2755 case XML_ATTRIBUTE_ENUMERATION:
2756 return(xmlValidateNmtokensValue(value));
2757 case XML_ATTRIBUTE_NMTOKEN:
2758 return(xmlValidateNmtokenValue(value));
2759 case XML_ATTRIBUTE_CDATA:
2760 break;
2761 }
2762 return(1);
2763}
2764
2765/**
2766 * xmlValidateAttributeValue2:
2767 * @ctxt: the validation context
2768 * @doc: the document
2769 * @name: the attribute name (used for error reporting only)
2770 * @type: the attribute type
2771 * @value: the attribute value
2772 *
2773 * Validate that the given attribute value match a given type.
2774 * This typically cannot be done before having finished parsing
2775 * the subsets.
2776 *
2777 * [ VC: IDREF ]
2778 * Values of type IDREF must match one of the declared IDs
2779 * Values of type IDREFS must match a sequence of the declared IDs
2780 * each Name must match the value of an ID attribute on some element
2781 * in the XML document; i.e. IDREF values must match the value of
2782 * some ID attribute
2783 *
2784 * [ VC: Entity Name ]
2785 * Values of type ENTITY must match one declared entity
2786 * Values of type ENTITIES must match a sequence of declared entities
2787 *
2788 * [ VC: Notation Attributes ]
2789 * all notation names in the declaration must be declared.
2790 *
2791 * returns 1 if valid or 0 otherwise
2792 */
2793
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002794static int
Owen Taylor3473f882001-02-23 17:55:21 +00002795xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2796 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2797 int ret = 1;
2798 switch (type) {
2799 case XML_ATTRIBUTE_IDREFS:
2800 case XML_ATTRIBUTE_IDREF:
2801 case XML_ATTRIBUTE_ID:
2802 case XML_ATTRIBUTE_NMTOKENS:
2803 case XML_ATTRIBUTE_ENUMERATION:
2804 case XML_ATTRIBUTE_NMTOKEN:
2805 case XML_ATTRIBUTE_CDATA:
2806 break;
2807 case XML_ATTRIBUTE_ENTITY: {
2808 xmlEntityPtr ent;
2809
2810 ent = xmlGetDocEntity(doc, value);
2811 if (ent == NULL) {
2812 VERROR(ctxt->userData,
2813 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2814 name, value);
2815 ret = 0;
2816 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2817 VERROR(ctxt->userData,
2818 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2819 name, value);
2820 ret = 0;
2821 }
2822 break;
2823 }
2824 case XML_ATTRIBUTE_ENTITIES: {
2825 xmlChar *dup, *nam = NULL, *cur, save;
2826 xmlEntityPtr ent;
2827
2828 dup = xmlStrdup(value);
2829 if (dup == NULL)
2830 return(0);
2831 cur = dup;
2832 while (*cur != 0) {
2833 nam = cur;
2834 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2835 save = *cur;
2836 *cur = 0;
2837 ent = xmlGetDocEntity(doc, nam);
2838 if (ent == NULL) {
2839 VERROR(ctxt->userData,
2840 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2841 name, nam);
2842 ret = 0;
2843 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2844 VERROR(ctxt->userData,
2845 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2846 name, nam);
2847 ret = 0;
2848 }
2849 if (save == 0)
2850 break;
2851 *cur = save;
2852 while (IS_BLANK(*cur)) cur++;
2853 }
2854 xmlFree(dup);
2855 break;
2856 }
2857 case XML_ATTRIBUTE_NOTATION: {
2858 xmlNotationPtr nota;
2859
2860 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2861 if ((nota == NULL) && (doc->extSubset != NULL))
2862 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2863
2864 if (nota == NULL) {
2865 VERROR(ctxt->userData,
2866 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2867 name, value);
2868 ret = 0;
2869 }
2870 break;
2871 }
2872 }
2873 return(ret);
2874}
2875
2876/**
2877 * xmlValidNormalizeAttributeValue:
2878 * @doc: the document
2879 * @elem: the parent
2880 * @name: the attribute name
2881 * @value: the attribute value
2882 *
2883 * Does the validation related extra step of the normalization of attribute
2884 * values:
2885 *
2886 * If the declared value is not CDATA, then the XML processor must further
2887 * process the normalized attribute value by discarding any leading and
2888 * trailing space (#x20) characters, and by replacing sequences of space
2889 * (#x20) characters by single space (#x20) character.
2890 *
2891 * returns a new normalized string if normalization is needed, NULL otherwise
2892 * the caller must free the returned value.
2893 */
2894
2895xmlChar *
2896xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
2897 const xmlChar *name, const xmlChar *value) {
2898 xmlChar *ret, *dst;
2899 const xmlChar *src;
2900 xmlAttributePtr attrDecl = NULL;
2901
2902 if (doc == NULL) return(NULL);
2903 if (elem == NULL) return(NULL);
2904 if (name == NULL) return(NULL);
2905 if (value == NULL) return(NULL);
2906
2907 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2908 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00002909 snprintf((char *) qname, sizeof(qname), "%s:%s",
2910 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002911 qname[sizeof(qname) - 1] = 0;
2912 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
2913 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2914 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
2915 }
2916 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
2917 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2918 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
2919
2920 if (attrDecl == NULL)
2921 return(NULL);
2922 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
2923 return(NULL);
2924
2925 ret = xmlStrdup(value);
2926 if (ret == NULL)
2927 return(NULL);
2928 src = value;
2929 dst = ret;
2930 while (*src == 0x20) src++;
2931 while (*src != 0) {
2932 if (*src == 0x20) {
2933 while (*src == 0x20) src++;
2934 if (*src != 0)
2935 *dst++ = 0x20;
2936 } else {
2937 *dst++ = *src++;
2938 }
2939 }
2940 *dst = 0;
2941 return(ret);
2942}
2943
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002944static void
Owen Taylor3473f882001-02-23 17:55:21 +00002945xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002946 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002947 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
2948}
2949
2950/**
2951 * xmlValidateAttributeDecl:
2952 * @ctxt: the validation context
2953 * @doc: a document instance
2954 * @attr: an attribute definition
2955 *
2956 * Try to validate a single attribute definition
2957 * basically it does the following checks as described by the
2958 * XML-1.0 recommendation:
2959 * - [ VC: Attribute Default Legal ]
2960 * - [ VC: Enumeration ]
2961 * - [ VC: ID Attribute Default ]
2962 *
2963 * The ID/IDREF uniqueness and matching are done separately
2964 *
2965 * returns 1 if valid or 0 otherwise
2966 */
2967
2968int
2969xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2970 xmlAttributePtr attr) {
2971 int ret = 1;
2972 int val;
2973 CHECK_DTD;
2974 if(attr == NULL) return(1);
2975
2976 /* Attribute Default Legal */
2977 /* Enumeration */
2978 if (attr->defaultValue != NULL) {
2979 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
2980 if (val == 0) {
2981 VERROR(ctxt->userData,
2982 "Syntax of default value for attribute %s on %s is not valid\n",
2983 attr->name, attr->elem);
2984 }
2985 ret &= val;
2986 }
2987
2988 /* ID Attribute Default */
2989 if ((attr->atype == XML_ATTRIBUTE_ID)&&
2990 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
2991 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
2992 VERROR(ctxt->userData,
2993 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
2994 attr->name, attr->elem);
2995 ret = 0;
2996 }
2997
2998 /* One ID per Element Type */
2999 if (attr->atype == XML_ATTRIBUTE_ID) {
3000 int nbId;
3001
3002 /* the trick is taht we parse DtD as their own internal subset */
3003 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3004 attr->elem);
3005 if (elem != NULL) {
3006 nbId = xmlScanIDAttributeDecl(NULL, elem);
3007 } else {
3008 xmlAttributeTablePtr table;
3009
3010 /*
3011 * The attribute may be declared in the internal subset and the
3012 * element in the external subset.
3013 */
3014 nbId = 0;
3015 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3016 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3017 xmlValidateAttributeIdCallback, &nbId);
3018 }
3019 if (nbId > 1) {
3020 VERROR(ctxt->userData,
3021 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3022 attr->elem, nbId, attr->name);
3023 } else if (doc->extSubset != NULL) {
3024 int extId = 0;
3025 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3026 if (elem != NULL) {
3027 extId = xmlScanIDAttributeDecl(NULL, elem);
3028 }
3029 if (extId > 1) {
3030 VERROR(ctxt->userData,
3031 "Element %s has %d ID attribute defined in the external subset : %s\n",
3032 attr->elem, extId, attr->name);
3033 } else if (extId + nbId > 1) {
3034 VERROR(ctxt->userData,
3035"Element %s has ID attributes defined in the internal and external subset : %s\n",
3036 attr->elem, attr->name);
3037 }
3038 }
3039 }
3040
3041 /* Validity Constraint: Enumeration */
3042 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3043 xmlEnumerationPtr tree = attr->tree;
3044 while (tree != NULL) {
3045 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3046 tree = tree->next;
3047 }
3048 if (tree == NULL) {
3049 VERROR(ctxt->userData,
3050"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3051 attr->defaultValue, attr->name, attr->elem);
3052 ret = 0;
3053 }
3054 }
3055
3056 return(ret);
3057}
3058
3059/**
3060 * xmlValidateElementDecl:
3061 * @ctxt: the validation context
3062 * @doc: a document instance
3063 * @elem: an element definition
3064 *
3065 * Try to validate a single element definition
3066 * basically it does the following checks as described by the
3067 * XML-1.0 recommendation:
3068 * - [ VC: One ID per Element Type ]
3069 * - [ VC: No Duplicate Types ]
3070 * - [ VC: Unique Element Type Declaration ]
3071 *
3072 * returns 1 if valid or 0 otherwise
3073 */
3074
3075int
3076xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3077 xmlElementPtr elem) {
3078 int ret = 1;
3079 xmlElementPtr tst;
3080
3081 CHECK_DTD;
3082
3083 if (elem == NULL) return(1);
3084
3085 /* No Duplicate Types */
3086 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3087 xmlElementContentPtr cur, next;
3088 const xmlChar *name;
3089
3090 cur = elem->content;
3091 while (cur != NULL) {
3092 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3093 if (cur->c1 == NULL) break;
3094 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3095 name = cur->c1->name;
3096 next = cur->c2;
3097 while (next != NULL) {
3098 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3099 if (xmlStrEqual(next->name, name)) {
3100 VERROR(ctxt->userData,
3101 "Definition of %s has duplicate references of %s\n",
3102 elem->name, name);
3103 ret = 0;
3104 }
3105 break;
3106 }
3107 if (next->c1 == NULL) break;
3108 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3109 if (xmlStrEqual(next->c1->name, name)) {
3110 VERROR(ctxt->userData,
3111 "Definition of %s has duplicate references of %s\n",
3112 elem->name, name);
3113 ret = 0;
3114 }
3115 next = next->c2;
3116 }
3117 }
3118 cur = cur->c2;
3119 }
3120 }
3121
3122 /* VC: Unique Element Type Declaration */
3123 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003124 if ((tst != NULL ) && (tst != elem) &&
3125 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003126 VERROR(ctxt->userData, "Redefinition of element %s\n",
3127 elem->name);
3128 ret = 0;
3129 }
3130 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003131 if ((tst != NULL ) && (tst != elem) &&
3132 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003133 VERROR(ctxt->userData, "Redefinition of element %s\n",
3134 elem->name);
3135 ret = 0;
3136 }
3137
Daniel Veillarda10efa82001-04-18 13:09:01 +00003138 /* One ID per Element Type
3139 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003140 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3141 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003142 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003143 return(ret);
3144}
3145
3146/**
3147 * xmlValidateOneAttribute:
3148 * @ctxt: the validation context
3149 * @doc: a document instance
3150 * @elem: an element instance
3151 * @attr: an attribute instance
3152 * @value: the attribute value (without entities processing)
3153 *
3154 * Try to validate a single attribute for an element
3155 * basically it does the following checks as described by the
3156 * XML-1.0 recommendation:
3157 * - [ VC: Attribute Value Type ]
3158 * - [ VC: Fixed Attribute Default ]
3159 * - [ VC: Entity Name ]
3160 * - [ VC: Name Token ]
3161 * - [ VC: ID ]
3162 * - [ VC: IDREF ]
3163 * - [ VC: Entity Name ]
3164 * - [ VC: Notation Attributes ]
3165 *
3166 * The ID/IDREF uniqueness and matching are done separately
3167 *
3168 * returns 1 if valid or 0 otherwise
3169 */
3170
3171int
3172xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3173 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3174 /* xmlElementPtr elemDecl; */
3175 xmlAttributePtr attrDecl = NULL;
3176 int val;
3177 int ret = 1;
3178
3179 CHECK_DTD;
3180 if ((elem == NULL) || (elem->name == NULL)) return(0);
3181 if ((attr == NULL) || (attr->name == NULL)) return(0);
3182
3183 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3184 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003185 snprintf((char *) qname, sizeof(qname), "%s:%s",
3186 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003187 qname[sizeof(qname) - 1] = 0;
3188 if (attr->ns != NULL) {
3189 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3190 attr->name, attr->ns->prefix);
3191 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3192 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3193 attr->name, attr->ns->prefix);
3194 } else {
3195 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3196 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3197 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3198 qname, attr->name);
3199 }
3200 }
3201 if (attrDecl == NULL) {
3202 if (attr->ns != NULL) {
3203 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3204 attr->name, attr->ns->prefix);
3205 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3206 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3207 attr->name, attr->ns->prefix);
3208 } else {
3209 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3210 elem->name, attr->name);
3211 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3212 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3213 elem->name, attr->name);
3214 }
3215 }
3216
3217
3218 /* Validity Constraint: Attribute Value Type */
3219 if (attrDecl == NULL) {
3220 VERROR(ctxt->userData,
3221 "No declaration for attribute %s on element %s\n",
3222 attr->name, elem->name);
3223 return(0);
3224 }
3225 attr->atype = attrDecl->atype;
3226
3227 val = xmlValidateAttributeValue(attrDecl->atype, value);
3228 if (val == 0) {
3229 VERROR(ctxt->userData,
3230 "Syntax of value for attribute %s on %s is not valid\n",
3231 attr->name, elem->name);
3232 ret = 0;
3233 }
3234
3235 /* Validity constraint: Fixed Attribute Default */
3236 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3237 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3238 VERROR(ctxt->userData,
3239 "Value for attribute %s on %s is differnt from default \"%s\"\n",
3240 attr->name, elem->name, attrDecl->defaultValue);
3241 ret = 0;
3242 }
3243 }
3244
3245 /* Validity Constraint: ID uniqueness */
3246 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3247 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3248 ret = 0;
3249 }
3250
3251 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3252 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3253 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3254 ret = 0;
3255 }
3256
3257 /* Validity Constraint: Notation Attributes */
3258 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3259 xmlEnumerationPtr tree = attrDecl->tree;
3260 xmlNotationPtr nota;
3261
3262 /* First check that the given NOTATION was declared */
3263 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3264 if (nota == NULL)
3265 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3266
3267 if (nota == NULL) {
3268 VERROR(ctxt->userData,
3269 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
3270 value, attr->name, elem->name);
3271 ret = 0;
3272 }
3273
3274 /* Second, verify that it's among the list */
3275 while (tree != NULL) {
3276 if (xmlStrEqual(tree->name, value)) break;
3277 tree = tree->next;
3278 }
3279 if (tree == NULL) {
3280 VERROR(ctxt->userData,
3281"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
3282 value, attr->name, elem->name);
3283 ret = 0;
3284 }
3285 }
3286
3287 /* Validity Constraint: Enumeration */
3288 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3289 xmlEnumerationPtr tree = attrDecl->tree;
3290 while (tree != NULL) {
3291 if (xmlStrEqual(tree->name, value)) break;
3292 tree = tree->next;
3293 }
3294 if (tree == NULL) {
3295 VERROR(ctxt->userData,
3296 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3297 value, attr->name, elem->name);
3298 ret = 0;
3299 }
3300 }
3301
3302 /* Fixed Attribute Default */
3303 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3304 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3305 VERROR(ctxt->userData,
3306 "Value for attribute %s on %s must be \"%s\"\n",
3307 attr->name, elem->name, attrDecl->defaultValue);
3308 ret = 0;
3309 }
3310
3311 /* Extra check for the attribute value */
3312 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3313 attrDecl->atype, value);
3314
3315 return(ret);
3316}
3317
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003318/**
3319 * xmlValidateSkipIgnorable:
3320 * @ctxt: the validation context
3321 * @child: the child list
3322 *
3323 * Skip ignorable elements w.r.t. the validation process
3324 *
3325 * returns the first element to consider for validation of the content model
3326 */
3327
3328static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003329xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003330 while (child != NULL) {
3331 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003332 /* These things are ignored (skipped) during validation. */
3333 case XML_PI_NODE:
3334 case XML_COMMENT_NODE:
3335 case XML_XINCLUDE_START:
3336 case XML_XINCLUDE_END:
3337 child = child->next;
3338 break;
3339 case XML_TEXT_NODE:
3340 if (xmlIsBlankNode(child))
3341 child = child->next;
3342 else
3343 return(child);
3344 break;
3345 /* keep current node */
3346 default:
3347 return(child);
3348 }
3349 }
3350 return(child);
3351}
3352
3353/**
3354 * xmlValidateElementType:
3355 * @ctxt: the validation context
3356 *
3357 * Try to validate the content model of an element internal function
3358 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003359 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3360 * reference is found and -3 if the validation succeeded but
3361 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003362 */
3363
3364static int
3365xmlValidateElementType(xmlValidCtxtPtr ctxt) {
3366 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003367 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003368
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003369 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003370 if ((NODE == NULL) && (CONT == NULL))
3371 return(1);
3372 if ((NODE == NULL) &&
3373 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3374 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3375 return(1);
3376 }
3377 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003378 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003379 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003380
3381 /*
3382 * We arrive here when more states need to be examined
3383 */
3384cont:
3385
3386 /*
3387 * We just recovered from a rollback generated by a possible
3388 * epsilon transition, go directly to the analysis phase
3389 */
3390 if (STATE == ROLLBACK_PARENT) {
3391 DEBUG_VALID_MSG("restaured parent branch");
3392 DEBUG_VALID_STATE(NODE, CONT)
3393 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003394 goto analyze;
3395 }
3396
3397 DEBUG_VALID_STATE(NODE, CONT)
3398 /*
3399 * we may have to save a backup state here. This is the equivalent
3400 * of handling epsilon transition in NFAs.
3401 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003402 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003403 ((CONT->parent == NULL) ||
3404 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003405 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003406 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
3407 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003408 DEBUG_VALID_MSG("saving parent branch");
3409 vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
3410 }
3411
3412
3413 /*
3414 * Check first if the content matches
3415 */
3416 switch (CONT->type) {
3417 case XML_ELEMENT_CONTENT_PCDATA:
3418 if (NODE == NULL) {
3419 DEBUG_VALID_MSG("pcdata failed no node");
3420 ret = 0;
3421 break;
3422 }
3423 if (NODE->type == XML_TEXT_NODE) {
3424 DEBUG_VALID_MSG("pcdata found, skip to next");
3425 /*
3426 * go to next element in the content model
3427 * skipping ignorable elems
3428 */
3429 do {
3430 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003431 NODE = xmlValidateSkipIgnorable(NODE);
3432 if ((NODE != NULL) &&
3433 (NODE->type == XML_ENTITY_REF_NODE))
3434 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003435 } while ((NODE != NULL) &&
3436 ((NODE->type != XML_ELEMENT_NODE) &&
3437 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003438 ret = 1;
3439 break;
3440 } else {
3441 DEBUG_VALID_MSG("pcdata failed");
3442 ret = 0;
3443 break;
3444 }
3445 break;
3446 case XML_ELEMENT_CONTENT_ELEMENT:
3447 if (NODE == NULL) {
3448 DEBUG_VALID_MSG("element failed no node");
3449 ret = 0;
3450 break;
3451 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003452 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3453 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003454 if (ret == 1) {
3455 DEBUG_VALID_MSG("element found, skip to next");
3456 /*
3457 * go to next element in the content model
3458 * skipping ignorable elems
3459 */
3460 do {
3461 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003462 NODE = xmlValidateSkipIgnorable(NODE);
3463 if ((NODE != NULL) &&
3464 (NODE->type == XML_ENTITY_REF_NODE))
3465 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003466 } while ((NODE != NULL) &&
3467 ((NODE->type != XML_ELEMENT_NODE) &&
3468 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003469 } else {
3470 DEBUG_VALID_MSG("element failed");
3471 ret = 0;
3472 break;
3473 }
3474 break;
3475 case XML_ELEMENT_CONTENT_OR:
3476 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003477 * Small optimization.
3478 */
3479 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3480 if ((NODE == NULL) ||
3481 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3482 DEPTH++;
3483 CONT = CONT->c2;
3484 goto cont;
3485 }
3486 }
3487
3488 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003489 * save the second branch 'or' branch
3490 */
3491 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard268fd1b2001-08-26 18:46:36 +00003492 vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3493 OCCURS, ROLLBACK_OR);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003494
3495 DEPTH++;
3496 CONT = CONT->c1;
3497 goto cont;
3498 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003499 /*
3500 * Small optimization.
3501 */
3502 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3503 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3504 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3505 if ((NODE == NULL) ||
3506 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3507 DEPTH++;
3508 CONT = CONT->c2;
3509 goto cont;
3510 }
3511 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003512 DEPTH++;
3513 CONT = CONT->c1;
3514 goto cont;
3515 }
3516
3517 /*
3518 * At this point handle going up in the tree
3519 */
3520 if (ret == -1) {
3521 DEBUG_VALID_MSG("error found returning");
3522 return(ret);
3523 }
3524analyze:
3525 while (CONT != NULL) {
3526 /*
3527 * First do the analysis depending on the occurence model at
3528 * this level.
3529 */
3530 if (ret == 0) {
3531 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003532 xmlNodePtr cur;
3533
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003534 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003535 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003536 DEBUG_VALID_MSG("Once branch failed, rollback");
3537 if (vstateVPop(ctxt) < 0 ) {
3538 DEBUG_VALID_MSG("exhaustion, failed");
3539 return(0);
3540 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003541 if (cur != ctxt->vstate->node)
3542 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003543 goto cont;
3544 case XML_ELEMENT_CONTENT_PLUS:
3545 if (OCCURENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003546 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003547 DEBUG_VALID_MSG("Plus branch failed, rollback");
3548 if (vstateVPop(ctxt) < 0 ) {
3549 DEBUG_VALID_MSG("exhaustion, failed");
3550 return(0);
3551 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003552 if (cur != ctxt->vstate->node)
3553 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003554 goto cont;
3555 }
3556 DEBUG_VALID_MSG("Plus branch found");
3557 ret = 1;
3558 break;
3559 case XML_ELEMENT_CONTENT_MULT:
3560#ifdef DEBUG_VALID_ALGO
3561 if (OCCURENCE == 0) {
3562 DEBUG_VALID_MSG("Mult branch failed");
3563 } else {
3564 DEBUG_VALID_MSG("Mult branch found");
3565 }
3566#endif
3567 ret = 1;
3568 break;
3569 case XML_ELEMENT_CONTENT_OPT:
3570 DEBUG_VALID_MSG("Option branch failed");
3571 ret = 1;
3572 break;
3573 }
3574 } else {
3575 switch (CONT->ocur) {
3576 case XML_ELEMENT_CONTENT_OPT:
3577 DEBUG_VALID_MSG("Option branch succeeded");
3578 ret = 1;
3579 break;
3580 case XML_ELEMENT_CONTENT_ONCE:
3581 DEBUG_VALID_MSG("Once branch succeeded");
3582 ret = 1;
3583 break;
3584 case XML_ELEMENT_CONTENT_PLUS:
3585 if (STATE == ROLLBACK_PARENT) {
3586 DEBUG_VALID_MSG("Plus branch rollback");
3587 ret = 1;
3588 break;
3589 }
3590 if (NODE == NULL) {
3591 DEBUG_VALID_MSG("Plus branch exhausted");
3592 ret = 1;
3593 break;
3594 }
3595 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
3596 SET_OCCURENCE;
3597 goto cont;
3598 case XML_ELEMENT_CONTENT_MULT:
3599 if (STATE == ROLLBACK_PARENT) {
3600 DEBUG_VALID_MSG("Mult branch rollback");
3601 ret = 1;
3602 break;
3603 }
3604 if (NODE == NULL) {
3605 DEBUG_VALID_MSG("Mult branch exhausted");
3606 ret = 1;
3607 break;
3608 }
3609 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003610 /* SET_OCCURENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003611 goto cont;
3612 }
3613 }
3614 STATE = 0;
3615
3616 /*
3617 * Then act accordingly at the parent level
3618 */
3619 RESET_OCCURENCE;
3620 if (CONT->parent == NULL)
3621 break;
3622
3623 switch (CONT->parent->type) {
3624 case XML_ELEMENT_CONTENT_PCDATA:
3625 DEBUG_VALID_MSG("Error: parent pcdata");
3626 return(-1);
3627 case XML_ELEMENT_CONTENT_ELEMENT:
3628 DEBUG_VALID_MSG("Error: parent element");
3629 return(-1);
3630 case XML_ELEMENT_CONTENT_OR:
3631 if (ret == 1) {
3632 DEBUG_VALID_MSG("Or succeeded");
3633 CONT = CONT->parent;
3634 DEPTH--;
3635 } else {
3636 DEBUG_VALID_MSG("Or failed");
3637 CONT = CONT->parent;
3638 DEPTH--;
3639 }
3640 break;
3641 case XML_ELEMENT_CONTENT_SEQ:
3642 if (ret == 0) {
3643 DEBUG_VALID_MSG("Sequence failed");
3644 CONT = CONT->parent;
3645 DEPTH--;
3646 } else if (CONT == CONT->parent->c1) {
3647 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3648 CONT = CONT->parent->c2;
3649 goto cont;
3650 } else {
3651 DEBUG_VALID_MSG("Sequence succeeded");
3652 CONT = CONT->parent;
3653 DEPTH--;
3654 }
3655 }
3656 }
3657 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003658 xmlNodePtr cur;
3659
3660 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003661 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3662 if (vstateVPop(ctxt) < 0 ) {
3663 DEBUG_VALID_MSG("exhaustion, failed");
3664 return(0);
3665 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003666 if (cur != ctxt->vstate->node)
3667 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003668 goto cont;
3669 }
3670 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003671 xmlNodePtr cur;
3672
3673 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003674 DEBUG_VALID_MSG("Failure, rollback");
3675 if (vstateVPop(ctxt) < 0 ) {
3676 DEBUG_VALID_MSG("exhaustion, failed");
3677 return(0);
3678 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003679 if (cur != ctxt->vstate->node)
3680 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003681 goto cont;
3682 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003683 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003684}
3685
3686/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00003687 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003688 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00003689 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003690 * @content: An element
3691 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3692 *
3693 * This will dump the list of elements to the buffer
3694 * Intended just for the debug routine
3695 */
3696static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00003697xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003698 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00003699 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003700
3701 if (node == NULL) return;
3702 if (glob) strcat(buf, "(");
3703 cur = node;
3704 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00003705 len = strlen(buf);
3706 if (size - len < 50) {
3707 if ((size - len > 4) && (buf[len - 1] != '.'))
3708 strcat(buf, " ...");
3709 return;
3710 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003711 switch (cur->type) {
3712 case XML_ELEMENT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003713 if (size - len < xmlStrlen(cur->name + 10)) {
3714 if ((size - len > 4) && (buf[len - 1] != '.'))
3715 strcat(buf, " ...");
3716 return;
3717 }
3718 strcat(buf, (char *) cur->name);
3719 if (cur->next != NULL)
3720 strcat(buf, " ");
3721 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003722 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003723 if (xmlIsBlankNode(cur))
3724 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003725 case XML_CDATA_SECTION_NODE:
3726 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003727 strcat(buf, "CDATA");
3728 if (cur->next != NULL)
3729 strcat(buf, " ");
3730 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003731 case XML_ATTRIBUTE_NODE:
3732 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003733#ifdef LIBXML_DOCB_ENABLED
3734 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003735#endif
3736 case XML_HTML_DOCUMENT_NODE:
3737 case XML_DOCUMENT_TYPE_NODE:
3738 case XML_DOCUMENT_FRAG_NODE:
3739 case XML_NOTATION_NODE:
3740 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003741 strcat(buf, "???");
3742 if (cur->next != NULL)
3743 strcat(buf, " ");
3744 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003745 case XML_ENTITY_NODE:
3746 case XML_PI_NODE:
3747 case XML_DTD_NODE:
3748 case XML_COMMENT_NODE:
3749 case XML_ELEMENT_DECL:
3750 case XML_ATTRIBUTE_DECL:
3751 case XML_ENTITY_DECL:
3752 case XML_XINCLUDE_START:
3753 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003754 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003755 }
3756 cur = cur->next;
3757 }
3758 if (glob) strcat(buf, ")");
3759}
3760
3761/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003762 * xmlValidateElementContent:
3763 * @ctxt: the validation context
3764 * @child: the child list
3765 * @cont: pointer to the content declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003766 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003767 *
3768 * Try to validate the content model of an element
3769 *
3770 * returns 1 if valid or 0 if not and -1 in case of error
3771 */
3772
3773static int
3774xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillard7533cc82001-04-24 15:52:00 +00003775 xmlElementContentPtr cont, int warn, const xmlChar *name) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003776 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003777 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003778
3779 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003780 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003781 */
3782 ctxt->vstateMax = 8;
3783 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
3784 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
3785 if (ctxt->vstateTab == NULL) {
3786 xmlGenericError(xmlGenericErrorContext,
3787 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003788 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003789 }
3790 /*
3791 * The first entry in the stack is reserved to the current state
3792 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00003793 ctxt->nodeMax = 0;
3794 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00003795 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003796 ctxt->vstate = &ctxt->vstateTab[0];
3797 ctxt->vstateNr = 1;
3798 CONT = cont;
3799 NODE = child;
3800 DEPTH = 0;
3801 OCCURS = 0;
3802 STATE = 0;
3803 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003804 if ((ret == -3) && (warn)) {
3805 VWARNING(ctxt->userData,
3806 "Element %s content model is ambiguous\n", name);
3807 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003808 /*
3809 * An entities reference appeared at this level.
3810 * Buid a minimal representation of this node content
3811 * sufficient to run the validation process on it
3812 */
3813 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003814 cur = child;
3815 while (cur != NULL) {
3816 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003817 case XML_ENTITY_REF_NODE:
3818 /*
3819 * Push the current node to be able to roll back
3820 * and process within the entity
3821 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003822 if ((cur->children != NULL) &&
3823 (cur->children->children != NULL)) {
3824 nodeVPush(ctxt, cur);
3825 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003826 continue;
3827 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00003828 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003829 case XML_TEXT_NODE:
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003830 case XML_CDATA_SECTION_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003831 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003832 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003833 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003834 case XML_ELEMENT_NODE:
3835 /*
3836 * Allocate a new node and minimally fills in
3837 * what's required
3838 */
3839 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
3840 if (tmp == NULL) {
3841 xmlGenericError(xmlGenericErrorContext,
3842 "xmlValidateElementContent : malloc failed\n");
3843 xmlFreeNodeList(repl);
3844 ret = -1;
3845 goto done;
3846 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003847 tmp->type = cur->type;
3848 tmp->name = cur->name;
3849 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003850 tmp->next = NULL;
3851 if (repl == NULL)
3852 repl = last = tmp;
3853 else {
3854 last->next = tmp;
3855 last = tmp;
3856 }
3857 break;
3858 default:
3859 break;
3860 }
3861 /*
3862 * Switch to next element
3863 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003864 cur = cur->next;
3865 while (cur == NULL) {
3866 cur = nodeVPop(ctxt);
3867 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003868 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003869 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003870 }
3871 }
3872
3873 /*
3874 * Relaunch the validation
3875 */
3876 ctxt->vstate = &ctxt->vstateTab[0];
3877 ctxt->vstateNr = 1;
3878 CONT = cont;
3879 NODE = repl;
3880 DEPTH = 0;
3881 OCCURS = 0;
3882 STATE = 0;
3883 ret = xmlValidateElementType(ctxt);
3884 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003885 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003886 char expr[5000];
3887 char list[5000];
3888
3889 expr[0] = 0;
Daniel Veillardd3d06722001-08-15 12:06:36 +00003890 xmlSnprintfElementContent(expr, 5000, cont, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003891 list[0] = 0;
3892 if (repl != NULL)
Daniel Veillardd3d06722001-08-15 12:06:36 +00003893 xmlSnprintfElements(list, 5000, repl, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003894 else
Daniel Veillardd3d06722001-08-15 12:06:36 +00003895 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003896
Daniel Veillard7533cc82001-04-24 15:52:00 +00003897 if (name != NULL) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003898 VERROR(ctxt->userData,
3899 "Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
Daniel Veillard7533cc82001-04-24 15:52:00 +00003900 name, expr, list);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003901 } else {
3902 VERROR(ctxt->userData,
3903 "Element content doesn't follow the Dtd\nExpecting %s, got %s\n",
3904 expr, list);
3905 }
3906 ret = 0;
3907 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003908 if (ret == -3)
3909 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003910
3911
3912done:
3913 /*
3914 * Deallocate the copy if done, and free up the validation stack
3915 */
3916 while (repl != NULL) {
3917 tmp = repl->next;
3918 xmlFree(repl);
3919 repl = tmp;
3920 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003921 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003922 if (ctxt->vstateTab != NULL) {
3923 xmlFree(ctxt->vstateTab);
3924 ctxt->vstateTab = NULL;
3925 }
3926 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00003927 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003928 if (ctxt->nodeTab != NULL) {
3929 xmlFree(ctxt->nodeTab);
3930 ctxt->nodeTab = NULL;
3931 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003932 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003933
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003934}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003935
Owen Taylor3473f882001-02-23 17:55:21 +00003936/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003937 * xmlValidateCdataElement:
3938 * @ctxt: the validation context
3939 * @doc: a document instance
3940 * @elem: an element instance
3941 *
3942 * Check that an element follows #CDATA
3943 *
3944 * returns 1 if valid or 0 otherwise
3945 */
3946static int
3947xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3948 xmlNodePtr elem) {
3949 int ret = 1;
3950 xmlNodePtr cur, child;
3951
3952 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
3953 return(0);
3954
3955 child = elem->children;
3956
3957 cur = child;
3958 while (cur != NULL) {
3959 switch (cur->type) {
3960 case XML_ENTITY_REF_NODE:
3961 /*
3962 * Push the current node to be able to roll back
3963 * and process within the entity
3964 */
3965 if ((cur->children != NULL) &&
3966 (cur->children->children != NULL)) {
3967 nodeVPush(ctxt, cur);
3968 cur = cur->children->children;
3969 continue;
3970 }
3971 break;
3972 case XML_COMMENT_NODE:
3973 case XML_PI_NODE:
3974 case XML_TEXT_NODE:
3975 case XML_CDATA_SECTION_NODE:
3976 break;
3977 default:
3978 ret = 0;
3979 goto done;
3980 }
3981 /*
3982 * Switch to next element
3983 */
3984 cur = cur->next;
3985 while (cur == NULL) {
3986 cur = nodeVPop(ctxt);
3987 if (cur == NULL)
3988 break;
3989 cur = cur->next;
3990 }
3991 }
3992done:
3993 ctxt->nodeMax = 0;
3994 ctxt->nodeNr = 0;
3995 if (ctxt->nodeTab != NULL) {
3996 xmlFree(ctxt->nodeTab);
3997 ctxt->nodeTab = NULL;
3998 }
3999 return(ret);
4000}
4001
4002/**
Owen Taylor3473f882001-02-23 17:55:21 +00004003 * xmlValidateOneElement:
4004 * @ctxt: the validation context
4005 * @doc: a document instance
4006 * @elem: an element instance
4007 *
4008 * Try to validate a single element and it's attributes,
4009 * basically it does the following checks as described by the
4010 * XML-1.0 recommendation:
4011 * - [ VC: Element Valid ]
4012 * - [ VC: Required Attribute ]
4013 * Then call xmlValidateOneAttribute() for each attribute present.
4014 *
4015 * The ID/IDREF checkings are done separately
4016 *
4017 * returns 1 if valid or 0 otherwise
4018 */
4019
4020int
4021xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4022 xmlNodePtr elem) {
4023 xmlElementPtr elemDecl = NULL;
4024 xmlElementContentPtr cont;
4025 xmlAttributePtr attr;
4026 xmlNodePtr child;
4027 int ret = 1;
4028 const xmlChar *name;
4029
4030 CHECK_DTD;
4031
4032 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004033 switch (elem->type) {
4034 case XML_ATTRIBUTE_NODE:
4035 VERROR(ctxt->userData,
4036 "Attribute element not expected here\n");
4037 return(0);
4038 case XML_TEXT_NODE:
4039 if (elem->children != NULL) {
4040 VERROR(ctxt->userData, "Text element has childs !\n");
4041 return(0);
4042 }
4043 if (elem->properties != NULL) {
4044 VERROR(ctxt->userData, "Text element has attributes !\n");
4045 return(0);
4046 }
4047 if (elem->ns != NULL) {
4048 VERROR(ctxt->userData, "Text element has namespace !\n");
4049 return(0);
4050 }
4051 if (elem->nsDef != NULL) {
4052 VERROR(ctxt->userData,
4053 "Text element carries namespace definitions !\n");
4054 return(0);
4055 }
4056 if (elem->content == NULL) {
4057 VERROR(ctxt->userData,
4058 "Text element has no content !\n");
4059 return(0);
4060 }
4061 return(1);
4062 case XML_XINCLUDE_START:
4063 case XML_XINCLUDE_END:
4064 return(1);
4065 case XML_CDATA_SECTION_NODE:
4066 case XML_ENTITY_REF_NODE:
4067 case XML_PI_NODE:
4068 case XML_COMMENT_NODE:
4069 return(1);
4070 case XML_ENTITY_NODE:
4071 VERROR(ctxt->userData,
4072 "Entity element not expected here\n");
4073 return(0);
4074 case XML_NOTATION_NODE:
4075 VERROR(ctxt->userData,
4076 "Notation element not expected here\n");
4077 return(0);
4078 case XML_DOCUMENT_NODE:
4079 case XML_DOCUMENT_TYPE_NODE:
4080 case XML_DOCUMENT_FRAG_NODE:
4081 VERROR(ctxt->userData,
4082 "Document element not expected here\n");
4083 return(0);
4084 case XML_HTML_DOCUMENT_NODE:
4085 VERROR(ctxt->userData,
4086 "\n");
4087 return(0);
4088 case XML_ELEMENT_NODE:
4089 break;
4090 default:
4091 VERROR(ctxt->userData,
4092 "unknown element type %d\n", elem->type);
4093 return(0);
4094 }
4095 if (elem->name == NULL) return(0);
4096
4097 /*
4098 * Fetch the declaration for the qualified name
4099 */
4100 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
4101 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
4102 elem->name, elem->ns->prefix);
4103 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4104 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
4105 elem->name, elem->ns->prefix);
4106 }
4107
4108 /*
4109 * Fetch the declaration for the non qualified name
4110 */
4111 if (elemDecl == NULL) {
4112 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
4113 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4114 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
4115 }
4116 if (elemDecl == NULL) {
4117 VERROR(ctxt->userData, "No declaration for element %s\n",
4118 elem->name);
4119 return(0);
4120 }
4121
4122 /* Check taht the element content matches the definition */
4123 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004124 case XML_ELEMENT_TYPE_UNDEFINED:
4125 VERROR(ctxt->userData, "No declaration for element %s\n",
4126 elem->name);
4127 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004128 case XML_ELEMENT_TYPE_EMPTY:
4129 if (elem->children != NULL) {
4130 VERROR(ctxt->userData,
4131 "Element %s was declared EMPTY this one has content\n",
4132 elem->name);
4133 ret = 0;
4134 }
4135 break;
4136 case XML_ELEMENT_TYPE_ANY:
4137 /* I don't think anything is required then */
4138 break;
4139 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004140 /* simple case of declared as #PCDATA */
4141 if ((elemDecl->content != NULL) &&
4142 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4143 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4144 if (!ret) {
4145 VERROR(ctxt->userData,
4146 "Element %s was declared #PCDATA but contains non text nodes\n",
4147 elem->name);
4148 }
4149 break;
4150 }
Owen Taylor3473f882001-02-23 17:55:21 +00004151 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004152 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004153 while (child != NULL) {
4154 if (child->type == XML_ELEMENT_NODE) {
4155 name = child->name;
4156 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4157 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004158 snprintf((char *) qname, sizeof(qname), "%s:%s",
4159 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004160 qname[sizeof(qname) - 1] = 0;
4161 cont = elemDecl->content;
4162 while (cont != NULL) {
4163 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4164 if (xmlStrEqual(cont->name, qname)) break;
4165 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4166 (cont->c1 != NULL) &&
4167 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4168 if (xmlStrEqual(cont->c1->name, qname)) break;
4169 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4170 (cont->c1 == NULL) ||
4171 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4172 /* Internal error !!! */
4173 xmlGenericError(xmlGenericErrorContext,
4174 "Internal: MIXED struct bad\n");
4175 break;
4176 }
4177 cont = cont->c2;
4178 }
4179 if (cont != NULL)
4180 goto child_ok;
4181 }
4182 cont = elemDecl->content;
4183 while (cont != NULL) {
4184 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4185 if (xmlStrEqual(cont->name, name)) break;
4186 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4187 (cont->c1 != NULL) &&
4188 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4189 if (xmlStrEqual(cont->c1->name, name)) break;
4190 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4191 (cont->c1 == NULL) ||
4192 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4193 /* Internal error !!! */
4194 xmlGenericError(xmlGenericErrorContext,
4195 "Internal: MIXED struct bad\n");
4196 break;
4197 }
4198 cont = cont->c2;
4199 }
4200 if (cont == NULL) {
4201 VERROR(ctxt->userData,
4202 "Element %s is not declared in %s list of possible childs\n",
4203 name, elem->name);
4204 ret = 0;
4205 }
4206 }
4207child_ok:
4208 child = child->next;
4209 }
4210 break;
4211 case XML_ELEMENT_TYPE_ELEMENT:
4212 child = elem->children;
4213 cont = elemDecl->content;
Daniel Veillard7533cc82001-04-24 15:52:00 +00004214 ret = xmlValidateElementContent(ctxt, child, cont, 1, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004215 break;
4216 }
4217
4218 /* [ VC: Required Attribute ] */
4219 attr = elemDecl->attributes;
4220 while (attr != NULL) {
4221 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
4222 xmlAttrPtr attrib;
4223 int qualified = -1;
4224
4225 attrib = elem->properties;
4226 while (attrib != NULL) {
4227 if (xmlStrEqual(attrib->name, attr->name)) {
4228 if (attr->prefix != NULL) {
4229 xmlNsPtr nameSpace = attrib->ns;
4230
4231 if (nameSpace == NULL)
4232 nameSpace = elem->ns;
4233 /*
4234 * qualified names handling is problematic, having a
4235 * different prefix should be possible but DTDs don't
4236 * allow to define the URI instead of the prefix :-(
4237 */
4238 if (nameSpace == NULL) {
4239 if (qualified < 0)
4240 qualified = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004241 } else if (!xmlStrEqual(nameSpace->prefix,
4242 attr->prefix)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004243 if (qualified < 1)
4244 qualified = 1;
4245 } else
4246 goto found;
4247 } else {
4248 /*
4249 * We should allow applications to define namespaces
4250 * for their application even if the DTD doesn't
4251 * carry one, otherwise, basically we would always
4252 * break.
4253 */
4254 goto found;
4255 }
4256 }
4257 attrib = attrib->next;
4258 }
4259 if (qualified == -1) {
4260 if (attr->prefix == NULL) {
4261 VERROR(ctxt->userData,
4262 "Element %s doesn't carry attribute %s\n",
4263 elem->name, attr->name);
4264 ret = 0;
4265 } else {
4266 VERROR(ctxt->userData,
4267 "Element %s doesn't carry attribute %s:%s\n",
4268 elem->name, attr->prefix,attr->name);
4269 ret = 0;
4270 }
4271 } else if (qualified == 0) {
4272 VWARNING(ctxt->userData,
4273 "Element %s required attribute %s:%s has no prefix\n",
4274 elem->name, attr->prefix,attr->name);
4275 } else if (qualified == 1) {
4276 VWARNING(ctxt->userData,
4277 "Element %s required attribute %s:%s has different prefix\n",
4278 elem->name, attr->prefix,attr->name);
4279 }
4280 }
4281found:
4282 attr = attr->nexth;
4283 }
4284 return(ret);
4285}
4286
4287/**
4288 * xmlValidateRoot:
4289 * @ctxt: the validation context
4290 * @doc: a document instance
4291 *
4292 * Try to validate a the root element
4293 * basically it does the following check as described by the
4294 * XML-1.0 recommendation:
4295 * - [ VC: Root Element Type ]
4296 * it doesn't try to recurse or apply other check to the element
4297 *
4298 * returns 1 if valid or 0 otherwise
4299 */
4300
4301int
4302xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4303 xmlNodePtr root;
4304 if (doc == NULL) return(0);
4305
4306 root = xmlDocGetRootElement(doc);
4307 if ((root == NULL) || (root->name == NULL)) {
4308 VERROR(ctxt->userData, "Not valid: no root element\n");
4309 return(0);
4310 }
4311
4312 /*
4313 * When doing post validation against a separate DTD, those may
4314 * no internal subset has been generated
4315 */
4316 if ((doc->intSubset != NULL) &&
4317 (doc->intSubset->name != NULL)) {
4318 /*
4319 * Check first the document root against the NQName
4320 */
4321 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4322 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4323 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004324 snprintf((char *) qname, sizeof(qname), "%s:%s",
4325 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004326 qname[sizeof(qname) - 1] = 0;
4327 if (xmlStrEqual(doc->intSubset->name, qname))
4328 goto name_ok;
4329 }
4330 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4331 (xmlStrEqual(root->name, BAD_CAST "html")))
4332 goto name_ok;
4333 VERROR(ctxt->userData,
4334 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4335 root->name, doc->intSubset->name);
4336 return(0);
4337
4338 }
4339 }
4340name_ok:
4341 return(1);
4342}
4343
4344
4345/**
4346 * xmlValidateElement:
4347 * @ctxt: the validation context
4348 * @doc: a document instance
4349 * @elem: an element instance
4350 *
4351 * Try to validate the subtree under an element
4352 *
4353 * returns 1 if valid or 0 otherwise
4354 */
4355
4356int
4357xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4358 xmlNodePtr child;
4359 xmlAttrPtr attr;
4360 xmlChar *value;
4361 int ret = 1;
4362
4363 if (elem == NULL) return(0);
4364
4365 /*
4366 * XInclude elements were added after parsing in the infoset,
4367 * they don't really mean anything validation wise.
4368 */
4369 if ((elem->type == XML_XINCLUDE_START) ||
4370 (elem->type == XML_XINCLUDE_END))
4371 return(1);
4372
4373 CHECK_DTD;
4374
Daniel Veillard10ea86c2001-06-20 13:55:33 +00004375 /*
4376 * Entities references have to be handled separately
4377 */
4378 if (elem->type == XML_ENTITY_REF_NODE) {
4379 return(1);
4380 }
4381
Owen Taylor3473f882001-02-23 17:55:21 +00004382 ret &= xmlValidateOneElement(ctxt, doc, elem);
4383 attr = elem->properties;
4384 while(attr != NULL) {
4385 value = xmlNodeListGetString(doc, attr->children, 0);
4386 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4387 if (value != NULL)
4388 xmlFree(value);
4389 attr= attr->next;
4390 }
4391 child = elem->children;
4392 while (child != NULL) {
4393 ret &= xmlValidateElement(ctxt, doc, child);
4394 child = child->next;
4395 }
4396
4397 return(ret);
4398}
4399
Daniel Veillard8730c562001-02-26 10:49:57 +00004400/**
4401 * xmlValidateRef:
4402 * @ref: A reference to be validated
4403 * @ctxt: Validation context
4404 * @name: Name of ID we are searching for
4405 *
4406 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004407static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004408xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004409 const xmlChar *name) {
4410 xmlAttrPtr id;
4411 xmlAttrPtr attr;
4412
4413 if (ref == NULL)
4414 return;
4415 attr = ref->attr;
4416 if (attr == NULL)
4417 return;
4418 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4419 id = xmlGetID(ctxt->doc, name);
4420 if (id == NULL) {
4421 VERROR(ctxt->userData,
4422 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4423 attr->name, name);
4424 ctxt->valid = 0;
4425 }
4426 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4427 xmlChar *dup, *str = NULL, *cur, save;
4428
4429 dup = xmlStrdup(name);
4430 if (dup == NULL) {
4431 ctxt->valid = 0;
4432 return;
4433 }
4434 cur = dup;
4435 while (*cur != 0) {
4436 str = cur;
4437 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4438 save = *cur;
4439 *cur = 0;
4440 id = xmlGetID(ctxt->doc, str);
4441 if (id == NULL) {
4442 VERROR(ctxt->userData,
4443 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4444 attr->name, str);
4445 ctxt->valid = 0;
4446 }
4447 if (save == 0)
4448 break;
4449 *cur = save;
4450 while (IS_BLANK(*cur)) cur++;
4451 }
4452 xmlFree(dup);
4453 }
4454}
4455
4456/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004457 * xmlWalkValidateList:
4458 * @data: Contents of current link
4459 * @user: Value supplied by the user
4460 *
4461 * Return 0 to abort the walk or 1 to continue
4462 */
4463static int
4464xmlWalkValidateList(const void *data, const void *user)
4465{
4466 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4467 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4468 return 1;
4469}
4470
4471/**
4472 * xmlValidateCheckRefCallback:
4473 * @ref_list: List of references
4474 * @ctxt: Validation context
4475 * @name: Name of ID we are searching for
4476 *
4477 */
4478static void
4479xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4480 const xmlChar *name) {
4481 xmlValidateMemo memo;
4482
4483 if (ref_list == NULL)
4484 return;
4485 memo.ctxt = ctxt;
4486 memo.name = name;
4487
4488 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4489
4490}
4491
4492/**
Owen Taylor3473f882001-02-23 17:55:21 +00004493 * xmlValidateDocumentFinal:
4494 * @ctxt: the validation context
4495 * @doc: a document instance
4496 *
4497 * Does the final step for the document validation once all the
4498 * incremental validation steps have been completed
4499 *
4500 * basically it does the following checks described by the XML Rec
4501 *
4502 *
4503 * returns 1 if valid or 0 otherwise
4504 */
4505
4506int
4507xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4508 xmlRefTablePtr table;
4509
4510 if (doc == NULL) {
4511 xmlGenericError(xmlGenericErrorContext,
4512 "xmlValidateDocumentFinal: doc == NULL\n");
4513 return(0);
4514 }
4515
4516 /*
4517 * Check all the NOTATION/NOTATIONS attributes
4518 */
4519 /*
4520 * Check all the ENTITY/ENTITIES attributes definition for validity
4521 */
4522 /*
4523 * Check all the IDREF/IDREFS attributes definition for validity
4524 */
4525 table = (xmlRefTablePtr) doc->refs;
4526 ctxt->doc = doc;
4527 ctxt->valid = 1;
4528 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4529 return(ctxt->valid);
4530}
4531
4532/**
4533 * xmlValidateDtd:
4534 * @ctxt: the validation context
4535 * @doc: a document instance
4536 * @dtd: a dtd instance
4537 *
4538 * Try to validate the document against the dtd instance
4539 *
4540 * basically it does check all the definitions in the DtD.
4541 *
4542 * returns 1 if valid or 0 otherwise
4543 */
4544
4545int
4546xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4547 int ret;
4548 xmlDtdPtr oldExt;
4549 xmlNodePtr root;
4550
4551 if (dtd == NULL) return(0);
4552 if (doc == NULL) return(0);
4553 oldExt = doc->extSubset;
4554 doc->extSubset = dtd;
4555 ret = xmlValidateRoot(ctxt, doc);
4556 if (ret == 0) {
4557 doc->extSubset = oldExt;
4558 return(ret);
4559 }
4560 if (doc->ids != NULL) {
4561 xmlFreeIDTable(doc->ids);
4562 doc->ids = NULL;
4563 }
4564 if (doc->refs != NULL) {
4565 xmlFreeRefTable(doc->refs);
4566 doc->refs = NULL;
4567 }
4568 root = xmlDocGetRootElement(doc);
4569 ret = xmlValidateElement(ctxt, doc, root);
4570 ret &= xmlValidateDocumentFinal(ctxt, doc);
4571 doc->extSubset = oldExt;
4572 return(ret);
4573}
4574
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004575static void
Owen Taylor3473f882001-02-23 17:55:21 +00004576xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004577 const xmlChar *name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004578 if (cur == NULL)
4579 return;
4580 switch (cur->atype) {
4581 case XML_ATTRIBUTE_CDATA:
4582 case XML_ATTRIBUTE_ID:
4583 case XML_ATTRIBUTE_IDREF :
4584 case XML_ATTRIBUTE_IDREFS:
4585 case XML_ATTRIBUTE_NMTOKEN:
4586 case XML_ATTRIBUTE_NMTOKENS:
4587 case XML_ATTRIBUTE_ENUMERATION:
4588 break;
4589 case XML_ATTRIBUTE_ENTITY:
4590 case XML_ATTRIBUTE_ENTITIES:
4591 case XML_ATTRIBUTE_NOTATION:
4592 if (cur->defaultValue != NULL) {
4593 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4594 cur->name, cur->atype, cur->defaultValue);
4595 }
4596 if (cur->tree != NULL) {
4597 xmlEnumerationPtr tree = cur->tree;
4598 while (tree != NULL) {
4599 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4600 cur->name, cur->atype, tree->name);
4601 tree = tree->next;
4602 }
4603 }
4604 }
4605}
4606
4607/**
4608 * xmlValidateDtdFinal:
4609 * @ctxt: the validation context
4610 * @doc: a document instance
4611 *
4612 * Does the final step for the dtds validation once all the
4613 * subsets have been parsed
4614 *
4615 * basically it does the following checks described by the XML Rec
4616 * - check that ENTITY and ENTITIES type attributes default or
4617 * possible values matches one of the defined entities.
4618 * - check that NOTATION type attributes default or
4619 * possible values matches one of the defined notations.
4620 *
4621 * returns 1 if valid or 0 otherwise
4622 */
4623
4624int
4625xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4626 int ret = 1;
4627 xmlDtdPtr dtd;
4628 xmlAttributeTablePtr table;
4629
4630 if (doc == NULL) return(0);
4631 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4632 return(0);
4633 ctxt->doc = doc;
4634 ctxt->valid = ret;
4635 dtd = doc->intSubset;
4636 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4637 table = (xmlAttributeTablePtr) dtd->attributes;
4638 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4639 }
4640 dtd = doc->extSubset;
4641 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4642 table = (xmlAttributeTablePtr) dtd->attributes;
4643 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4644 }
4645 return(ctxt->valid);
4646}
4647
4648/**
4649 * xmlValidateDocument:
4650 * @ctxt: the validation context
4651 * @doc: a document instance
4652 *
4653 * Try to validate the document instance
4654 *
4655 * basically it does the all the checks described by the XML Rec
4656 * i.e. validates the internal and external subset (if present)
4657 * and validate the document tree.
4658 *
4659 * returns 1 if valid or 0 otherwise
4660 */
4661
4662int
4663xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4664 int ret;
4665 xmlNodePtr root;
4666
4667 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4668 return(0);
4669 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4670 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4671 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4672 doc->intSubset->SystemID);
4673 if (doc->extSubset == NULL) {
4674 if (doc->intSubset->SystemID != NULL) {
4675 VERROR(ctxt->userData,
4676 "Could not load the external subset \"%s\"\n",
4677 doc->intSubset->SystemID);
4678 } else {
4679 VERROR(ctxt->userData,
4680 "Could not load the external subset \"%s\"\n",
4681 doc->intSubset->ExternalID);
4682 }
4683 return(0);
4684 }
4685 }
4686
4687 if (doc->ids != NULL) {
4688 xmlFreeIDTable(doc->ids);
4689 doc->ids = NULL;
4690 }
4691 if (doc->refs != NULL) {
4692 xmlFreeRefTable(doc->refs);
4693 doc->refs = NULL;
4694 }
4695 ret = xmlValidateDtdFinal(ctxt, doc);
4696 if (!xmlValidateRoot(ctxt, doc)) return(0);
4697
4698 root = xmlDocGetRootElement(doc);
4699 ret &= xmlValidateElement(ctxt, doc, root);
4700 ret &= xmlValidateDocumentFinal(ctxt, doc);
4701 return(ret);
4702}
4703
4704
4705/************************************************************************
4706 * *
4707 * Routines for dynamic validation editing *
4708 * *
4709 ************************************************************************/
4710
4711/**
4712 * xmlValidGetPotentialChildren:
4713 * @ctree: an element content tree
4714 * @list: an array to store the list of child names
4715 * @len: a pointer to the number of element in the list
4716 * @max: the size of the array
4717 *
4718 * Build/extend a list of potential children allowed by the content tree
4719 *
4720 * returns the number of element in the list, or -1 in case of error.
4721 */
4722
4723int
4724xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4725 int *len, int max) {
4726 int i;
4727
4728 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4729 return(-1);
4730 if (*len >= max) return(*len);
4731
4732 switch (ctree->type) {
4733 case XML_ELEMENT_CONTENT_PCDATA:
4734 for (i = 0; i < *len;i++)
4735 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4736 list[(*len)++] = BAD_CAST "#PCDATA";
4737 break;
4738 case XML_ELEMENT_CONTENT_ELEMENT:
4739 for (i = 0; i < *len;i++)
4740 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4741 list[(*len)++] = ctree->name;
4742 break;
4743 case XML_ELEMENT_CONTENT_SEQ:
4744 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4745 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4746 break;
4747 case XML_ELEMENT_CONTENT_OR:
4748 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4749 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4750 break;
4751 }
4752
4753 return(*len);
4754}
4755
4756/**
4757 * xmlValidGetValidElements:
4758 * @prev: an element to insert after
4759 * @next: an element to insert next
4760 * @list: an array to store the list of child names
4761 * @max: the size of the array
4762 *
4763 * This function returns the list of authorized children to insert
4764 * within an existing tree while respecting the validity constraints
4765 * forced by the Dtd. The insertion point is defined using @prev and
4766 * @next in the following ways:
4767 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4768 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4769 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4770 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4771 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4772 *
4773 * pointers to the element names are inserted at the beginning of the array
4774 * and do not need to be freed.
4775 *
4776 * returns the number of element in the list, or -1 in case of error. If
4777 * the function returns the value @max the caller is invited to grow the
4778 * receiving array and retry.
4779 */
4780
4781int
4782xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4783 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004784 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00004785 int nb_valid_elements = 0;
4786 const xmlChar *elements[256];
4787 int nb_elements = 0, i;
4788
4789 xmlNode *ref_node;
4790 xmlNode *parent;
4791 xmlNode *test_node;
4792
4793 xmlNode *prev_next;
4794 xmlNode *next_prev;
4795 xmlNode *parent_childs;
4796 xmlNode *parent_last;
4797
4798 xmlElement *element_desc;
4799
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004800 vctxt.userData = NULL;
4801 vctxt.error = NULL;
4802 vctxt.warning = NULL;
4803
Owen Taylor3473f882001-02-23 17:55:21 +00004804 if (prev == NULL && next == NULL)
4805 return(-1);
4806
4807 if (list == NULL) return(-1);
4808 if (max <= 0) return(-1);
4809
4810 nb_valid_elements = 0;
4811 ref_node = prev ? prev : next;
4812 parent = ref_node->parent;
4813
4814 /*
4815 * Retrieves the parent element declaration
4816 */
4817 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
4818 parent->name);
4819 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
4820 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
4821 parent->name);
4822 if (element_desc == NULL) return(-1);
4823
4824 /*
4825 * Do a backup of the current tree structure
4826 */
4827 prev_next = prev ? prev->next : NULL;
4828 next_prev = next ? next->prev : NULL;
4829 parent_childs = parent->children;
4830 parent_last = parent->last;
4831
4832 /*
4833 * Creates a dummy node and insert it into the tree
4834 */
4835 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
4836 test_node->doc = ref_node->doc;
4837 test_node->parent = parent;
4838 test_node->prev = prev;
4839 test_node->next = next;
4840
4841 if (prev) prev->next = test_node;
4842 else parent->children = test_node;
4843
4844 if (next) next->prev = test_node;
4845 else parent->last = test_node;
4846
4847 /*
4848 * Insert each potential child node and check if the parent is
4849 * still valid
4850 */
4851 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
4852 elements, &nb_elements, 256);
4853
4854 for (i = 0;i < nb_elements;i++) {
4855 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004856 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004857 int j;
4858
4859 for (j = 0; j < nb_valid_elements;j++)
4860 if (xmlStrEqual(elements[i], list[j])) break;
4861 list[nb_valid_elements++] = elements[i];
4862 if (nb_valid_elements >= max) break;
4863 }
4864 }
4865
4866 /*
4867 * Restore the tree structure
4868 */
4869 if (prev) prev->next = prev_next;
4870 if (next) next->prev = next_prev;
4871 parent->children = parent_childs;
4872 parent->last = parent_last;
4873
4874 return(nb_valid_elements);
4875}