blob: de44a55c2f75a8d5b01e7b5ac988548ebfc63a74 [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 Veillardcbaf3992001-12-31 16:16:02 +000075 * debug/maintain than a generic NFA -> DFA state based algo. The
Daniel Veillarddab4cb32001-04-20 13:03:48 +000076 * 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 */
Daniel Veillardcbaf3992001-12-31 16:16:02 +000088 long occurs;/* bitfield for multiple occurrences */
Daniel Veillarddab4cb32001-04-20 13:03:48 +000089 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
Daniel Veillard5344c602001-12-31 16:37:34 +0000100#define OCCURRENCE (ctxt->vstate->occurs & (1 << DEPTH))
101#define PARENT_OCCURRENCE (ctxt->vstate->occurs & ((1 << DEPTH) - 1))
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000102
Daniel Veillard5344c602001-12-31 16:37:34 +0000103#define SET_OCCURRENCE ctxt->vstate->occurs |= (1 << DEPTH)
104#define RESET_OCCURRENCE ctxt->vstate->occurs &= ((1 << DEPTH) - 1)
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000105
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
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000305/* TODO: use hash table for accesses to elem and attribute definitions */
Owen Taylor3473f882001-02-23 17:55:21 +0000306
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
Daniel Veillarda10efa82001-04-18 13:09:01 +0000318static xmlElementPtr xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name,
319 int create);
Owen Taylor3473f882001-02-23 17:55:21 +0000320xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
321
322/************************************************************************
323 * *
324 * QName handling helper *
325 * *
326 ************************************************************************/
327
328/**
329 * xmlSplitQName2:
330 * @name: an XML parser context
331 * @prefix: a xmlChar **
332 *
333 * parse an XML qualified name string
334 *
335 * [NS 5] QName ::= (Prefix ':')? LocalPart
336 *
337 * [NS 6] Prefix ::= NCName
338 *
339 * [NS 7] LocalPart ::= NCName
340 *
341 * Returns NULL if not a QName, otherwise the local part, and prefix
342 * is updated to get the Prefix if any.
343 */
344
345xmlChar *
346xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
347 int len = 0;
348 xmlChar *ret = NULL;
349
350 *prefix = NULL;
351
Daniel Veillardf4309d72001-10-02 09:28:58 +0000352#ifndef XML_XML_NAMESPACE
Owen Taylor3473f882001-02-23 17:55:21 +0000353 /* xml: prefix is not really a namespace */
354 if ((name[0] == 'x') && (name[1] == 'm') &&
355 (name[2] == 'l') && (name[3] == ':'))
356 return(NULL);
Daniel Veillardf4309d72001-10-02 09:28:58 +0000357#endif
Owen Taylor3473f882001-02-23 17:55:21 +0000358
359 /* nasty but valid */
360 if (name[0] == ':')
361 return(NULL);
362
363 /*
364 * we are not trying to validate but just to cut, and yes it will
365 * work even if this is as set of UTF-8 encoded chars
366 */
367 while ((name[len] != 0) && (name[len] != ':'))
368 len++;
369
370 if (name[len] == 0)
371 return(NULL);
372
373 *prefix = xmlStrndup(name, len);
374 ret = xmlStrdup(&name[len + 1]);
375
376 return(ret);
377}
378
379/****************************************************************
380 * *
381 * Util functions for data allocation/deallocation *
382 * *
383 ****************************************************************/
384
385/**
386 * xmlNewElementContent:
387 * @name: the subelement name or NULL
388 * @type: the type of element content decl
389 *
390 * Allocate an element content structure.
391 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000392 * Returns NULL if not, otherwise the new element content structure
Owen Taylor3473f882001-02-23 17:55:21 +0000393 */
394xmlElementContentPtr
395xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
396 xmlElementContentPtr ret;
397
398 switch(type) {
399 case XML_ELEMENT_CONTENT_ELEMENT:
400 if (name == NULL) {
401 xmlGenericError(xmlGenericErrorContext,
402 "xmlNewElementContent : name == NULL !\n");
403 }
404 break;
405 case XML_ELEMENT_CONTENT_PCDATA:
406 case XML_ELEMENT_CONTENT_SEQ:
407 case XML_ELEMENT_CONTENT_OR:
408 if (name != NULL) {
409 xmlGenericError(xmlGenericErrorContext,
410 "xmlNewElementContent : name != NULL !\n");
411 }
412 break;
413 default:
414 xmlGenericError(xmlGenericErrorContext,
415 "xmlNewElementContent: unknown type %d\n", type);
416 return(NULL);
417 }
418 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
419 if (ret == NULL) {
420 xmlGenericError(xmlGenericErrorContext,
421 "xmlNewElementContent : out of memory!\n");
422 return(NULL);
423 }
424 ret->type = type;
425 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000426 if (name != NULL) {
427 xmlChar *prefix = NULL;
428 ret->name = xmlSplitQName2(name, &prefix);
429 if (ret->name == NULL)
430 ret->name = xmlStrdup(name);
431 ret->prefix = prefix;
432 } else {
Owen Taylor3473f882001-02-23 17:55:21 +0000433 ret->name = NULL;
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000434 ret->prefix = NULL;
435 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000436 ret->c1 = ret->c2 = ret->parent = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000437 return(ret);
438}
439
440/**
441 * xmlCopyElementContent:
442 * @content: An element content pointer.
443 *
444 * Build a copy of an element content description.
445 *
446 * Returns the new xmlElementContentPtr or NULL in case of error.
447 */
448xmlElementContentPtr
449xmlCopyElementContent(xmlElementContentPtr cur) {
450 xmlElementContentPtr ret;
451
452 if (cur == NULL) return(NULL);
453 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
454 if (ret == NULL) {
455 xmlGenericError(xmlGenericErrorContext,
456 "xmlCopyElementContent : out of memory\n");
457 return(NULL);
458 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000459 if (cur->prefix != NULL)
460 ret->prefix = xmlStrdup(cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000461 ret->ocur = cur->ocur;
462 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000463 if (ret->c1 != NULL)
464 ret->c1->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000465 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000466 if (ret->c2 != NULL)
467 ret->c2->parent = ret;
Owen Taylor3473f882001-02-23 17:55:21 +0000468 return(ret);
469}
470
471/**
472 * xmlFreeElementContent:
473 * @cur: the element content tree to free
474 *
475 * Free an element content structure. This is a recursive call !
476 */
477void
478xmlFreeElementContent(xmlElementContentPtr cur) {
479 if (cur == NULL) return;
480 switch (cur->type) {
481 case XML_ELEMENT_CONTENT_PCDATA:
482 case XML_ELEMENT_CONTENT_ELEMENT:
483 case XML_ELEMENT_CONTENT_SEQ:
484 case XML_ELEMENT_CONTENT_OR:
485 break;
486 default:
487 xmlGenericError(xmlGenericErrorContext,
488 "xmlFreeElementContent : type %d\n", cur->type);
489 return;
490 }
491 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
492 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
493 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000494 if (cur->prefix != NULL) xmlFree((xmlChar *) cur->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000495 xmlFree(cur);
496}
497
498/**
499 * xmlDumpElementContent:
500 * @buf: An XML buffer
501 * @content: An element table
502 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
503 *
504 * This will dump the content of the element table as an XML DTD definition
505 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000506static void
Owen Taylor3473f882001-02-23 17:55:21 +0000507xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
508 if (content == NULL) return;
509
510 if (glob) xmlBufferWriteChar(buf, "(");
511 switch (content->type) {
512 case XML_ELEMENT_CONTENT_PCDATA:
513 xmlBufferWriteChar(buf, "#PCDATA");
514 break;
515 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000516 if (content->prefix != NULL) {
517 xmlBufferWriteCHAR(buf, content->prefix);
518 xmlBufferWriteChar(buf, ":");
519 }
Owen Taylor3473f882001-02-23 17:55:21 +0000520 xmlBufferWriteCHAR(buf, content->name);
521 break;
522 case XML_ELEMENT_CONTENT_SEQ:
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_OR)
530 xmlDumpElementContent(buf, content->c2, 1);
531 else
532 xmlDumpElementContent(buf, content->c2, 0);
533 break;
534 case XML_ELEMENT_CONTENT_OR:
535 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
536 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
537 xmlDumpElementContent(buf, content->c1, 1);
538 else
539 xmlDumpElementContent(buf, content->c1, 0);
540 xmlBufferWriteChar(buf, " | ");
541 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
542 xmlDumpElementContent(buf, content->c2, 1);
543 else
544 xmlDumpElementContent(buf, content->c2, 0);
545 break;
546 default:
547 xmlGenericError(xmlGenericErrorContext,
548 "xmlDumpElementContent: unknown type %d\n",
549 content->type);
550 }
551 if (glob)
552 xmlBufferWriteChar(buf, ")");
553 switch (content->ocur) {
554 case XML_ELEMENT_CONTENT_ONCE:
555 break;
556 case XML_ELEMENT_CONTENT_OPT:
557 xmlBufferWriteChar(buf, "?");
558 break;
559 case XML_ELEMENT_CONTENT_MULT:
560 xmlBufferWriteChar(buf, "*");
561 break;
562 case XML_ELEMENT_CONTENT_PLUS:
563 xmlBufferWriteChar(buf, "+");
564 break;
565 }
566}
567
568/**
569 * xmlSprintfElementContent:
570 * @buf: an output buffer
571 * @content: An element table
572 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
573 *
Daniel Veillardd3d06722001-08-15 12:06:36 +0000574 * Deprecated, unsafe, use xmlSnprintfElementContent
575 */
576void
577xmlSprintfElementContent(char *buf ATTRIBUTE_UNUSED,
578 xmlElementContentPtr content ATTRIBUTE_UNUSED,
579 int glob ATTRIBUTE_UNUSED) {
580}
581
582/**
583 * xmlSnprintfElementContent:
584 * @buf: an output buffer
585 * @size: the buffer size
586 * @content: An element table
587 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
588 *
Owen Taylor3473f882001-02-23 17:55:21 +0000589 * This will dump the content of the element content definition
590 * Intended just for the debug routine
591 */
592void
Daniel Veillardd3d06722001-08-15 12:06:36 +0000593xmlSnprintfElementContent(char *buf, int size, xmlElementContentPtr content, int glob) {
594 int len;
595
Owen Taylor3473f882001-02-23 17:55:21 +0000596 if (content == NULL) return;
Daniel Veillardd3d06722001-08-15 12:06:36 +0000597 len = strlen(buf);
598 if (size - len < 50) {
599 if ((size - len > 4) && (buf[len - 1] != '.'))
600 strcat(buf, " ...");
601 return;
602 }
Owen Taylor3473f882001-02-23 17:55:21 +0000603 if (glob) strcat(buf, "(");
604 switch (content->type) {
605 case XML_ELEMENT_CONTENT_PCDATA:
606 strcat(buf, "#PCDATA");
607 break;
608 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000609 if (content->prefix != NULL) {
610 if (size - len < xmlStrlen(content->prefix + 10)) {
611 strcat(buf, " ...");
612 return;
613 }
614 strcat(buf, (char *) content->prefix);
615 strcat(buf, ":");
616 }
Daniel Veillardd3d06722001-08-15 12:06:36 +0000617 if (size - len < xmlStrlen(content->name + 10)) {
618 strcat(buf, " ...");
619 return;
620 }
Owen Taylor3473f882001-02-23 17:55:21 +0000621 strcat(buf, (char *) content->name);
622 break;
623 case XML_ELEMENT_CONTENT_SEQ:
624 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
625 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000626 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000627 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000628 xmlSnprintfElementContent(buf, size, content->c1, 0);
629 len = strlen(buf);
630 if (size - len < 50) {
631 if ((size - len > 4) && (buf[len - 1] != '.'))
632 strcat(buf, " ...");
633 return;
634 }
Owen Taylor3473f882001-02-23 17:55:21 +0000635 strcat(buf, " , ");
636 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000637 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000638 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000639 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000640 break;
641 case XML_ELEMENT_CONTENT_OR:
642 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
643 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillardd3d06722001-08-15 12:06:36 +0000644 xmlSnprintfElementContent(buf, size, content->c1, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000645 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000646 xmlSnprintfElementContent(buf, size, content->c1, 0);
647 len = strlen(buf);
648 if (size - len < 50) {
649 if ((size - len > 4) && (buf[len - 1] != '.'))
650 strcat(buf, " ...");
651 return;
652 }
Owen Taylor3473f882001-02-23 17:55:21 +0000653 strcat(buf, " | ");
654 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
Daniel Veillardd3d06722001-08-15 12:06:36 +0000655 xmlSnprintfElementContent(buf, size, content->c2, 1);
Owen Taylor3473f882001-02-23 17:55:21 +0000656 else
Daniel Veillardd3d06722001-08-15 12:06:36 +0000657 xmlSnprintfElementContent(buf, size, content->c2, 0);
Owen Taylor3473f882001-02-23 17:55:21 +0000658 break;
659 }
660 if (glob)
661 strcat(buf, ")");
662 switch (content->ocur) {
663 case XML_ELEMENT_CONTENT_ONCE:
664 break;
665 case XML_ELEMENT_CONTENT_OPT:
666 strcat(buf, "?");
667 break;
668 case XML_ELEMENT_CONTENT_MULT:
669 strcat(buf, "*");
670 break;
671 case XML_ELEMENT_CONTENT_PLUS:
672 strcat(buf, "+");
673 break;
674 }
675}
676
677/****************************************************************
678 * *
679 * Registration of DTD declarations *
680 * *
681 ****************************************************************/
682
683/**
684 * xmlCreateElementTable:
685 *
686 * create and initialize an empty element hash table.
687 *
688 * Returns the xmlElementTablePtr just created or NULL in case of error.
689 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000690static xmlElementTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +0000691xmlCreateElementTable(void) {
692 return(xmlHashCreate(0));
693}
694
695/**
696 * xmlFreeElement:
697 * @elem: An element
698 *
699 * Deallocate the memory used by an element definition
700 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000701static void
Owen Taylor3473f882001-02-23 17:55:21 +0000702xmlFreeElement(xmlElementPtr elem) {
703 if (elem == NULL) return;
704 xmlUnlinkNode((xmlNodePtr) elem);
705 xmlFreeElementContent(elem->content);
706 if (elem->name != NULL)
707 xmlFree((xmlChar *) elem->name);
708 if (elem->prefix != NULL)
709 xmlFree((xmlChar *) elem->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +0000710 xmlFree(elem);
711}
712
713
714/**
715 * xmlAddElementDecl:
716 * @ctxt: the validation context
717 * @dtd: pointer to the DTD
718 * @name: the entity name
719 * @type: the element type
720 * @content: the element content tree or NULL
721 *
722 * Register a new element declaration
723 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000724 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +0000725 */
726xmlElementPtr
727xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
728 xmlElementTypeVal type,
729 xmlElementContentPtr content) {
730 xmlElementPtr ret;
731 xmlElementTablePtr table;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000732 xmlAttributePtr oldAttributes = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000733 xmlChar *ns, *uqname;
734
735 if (dtd == NULL) {
736 xmlGenericError(xmlGenericErrorContext,
737 "xmlAddElementDecl: dtd == NULL\n");
738 return(NULL);
739 }
740 if (name == NULL) {
741 xmlGenericError(xmlGenericErrorContext,
742 "xmlAddElementDecl: name == NULL\n");
743 return(NULL);
744 }
745 switch (type) {
746 case XML_ELEMENT_TYPE_EMPTY:
747 if (content != NULL) {
748 xmlGenericError(xmlGenericErrorContext,
749 "xmlAddElementDecl: content != NULL for EMPTY\n");
750 return(NULL);
751 }
752 break;
753 case XML_ELEMENT_TYPE_ANY:
754 if (content != NULL) {
755 xmlGenericError(xmlGenericErrorContext,
756 "xmlAddElementDecl: content != NULL for ANY\n");
757 return(NULL);
758 }
759 break;
760 case XML_ELEMENT_TYPE_MIXED:
761 if (content == NULL) {
762 xmlGenericError(xmlGenericErrorContext,
763 "xmlAddElementDecl: content == NULL for MIXED\n");
764 return(NULL);
765 }
766 break;
767 case XML_ELEMENT_TYPE_ELEMENT:
768 if (content == NULL) {
769 xmlGenericError(xmlGenericErrorContext,
770 "xmlAddElementDecl: content == NULL for ELEMENT\n");
771 return(NULL);
772 }
773 break;
774 default:
775 xmlGenericError(xmlGenericErrorContext,
776 "xmlAddElementDecl: unknown type %d\n", type);
777 return(NULL);
778 }
779
780 /*
781 * check if name is a QName
782 */
783 uqname = xmlSplitQName2(name, &ns);
784 if (uqname != NULL)
785 name = uqname;
786
787 /*
788 * Create the Element table if needed.
789 */
790 table = (xmlElementTablePtr) dtd->elements;
791 if (table == NULL) {
792 table = xmlCreateElementTable();
793 dtd->elements = (void *) table;
794 }
795 if (table == NULL) {
796 xmlGenericError(xmlGenericErrorContext,
797 "xmlAddElementDecl: Table creation failed!\n");
798 return(NULL);
799 }
800
Daniel Veillarda10efa82001-04-18 13:09:01 +0000801 /*
802 * lookup old attributes inserted on an undefined element in the
803 * internal subset.
804 */
805 if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) {
806 ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns);
807 if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) {
808 oldAttributes = ret->attributes;
809 ret->attributes = NULL;
810 xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL);
811 xmlFreeElement(ret);
812 }
Owen Taylor3473f882001-02-23 17:55:21 +0000813 }
Owen Taylor3473f882001-02-23 17:55:21 +0000814
815 /*
Daniel Veillarda10efa82001-04-18 13:09:01 +0000816 * The element may already be present if one of its attribute
817 * was registered first
818 */
819 ret = xmlHashLookup2(table, name, ns);
820 if (ret != NULL) {
821 if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) {
822 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000823 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +0000824 */
825 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
826 if (uqname != NULL)
827 xmlFree(uqname);
828 return(NULL);
829 }
830 } else {
831 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
832 if (ret == NULL) {
833 xmlGenericError(xmlGenericErrorContext,
834 "xmlAddElementDecl: out of memory\n");
835 return(NULL);
836 }
837 memset(ret, 0, sizeof(xmlElement));
838 ret->type = XML_ELEMENT_DECL;
839
840 /*
841 * fill the structure.
842 */
843 ret->name = xmlStrdup(name);
844 ret->prefix = ns;
845
846 /*
847 * Validity Check:
848 * Insertion must not fail
849 */
850 if (xmlHashAddEntry2(table, name, ns, ret)) {
851 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000852 * The element is already defined in this DTD.
Daniel Veillarda10efa82001-04-18 13:09:01 +0000853 */
854 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
855 xmlFreeElement(ret);
856 if (uqname != NULL)
857 xmlFree(uqname);
858 return(NULL);
859 }
860 }
861
862 /*
863 * Finish to fill the structure.
Owen Taylor3473f882001-02-23 17:55:21 +0000864 */
865 ret->etype = type;
Owen Taylor3473f882001-02-23 17:55:21 +0000866 ret->content = xmlCopyElementContent(content);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000867 ret->attributes = oldAttributes;
Owen Taylor3473f882001-02-23 17:55:21 +0000868
869 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +0000870 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +0000871 */
872 ret->parent = dtd;
873 ret->doc = dtd->doc;
874 if (dtd->last == NULL) {
875 dtd->children = dtd->last = (xmlNodePtr) ret;
876 } else {
877 dtd->last->next = (xmlNodePtr) ret;
878 ret->prev = dtd->last;
879 dtd->last = (xmlNodePtr) ret;
880 }
881 if (uqname != NULL)
882 xmlFree(uqname);
883 return(ret);
884}
885
886/**
887 * xmlFreeElementTable:
888 * @table: An element table
889 *
890 * Deallocate the memory used by an element hash table.
891 */
892void
893xmlFreeElementTable(xmlElementTablePtr table) {
894 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
895}
896
897/**
898 * xmlCopyElement:
899 * @elem: An element
900 *
901 * Build a copy of an element.
902 *
903 * Returns the new xmlElementPtr or NULL in case of error.
904 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +0000905static xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +0000906xmlCopyElement(xmlElementPtr elem) {
907 xmlElementPtr cur;
908
909 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
910 if (cur == NULL) {
911 xmlGenericError(xmlGenericErrorContext,
912 "xmlCopyElement: out of memory !\n");
913 return(NULL);
914 }
915 memset(cur, 0, sizeof(xmlElement));
916 cur->type = XML_ELEMENT_DECL;
917 cur->etype = elem->etype;
918 if (elem->name != NULL)
919 cur->name = xmlStrdup(elem->name);
920 else
921 cur->name = NULL;
922 if (elem->prefix != NULL)
923 cur->prefix = xmlStrdup(elem->prefix);
924 else
925 cur->prefix = NULL;
926 cur->content = xmlCopyElementContent(elem->content);
927 /* TODO : rebuild the attribute list on the copy */
928 cur->attributes = NULL;
929 return(cur);
930}
931
932/**
933 * xmlCopyElementTable:
934 * @table: An element table
935 *
936 * Build a copy of an element table.
937 *
938 * Returns the new xmlElementTablePtr or NULL in case of error.
939 */
940xmlElementTablePtr
941xmlCopyElementTable(xmlElementTablePtr table) {
942 return((xmlElementTablePtr) xmlHashCopy(table,
943 (xmlHashCopier) xmlCopyElement));
944}
945
946/**
947 * xmlDumpElementDecl:
948 * @buf: the XML buffer output
949 * @elem: An element table
950 *
951 * This will dump the content of the element declaration as an XML
952 * DTD definition
953 */
954void
955xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
956 switch (elem->etype) {
957 case XML_ELEMENT_TYPE_EMPTY:
958 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000959 if (elem->prefix != NULL) {
960 xmlBufferWriteCHAR(buf, elem->prefix);
961 xmlBufferWriteChar(buf, ":");
962 }
Owen Taylor3473f882001-02-23 17:55:21 +0000963 xmlBufferWriteCHAR(buf, elem->name);
964 xmlBufferWriteChar(buf, " EMPTY>\n");
965 break;
966 case XML_ELEMENT_TYPE_ANY:
967 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000968 if (elem->prefix != NULL) {
969 xmlBufferWriteCHAR(buf, elem->prefix);
970 xmlBufferWriteChar(buf, ":");
971 }
Owen Taylor3473f882001-02-23 17:55:21 +0000972 xmlBufferWriteCHAR(buf, elem->name);
973 xmlBufferWriteChar(buf, " ANY>\n");
974 break;
975 case XML_ELEMENT_TYPE_MIXED:
976 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000977 if (elem->prefix != NULL) {
978 xmlBufferWriteCHAR(buf, elem->prefix);
979 xmlBufferWriteChar(buf, ":");
980 }
Owen Taylor3473f882001-02-23 17:55:21 +0000981 xmlBufferWriteCHAR(buf, elem->name);
982 xmlBufferWriteChar(buf, " ");
983 xmlDumpElementContent(buf, elem->content, 1);
984 xmlBufferWriteChar(buf, ">\n");
985 break;
986 case XML_ELEMENT_TYPE_ELEMENT:
987 xmlBufferWriteChar(buf, "<!ELEMENT ");
Daniel Veillardbe480fb2001-11-08 23:36:42 +0000988 if (elem->prefix != NULL) {
989 xmlBufferWriteCHAR(buf, elem->prefix);
990 xmlBufferWriteChar(buf, ":");
991 }
Owen Taylor3473f882001-02-23 17:55:21 +0000992 xmlBufferWriteCHAR(buf, elem->name);
993 xmlBufferWriteChar(buf, " ");
994 xmlDumpElementContent(buf, elem->content, 1);
995 xmlBufferWriteChar(buf, ">\n");
996 break;
997 default:
998 xmlGenericError(xmlGenericErrorContext,
999 "xmlDumpElementDecl: internal: unknown type %d\n",
1000 elem->etype);
1001 }
1002}
1003
1004/**
1005 * xmlDumpElementTable:
1006 * @buf: the XML buffer output
1007 * @table: An element table
1008 *
1009 * This will dump the content of the element table as an XML DTD definition
1010 */
1011void
1012xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
1013 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
1014}
1015
1016/**
1017 * xmlCreateEnumeration:
1018 * @name: the enumeration name or NULL
1019 *
1020 * create and initialize an enumeration attribute node.
1021 *
1022 * Returns the xmlEnumerationPtr just created or NULL in case
1023 * of error.
1024 */
1025xmlEnumerationPtr
1026xmlCreateEnumeration(xmlChar *name) {
1027 xmlEnumerationPtr ret;
1028
1029 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
1030 if (ret == NULL) {
1031 xmlGenericError(xmlGenericErrorContext,
1032 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
1033 (long)sizeof(xmlEnumeration));
1034 return(NULL);
1035 }
1036 memset(ret, 0, sizeof(xmlEnumeration));
1037
1038 if (name != NULL)
1039 ret->name = xmlStrdup(name);
1040 return(ret);
1041}
1042
1043/**
1044 * xmlFreeEnumeration:
1045 * @cur: the tree to free.
1046 *
1047 * free an enumeration attribute node (recursive).
1048 */
1049void
1050xmlFreeEnumeration(xmlEnumerationPtr cur) {
1051 if (cur == NULL) return;
1052
1053 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
1054
1055 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Owen Taylor3473f882001-02-23 17:55:21 +00001056 xmlFree(cur);
1057}
1058
1059/**
1060 * xmlCopyEnumeration:
1061 * @cur: the tree to copy.
1062 *
1063 * Copy an enumeration attribute node (recursive).
1064 *
1065 * Returns the xmlEnumerationPtr just created or NULL in case
1066 * of error.
1067 */
1068xmlEnumerationPtr
1069xmlCopyEnumeration(xmlEnumerationPtr cur) {
1070 xmlEnumerationPtr ret;
1071
1072 if (cur == NULL) return(NULL);
1073 ret = xmlCreateEnumeration((xmlChar *) cur->name);
1074
1075 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
1076 else ret->next = NULL;
1077
1078 return(ret);
1079}
1080
1081/**
1082 * xmlDumpEnumeration:
1083 * @buf: the XML buffer output
1084 * @enum: An enumeration
1085 *
1086 * This will dump the content of the enumeration
1087 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001088static void
Owen Taylor3473f882001-02-23 17:55:21 +00001089xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
1090 if (cur == NULL) return;
1091
1092 xmlBufferWriteCHAR(buf, cur->name);
1093 if (cur->next == NULL)
1094 xmlBufferWriteChar(buf, ")");
1095 else {
1096 xmlBufferWriteChar(buf, " | ");
1097 xmlDumpEnumeration(buf, cur->next);
1098 }
1099}
1100
1101/**
1102 * xmlCreateAttributeTable:
1103 *
1104 * create and initialize an empty attribute hash table.
1105 *
1106 * Returns the xmlAttributeTablePtr just created or NULL in case
1107 * of error.
1108 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001109static xmlAttributeTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001110xmlCreateAttributeTable(void) {
1111 return(xmlHashCreate(0));
1112}
1113
1114/**
1115 * xmlScanAttributeDeclCallback:
1116 * @attr: the attribute decl
1117 * @list: the list to update
1118 *
1119 * Callback called by xmlScanAttributeDecl when a new attribute
1120 * has to be entered in the list.
1121 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001122static void
Owen Taylor3473f882001-02-23 17:55:21 +00001123xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001124 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00001125 attr->nexth = *list;
1126 *list = attr;
1127}
1128
1129/**
1130 * xmlScanAttributeDecl:
1131 * @dtd: pointer to the DTD
1132 * @elem: the element name
1133 *
1134 * When inserting a new element scan the DtD for existing attributes
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001135 * for that element and initialize the Attribute chain
Owen Taylor3473f882001-02-23 17:55:21 +00001136 *
1137 * Returns the pointer to the first attribute decl in the chain,
1138 * possibly NULL.
1139 */
1140xmlAttributePtr
1141xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
1142 xmlAttributePtr ret = NULL;
1143 xmlAttributeTablePtr table;
1144
1145 if (dtd == NULL) {
1146 xmlGenericError(xmlGenericErrorContext,
1147 "xmlScanAttributeDecl: dtd == NULL\n");
1148 return(NULL);
1149 }
1150 if (elem == NULL) {
1151 xmlGenericError(xmlGenericErrorContext,
1152 "xmlScanAttributeDecl: elem == NULL\n");
1153 return(NULL);
1154 }
1155 table = (xmlAttributeTablePtr) dtd->attributes;
1156 if (table == NULL)
1157 return(NULL);
1158
1159 /* WRONG !!! */
1160 xmlHashScan3(table, NULL, NULL, elem,
1161 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
1162 return(ret);
1163}
1164
1165/**
1166 * xmlScanIDAttributeDecl:
1167 * @ctxt: the validation context
1168 * @elem: the element name
1169 *
1170 * Verify that the element don't have too many ID attributes
1171 * declared.
1172 *
1173 * Returns the number of ID attributes found.
1174 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001175static int
Owen Taylor3473f882001-02-23 17:55:21 +00001176xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
1177 xmlAttributePtr cur;
1178 int ret = 0;
1179
1180 if (elem == NULL) return(0);
1181 cur = elem->attributes;
1182 while (cur != NULL) {
1183 if (cur->atype == XML_ATTRIBUTE_ID) {
1184 ret ++;
1185 if (ret > 1)
1186 VERROR(ctxt->userData,
Daniel Veillarda10efa82001-04-18 13:09:01 +00001187 "Element %s has too many ID attributes defined : %s\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001188 elem->name, cur->name);
1189 }
1190 cur = cur->nexth;
1191 }
1192 return(ret);
1193}
1194
1195/**
1196 * xmlFreeAttribute:
1197 * @elem: An attribute
1198 *
1199 * Deallocate the memory used by an attribute definition
1200 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001201static void
Owen Taylor3473f882001-02-23 17:55:21 +00001202xmlFreeAttribute(xmlAttributePtr attr) {
1203 if (attr == NULL) return;
1204 xmlUnlinkNode((xmlNodePtr) attr);
1205 if (attr->tree != NULL)
1206 xmlFreeEnumeration(attr->tree);
1207 if (attr->elem != NULL)
1208 xmlFree((xmlChar *) attr->elem);
1209 if (attr->name != NULL)
1210 xmlFree((xmlChar *) attr->name);
1211 if (attr->defaultValue != NULL)
1212 xmlFree((xmlChar *) attr->defaultValue);
1213 if (attr->prefix != NULL)
1214 xmlFree((xmlChar *) attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001215 xmlFree(attr);
1216}
1217
1218
1219/**
1220 * xmlAddAttributeDecl:
1221 * @ctxt: the validation context
1222 * @dtd: pointer to the DTD
1223 * @elem: the element name
1224 * @name: the attribute name
1225 * @ns: the attribute namespace prefix
1226 * @type: the attribute type
1227 * @def: the attribute default type
1228 * @defaultValue: the attribute default value
1229 * @tree: if it's an enumeration, the associated list
1230 *
1231 * Register a new attribute declaration
1232 * Note that @tree becomes the ownership of the DTD
1233 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001234 * Returns NULL if not new, otherwise the attribute decl
Owen Taylor3473f882001-02-23 17:55:21 +00001235 */
1236xmlAttributePtr
1237xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
1238 const xmlChar *name, const xmlChar *ns,
1239 xmlAttributeType type, xmlAttributeDefault def,
1240 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
1241 xmlAttributePtr ret;
1242 xmlAttributeTablePtr table;
1243 xmlElementPtr elemDef;
1244
1245 if (dtd == NULL) {
1246 xmlGenericError(xmlGenericErrorContext,
1247 "xmlAddAttributeDecl: dtd == NULL\n");
1248 xmlFreeEnumeration(tree);
1249 return(NULL);
1250 }
1251 if (name == NULL) {
1252 xmlGenericError(xmlGenericErrorContext,
1253 "xmlAddAttributeDecl: name == NULL\n");
1254 xmlFreeEnumeration(tree);
1255 return(NULL);
1256 }
1257 if (elem == NULL) {
1258 xmlGenericError(xmlGenericErrorContext,
1259 "xmlAddAttributeDecl: elem == NULL\n");
1260 xmlFreeEnumeration(tree);
1261 return(NULL);
1262 }
1263 /*
1264 * Check the type and possibly the default value.
1265 */
1266 switch (type) {
1267 case XML_ATTRIBUTE_CDATA:
1268 break;
1269 case XML_ATTRIBUTE_ID:
1270 break;
1271 case XML_ATTRIBUTE_IDREF:
1272 break;
1273 case XML_ATTRIBUTE_IDREFS:
1274 break;
1275 case XML_ATTRIBUTE_ENTITY:
1276 break;
1277 case XML_ATTRIBUTE_ENTITIES:
1278 break;
1279 case XML_ATTRIBUTE_NMTOKEN:
1280 break;
1281 case XML_ATTRIBUTE_NMTOKENS:
1282 break;
1283 case XML_ATTRIBUTE_ENUMERATION:
1284 break;
1285 case XML_ATTRIBUTE_NOTATION:
1286 break;
1287 default:
1288 xmlGenericError(xmlGenericErrorContext,
1289 "xmlAddAttributeDecl: unknown type %d\n", type);
1290 xmlFreeEnumeration(tree);
1291 return(NULL);
1292 }
1293 if ((defaultValue != NULL) &&
1294 (!xmlValidateAttributeValue(type, defaultValue))) {
1295 VERROR(ctxt->userData, "Attribute %s on %s: invalid default value\n",
1296 elem, name, defaultValue);
1297 defaultValue = NULL;
1298 }
1299
1300 /*
1301 * Create the Attribute table if needed.
1302 */
1303 table = (xmlAttributeTablePtr) dtd->attributes;
1304 if (table == NULL) {
1305 table = xmlCreateAttributeTable();
1306 dtd->attributes = (void *) table;
1307 }
1308 if (table == NULL) {
1309 xmlGenericError(xmlGenericErrorContext,
1310 "xmlAddAttributeDecl: Table creation failed!\n");
1311 return(NULL);
1312 }
1313
1314
1315 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1316 if (ret == NULL) {
1317 xmlGenericError(xmlGenericErrorContext,
1318 "xmlAddAttributeDecl: out of memory\n");
1319 return(NULL);
1320 }
1321 memset(ret, 0, sizeof(xmlAttribute));
1322 ret->type = XML_ATTRIBUTE_DECL;
1323
1324 /*
1325 * fill the structure.
1326 */
1327 ret->atype = type;
1328 ret->name = xmlStrdup(name);
1329 ret->prefix = xmlStrdup(ns);
1330 ret->elem = xmlStrdup(elem);
1331 ret->def = def;
1332 ret->tree = tree;
1333 if (defaultValue != NULL)
1334 ret->defaultValue = xmlStrdup(defaultValue);
1335
1336 /*
1337 * Validity Check:
1338 * Search the DTD for previous declarations of the ATTLIST
1339 */
1340 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1341 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001342 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001343 */
1344 VWARNING(ctxt->userData,
1345 "Attribute %s on %s: already defined\n",
1346 name, elem);
1347 xmlFreeAttribute(ret);
1348 return(NULL);
1349 }
1350
1351 /*
1352 * Validity Check:
1353 * Multiple ID per element
1354 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001355 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001356 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001357
Owen Taylor3473f882001-02-23 17:55:21 +00001358 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001359 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001360 VERROR(ctxt->userData,
1361 "Element %s has too may ID attributes defined : %s\n",
1362 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001363 ctxt->valid = 0;
1364 }
1365
Daniel Veillard48da9102001-08-07 01:10:10 +00001366 /*
1367 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001368 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001369 */
1370 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1371 ((ret->prefix != NULL &&
1372 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1373 ret->nexth = elemDef->attributes;
1374 elemDef->attributes = ret;
1375 } else {
1376 xmlAttributePtr tmp = elemDef->attributes;
1377
1378 while ((tmp != NULL) &&
1379 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1380 ((ret->prefix != NULL &&
1381 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1382 if (tmp->nexth == NULL)
1383 break;
1384 tmp = tmp->nexth;
1385 }
1386 if (tmp != NULL) {
1387 ret->nexth = tmp->nexth;
1388 tmp->nexth = ret;
1389 } else {
1390 ret->nexth = elemDef->attributes;
1391 elemDef->attributes = ret;
1392 }
1393 }
Owen Taylor3473f882001-02-23 17:55:21 +00001394 }
1395
1396 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001397 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001398 */
1399 ret->parent = dtd;
1400 ret->doc = dtd->doc;
1401 if (dtd->last == NULL) {
1402 dtd->children = dtd->last = (xmlNodePtr) ret;
1403 } else {
1404 dtd->last->next = (xmlNodePtr) ret;
1405 ret->prev = dtd->last;
1406 dtd->last = (xmlNodePtr) ret;
1407 }
1408 return(ret);
1409}
1410
1411/**
1412 * xmlFreeAttributeTable:
1413 * @table: An attribute table
1414 *
1415 * Deallocate the memory used by an entities hash table.
1416 */
1417void
1418xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1419 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1420}
1421
1422/**
1423 * xmlCopyAttribute:
1424 * @attr: An attribute
1425 *
1426 * Build a copy of an attribute.
1427 *
1428 * Returns the new xmlAttributePtr or NULL in case of error.
1429 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001430static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001431xmlCopyAttribute(xmlAttributePtr attr) {
1432 xmlAttributePtr cur;
1433
1434 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1435 if (cur == NULL) {
1436 xmlGenericError(xmlGenericErrorContext,
1437 "xmlCopyAttribute: out of memory !\n");
1438 return(NULL);
1439 }
1440 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001441 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001442 cur->atype = attr->atype;
1443 cur->def = attr->def;
1444 cur->tree = xmlCopyEnumeration(attr->tree);
1445 if (attr->elem != NULL)
1446 cur->elem = xmlStrdup(attr->elem);
1447 if (attr->name != NULL)
1448 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001449 if (attr->prefix != NULL)
1450 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001451 if (attr->defaultValue != NULL)
1452 cur->defaultValue = xmlStrdup(attr->defaultValue);
1453 return(cur);
1454}
1455
1456/**
1457 * xmlCopyAttributeTable:
1458 * @table: An attribute table
1459 *
1460 * Build a copy of an attribute table.
1461 *
1462 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1463 */
1464xmlAttributeTablePtr
1465xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1466 return((xmlAttributeTablePtr) xmlHashCopy(table,
1467 (xmlHashCopier) xmlCopyAttribute));
1468}
1469
1470/**
1471 * xmlDumpAttributeDecl:
1472 * @buf: the XML buffer output
1473 * @attr: An attribute declaration
1474 *
1475 * This will dump the content of the attribute declaration as an XML
1476 * DTD definition
1477 */
1478void
1479xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1480 xmlBufferWriteChar(buf, "<!ATTLIST ");
1481 xmlBufferWriteCHAR(buf, attr->elem);
1482 xmlBufferWriteChar(buf, " ");
1483 if (attr->prefix != NULL) {
1484 xmlBufferWriteCHAR(buf, attr->prefix);
1485 xmlBufferWriteChar(buf, ":");
1486 }
1487 xmlBufferWriteCHAR(buf, attr->name);
1488 switch (attr->atype) {
1489 case XML_ATTRIBUTE_CDATA:
1490 xmlBufferWriteChar(buf, " CDATA");
1491 break;
1492 case XML_ATTRIBUTE_ID:
1493 xmlBufferWriteChar(buf, " ID");
1494 break;
1495 case XML_ATTRIBUTE_IDREF:
1496 xmlBufferWriteChar(buf, " IDREF");
1497 break;
1498 case XML_ATTRIBUTE_IDREFS:
1499 xmlBufferWriteChar(buf, " IDREFS");
1500 break;
1501 case XML_ATTRIBUTE_ENTITY:
1502 xmlBufferWriteChar(buf, " ENTITY");
1503 break;
1504 case XML_ATTRIBUTE_ENTITIES:
1505 xmlBufferWriteChar(buf, " ENTITIES");
1506 break;
1507 case XML_ATTRIBUTE_NMTOKEN:
1508 xmlBufferWriteChar(buf, " NMTOKEN");
1509 break;
1510 case XML_ATTRIBUTE_NMTOKENS:
1511 xmlBufferWriteChar(buf, " NMTOKENS");
1512 break;
1513 case XML_ATTRIBUTE_ENUMERATION:
1514 xmlBufferWriteChar(buf, " (");
1515 xmlDumpEnumeration(buf, attr->tree);
1516 break;
1517 case XML_ATTRIBUTE_NOTATION:
1518 xmlBufferWriteChar(buf, " NOTATION (");
1519 xmlDumpEnumeration(buf, attr->tree);
1520 break;
1521 default:
1522 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001523 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001524 attr->atype);
1525 }
1526 switch (attr->def) {
1527 case XML_ATTRIBUTE_NONE:
1528 break;
1529 case XML_ATTRIBUTE_REQUIRED:
1530 xmlBufferWriteChar(buf, " #REQUIRED");
1531 break;
1532 case XML_ATTRIBUTE_IMPLIED:
1533 xmlBufferWriteChar(buf, " #IMPLIED");
1534 break;
1535 case XML_ATTRIBUTE_FIXED:
1536 xmlBufferWriteChar(buf, " #FIXED");
1537 break;
1538 default:
1539 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001540 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001541 attr->def);
1542 }
1543 if (attr->defaultValue != NULL) {
1544 xmlBufferWriteChar(buf, " ");
1545 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1546 }
1547 xmlBufferWriteChar(buf, ">\n");
1548}
1549
1550/**
1551 * xmlDumpAttributeTable:
1552 * @buf: the XML buffer output
1553 * @table: An attribute table
1554 *
1555 * This will dump the content of the attribute table as an XML DTD definition
1556 */
1557void
1558xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1559 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1560}
1561
1562/************************************************************************
1563 * *
1564 * NOTATIONs *
1565 * *
1566 ************************************************************************/
1567/**
1568 * xmlCreateNotationTable:
1569 *
1570 * create and initialize an empty notation hash table.
1571 *
1572 * Returns the xmlNotationTablePtr just created or NULL in case
1573 * of error.
1574 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001575static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001576xmlCreateNotationTable(void) {
1577 return(xmlHashCreate(0));
1578}
1579
1580/**
1581 * xmlFreeNotation:
1582 * @not: A notation
1583 *
1584 * Deallocate the memory used by an notation definition
1585 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001586static void
Owen Taylor3473f882001-02-23 17:55:21 +00001587xmlFreeNotation(xmlNotationPtr nota) {
1588 if (nota == NULL) return;
1589 if (nota->name != NULL)
1590 xmlFree((xmlChar *) nota->name);
1591 if (nota->PublicID != NULL)
1592 xmlFree((xmlChar *) nota->PublicID);
1593 if (nota->SystemID != NULL)
1594 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001595 xmlFree(nota);
1596}
1597
1598
1599/**
1600 * xmlAddNotationDecl:
1601 * @dtd: pointer to the DTD
1602 * @ctxt: the validation context
1603 * @name: the entity name
1604 * @PublicID: the public identifier or NULL
1605 * @SystemID: the system identifier or NULL
1606 *
1607 * Register a new notation declaration
1608 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001609 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001610 */
1611xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001612xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001613 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001614 const xmlChar *PublicID, const xmlChar *SystemID) {
1615 xmlNotationPtr ret;
1616 xmlNotationTablePtr table;
1617
1618 if (dtd == NULL) {
1619 xmlGenericError(xmlGenericErrorContext,
1620 "xmlAddNotationDecl: dtd == NULL\n");
1621 return(NULL);
1622 }
1623 if (name == NULL) {
1624 xmlGenericError(xmlGenericErrorContext,
1625 "xmlAddNotationDecl: name == NULL\n");
1626 return(NULL);
1627 }
1628 if ((PublicID == NULL) && (SystemID == NULL)) {
1629 xmlGenericError(xmlGenericErrorContext,
1630 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
1631 }
1632
1633 /*
1634 * Create the Notation table if needed.
1635 */
1636 table = (xmlNotationTablePtr) dtd->notations;
1637 if (table == NULL)
1638 dtd->notations = table = xmlCreateNotationTable();
1639 if (table == NULL) {
1640 xmlGenericError(xmlGenericErrorContext,
1641 "xmlAddNotationDecl: Table creation failed!\n");
1642 return(NULL);
1643 }
1644
1645 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1646 if (ret == NULL) {
1647 xmlGenericError(xmlGenericErrorContext,
1648 "xmlAddNotationDecl: out of memory\n");
1649 return(NULL);
1650 }
1651 memset(ret, 0, sizeof(xmlNotation));
1652
1653 /*
1654 * fill the structure.
1655 */
1656 ret->name = xmlStrdup(name);
1657 if (SystemID != NULL)
1658 ret->SystemID = xmlStrdup(SystemID);
1659 if (PublicID != NULL)
1660 ret->PublicID = xmlStrdup(PublicID);
1661
1662 /*
1663 * Validity Check:
1664 * Check the DTD for previous declarations of the ATTLIST
1665 */
1666 if (xmlHashAddEntry(table, name, ret)) {
1667 xmlGenericError(xmlGenericErrorContext,
1668 "xmlAddNotationDecl: %s already defined\n", name);
1669 xmlFreeNotation(ret);
1670 return(NULL);
1671 }
1672 return(ret);
1673}
1674
1675/**
1676 * xmlFreeNotationTable:
1677 * @table: An notation table
1678 *
1679 * Deallocate the memory used by an entities hash table.
1680 */
1681void
1682xmlFreeNotationTable(xmlNotationTablePtr table) {
1683 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1684}
1685
1686/**
1687 * xmlCopyNotation:
1688 * @nota: A notation
1689 *
1690 * Build a copy of a notation.
1691 *
1692 * Returns the new xmlNotationPtr or NULL in case of error.
1693 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001694static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001695xmlCopyNotation(xmlNotationPtr nota) {
1696 xmlNotationPtr cur;
1697
1698 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1699 if (cur == NULL) {
1700 xmlGenericError(xmlGenericErrorContext,
1701 "xmlCopyNotation: out of memory !\n");
1702 return(NULL);
1703 }
1704 if (nota->name != NULL)
1705 cur->name = xmlStrdup(nota->name);
1706 else
1707 cur->name = NULL;
1708 if (nota->PublicID != NULL)
1709 cur->PublicID = xmlStrdup(nota->PublicID);
1710 else
1711 cur->PublicID = NULL;
1712 if (nota->SystemID != NULL)
1713 cur->SystemID = xmlStrdup(nota->SystemID);
1714 else
1715 cur->SystemID = NULL;
1716 return(cur);
1717}
1718
1719/**
1720 * xmlCopyNotationTable:
1721 * @table: A notation table
1722 *
1723 * Build a copy of a notation table.
1724 *
1725 * Returns the new xmlNotationTablePtr or NULL in case of error.
1726 */
1727xmlNotationTablePtr
1728xmlCopyNotationTable(xmlNotationTablePtr table) {
1729 return((xmlNotationTablePtr) xmlHashCopy(table,
1730 (xmlHashCopier) xmlCopyNotation));
1731}
1732
1733/**
1734 * xmlDumpNotationDecl:
1735 * @buf: the XML buffer output
1736 * @nota: A notation declaration
1737 *
1738 * This will dump the content the notation declaration as an XML DTD definition
1739 */
1740void
1741xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1742 xmlBufferWriteChar(buf, "<!NOTATION ");
1743 xmlBufferWriteCHAR(buf, nota->name);
1744 if (nota->PublicID != NULL) {
1745 xmlBufferWriteChar(buf, " PUBLIC ");
1746 xmlBufferWriteQuotedString(buf, nota->PublicID);
1747 if (nota->SystemID != NULL) {
1748 xmlBufferWriteChar(buf, " ");
1749 xmlBufferWriteCHAR(buf, nota->SystemID);
1750 }
1751 } else {
1752 xmlBufferWriteChar(buf, " SYSTEM ");
1753 xmlBufferWriteCHAR(buf, nota->SystemID);
1754 }
1755 xmlBufferWriteChar(buf, " >\n");
1756}
1757
1758/**
1759 * xmlDumpNotationTable:
1760 * @buf: the XML buffer output
1761 * @table: A notation table
1762 *
1763 * This will dump the content of the notation table as an XML DTD definition
1764 */
1765void
1766xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1767 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1768}
1769
1770/************************************************************************
1771 * *
1772 * IDs *
1773 * *
1774 ************************************************************************/
1775/**
1776 * xmlCreateIDTable:
1777 *
1778 * create and initialize an empty id hash table.
1779 *
1780 * Returns the xmlIDTablePtr just created or NULL in case
1781 * of error.
1782 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001783static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001784xmlCreateIDTable(void) {
1785 return(xmlHashCreate(0));
1786}
1787
1788/**
1789 * xmlFreeID:
1790 * @not: A id
1791 *
1792 * Deallocate the memory used by an id definition
1793 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001794static void
Owen Taylor3473f882001-02-23 17:55:21 +00001795xmlFreeID(xmlIDPtr id) {
1796 if (id == NULL) return;
1797 if (id->value != NULL)
1798 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001799 xmlFree(id);
1800}
1801
1802/**
1803 * xmlAddID:
1804 * @ctxt: the validation context
1805 * @doc: pointer to the document
1806 * @value: the value name
1807 * @attr: the attribute holding the ID
1808 *
1809 * Register a new id declaration
1810 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001811 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001812 */
1813xmlIDPtr
1814xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1815 xmlAttrPtr attr) {
1816 xmlIDPtr ret;
1817 xmlIDTablePtr table;
1818
1819 if (doc == NULL) {
1820 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001821 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001822 return(NULL);
1823 }
1824 if (value == NULL) {
1825 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001826 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001827 return(NULL);
1828 }
1829 if (attr == NULL) {
1830 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001831 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001832 return(NULL);
1833 }
1834
1835 /*
1836 * Create the ID table if needed.
1837 */
1838 table = (xmlIDTablePtr) doc->ids;
1839 if (table == NULL)
1840 doc->ids = table = xmlCreateIDTable();
1841 if (table == NULL) {
1842 xmlGenericError(xmlGenericErrorContext,
1843 "xmlAddID: Table creation failed!\n");
1844 return(NULL);
1845 }
1846
1847 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1848 if (ret == NULL) {
1849 xmlGenericError(xmlGenericErrorContext,
1850 "xmlAddID: out of memory\n");
1851 return(NULL);
1852 }
1853
1854 /*
1855 * fill the structure.
1856 */
1857 ret->value = xmlStrdup(value);
1858 ret->attr = attr;
1859
1860 if (xmlHashAddEntry(table, value, ret) < 0) {
1861 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001862 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001863 */
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00001864 if (ctxt != NULL)
1865 VERROR(ctxt->userData, "ID %s already defined\n", value);
Owen Taylor3473f882001-02-23 17:55:21 +00001866 xmlFreeID(ret);
1867 return(NULL);
1868 }
1869 return(ret);
1870}
1871
1872/**
1873 * xmlFreeIDTable:
1874 * @table: An id table
1875 *
1876 * Deallocate the memory used by an ID hash table.
1877 */
1878void
1879xmlFreeIDTable(xmlIDTablePtr table) {
1880 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1881}
1882
1883/**
1884 * xmlIsID:
1885 * @doc: the document
1886 * @elem: the element carrying the attribute
1887 * @attr: the attribute
1888 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001889 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00001890 * then this is simple, otherwise we use an heuristic: name ID (upper
1891 * or lowercase).
1892 *
1893 * Returns 0 or 1 depending on the lookup result
1894 */
1895int
1896xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1897 if (doc == NULL) return(0);
1898 if (attr == NULL) return(0);
1899 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1900 return(0);
1901 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1902 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1903 (xmlStrEqual(BAD_CAST "name", attr->name)))
1904 return(1);
1905 return(0);
1906 } else {
1907 xmlAttributePtr attrDecl;
1908
1909 if (elem == NULL) return(0);
1910 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1911 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1912 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1913 attr->name);
1914
1915 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1916 return(1);
1917 }
1918 return(0);
1919}
1920
1921/**
1922 * xmlRemoveID
1923 * @doc: the document
1924 * @attr: the attribute
1925 *
1926 * Remove the given attribute from the ID table maintained internally.
1927 *
1928 * Returns -1 if the lookup failed and 0 otherwise
1929 */
1930int
1931xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1932 xmlAttrPtr cur;
1933 xmlIDTablePtr table;
1934 xmlChar *ID;
1935
1936 if (doc == NULL) return(-1);
1937 if (attr == NULL) return(-1);
1938 table = (xmlIDTablePtr) doc->ids;
1939 if (table == NULL)
1940 return(-1);
1941
1942 if (attr == NULL)
1943 return(-1);
1944 ID = xmlNodeListGetString(doc, attr->children, 1);
1945 if (ID == NULL)
1946 return(-1);
1947 cur = xmlHashLookup(table, ID);
1948 if (cur != attr) {
1949 xmlFree(ID);
1950 return(-1);
1951 }
1952 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1953 xmlFree(ID);
1954 return(0);
1955}
1956
1957/**
1958 * xmlGetID:
1959 * @doc: pointer to the document
1960 * @ID: the ID value
1961 *
1962 * Search the attribute declaring the given ID
1963 *
1964 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1965 */
1966xmlAttrPtr
1967xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
1968 xmlIDTablePtr table;
1969 xmlIDPtr id;
1970
1971 if (doc == NULL) {
1972 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
1973 return(NULL);
1974 }
1975
1976 if (ID == NULL) {
1977 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
1978 return(NULL);
1979 }
1980
1981 table = (xmlIDTablePtr) doc->ids;
1982 if (table == NULL)
1983 return(NULL);
1984
1985 id = xmlHashLookup(table, ID);
1986 if (id == NULL)
1987 return(NULL);
1988 return(id->attr);
1989}
1990
1991/************************************************************************
1992 * *
1993 * Refs *
1994 * *
1995 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00001996typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00001997{
1998 xmlListPtr l;
1999 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002000} xmlRemoveMemo;
2001
2002typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2003
2004typedef struct xmlValidateMemo_t
2005{
2006 xmlValidCtxtPtr ctxt;
2007 const xmlChar *name;
2008} xmlValidateMemo;
2009
2010typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002011
2012/**
2013 * xmlCreateRefTable:
2014 *
2015 * create and initialize an empty ref hash table.
2016 *
2017 * Returns the xmlRefTablePtr just created or NULL in case
2018 * of error.
2019 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002020static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002021xmlCreateRefTable(void) {
2022 return(xmlHashCreate(0));
2023}
2024
2025/**
2026 * xmlFreeRef:
2027 * @lk: A list link
2028 *
2029 * Deallocate the memory used by a ref definition
2030 */
2031static void
2032xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002033 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2034 if (ref == NULL) return;
2035 if (ref->value != NULL)
2036 xmlFree((xmlChar *)ref->value);
2037 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002038}
2039
2040/**
2041 * xmlFreeRefList:
2042 * @list_ref: A list of references.
2043 *
2044 * Deallocate the memory used by a list of references
2045 */
2046static void
2047xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002048 if (list_ref == NULL) return;
2049 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002050}
2051
2052/**
2053 * xmlWalkRemoveRef:
2054 * @data: Contents of current link
2055 * @user: Value supplied by the user
2056 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002057 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002058 */
2059static int
2060xmlWalkRemoveRef(const void *data, const void *user)
2061{
Daniel Veillard37721922001-05-04 15:21:12 +00002062 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2063 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2064 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002065
Daniel Veillard37721922001-05-04 15:21:12 +00002066 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2067 xmlListRemoveFirst(ref_list, (void *)data);
2068 return 0;
2069 }
2070 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002071}
2072
2073/**
2074 * xmlAddRef:
2075 * @ctxt: the validation context
2076 * @doc: pointer to the document
2077 * @value: the value name
2078 * @attr: the attribute holding the Ref
2079 *
2080 * Register a new ref declaration
2081 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002082 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002083 */
2084xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002085xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002086 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002087 xmlRefPtr ret;
2088 xmlRefTablePtr table;
2089 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002090
Daniel Veillard37721922001-05-04 15:21:12 +00002091 if (doc == NULL) {
2092 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002093 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002094 return(NULL);
2095 }
2096 if (value == NULL) {
2097 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002098 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002099 return(NULL);
2100 }
2101 if (attr == NULL) {
2102 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002103 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002104 return(NULL);
2105 }
Owen Taylor3473f882001-02-23 17:55:21 +00002106
Daniel Veillard37721922001-05-04 15:21:12 +00002107 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002108 * Create the Ref table if needed.
2109 */
Daniel Veillard37721922001-05-04 15:21:12 +00002110 table = (xmlRefTablePtr) doc->refs;
2111 if (table == NULL)
2112 doc->refs = table = xmlCreateRefTable();
2113 if (table == NULL) {
2114 xmlGenericError(xmlGenericErrorContext,
2115 "xmlAddRef: Table creation failed!\n");
2116 return(NULL);
2117 }
Owen Taylor3473f882001-02-23 17:55:21 +00002118
Daniel Veillard37721922001-05-04 15:21:12 +00002119 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2120 if (ret == NULL) {
2121 xmlGenericError(xmlGenericErrorContext,
2122 "xmlAddRef: out of memory\n");
2123 return(NULL);
2124 }
Owen Taylor3473f882001-02-23 17:55:21 +00002125
Daniel Veillard37721922001-05-04 15:21:12 +00002126 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002127 * fill the structure.
2128 */
Daniel Veillard37721922001-05-04 15:21:12 +00002129 ret->value = xmlStrdup(value);
2130 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002131
Daniel Veillard37721922001-05-04 15:21:12 +00002132 /* To add a reference :-
2133 * References are maintained as a list of references,
2134 * Lookup the entry, if no entry create new nodelist
2135 * Add the owning node to the NodeList
2136 * Return the ref
2137 */
Owen Taylor3473f882001-02-23 17:55:21 +00002138
Daniel Veillard37721922001-05-04 15:21:12 +00002139 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2140 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2141 xmlGenericError(xmlGenericErrorContext,
2142 "xmlAddRef: Reference list creation failed!\n");
2143 return(NULL);
2144 }
2145 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2146 xmlListDelete(ref_list);
2147 xmlGenericError(xmlGenericErrorContext,
2148 "xmlAddRef: Reference list insertion failed!\n");
2149 return(NULL);
2150 }
2151 }
2152 xmlListInsert(ref_list, ret);
2153 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002154}
2155
2156/**
2157 * xmlFreeRefTable:
2158 * @table: An ref table
2159 *
2160 * Deallocate the memory used by an Ref hash table.
2161 */
2162void
2163xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002164 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002165}
2166
2167/**
2168 * xmlIsRef:
2169 * @doc: the document
2170 * @elem: the element carrying the attribute
2171 * @attr: the attribute
2172 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002173 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002174 * then this is simple, otherwise we use an heuristic: name Ref (upper
2175 * or lowercase).
2176 *
2177 * Returns 0 or 1 depending on the lookup result
2178 */
2179int
2180xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002181 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2182 return(0);
2183 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2184 /* TODO @@@ */
2185 return(0);
2186 } else {
2187 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002188
Daniel Veillard37721922001-05-04 15:21:12 +00002189 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2190 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2191 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2192 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002193
Daniel Veillard37721922001-05-04 15:21:12 +00002194 if ((attrDecl != NULL) &&
2195 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2196 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2197 return(1);
2198 }
2199 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002200}
2201
2202/**
2203 * xmlRemoveRef
2204 * @doc: the document
2205 * @attr: the attribute
2206 *
2207 * Remove the given attribute from the Ref table maintained internally.
2208 *
2209 * Returns -1 if the lookup failed and 0 otherwise
2210 */
2211int
2212xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002213 xmlListPtr ref_list;
2214 xmlRefTablePtr table;
2215 xmlChar *ID;
2216 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002217
Daniel Veillard37721922001-05-04 15:21:12 +00002218 if (doc == NULL) return(-1);
2219 if (attr == NULL) return(-1);
2220 table = (xmlRefTablePtr) doc->refs;
2221 if (table == NULL)
2222 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002223
Daniel Veillard37721922001-05-04 15:21:12 +00002224 if (attr == NULL)
2225 return(-1);
2226 ID = xmlNodeListGetString(doc, attr->children, 1);
2227 if (ID == NULL)
2228 return(-1);
2229 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002230
Daniel Veillard37721922001-05-04 15:21:12 +00002231 if(ref_list == NULL) {
2232 xmlFree(ID);
2233 return (-1);
2234 }
2235 /* At this point, ref_list refers to a list of references which
2236 * have the same key as the supplied attr. Our list of references
2237 * is ordered by reference address and we don't have that information
2238 * here to use when removing. We'll have to walk the list and
2239 * check for a matching attribute, when we find one stop the walk
2240 * and remove the entry.
2241 * The list is ordered by reference, so that means we don't have the
2242 * key. Passing the list and the reference to the walker means we
2243 * will have enough data to be able to remove the entry.
2244 */
2245 target.l = ref_list;
2246 target.ap = attr;
2247
2248 /* Remove the supplied attr from our list */
2249 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002250
Daniel Veillard37721922001-05-04 15:21:12 +00002251 /*If the list is empty then remove the list entry in the hash */
2252 if (xmlListEmpty(ref_list))
2253 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2254 xmlFreeRefList);
2255 xmlFree(ID);
2256 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002257}
2258
2259/**
2260 * xmlGetRefs:
2261 * @doc: pointer to the document
2262 * @ID: the ID value
2263 *
2264 * Find the set of references for the supplied ID.
2265 *
2266 * Returns NULL if not found, otherwise node set for the ID.
2267 */
2268xmlListPtr
2269xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002270 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002271
Daniel Veillard37721922001-05-04 15:21:12 +00002272 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002273 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002274 return(NULL);
2275 }
Owen Taylor3473f882001-02-23 17:55:21 +00002276
Daniel Veillard37721922001-05-04 15:21:12 +00002277 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002278 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002279 return(NULL);
2280 }
Owen Taylor3473f882001-02-23 17:55:21 +00002281
Daniel Veillard37721922001-05-04 15:21:12 +00002282 table = (xmlRefTablePtr) doc->refs;
2283 if (table == NULL)
2284 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002285
Daniel Veillard37721922001-05-04 15:21:12 +00002286 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002287}
2288
2289/************************************************************************
2290 * *
2291 * Routines for validity checking *
2292 * *
2293 ************************************************************************/
2294
2295/**
2296 * xmlGetDtdElementDesc:
2297 * @dtd: a pointer to the DtD to search
2298 * @name: the element name
2299 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002300 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002301 *
2302 * returns the xmlElementPtr if found or NULL
2303 */
2304
2305xmlElementPtr
2306xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2307 xmlElementTablePtr table;
2308 xmlElementPtr cur;
2309 xmlChar *uqname = NULL, *prefix = NULL;
2310
2311 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002312 if (dtd->elements == NULL)
2313 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002314 table = (xmlElementTablePtr) dtd->elements;
2315
2316 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002317 if (uqname != NULL)
2318 name = uqname;
2319 cur = xmlHashLookup2(table, name, prefix);
2320 if (prefix != NULL) xmlFree(prefix);
2321 if (uqname != NULL) xmlFree(uqname);
2322 return(cur);
2323}
2324/**
2325 * xmlGetDtdElementDesc2:
2326 * @dtd: a pointer to the DtD to search
2327 * @name: the element name
2328 * @create: create an empty description if not found
2329 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002330 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002331 *
2332 * returns the xmlElementPtr if found or NULL
2333 */
2334
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002335static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002336xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2337 xmlElementTablePtr table;
2338 xmlElementPtr cur;
2339 xmlChar *uqname = NULL, *prefix = NULL;
2340
2341 if (dtd == NULL) return(NULL);
2342 if (dtd->elements == NULL) {
2343 if (!create)
2344 return(NULL);
2345 /*
2346 * Create the Element table if needed.
2347 */
2348 table = (xmlElementTablePtr) dtd->elements;
2349 if (table == NULL) {
2350 table = xmlCreateElementTable();
2351 dtd->elements = (void *) table;
2352 }
2353 if (table == NULL) {
2354 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002355 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002356 return(NULL);
2357 }
2358 }
2359 table = (xmlElementTablePtr) dtd->elements;
2360
2361 uqname = xmlSplitQName2(name, &prefix);
2362 if (uqname != NULL)
2363 name = uqname;
2364 cur = xmlHashLookup2(table, name, prefix);
2365 if ((cur == NULL) && (create)) {
2366 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2367 if (cur == NULL) {
2368 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002369 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002370 return(NULL);
2371 }
2372 memset(cur, 0, sizeof(xmlElement));
2373 cur->type = XML_ELEMENT_DECL;
2374
2375 /*
2376 * fill the structure.
2377 */
2378 cur->name = xmlStrdup(name);
2379 cur->prefix = xmlStrdup(prefix);
2380 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2381
2382 xmlHashAddEntry2(table, name, prefix, cur);
2383 }
2384 if (prefix != NULL) xmlFree(prefix);
2385 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002386 return(cur);
2387}
2388
2389/**
2390 * xmlGetDtdQElementDesc:
2391 * @dtd: a pointer to the DtD to search
2392 * @name: the element name
2393 * @prefix: the element namespace prefix
2394 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002395 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002396 *
2397 * returns the xmlElementPtr if found or NULL
2398 */
2399
Daniel Veillard48da9102001-08-07 01:10:10 +00002400xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002401xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2402 const xmlChar *prefix) {
2403 xmlElementTablePtr table;
2404
2405 if (dtd == NULL) return(NULL);
2406 if (dtd->elements == NULL) return(NULL);
2407 table = (xmlElementTablePtr) dtd->elements;
2408
2409 return(xmlHashLookup2(table, name, prefix));
2410}
2411
2412/**
2413 * xmlGetDtdAttrDesc:
2414 * @dtd: a pointer to the DtD to search
2415 * @elem: the element name
2416 * @name: the attribute name
2417 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002418 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002419 * this element.
2420 *
2421 * returns the xmlAttributePtr if found or NULL
2422 */
2423
2424xmlAttributePtr
2425xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2426 xmlAttributeTablePtr table;
2427 xmlAttributePtr cur;
2428 xmlChar *uqname = NULL, *prefix = NULL;
2429
2430 if (dtd == NULL) return(NULL);
2431 if (dtd->attributes == NULL) return(NULL);
2432
2433 table = (xmlAttributeTablePtr) dtd->attributes;
2434 if (table == NULL)
2435 return(NULL);
2436
2437 uqname = xmlSplitQName2(name, &prefix);
2438
2439 if (uqname != NULL) {
2440 cur = xmlHashLookup3(table, uqname, prefix, elem);
2441 if (prefix != NULL) xmlFree(prefix);
2442 if (uqname != NULL) xmlFree(uqname);
2443 } else
2444 cur = xmlHashLookup3(table, name, NULL, elem);
2445 return(cur);
2446}
2447
2448/**
2449 * xmlGetDtdQAttrDesc:
2450 * @dtd: a pointer to the DtD to search
2451 * @elem: the element name
2452 * @name: the attribute name
2453 * @prefix: the attribute namespace prefix
2454 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002455 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002456 * this element.
2457 *
2458 * returns the xmlAttributePtr if found or NULL
2459 */
2460
Daniel Veillard48da9102001-08-07 01:10:10 +00002461xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002462xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2463 const xmlChar *prefix) {
2464 xmlAttributeTablePtr table;
2465
2466 if (dtd == NULL) return(NULL);
2467 if (dtd->attributes == NULL) return(NULL);
2468 table = (xmlAttributeTablePtr) dtd->attributes;
2469
2470 return(xmlHashLookup3(table, name, prefix, elem));
2471}
2472
2473/**
2474 * xmlGetDtdNotationDesc:
2475 * @dtd: a pointer to the DtD to search
2476 * @name: the notation name
2477 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002478 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002479 *
2480 * returns the xmlNotationPtr if found or NULL
2481 */
2482
2483xmlNotationPtr
2484xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2485 xmlNotationTablePtr table;
2486
2487 if (dtd == NULL) return(NULL);
2488 if (dtd->notations == NULL) return(NULL);
2489 table = (xmlNotationTablePtr) dtd->notations;
2490
2491 return(xmlHashLookup(table, name));
2492}
2493
2494/**
2495 * xmlValidateNotationUse:
2496 * @ctxt: the validation context
2497 * @doc: the document
2498 * @notationName: the notation name to check
2499 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002500 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002501 * - [ VC: Notation Declared ]
2502 *
2503 * returns 1 if valid or 0 otherwise
2504 */
2505
2506int
2507xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2508 const xmlChar *notationName) {
2509 xmlNotationPtr notaDecl;
2510 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2511
2512 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2513 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2514 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2515
2516 if (notaDecl == NULL) {
2517 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2518 notationName);
2519 return(0);
2520 }
2521 return(1);
2522}
2523
2524/**
2525 * xmlIsMixedElement
2526 * @doc: the document
2527 * @name: the element name
2528 *
2529 * Search in the DtDs whether an element accept Mixed content (or ANY)
2530 * basically if it is supposed to accept text childs
2531 *
2532 * returns 0 if no, 1 if yes, and -1 if no element description is available
2533 */
2534
2535int
2536xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2537 xmlElementPtr elemDecl;
2538
2539 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2540
2541 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2542 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2543 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2544 if (elemDecl == NULL) return(-1);
2545 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002546 case XML_ELEMENT_TYPE_UNDEFINED:
2547 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002548 case XML_ELEMENT_TYPE_ELEMENT:
2549 return(0);
2550 case XML_ELEMENT_TYPE_EMPTY:
2551 /*
2552 * return 1 for EMPTY since we want VC error to pop up
2553 * on <empty> </empty> for example
2554 */
2555 case XML_ELEMENT_TYPE_ANY:
2556 case XML_ELEMENT_TYPE_MIXED:
2557 return(1);
2558 }
2559 return(1);
2560}
2561
2562/**
2563 * xmlValidateNameValue:
2564 * @value: an Name value
2565 *
2566 * Validate that the given value match Name production
2567 *
2568 * returns 1 if valid or 0 otherwise
2569 */
2570
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002571static int
Owen Taylor3473f882001-02-23 17:55:21 +00002572xmlValidateNameValue(const xmlChar *value) {
2573 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002574 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002575
2576 if (value == NULL) return(0);
2577 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002578 val = xmlStringCurrentChar(NULL, cur, &len);
2579 cur += len;
2580 if (!IS_LETTER(val) && (val != '_') &&
2581 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002582 return(0);
2583 }
2584
Daniel Veillardd8224e02002-01-13 15:43:22 +00002585 val = xmlStringCurrentChar(NULL, cur, &len);
2586 cur += len;
2587 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2588 (val == '.') || (val == '-') ||
2589 (val == '_') || (val == ':') ||
2590 (IS_COMBINING(val)) ||
2591 (IS_EXTENDER(val))) {
2592 val = xmlStringCurrentChar(NULL, cur, &len);
2593 cur += len;
2594 }
Owen Taylor3473f882001-02-23 17:55:21 +00002595
Daniel Veillardd8224e02002-01-13 15:43:22 +00002596 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002597
2598 return(1);
2599}
2600
2601/**
2602 * xmlValidateNamesValue:
2603 * @value: an Names value
2604 *
2605 * Validate that the given value match Names production
2606 *
2607 * returns 1 if valid or 0 otherwise
2608 */
2609
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002610static int
Owen Taylor3473f882001-02-23 17:55:21 +00002611xmlValidateNamesValue(const xmlChar *value) {
2612 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002613 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002614
2615 if (value == NULL) return(0);
2616 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002617 val = xmlStringCurrentChar(NULL, cur, &len);
2618 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002619
Daniel Veillardd8224e02002-01-13 15:43:22 +00002620 if (!IS_LETTER(val) && (val != '_') &&
2621 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002622 return(0);
2623 }
2624
Daniel Veillardd8224e02002-01-13 15:43:22 +00002625 val = xmlStringCurrentChar(NULL, cur, &len);
2626 cur += len;
2627 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2628 (val == '.') || (val == '-') ||
2629 (val == '_') || (val == ':') ||
2630 (IS_COMBINING(val)) ||
2631 (IS_EXTENDER(val))) {
2632 val = xmlStringCurrentChar(NULL, cur, &len);
2633 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002634 }
2635
Daniel Veillardd8224e02002-01-13 15:43:22 +00002636 while (IS_BLANK(val)) {
2637 while (IS_BLANK(val)) {
2638 val = xmlStringCurrentChar(NULL, cur, &len);
2639 cur += len;
2640 }
2641
2642 if (!IS_LETTER(val) && (val != '_') &&
2643 (val != ':')) {
2644 return(0);
2645 }
2646 val = xmlStringCurrentChar(NULL, cur, &len);
2647 cur += len;
2648
2649 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2650 (val == '.') || (val == '-') ||
2651 (val == '_') || (val == ':') ||
2652 (IS_COMBINING(val)) ||
2653 (IS_EXTENDER(val))) {
2654 val = xmlStringCurrentChar(NULL, cur, &len);
2655 cur += len;
2656 }
2657 }
2658
2659 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002660
2661 return(1);
2662}
2663
2664/**
2665 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002666 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002667 *
2668 * Validate that the given value match Nmtoken production
2669 *
2670 * [ VC: Name Token ]
2671 *
2672 * returns 1 if valid or 0 otherwise
2673 */
2674
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002675static int
Owen Taylor3473f882001-02-23 17:55:21 +00002676xmlValidateNmtokenValue(const xmlChar *value) {
2677 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002678 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002679
2680 if (value == NULL) return(0);
2681 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002682 val = xmlStringCurrentChar(NULL, cur, &len);
2683 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002684
Daniel Veillardd8224e02002-01-13 15:43:22 +00002685 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2686 (val != '.') && (val != '-') &&
2687 (val != '_') && (val != ':') &&
2688 (!IS_COMBINING(val)) &&
2689 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002690 return(0);
2691
Daniel Veillardd8224e02002-01-13 15:43:22 +00002692 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2693 (val == '.') || (val == '-') ||
2694 (val == '_') || (val == ':') ||
2695 (IS_COMBINING(val)) ||
2696 (IS_EXTENDER(val))) {
2697 val = xmlStringCurrentChar(NULL, cur, &len);
2698 cur += len;
2699 }
Owen Taylor3473f882001-02-23 17:55:21 +00002700
Daniel Veillardd8224e02002-01-13 15:43:22 +00002701 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002702
2703 return(1);
2704}
2705
2706/**
2707 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002708 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00002709 *
2710 * Validate that the given value match Nmtokens production
2711 *
2712 * [ VC: Name Token ]
2713 *
2714 * returns 1 if valid or 0 otherwise
2715 */
2716
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002717static int
Owen Taylor3473f882001-02-23 17:55:21 +00002718xmlValidateNmtokensValue(const xmlChar *value) {
2719 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002720 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002721
2722 if (value == NULL) return(0);
2723 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002724 val = xmlStringCurrentChar(NULL, cur, &len);
2725 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002726
Daniel Veillardd8224e02002-01-13 15:43:22 +00002727 while (IS_BLANK(val)) {
2728 val = xmlStringCurrentChar(NULL, cur, &len);
2729 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002730 }
2731
Daniel Veillardd8224e02002-01-13 15:43:22 +00002732 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2733 (val != '.') && (val != '-') &&
2734 (val != '_') && (val != ':') &&
2735 (!IS_COMBINING(val)) &&
2736 (!IS_EXTENDER(val)))
2737 return(0);
2738
2739 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2740 (val == '.') || (val == '-') ||
2741 (val == '_') || (val == ':') ||
2742 (IS_COMBINING(val)) ||
2743 (IS_EXTENDER(val))) {
2744 val = xmlStringCurrentChar(NULL, cur, &len);
2745 cur += len;
2746 }
2747
2748 while (IS_BLANK(val)) {
2749 while (IS_BLANK(val)) {
2750 val = xmlStringCurrentChar(NULL, cur, &len);
2751 cur += len;
2752 }
2753 if (val == 0) return(1);
2754
2755 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2756 (val != '.') && (val != '-') &&
2757 (val != '_') && (val != ':') &&
2758 (!IS_COMBINING(val)) &&
2759 (!IS_EXTENDER(val)))
2760 return(0);
2761
2762 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2763 (val == '.') || (val == '-') ||
2764 (val == '_') || (val == ':') ||
2765 (IS_COMBINING(val)) ||
2766 (IS_EXTENDER(val))) {
2767 val = xmlStringCurrentChar(NULL, cur, &len);
2768 cur += len;
2769 }
2770 }
2771
2772 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002773
2774 return(1);
2775}
2776
2777/**
2778 * xmlValidateNotationDecl:
2779 * @ctxt: the validation context
2780 * @doc: a document instance
2781 * @nota: a notation definition
2782 *
2783 * Try to validate a single notation definition
2784 * basically it does the following checks as described by the
2785 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002786 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00002787 * But this function get called anyway ...
2788 *
2789 * returns 1 if valid or 0 otherwise
2790 */
2791
2792int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002793xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2794 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002795 int ret = 1;
2796
2797 return(ret);
2798}
2799
2800/**
2801 * xmlValidateAttributeValue:
2802 * @type: an attribute type
2803 * @value: an attribute value
2804 *
2805 * Validate that the given attribute value match the proper production
2806 *
2807 * [ VC: ID ]
2808 * Values of type ID must match the Name production....
2809 *
2810 * [ VC: IDREF ]
2811 * Values of type IDREF must match the Name production, and values
2812 * of type IDREFS must match Names ...
2813 *
2814 * [ VC: Entity Name ]
2815 * Values of type ENTITY must match the Name production, values
2816 * of type ENTITIES must match Names ...
2817 *
2818 * [ VC: Name Token ]
2819 * Values of type NMTOKEN must match the Nmtoken production; values
2820 * of type NMTOKENS must match Nmtokens.
2821 *
2822 * returns 1 if valid or 0 otherwise
2823 */
2824
2825int
2826xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2827 switch (type) {
2828 case XML_ATTRIBUTE_ENTITIES:
2829 case XML_ATTRIBUTE_IDREFS:
2830 return(xmlValidateNamesValue(value));
2831 case XML_ATTRIBUTE_ENTITY:
2832 case XML_ATTRIBUTE_IDREF:
2833 case XML_ATTRIBUTE_ID:
2834 case XML_ATTRIBUTE_NOTATION:
2835 return(xmlValidateNameValue(value));
2836 case XML_ATTRIBUTE_NMTOKENS:
2837 case XML_ATTRIBUTE_ENUMERATION:
2838 return(xmlValidateNmtokensValue(value));
2839 case XML_ATTRIBUTE_NMTOKEN:
2840 return(xmlValidateNmtokenValue(value));
2841 case XML_ATTRIBUTE_CDATA:
2842 break;
2843 }
2844 return(1);
2845}
2846
2847/**
2848 * xmlValidateAttributeValue2:
2849 * @ctxt: the validation context
2850 * @doc: the document
2851 * @name: the attribute name (used for error reporting only)
2852 * @type: the attribute type
2853 * @value: the attribute value
2854 *
2855 * Validate that the given attribute value match a given type.
2856 * This typically cannot be done before having finished parsing
2857 * the subsets.
2858 *
2859 * [ VC: IDREF ]
2860 * Values of type IDREF must match one of the declared IDs
2861 * Values of type IDREFS must match a sequence of the declared IDs
2862 * each Name must match the value of an ID attribute on some element
2863 * in the XML document; i.e. IDREF values must match the value of
2864 * some ID attribute
2865 *
2866 * [ VC: Entity Name ]
2867 * Values of type ENTITY must match one declared entity
2868 * Values of type ENTITIES must match a sequence of declared entities
2869 *
2870 * [ VC: Notation Attributes ]
2871 * all notation names in the declaration must be declared.
2872 *
2873 * returns 1 if valid or 0 otherwise
2874 */
2875
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002876static int
Owen Taylor3473f882001-02-23 17:55:21 +00002877xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2878 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2879 int ret = 1;
2880 switch (type) {
2881 case XML_ATTRIBUTE_IDREFS:
2882 case XML_ATTRIBUTE_IDREF:
2883 case XML_ATTRIBUTE_ID:
2884 case XML_ATTRIBUTE_NMTOKENS:
2885 case XML_ATTRIBUTE_ENUMERATION:
2886 case XML_ATTRIBUTE_NMTOKEN:
2887 case XML_ATTRIBUTE_CDATA:
2888 break;
2889 case XML_ATTRIBUTE_ENTITY: {
2890 xmlEntityPtr ent;
2891
2892 ent = xmlGetDocEntity(doc, value);
2893 if (ent == NULL) {
2894 VERROR(ctxt->userData,
2895 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2896 name, value);
2897 ret = 0;
2898 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2899 VERROR(ctxt->userData,
2900 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2901 name, value);
2902 ret = 0;
2903 }
2904 break;
2905 }
2906 case XML_ATTRIBUTE_ENTITIES: {
2907 xmlChar *dup, *nam = NULL, *cur, save;
2908 xmlEntityPtr ent;
2909
2910 dup = xmlStrdup(value);
2911 if (dup == NULL)
2912 return(0);
2913 cur = dup;
2914 while (*cur != 0) {
2915 nam = cur;
2916 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2917 save = *cur;
2918 *cur = 0;
2919 ent = xmlGetDocEntity(doc, nam);
2920 if (ent == NULL) {
2921 VERROR(ctxt->userData,
2922 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2923 name, nam);
2924 ret = 0;
2925 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2926 VERROR(ctxt->userData,
2927 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2928 name, nam);
2929 ret = 0;
2930 }
2931 if (save == 0)
2932 break;
2933 *cur = save;
2934 while (IS_BLANK(*cur)) cur++;
2935 }
2936 xmlFree(dup);
2937 break;
2938 }
2939 case XML_ATTRIBUTE_NOTATION: {
2940 xmlNotationPtr nota;
2941
2942 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2943 if ((nota == NULL) && (doc->extSubset != NULL))
2944 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2945
2946 if (nota == NULL) {
2947 VERROR(ctxt->userData,
2948 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2949 name, value);
2950 ret = 0;
2951 }
2952 break;
2953 }
2954 }
2955 return(ret);
2956}
2957
2958/**
2959 * xmlValidNormalizeAttributeValue:
2960 * @doc: the document
2961 * @elem: the parent
2962 * @name: the attribute name
2963 * @value: the attribute value
2964 *
2965 * Does the validation related extra step of the normalization of attribute
2966 * values:
2967 *
2968 * If the declared value is not CDATA, then the XML processor must further
2969 * process the normalized attribute value by discarding any leading and
2970 * trailing space (#x20) characters, and by replacing sequences of space
2971 * (#x20) characters by single space (#x20) character.
2972 *
2973 * returns a new normalized string if normalization is needed, NULL otherwise
2974 * the caller must free the returned value.
2975 */
2976
2977xmlChar *
2978xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
2979 const xmlChar *name, const xmlChar *value) {
2980 xmlChar *ret, *dst;
2981 const xmlChar *src;
2982 xmlAttributePtr attrDecl = NULL;
2983
2984 if (doc == NULL) return(NULL);
2985 if (elem == NULL) return(NULL);
2986 if (name == NULL) return(NULL);
2987 if (value == NULL) return(NULL);
2988
2989 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2990 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00002991 snprintf((char *) qname, sizeof(qname), "%s:%s",
2992 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002993 qname[sizeof(qname) - 1] = 0;
2994 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
2995 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2996 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
2997 }
2998 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
2999 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3000 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3001
3002 if (attrDecl == NULL)
3003 return(NULL);
3004 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3005 return(NULL);
3006
3007 ret = xmlStrdup(value);
3008 if (ret == NULL)
3009 return(NULL);
3010 src = value;
3011 dst = ret;
3012 while (*src == 0x20) src++;
3013 while (*src != 0) {
3014 if (*src == 0x20) {
3015 while (*src == 0x20) src++;
3016 if (*src != 0)
3017 *dst++ = 0x20;
3018 } else {
3019 *dst++ = *src++;
3020 }
3021 }
3022 *dst = 0;
3023 return(ret);
3024}
3025
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003026static void
Owen Taylor3473f882001-02-23 17:55:21 +00003027xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003028 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003029 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3030}
3031
3032/**
3033 * xmlValidateAttributeDecl:
3034 * @ctxt: the validation context
3035 * @doc: a document instance
3036 * @attr: an attribute definition
3037 *
3038 * Try to validate a single attribute definition
3039 * basically it does the following checks as described by the
3040 * XML-1.0 recommendation:
3041 * - [ VC: Attribute Default Legal ]
3042 * - [ VC: Enumeration ]
3043 * - [ VC: ID Attribute Default ]
3044 *
3045 * The ID/IDREF uniqueness and matching are done separately
3046 *
3047 * returns 1 if valid or 0 otherwise
3048 */
3049
3050int
3051xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3052 xmlAttributePtr attr) {
3053 int ret = 1;
3054 int val;
3055 CHECK_DTD;
3056 if(attr == NULL) return(1);
3057
3058 /* Attribute Default Legal */
3059 /* Enumeration */
3060 if (attr->defaultValue != NULL) {
3061 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3062 if (val == 0) {
3063 VERROR(ctxt->userData,
3064 "Syntax of default value for attribute %s on %s is not valid\n",
3065 attr->name, attr->elem);
3066 }
3067 ret &= val;
3068 }
3069
3070 /* ID Attribute Default */
3071 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3072 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3073 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3074 VERROR(ctxt->userData,
3075 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
3076 attr->name, attr->elem);
3077 ret = 0;
3078 }
3079
3080 /* One ID per Element Type */
3081 if (attr->atype == XML_ATTRIBUTE_ID) {
3082 int nbId;
3083
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003084 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003085 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3086 attr->elem);
3087 if (elem != NULL) {
3088 nbId = xmlScanIDAttributeDecl(NULL, elem);
3089 } else {
3090 xmlAttributeTablePtr table;
3091
3092 /*
3093 * The attribute may be declared in the internal subset and the
3094 * element in the external subset.
3095 */
3096 nbId = 0;
3097 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3098 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3099 xmlValidateAttributeIdCallback, &nbId);
3100 }
3101 if (nbId > 1) {
3102 VERROR(ctxt->userData,
3103 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3104 attr->elem, nbId, attr->name);
3105 } else if (doc->extSubset != NULL) {
3106 int extId = 0;
3107 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3108 if (elem != NULL) {
3109 extId = xmlScanIDAttributeDecl(NULL, elem);
3110 }
3111 if (extId > 1) {
3112 VERROR(ctxt->userData,
3113 "Element %s has %d ID attribute defined in the external subset : %s\n",
3114 attr->elem, extId, attr->name);
3115 } else if (extId + nbId > 1) {
3116 VERROR(ctxt->userData,
3117"Element %s has ID attributes defined in the internal and external subset : %s\n",
3118 attr->elem, attr->name);
3119 }
3120 }
3121 }
3122
3123 /* Validity Constraint: Enumeration */
3124 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3125 xmlEnumerationPtr tree = attr->tree;
3126 while (tree != NULL) {
3127 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3128 tree = tree->next;
3129 }
3130 if (tree == NULL) {
3131 VERROR(ctxt->userData,
3132"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3133 attr->defaultValue, attr->name, attr->elem);
3134 ret = 0;
3135 }
3136 }
3137
3138 return(ret);
3139}
3140
3141/**
3142 * xmlValidateElementDecl:
3143 * @ctxt: the validation context
3144 * @doc: a document instance
3145 * @elem: an element definition
3146 *
3147 * Try to validate a single element definition
3148 * basically it does the following checks as described by the
3149 * XML-1.0 recommendation:
3150 * - [ VC: One ID per Element Type ]
3151 * - [ VC: No Duplicate Types ]
3152 * - [ VC: Unique Element Type Declaration ]
3153 *
3154 * returns 1 if valid or 0 otherwise
3155 */
3156
3157int
3158xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3159 xmlElementPtr elem) {
3160 int ret = 1;
3161 xmlElementPtr tst;
3162
3163 CHECK_DTD;
3164
3165 if (elem == NULL) return(1);
3166
3167 /* No Duplicate Types */
3168 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3169 xmlElementContentPtr cur, next;
3170 const xmlChar *name;
3171
3172 cur = elem->content;
3173 while (cur != NULL) {
3174 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3175 if (cur->c1 == NULL) break;
3176 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3177 name = cur->c1->name;
3178 next = cur->c2;
3179 while (next != NULL) {
3180 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3181 if (xmlStrEqual(next->name, name)) {
3182 VERROR(ctxt->userData,
3183 "Definition of %s has duplicate references of %s\n",
3184 elem->name, name);
3185 ret = 0;
3186 }
3187 break;
3188 }
3189 if (next->c1 == NULL) break;
3190 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3191 if (xmlStrEqual(next->c1->name, name)) {
3192 VERROR(ctxt->userData,
3193 "Definition of %s has duplicate references of %s\n",
3194 elem->name, name);
3195 ret = 0;
3196 }
3197 next = next->c2;
3198 }
3199 }
3200 cur = cur->c2;
3201 }
3202 }
3203
3204 /* VC: Unique Element Type Declaration */
3205 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003206 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003207 ((tst->prefix == elem->prefix) ||
3208 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003209 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003210 VERROR(ctxt->userData, "Redefinition of element %s\n",
3211 elem->name);
3212 ret = 0;
3213 }
3214 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003215 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003216 ((tst->prefix == elem->prefix) ||
3217 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003218 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003219 VERROR(ctxt->userData, "Redefinition of element %s\n",
3220 elem->name);
3221 ret = 0;
3222 }
3223
Daniel Veillarda10efa82001-04-18 13:09:01 +00003224 /* One ID per Element Type
3225 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003226 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3227 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003228 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003229 return(ret);
3230}
3231
3232/**
3233 * xmlValidateOneAttribute:
3234 * @ctxt: the validation context
3235 * @doc: a document instance
3236 * @elem: an element instance
3237 * @attr: an attribute instance
3238 * @value: the attribute value (without entities processing)
3239 *
3240 * Try to validate a single attribute for an element
3241 * basically it does the following checks as described by the
3242 * XML-1.0 recommendation:
3243 * - [ VC: Attribute Value Type ]
3244 * - [ VC: Fixed Attribute Default ]
3245 * - [ VC: Entity Name ]
3246 * - [ VC: Name Token ]
3247 * - [ VC: ID ]
3248 * - [ VC: IDREF ]
3249 * - [ VC: Entity Name ]
3250 * - [ VC: Notation Attributes ]
3251 *
3252 * The ID/IDREF uniqueness and matching are done separately
3253 *
3254 * returns 1 if valid or 0 otherwise
3255 */
3256
3257int
3258xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3259 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3260 /* xmlElementPtr elemDecl; */
3261 xmlAttributePtr attrDecl = NULL;
3262 int val;
3263 int ret = 1;
3264
3265 CHECK_DTD;
3266 if ((elem == NULL) || (elem->name == NULL)) return(0);
3267 if ((attr == NULL) || (attr->name == NULL)) return(0);
3268
3269 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3270 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003271 snprintf((char *) qname, sizeof(qname), "%s:%s",
3272 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003273 qname[sizeof(qname) - 1] = 0;
3274 if (attr->ns != NULL) {
3275 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3276 attr->name, attr->ns->prefix);
3277 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3278 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3279 attr->name, attr->ns->prefix);
3280 } else {
3281 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3282 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3283 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3284 qname, attr->name);
3285 }
3286 }
3287 if (attrDecl == NULL) {
3288 if (attr->ns != NULL) {
3289 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3290 attr->name, attr->ns->prefix);
3291 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3292 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3293 attr->name, attr->ns->prefix);
3294 } else {
3295 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3296 elem->name, attr->name);
3297 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3298 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3299 elem->name, attr->name);
3300 }
3301 }
3302
3303
3304 /* Validity Constraint: Attribute Value Type */
3305 if (attrDecl == NULL) {
3306 VERROR(ctxt->userData,
3307 "No declaration for attribute %s on element %s\n",
3308 attr->name, elem->name);
3309 return(0);
3310 }
3311 attr->atype = attrDecl->atype;
3312
3313 val = xmlValidateAttributeValue(attrDecl->atype, value);
3314 if (val == 0) {
3315 VERROR(ctxt->userData,
3316 "Syntax of value for attribute %s on %s is not valid\n",
3317 attr->name, elem->name);
3318 ret = 0;
3319 }
3320
3321 /* Validity constraint: Fixed Attribute Default */
3322 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3323 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3324 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003325 "Value for attribute %s on %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003326 attr->name, elem->name, attrDecl->defaultValue);
3327 ret = 0;
3328 }
3329 }
3330
3331 /* Validity Constraint: ID uniqueness */
3332 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3333 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3334 ret = 0;
3335 }
3336
3337 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3338 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3339 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3340 ret = 0;
3341 }
3342
3343 /* Validity Constraint: Notation Attributes */
3344 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3345 xmlEnumerationPtr tree = attrDecl->tree;
3346 xmlNotationPtr nota;
3347
3348 /* First check that the given NOTATION was declared */
3349 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3350 if (nota == NULL)
3351 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3352
3353 if (nota == NULL) {
3354 VERROR(ctxt->userData,
3355 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
3356 value, attr->name, elem->name);
3357 ret = 0;
3358 }
3359
3360 /* Second, verify that it's among the list */
3361 while (tree != NULL) {
3362 if (xmlStrEqual(tree->name, value)) break;
3363 tree = tree->next;
3364 }
3365 if (tree == NULL) {
3366 VERROR(ctxt->userData,
3367"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
3368 value, attr->name, elem->name);
3369 ret = 0;
3370 }
3371 }
3372
3373 /* Validity Constraint: Enumeration */
3374 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3375 xmlEnumerationPtr tree = attrDecl->tree;
3376 while (tree != NULL) {
3377 if (xmlStrEqual(tree->name, value)) break;
3378 tree = tree->next;
3379 }
3380 if (tree == NULL) {
3381 VERROR(ctxt->userData,
3382 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3383 value, attr->name, elem->name);
3384 ret = 0;
3385 }
3386 }
3387
3388 /* Fixed Attribute Default */
3389 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3390 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3391 VERROR(ctxt->userData,
3392 "Value for attribute %s on %s must be \"%s\"\n",
3393 attr->name, elem->name, attrDecl->defaultValue);
3394 ret = 0;
3395 }
3396
3397 /* Extra check for the attribute value */
3398 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3399 attrDecl->atype, value);
3400
3401 return(ret);
3402}
3403
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003404/**
3405 * xmlValidateSkipIgnorable:
3406 * @ctxt: the validation context
3407 * @child: the child list
3408 *
3409 * Skip ignorable elements w.r.t. the validation process
3410 *
3411 * returns the first element to consider for validation of the content model
3412 */
3413
3414static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003415xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003416 while (child != NULL) {
3417 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003418 /* These things are ignored (skipped) during validation. */
3419 case XML_PI_NODE:
3420 case XML_COMMENT_NODE:
3421 case XML_XINCLUDE_START:
3422 case XML_XINCLUDE_END:
3423 child = child->next;
3424 break;
3425 case XML_TEXT_NODE:
3426 if (xmlIsBlankNode(child))
3427 child = child->next;
3428 else
3429 return(child);
3430 break;
3431 /* keep current node */
3432 default:
3433 return(child);
3434 }
3435 }
3436 return(child);
3437}
3438
3439/**
3440 * xmlValidateElementType:
3441 * @ctxt: the validation context
3442 *
3443 * Try to validate the content model of an element internal function
3444 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003445 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3446 * reference is found and -3 if the validation succeeded but
3447 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003448 */
3449
3450static int
3451xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003452 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003453 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003454
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003455 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003456 if ((NODE == NULL) && (CONT == NULL))
3457 return(1);
3458 if ((NODE == NULL) &&
3459 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3460 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3461 return(1);
3462 }
3463 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003464 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003465 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003466
3467 /*
3468 * We arrive here when more states need to be examined
3469 */
3470cont:
3471
3472 /*
3473 * We just recovered from a rollback generated by a possible
3474 * epsilon transition, go directly to the analysis phase
3475 */
3476 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003477 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003478 DEBUG_VALID_STATE(NODE, CONT)
3479 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003480 goto analyze;
3481 }
3482
3483 DEBUG_VALID_STATE(NODE, CONT)
3484 /*
3485 * we may have to save a backup state here. This is the equivalent
3486 * of handling epsilon transition in NFAs.
3487 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003488 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003489 ((CONT->parent == NULL) ||
3490 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003491 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003492 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003493 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003494 DEBUG_VALID_MSG("saving parent branch");
3495 vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
3496 }
3497
3498
3499 /*
3500 * Check first if the content matches
3501 */
3502 switch (CONT->type) {
3503 case XML_ELEMENT_CONTENT_PCDATA:
3504 if (NODE == NULL) {
3505 DEBUG_VALID_MSG("pcdata failed no node");
3506 ret = 0;
3507 break;
3508 }
3509 if (NODE->type == XML_TEXT_NODE) {
3510 DEBUG_VALID_MSG("pcdata found, skip to next");
3511 /*
3512 * go to next element in the content model
3513 * skipping ignorable elems
3514 */
3515 do {
3516 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003517 NODE = xmlValidateSkipIgnorable(NODE);
3518 if ((NODE != NULL) &&
3519 (NODE->type == XML_ENTITY_REF_NODE))
3520 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003521 } while ((NODE != NULL) &&
3522 ((NODE->type != XML_ELEMENT_NODE) &&
3523 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003524 ret = 1;
3525 break;
3526 } else {
3527 DEBUG_VALID_MSG("pcdata failed");
3528 ret = 0;
3529 break;
3530 }
3531 break;
3532 case XML_ELEMENT_CONTENT_ELEMENT:
3533 if (NODE == NULL) {
3534 DEBUG_VALID_MSG("element failed no node");
3535 ret = 0;
3536 break;
3537 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003538 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3539 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003540 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003541 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3542 ret = (CONT->prefix == NULL);
3543 } else if (CONT->prefix == NULL) {
3544 ret = 0;
3545 } else {
3546 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3547 }
3548 }
3549 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003550 DEBUG_VALID_MSG("element found, skip to next");
3551 /*
3552 * go to next element in the content model
3553 * skipping ignorable elems
3554 */
3555 do {
3556 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003557 NODE = xmlValidateSkipIgnorable(NODE);
3558 if ((NODE != NULL) &&
3559 (NODE->type == XML_ENTITY_REF_NODE))
3560 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003561 } while ((NODE != NULL) &&
3562 ((NODE->type != XML_ELEMENT_NODE) &&
3563 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003564 } else {
3565 DEBUG_VALID_MSG("element failed");
3566 ret = 0;
3567 break;
3568 }
3569 break;
3570 case XML_ELEMENT_CONTENT_OR:
3571 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003572 * Small optimization.
3573 */
3574 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3575 if ((NODE == NULL) ||
3576 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3577 DEPTH++;
3578 CONT = CONT->c2;
3579 goto cont;
3580 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003581 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3582 ret = (CONT->c1->prefix == NULL);
3583 } else if (CONT->c1->prefix == NULL) {
3584 ret = 0;
3585 } else {
3586 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3587 }
3588 if (ret == 0) {
3589 DEPTH++;
3590 CONT = CONT->c2;
3591 goto cont;
3592 }
Daniel Veillard85349052001-04-20 13:48:21 +00003593 }
3594
3595 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003596 * save the second branch 'or' branch
3597 */
3598 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard268fd1b2001-08-26 18:46:36 +00003599 vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3600 OCCURS, ROLLBACK_OR);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003601
3602 DEPTH++;
3603 CONT = CONT->c1;
3604 goto cont;
3605 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003606 /*
3607 * Small optimization.
3608 */
3609 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3610 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3611 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3612 if ((NODE == NULL) ||
3613 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3614 DEPTH++;
3615 CONT = CONT->c2;
3616 goto cont;
3617 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003618 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3619 ret = (CONT->c1->prefix == NULL);
3620 } else if (CONT->c1->prefix == NULL) {
3621 ret = 0;
3622 } else {
3623 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3624 }
3625 if (ret == 0) {
3626 DEPTH++;
3627 CONT = CONT->c2;
3628 goto cont;
3629 }
Daniel Veillard1d047672001-06-09 16:41:01 +00003630 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003631 DEPTH++;
3632 CONT = CONT->c1;
3633 goto cont;
3634 }
3635
3636 /*
3637 * At this point handle going up in the tree
3638 */
3639 if (ret == -1) {
3640 DEBUG_VALID_MSG("error found returning");
3641 return(ret);
3642 }
3643analyze:
3644 while (CONT != NULL) {
3645 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003646 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003647 * this level.
3648 */
3649 if (ret == 0) {
3650 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003651 xmlNodePtr cur;
3652
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003653 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003654 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003655 DEBUG_VALID_MSG("Once branch failed, rollback");
3656 if (vstateVPop(ctxt) < 0 ) {
3657 DEBUG_VALID_MSG("exhaustion, failed");
3658 return(0);
3659 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003660 if (cur != ctxt->vstate->node)
3661 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003662 goto cont;
3663 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00003664 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003665 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003666 DEBUG_VALID_MSG("Plus branch failed, rollback");
3667 if (vstateVPop(ctxt) < 0 ) {
3668 DEBUG_VALID_MSG("exhaustion, failed");
3669 return(0);
3670 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003671 if (cur != ctxt->vstate->node)
3672 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003673 goto cont;
3674 }
3675 DEBUG_VALID_MSG("Plus branch found");
3676 ret = 1;
3677 break;
3678 case XML_ELEMENT_CONTENT_MULT:
3679#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00003680 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003681 DEBUG_VALID_MSG("Mult branch failed");
3682 } else {
3683 DEBUG_VALID_MSG("Mult branch found");
3684 }
3685#endif
3686 ret = 1;
3687 break;
3688 case XML_ELEMENT_CONTENT_OPT:
3689 DEBUG_VALID_MSG("Option branch failed");
3690 ret = 1;
3691 break;
3692 }
3693 } else {
3694 switch (CONT->ocur) {
3695 case XML_ELEMENT_CONTENT_OPT:
3696 DEBUG_VALID_MSG("Option branch succeeded");
3697 ret = 1;
3698 break;
3699 case XML_ELEMENT_CONTENT_ONCE:
3700 DEBUG_VALID_MSG("Once branch succeeded");
3701 ret = 1;
3702 break;
3703 case XML_ELEMENT_CONTENT_PLUS:
3704 if (STATE == ROLLBACK_PARENT) {
3705 DEBUG_VALID_MSG("Plus branch rollback");
3706 ret = 1;
3707 break;
3708 }
3709 if (NODE == NULL) {
3710 DEBUG_VALID_MSG("Plus branch exhausted");
3711 ret = 1;
3712 break;
3713 }
3714 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003715 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003716 goto cont;
3717 case XML_ELEMENT_CONTENT_MULT:
3718 if (STATE == ROLLBACK_PARENT) {
3719 DEBUG_VALID_MSG("Mult branch rollback");
3720 ret = 1;
3721 break;
3722 }
3723 if (NODE == NULL) {
3724 DEBUG_VALID_MSG("Mult branch exhausted");
3725 ret = 1;
3726 break;
3727 }
3728 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003729 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003730 goto cont;
3731 }
3732 }
3733 STATE = 0;
3734
3735 /*
3736 * Then act accordingly at the parent level
3737 */
Daniel Veillard5344c602001-12-31 16:37:34 +00003738 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003739 if (CONT->parent == NULL)
3740 break;
3741
3742 switch (CONT->parent->type) {
3743 case XML_ELEMENT_CONTENT_PCDATA:
3744 DEBUG_VALID_MSG("Error: parent pcdata");
3745 return(-1);
3746 case XML_ELEMENT_CONTENT_ELEMENT:
3747 DEBUG_VALID_MSG("Error: parent element");
3748 return(-1);
3749 case XML_ELEMENT_CONTENT_OR:
3750 if (ret == 1) {
3751 DEBUG_VALID_MSG("Or succeeded");
3752 CONT = CONT->parent;
3753 DEPTH--;
3754 } else {
3755 DEBUG_VALID_MSG("Or failed");
3756 CONT = CONT->parent;
3757 DEPTH--;
3758 }
3759 break;
3760 case XML_ELEMENT_CONTENT_SEQ:
3761 if (ret == 0) {
3762 DEBUG_VALID_MSG("Sequence failed");
3763 CONT = CONT->parent;
3764 DEPTH--;
3765 } else if (CONT == CONT->parent->c1) {
3766 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3767 CONT = CONT->parent->c2;
3768 goto cont;
3769 } else {
3770 DEBUG_VALID_MSG("Sequence succeeded");
3771 CONT = CONT->parent;
3772 DEPTH--;
3773 }
3774 }
3775 }
3776 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003777 xmlNodePtr cur;
3778
3779 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003780 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3781 if (vstateVPop(ctxt) < 0 ) {
3782 DEBUG_VALID_MSG("exhaustion, failed");
3783 return(0);
3784 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003785 if (cur != ctxt->vstate->node)
3786 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003787 goto cont;
3788 }
3789 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003790 xmlNodePtr cur;
3791
3792 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003793 DEBUG_VALID_MSG("Failure, rollback");
3794 if (vstateVPop(ctxt) < 0 ) {
3795 DEBUG_VALID_MSG("exhaustion, failed");
3796 return(0);
3797 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003798 if (cur != ctxt->vstate->node)
3799 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003800 goto cont;
3801 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003802 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003803}
3804
3805/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00003806 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003807 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00003808 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003809 * @content: An element
3810 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3811 *
3812 * This will dump the list of elements to the buffer
3813 * Intended just for the debug routine
3814 */
3815static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00003816xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003817 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00003818 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003819
3820 if (node == NULL) return;
3821 if (glob) strcat(buf, "(");
3822 cur = node;
3823 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00003824 len = strlen(buf);
3825 if (size - len < 50) {
3826 if ((size - len > 4) && (buf[len - 1] != '.'))
3827 strcat(buf, " ...");
3828 return;
3829 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003830 switch (cur->type) {
3831 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003832 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
3833 if (size - len < xmlStrlen(cur->ns->prefix + 10)) {
3834 if ((size - len > 4) && (buf[len - 1] != '.'))
3835 strcat(buf, " ...");
3836 return;
3837 }
3838 strcat(buf, (char *) cur->ns->prefix);
3839 strcat(buf, ":");
3840 }
Daniel Veillardd3d06722001-08-15 12:06:36 +00003841 if (size - len < xmlStrlen(cur->name + 10)) {
3842 if ((size - len > 4) && (buf[len - 1] != '.'))
3843 strcat(buf, " ...");
3844 return;
3845 }
3846 strcat(buf, (char *) cur->name);
3847 if (cur->next != NULL)
3848 strcat(buf, " ");
3849 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003850 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003851 if (xmlIsBlankNode(cur))
3852 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003853 case XML_CDATA_SECTION_NODE:
3854 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003855 strcat(buf, "CDATA");
3856 if (cur->next != NULL)
3857 strcat(buf, " ");
3858 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003859 case XML_ATTRIBUTE_NODE:
3860 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003861#ifdef LIBXML_DOCB_ENABLED
3862 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003863#endif
3864 case XML_HTML_DOCUMENT_NODE:
3865 case XML_DOCUMENT_TYPE_NODE:
3866 case XML_DOCUMENT_FRAG_NODE:
3867 case XML_NOTATION_NODE:
3868 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003869 strcat(buf, "???");
3870 if (cur->next != NULL)
3871 strcat(buf, " ");
3872 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003873 case XML_ENTITY_NODE:
3874 case XML_PI_NODE:
3875 case XML_DTD_NODE:
3876 case XML_COMMENT_NODE:
3877 case XML_ELEMENT_DECL:
3878 case XML_ATTRIBUTE_DECL:
3879 case XML_ENTITY_DECL:
3880 case XML_XINCLUDE_START:
3881 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003882 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003883 }
3884 cur = cur->next;
3885 }
3886 if (glob) strcat(buf, ")");
3887}
3888
3889/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003890 * xmlValidateElementContent:
3891 * @ctxt: the validation context
3892 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003893 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003894 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003895 *
3896 * Try to validate the content model of an element
3897 *
3898 * returns 1 if valid or 0 if not and -1 in case of error
3899 */
3900
3901static int
3902xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003903 xmlElementPtr elemDecl, int warn) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003904 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003905 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003906 xmlElementContentPtr cont;
3907 const xmlChar *name;
3908
3909 if (elemDecl == NULL)
3910 return(-1);
3911 cont = elemDecl->content;
3912 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003913
3914 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003915 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003916 */
3917 ctxt->vstateMax = 8;
3918 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
3919 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
3920 if (ctxt->vstateTab == NULL) {
3921 xmlGenericError(xmlGenericErrorContext,
3922 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003923 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003924 }
3925 /*
3926 * The first entry in the stack is reserved to the current state
3927 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00003928 ctxt->nodeMax = 0;
3929 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00003930 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003931 ctxt->vstate = &ctxt->vstateTab[0];
3932 ctxt->vstateNr = 1;
3933 CONT = cont;
3934 NODE = child;
3935 DEPTH = 0;
3936 OCCURS = 0;
3937 STATE = 0;
3938 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003939 if ((ret == -3) && (warn)) {
3940 VWARNING(ctxt->userData,
3941 "Element %s content model is ambiguous\n", name);
3942 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003943 /*
3944 * An entities reference appeared at this level.
3945 * Buid a minimal representation of this node content
3946 * sufficient to run the validation process on it
3947 */
3948 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003949 cur = child;
3950 while (cur != NULL) {
3951 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003952 case XML_ENTITY_REF_NODE:
3953 /*
3954 * Push the current node to be able to roll back
3955 * and process within the entity
3956 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003957 if ((cur->children != NULL) &&
3958 (cur->children->children != NULL)) {
3959 nodeVPush(ctxt, cur);
3960 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003961 continue;
3962 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00003963 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003964 case XML_TEXT_NODE:
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003965 case XML_CDATA_SECTION_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003966 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003967 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003968 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003969 case XML_ELEMENT_NODE:
3970 /*
3971 * Allocate a new node and minimally fills in
3972 * what's required
3973 */
3974 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
3975 if (tmp == NULL) {
3976 xmlGenericError(xmlGenericErrorContext,
3977 "xmlValidateElementContent : malloc failed\n");
3978 xmlFreeNodeList(repl);
3979 ret = -1;
3980 goto done;
3981 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003982 tmp->type = cur->type;
3983 tmp->name = cur->name;
3984 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003985 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00003986 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003987 if (repl == NULL)
3988 repl = last = tmp;
3989 else {
3990 last->next = tmp;
3991 last = tmp;
3992 }
3993 break;
3994 default:
3995 break;
3996 }
3997 /*
3998 * Switch to next element
3999 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004000 cur = cur->next;
4001 while (cur == NULL) {
4002 cur = nodeVPop(ctxt);
4003 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004004 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004005 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004006 }
4007 }
4008
4009 /*
4010 * Relaunch the validation
4011 */
4012 ctxt->vstate = &ctxt->vstateTab[0];
4013 ctxt->vstateNr = 1;
4014 CONT = cont;
4015 NODE = repl;
4016 DEPTH = 0;
4017 OCCURS = 0;
4018 STATE = 0;
4019 ret = xmlValidateElementType(ctxt);
4020 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004021 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004022 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4023 char expr[5000];
4024 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004025
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004026 expr[0] = 0;
4027 xmlSnprintfElementContent(expr, 5000, cont, 1);
4028 list[0] = 0;
4029 if (repl != NULL)
4030 xmlSnprintfElements(list, 5000, repl, 1);
4031 else
4032 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004033
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004034 if (name != NULL) {
4035 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004036 "Element %s content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004037 name, expr, list);
4038 } else {
4039 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004040 "Element content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004041 expr, list);
4042 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004043 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004044 if (name != NULL) {
4045 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004046 "Element %s content doesn't follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004047 name);
4048 } else {
4049 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004050 "Element content doesn't follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004051 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004052 }
4053 ret = 0;
4054 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004055 if (ret == -3)
4056 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004057
4058
4059done:
4060 /*
4061 * Deallocate the copy if done, and free up the validation stack
4062 */
4063 while (repl != NULL) {
4064 tmp = repl->next;
4065 xmlFree(repl);
4066 repl = tmp;
4067 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004068 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004069 if (ctxt->vstateTab != NULL) {
4070 xmlFree(ctxt->vstateTab);
4071 ctxt->vstateTab = NULL;
4072 }
4073 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004074 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004075 if (ctxt->nodeTab != NULL) {
4076 xmlFree(ctxt->nodeTab);
4077 ctxt->nodeTab = NULL;
4078 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004079 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004080
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004081}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004082
Owen Taylor3473f882001-02-23 17:55:21 +00004083/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004084 * xmlValidateCdataElement:
4085 * @ctxt: the validation context
4086 * @doc: a document instance
4087 * @elem: an element instance
4088 *
4089 * Check that an element follows #CDATA
4090 *
4091 * returns 1 if valid or 0 otherwise
4092 */
4093static int
4094xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4095 xmlNodePtr elem) {
4096 int ret = 1;
4097 xmlNodePtr cur, child;
4098
4099 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4100 return(0);
4101
4102 child = elem->children;
4103
4104 cur = child;
4105 while (cur != NULL) {
4106 switch (cur->type) {
4107 case XML_ENTITY_REF_NODE:
4108 /*
4109 * Push the current node to be able to roll back
4110 * and process within the entity
4111 */
4112 if ((cur->children != NULL) &&
4113 (cur->children->children != NULL)) {
4114 nodeVPush(ctxt, cur);
4115 cur = cur->children->children;
4116 continue;
4117 }
4118 break;
4119 case XML_COMMENT_NODE:
4120 case XML_PI_NODE:
4121 case XML_TEXT_NODE:
4122 case XML_CDATA_SECTION_NODE:
4123 break;
4124 default:
4125 ret = 0;
4126 goto done;
4127 }
4128 /*
4129 * Switch to next element
4130 */
4131 cur = cur->next;
4132 while (cur == NULL) {
4133 cur = nodeVPop(ctxt);
4134 if (cur == NULL)
4135 break;
4136 cur = cur->next;
4137 }
4138 }
4139done:
4140 ctxt->nodeMax = 0;
4141 ctxt->nodeNr = 0;
4142 if (ctxt->nodeTab != NULL) {
4143 xmlFree(ctxt->nodeTab);
4144 ctxt->nodeTab = NULL;
4145 }
4146 return(ret);
4147}
4148
4149/**
Owen Taylor3473f882001-02-23 17:55:21 +00004150 * xmlValidateOneElement:
4151 * @ctxt: the validation context
4152 * @doc: a document instance
4153 * @elem: an element instance
4154 *
4155 * Try to validate a single element and it's attributes,
4156 * basically it does the following checks as described by the
4157 * XML-1.0 recommendation:
4158 * - [ VC: Element Valid ]
4159 * - [ VC: Required Attribute ]
4160 * Then call xmlValidateOneAttribute() for each attribute present.
4161 *
4162 * The ID/IDREF checkings are done separately
4163 *
4164 * returns 1 if valid or 0 otherwise
4165 */
4166
4167int
4168xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4169 xmlNodePtr elem) {
4170 xmlElementPtr elemDecl = NULL;
4171 xmlElementContentPtr cont;
4172 xmlAttributePtr attr;
4173 xmlNodePtr child;
4174 int ret = 1;
4175 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004176 const xmlChar *prefix = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00004177
4178 CHECK_DTD;
4179
4180 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004181 switch (elem->type) {
4182 case XML_ATTRIBUTE_NODE:
4183 VERROR(ctxt->userData,
4184 "Attribute element not expected here\n");
4185 return(0);
4186 case XML_TEXT_NODE:
4187 if (elem->children != NULL) {
4188 VERROR(ctxt->userData, "Text element has childs !\n");
4189 return(0);
4190 }
4191 if (elem->properties != NULL) {
4192 VERROR(ctxt->userData, "Text element has attributes !\n");
4193 return(0);
4194 }
4195 if (elem->ns != NULL) {
4196 VERROR(ctxt->userData, "Text element has namespace !\n");
4197 return(0);
4198 }
4199 if (elem->nsDef != NULL) {
4200 VERROR(ctxt->userData,
4201 "Text element carries namespace definitions !\n");
4202 return(0);
4203 }
4204 if (elem->content == NULL) {
4205 VERROR(ctxt->userData,
4206 "Text element has no content !\n");
4207 return(0);
4208 }
4209 return(1);
4210 case XML_XINCLUDE_START:
4211 case XML_XINCLUDE_END:
4212 return(1);
4213 case XML_CDATA_SECTION_NODE:
4214 case XML_ENTITY_REF_NODE:
4215 case XML_PI_NODE:
4216 case XML_COMMENT_NODE:
4217 return(1);
4218 case XML_ENTITY_NODE:
4219 VERROR(ctxt->userData,
4220 "Entity element not expected here\n");
4221 return(0);
4222 case XML_NOTATION_NODE:
4223 VERROR(ctxt->userData,
4224 "Notation element not expected here\n");
4225 return(0);
4226 case XML_DOCUMENT_NODE:
4227 case XML_DOCUMENT_TYPE_NODE:
4228 case XML_DOCUMENT_FRAG_NODE:
4229 VERROR(ctxt->userData,
4230 "Document element not expected here\n");
4231 return(0);
4232 case XML_HTML_DOCUMENT_NODE:
4233 VERROR(ctxt->userData,
4234 "\n");
4235 return(0);
4236 case XML_ELEMENT_NODE:
4237 break;
4238 default:
4239 VERROR(ctxt->userData,
4240 "unknown element type %d\n", elem->type);
4241 return(0);
4242 }
4243 if (elem->name == NULL) return(0);
4244
4245 /*
4246 * Fetch the declaration for the qualified name
4247 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004248 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4249 prefix = elem->ns->prefix;
4250
4251 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004252 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004253 elem->name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00004254 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4255 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004256 elem->name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00004257 }
4258
4259 /*
4260 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004261 * This is "non-strict" validation should be done on the
4262 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004263 */
4264 if (elemDecl == NULL) {
4265 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
4266 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4267 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
4268 }
4269 if (elemDecl == NULL) {
4270 VERROR(ctxt->userData, "No declaration for element %s\n",
4271 elem->name);
4272 return(0);
4273 }
4274
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004275 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004276 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004277 case XML_ELEMENT_TYPE_UNDEFINED:
4278 VERROR(ctxt->userData, "No declaration for element %s\n",
4279 elem->name);
4280 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004281 case XML_ELEMENT_TYPE_EMPTY:
4282 if (elem->children != NULL) {
4283 VERROR(ctxt->userData,
4284 "Element %s was declared EMPTY this one has content\n",
4285 elem->name);
4286 ret = 0;
4287 }
4288 break;
4289 case XML_ELEMENT_TYPE_ANY:
4290 /* I don't think anything is required then */
4291 break;
4292 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004293 /* simple case of declared as #PCDATA */
4294 if ((elemDecl->content != NULL) &&
4295 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4296 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4297 if (!ret) {
4298 VERROR(ctxt->userData,
4299 "Element %s was declared #PCDATA but contains non text nodes\n",
4300 elem->name);
4301 }
4302 break;
4303 }
Owen Taylor3473f882001-02-23 17:55:21 +00004304 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004305 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004306 while (child != NULL) {
4307 if (child->type == XML_ELEMENT_NODE) {
4308 name = child->name;
4309 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4310 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004311 snprintf((char *) qname, sizeof(qname), "%s:%s",
4312 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004313 qname[sizeof(qname) - 1] = 0;
4314 cont = elemDecl->content;
4315 while (cont != NULL) {
4316 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4317 if (xmlStrEqual(cont->name, qname)) break;
4318 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4319 (cont->c1 != NULL) &&
4320 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4321 if (xmlStrEqual(cont->c1->name, qname)) break;
4322 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4323 (cont->c1 == NULL) ||
4324 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4325 /* Internal error !!! */
4326 xmlGenericError(xmlGenericErrorContext,
4327 "Internal: MIXED struct bad\n");
4328 break;
4329 }
4330 cont = cont->c2;
4331 }
4332 if (cont != NULL)
4333 goto child_ok;
4334 }
4335 cont = elemDecl->content;
4336 while (cont != NULL) {
4337 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4338 if (xmlStrEqual(cont->name, name)) break;
4339 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4340 (cont->c1 != NULL) &&
4341 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4342 if (xmlStrEqual(cont->c1->name, name)) break;
4343 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4344 (cont->c1 == NULL) ||
4345 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4346 /* Internal error !!! */
4347 xmlGenericError(xmlGenericErrorContext,
4348 "Internal: MIXED struct bad\n");
4349 break;
4350 }
4351 cont = cont->c2;
4352 }
4353 if (cont == NULL) {
4354 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004355 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004356 name, elem->name);
4357 ret = 0;
4358 }
4359 }
4360child_ok:
4361 child = child->next;
4362 }
4363 break;
4364 case XML_ELEMENT_TYPE_ELEMENT:
4365 child = elem->children;
4366 cont = elemDecl->content;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004367 ret = xmlValidateElementContent(ctxt, child, elemDecl, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00004368 break;
4369 }
4370
4371 /* [ VC: Required Attribute ] */
4372 attr = elemDecl->attributes;
4373 while (attr != NULL) {
4374 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004375 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00004376
Daniel Veillarde4301c82002-02-13 13:32:35 +00004377 if ((attr->prefix == NULL) &&
4378 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4379 xmlNsPtr ns;
4380
4381 ns = elem->nsDef;
4382 while (ns != NULL) {
4383 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00004384 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004385 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00004386 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004387 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4388 xmlNsPtr ns;
4389
4390 ns = elem->nsDef;
4391 while (ns != NULL) {
4392 if (xmlStrEqual(attr->name, ns->prefix))
4393 goto found;
4394 ns = ns->next;
4395 }
4396 } else {
4397 xmlAttrPtr attrib;
4398
4399 attrib = elem->properties;
4400 while (attrib != NULL) {
4401 if (xmlStrEqual(attrib->name, attr->name)) {
4402 if (attr->prefix != NULL) {
4403 xmlNsPtr nameSpace = attrib->ns;
4404
4405 if (nameSpace == NULL)
4406 nameSpace = elem->ns;
4407 /*
4408 * qualified names handling is problematic, having a
4409 * different prefix should be possible but DTDs don't
4410 * allow to define the URI instead of the prefix :-(
4411 */
4412 if (nameSpace == NULL) {
4413 if (qualified < 0)
4414 qualified = 0;
4415 } else if (!xmlStrEqual(nameSpace->prefix,
4416 attr->prefix)) {
4417 if (qualified < 1)
4418 qualified = 1;
4419 } else
4420 goto found;
4421 } else {
4422 /*
4423 * We should allow applications to define namespaces
4424 * for their application even if the DTD doesn't
4425 * carry one, otherwise, basically we would always
4426 * break.
4427 */
4428 goto found;
4429 }
4430 }
4431 attrib = attrib->next;
4432 }
Owen Taylor3473f882001-02-23 17:55:21 +00004433 }
4434 if (qualified == -1) {
4435 if (attr->prefix == NULL) {
4436 VERROR(ctxt->userData,
4437 "Element %s doesn't carry attribute %s\n",
4438 elem->name, attr->name);
4439 ret = 0;
4440 } else {
4441 VERROR(ctxt->userData,
4442 "Element %s doesn't carry attribute %s:%s\n",
4443 elem->name, attr->prefix,attr->name);
4444 ret = 0;
4445 }
4446 } else if (qualified == 0) {
4447 VWARNING(ctxt->userData,
4448 "Element %s required attribute %s:%s has no prefix\n",
4449 elem->name, attr->prefix,attr->name);
4450 } else if (qualified == 1) {
4451 VWARNING(ctxt->userData,
4452 "Element %s required attribute %s:%s has different prefix\n",
4453 elem->name, attr->prefix,attr->name);
4454 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004455 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
4456 /*
4457 * Special tests checking #FIXED namespace declarations
4458 * have the right value since this is not done as an
4459 * attribute checking
4460 */
4461 if ((attr->prefix == NULL) &&
4462 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4463 xmlNsPtr ns;
4464
4465 ns = elem->nsDef;
4466 while (ns != NULL) {
4467 if (ns->prefix == NULL) {
4468 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
4469 VERROR(ctxt->userData,
4470 "Element %s namespace name for default namespace does not match the DTD\n",
4471 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00004472 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004473 }
4474 goto found;
4475 }
4476 ns = ns->next;
4477 }
4478 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4479 xmlNsPtr ns;
4480
4481 ns = elem->nsDef;
4482 while (ns != NULL) {
4483 if (xmlStrEqual(attr->name, ns->prefix)) {
4484 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
4485 VERROR(ctxt->userData,
4486 "Element %s namespace name for %s doesn't match the DTD\n",
4487 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00004488 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004489 }
4490 goto found;
4491 }
4492 ns = ns->next;
4493 }
4494 }
Owen Taylor3473f882001-02-23 17:55:21 +00004495 }
4496found:
4497 attr = attr->nexth;
4498 }
4499 return(ret);
4500}
4501
4502/**
4503 * xmlValidateRoot:
4504 * @ctxt: the validation context
4505 * @doc: a document instance
4506 *
4507 * Try to validate a the root element
4508 * basically it does the following check as described by the
4509 * XML-1.0 recommendation:
4510 * - [ VC: Root Element Type ]
4511 * it doesn't try to recurse or apply other check to the element
4512 *
4513 * returns 1 if valid or 0 otherwise
4514 */
4515
4516int
4517xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4518 xmlNodePtr root;
4519 if (doc == NULL) return(0);
4520
4521 root = xmlDocGetRootElement(doc);
4522 if ((root == NULL) || (root->name == NULL)) {
4523 VERROR(ctxt->userData, "Not valid: no root element\n");
4524 return(0);
4525 }
4526
4527 /*
4528 * When doing post validation against a separate DTD, those may
4529 * no internal subset has been generated
4530 */
4531 if ((doc->intSubset != NULL) &&
4532 (doc->intSubset->name != NULL)) {
4533 /*
4534 * Check first the document root against the NQName
4535 */
4536 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4537 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4538 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004539 snprintf((char *) qname, sizeof(qname), "%s:%s",
4540 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004541 qname[sizeof(qname) - 1] = 0;
4542 if (xmlStrEqual(doc->intSubset->name, qname))
4543 goto name_ok;
4544 }
4545 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4546 (xmlStrEqual(root->name, BAD_CAST "html")))
4547 goto name_ok;
4548 VERROR(ctxt->userData,
4549 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4550 root->name, doc->intSubset->name);
4551 return(0);
4552
4553 }
4554 }
4555name_ok:
4556 return(1);
4557}
4558
4559
4560/**
4561 * xmlValidateElement:
4562 * @ctxt: the validation context
4563 * @doc: a document instance
4564 * @elem: an element instance
4565 *
4566 * Try to validate the subtree under an element
4567 *
4568 * returns 1 if valid or 0 otherwise
4569 */
4570
4571int
4572xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4573 xmlNodePtr child;
4574 xmlAttrPtr attr;
4575 xmlChar *value;
4576 int ret = 1;
4577
4578 if (elem == NULL) return(0);
4579
4580 /*
4581 * XInclude elements were added after parsing in the infoset,
4582 * they don't really mean anything validation wise.
4583 */
4584 if ((elem->type == XML_XINCLUDE_START) ||
4585 (elem->type == XML_XINCLUDE_END))
4586 return(1);
4587
4588 CHECK_DTD;
4589
Daniel Veillard10ea86c2001-06-20 13:55:33 +00004590 /*
4591 * Entities references have to be handled separately
4592 */
4593 if (elem->type == XML_ENTITY_REF_NODE) {
4594 return(1);
4595 }
4596
Owen Taylor3473f882001-02-23 17:55:21 +00004597 ret &= xmlValidateOneElement(ctxt, doc, elem);
4598 attr = elem->properties;
4599 while(attr != NULL) {
4600 value = xmlNodeListGetString(doc, attr->children, 0);
4601 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4602 if (value != NULL)
4603 xmlFree(value);
4604 attr= attr->next;
4605 }
4606 child = elem->children;
4607 while (child != NULL) {
4608 ret &= xmlValidateElement(ctxt, doc, child);
4609 child = child->next;
4610 }
4611
4612 return(ret);
4613}
4614
Daniel Veillard8730c562001-02-26 10:49:57 +00004615/**
4616 * xmlValidateRef:
4617 * @ref: A reference to be validated
4618 * @ctxt: Validation context
4619 * @name: Name of ID we are searching for
4620 *
4621 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004622static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004623xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004624 const xmlChar *name) {
4625 xmlAttrPtr id;
4626 xmlAttrPtr attr;
4627
4628 if (ref == NULL)
4629 return;
4630 attr = ref->attr;
4631 if (attr == NULL)
4632 return;
4633 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4634 id = xmlGetID(ctxt->doc, name);
4635 if (id == NULL) {
4636 VERROR(ctxt->userData,
4637 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4638 attr->name, name);
4639 ctxt->valid = 0;
4640 }
4641 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4642 xmlChar *dup, *str = NULL, *cur, save;
4643
4644 dup = xmlStrdup(name);
4645 if (dup == NULL) {
4646 ctxt->valid = 0;
4647 return;
4648 }
4649 cur = dup;
4650 while (*cur != 0) {
4651 str = cur;
4652 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4653 save = *cur;
4654 *cur = 0;
4655 id = xmlGetID(ctxt->doc, str);
4656 if (id == NULL) {
4657 VERROR(ctxt->userData,
4658 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4659 attr->name, str);
4660 ctxt->valid = 0;
4661 }
4662 if (save == 0)
4663 break;
4664 *cur = save;
4665 while (IS_BLANK(*cur)) cur++;
4666 }
4667 xmlFree(dup);
4668 }
4669}
4670
4671/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004672 * xmlWalkValidateList:
4673 * @data: Contents of current link
4674 * @user: Value supplied by the user
4675 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004676 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00004677 */
4678static int
4679xmlWalkValidateList(const void *data, const void *user)
4680{
4681 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4682 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4683 return 1;
4684}
4685
4686/**
4687 * xmlValidateCheckRefCallback:
4688 * @ref_list: List of references
4689 * @ctxt: Validation context
4690 * @name: Name of ID we are searching for
4691 *
4692 */
4693static void
4694xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4695 const xmlChar *name) {
4696 xmlValidateMemo memo;
4697
4698 if (ref_list == NULL)
4699 return;
4700 memo.ctxt = ctxt;
4701 memo.name = name;
4702
4703 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4704
4705}
4706
4707/**
Owen Taylor3473f882001-02-23 17:55:21 +00004708 * xmlValidateDocumentFinal:
4709 * @ctxt: the validation context
4710 * @doc: a document instance
4711 *
4712 * Does the final step for the document validation once all the
4713 * incremental validation steps have been completed
4714 *
4715 * basically it does the following checks described by the XML Rec
4716 *
4717 *
4718 * returns 1 if valid or 0 otherwise
4719 */
4720
4721int
4722xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4723 xmlRefTablePtr table;
4724
4725 if (doc == NULL) {
4726 xmlGenericError(xmlGenericErrorContext,
4727 "xmlValidateDocumentFinal: doc == NULL\n");
4728 return(0);
4729 }
4730
4731 /*
4732 * Check all the NOTATION/NOTATIONS attributes
4733 */
4734 /*
4735 * Check all the ENTITY/ENTITIES attributes definition for validity
4736 */
4737 /*
4738 * Check all the IDREF/IDREFS attributes definition for validity
4739 */
4740 table = (xmlRefTablePtr) doc->refs;
4741 ctxt->doc = doc;
4742 ctxt->valid = 1;
4743 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4744 return(ctxt->valid);
4745}
4746
4747/**
4748 * xmlValidateDtd:
4749 * @ctxt: the validation context
4750 * @doc: a document instance
4751 * @dtd: a dtd instance
4752 *
4753 * Try to validate the document against the dtd instance
4754 *
4755 * basically it does check all the definitions in the DtD.
4756 *
4757 * returns 1 if valid or 0 otherwise
4758 */
4759
4760int
4761xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4762 int ret;
4763 xmlDtdPtr oldExt;
4764 xmlNodePtr root;
4765
4766 if (dtd == NULL) return(0);
4767 if (doc == NULL) return(0);
4768 oldExt = doc->extSubset;
4769 doc->extSubset = dtd;
4770 ret = xmlValidateRoot(ctxt, doc);
4771 if (ret == 0) {
4772 doc->extSubset = oldExt;
4773 return(ret);
4774 }
4775 if (doc->ids != NULL) {
4776 xmlFreeIDTable(doc->ids);
4777 doc->ids = NULL;
4778 }
4779 if (doc->refs != NULL) {
4780 xmlFreeRefTable(doc->refs);
4781 doc->refs = NULL;
4782 }
4783 root = xmlDocGetRootElement(doc);
4784 ret = xmlValidateElement(ctxt, doc, root);
4785 ret &= xmlValidateDocumentFinal(ctxt, doc);
4786 doc->extSubset = oldExt;
4787 return(ret);
4788}
4789
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004790static void
Owen Taylor3473f882001-02-23 17:55:21 +00004791xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004792 const xmlChar *name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004793 if (cur == NULL)
4794 return;
4795 switch (cur->atype) {
4796 case XML_ATTRIBUTE_CDATA:
4797 case XML_ATTRIBUTE_ID:
4798 case XML_ATTRIBUTE_IDREF :
4799 case XML_ATTRIBUTE_IDREFS:
4800 case XML_ATTRIBUTE_NMTOKEN:
4801 case XML_ATTRIBUTE_NMTOKENS:
4802 case XML_ATTRIBUTE_ENUMERATION:
4803 break;
4804 case XML_ATTRIBUTE_ENTITY:
4805 case XML_ATTRIBUTE_ENTITIES:
4806 case XML_ATTRIBUTE_NOTATION:
4807 if (cur->defaultValue != NULL) {
4808 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4809 cur->name, cur->atype, cur->defaultValue);
4810 }
4811 if (cur->tree != NULL) {
4812 xmlEnumerationPtr tree = cur->tree;
4813 while (tree != NULL) {
4814 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4815 cur->name, cur->atype, tree->name);
4816 tree = tree->next;
4817 }
4818 }
4819 }
4820}
4821
4822/**
4823 * xmlValidateDtdFinal:
4824 * @ctxt: the validation context
4825 * @doc: a document instance
4826 *
4827 * Does the final step for the dtds validation once all the
4828 * subsets have been parsed
4829 *
4830 * basically it does the following checks described by the XML Rec
4831 * - check that ENTITY and ENTITIES type attributes default or
4832 * possible values matches one of the defined entities.
4833 * - check that NOTATION type attributes default or
4834 * possible values matches one of the defined notations.
4835 *
4836 * returns 1 if valid or 0 otherwise
4837 */
4838
4839int
4840xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4841 int ret = 1;
4842 xmlDtdPtr dtd;
4843 xmlAttributeTablePtr table;
4844
4845 if (doc == NULL) return(0);
4846 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4847 return(0);
4848 ctxt->doc = doc;
4849 ctxt->valid = ret;
4850 dtd = doc->intSubset;
4851 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4852 table = (xmlAttributeTablePtr) dtd->attributes;
4853 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4854 }
4855 dtd = doc->extSubset;
4856 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4857 table = (xmlAttributeTablePtr) dtd->attributes;
4858 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4859 }
4860 return(ctxt->valid);
4861}
4862
4863/**
4864 * xmlValidateDocument:
4865 * @ctxt: the validation context
4866 * @doc: a document instance
4867 *
4868 * Try to validate the document instance
4869 *
4870 * basically it does the all the checks described by the XML Rec
4871 * i.e. validates the internal and external subset (if present)
4872 * and validate the document tree.
4873 *
4874 * returns 1 if valid or 0 otherwise
4875 */
4876
4877int
4878xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4879 int ret;
4880 xmlNodePtr root;
4881
4882 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4883 return(0);
4884 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4885 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4886 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4887 doc->intSubset->SystemID);
4888 if (doc->extSubset == NULL) {
4889 if (doc->intSubset->SystemID != NULL) {
4890 VERROR(ctxt->userData,
4891 "Could not load the external subset \"%s\"\n",
4892 doc->intSubset->SystemID);
4893 } else {
4894 VERROR(ctxt->userData,
4895 "Could not load the external subset \"%s\"\n",
4896 doc->intSubset->ExternalID);
4897 }
4898 return(0);
4899 }
4900 }
4901
4902 if (doc->ids != NULL) {
4903 xmlFreeIDTable(doc->ids);
4904 doc->ids = NULL;
4905 }
4906 if (doc->refs != NULL) {
4907 xmlFreeRefTable(doc->refs);
4908 doc->refs = NULL;
4909 }
4910 ret = xmlValidateDtdFinal(ctxt, doc);
4911 if (!xmlValidateRoot(ctxt, doc)) return(0);
4912
4913 root = xmlDocGetRootElement(doc);
4914 ret &= xmlValidateElement(ctxt, doc, root);
4915 ret &= xmlValidateDocumentFinal(ctxt, doc);
4916 return(ret);
4917}
4918
4919
4920/************************************************************************
4921 * *
4922 * Routines for dynamic validation editing *
4923 * *
4924 ************************************************************************/
4925
4926/**
4927 * xmlValidGetPotentialChildren:
4928 * @ctree: an element content tree
4929 * @list: an array to store the list of child names
4930 * @len: a pointer to the number of element in the list
4931 * @max: the size of the array
4932 *
4933 * Build/extend a list of potential children allowed by the content tree
4934 *
4935 * returns the number of element in the list, or -1 in case of error.
4936 */
4937
4938int
4939xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4940 int *len, int max) {
4941 int i;
4942
4943 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4944 return(-1);
4945 if (*len >= max) return(*len);
4946
4947 switch (ctree->type) {
4948 case XML_ELEMENT_CONTENT_PCDATA:
4949 for (i = 0; i < *len;i++)
4950 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4951 list[(*len)++] = BAD_CAST "#PCDATA";
4952 break;
4953 case XML_ELEMENT_CONTENT_ELEMENT:
4954 for (i = 0; i < *len;i++)
4955 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4956 list[(*len)++] = ctree->name;
4957 break;
4958 case XML_ELEMENT_CONTENT_SEQ:
4959 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4960 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4961 break;
4962 case XML_ELEMENT_CONTENT_OR:
4963 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4964 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4965 break;
4966 }
4967
4968 return(*len);
4969}
4970
4971/**
4972 * xmlValidGetValidElements:
4973 * @prev: an element to insert after
4974 * @next: an element to insert next
4975 * @list: an array to store the list of child names
4976 * @max: the size of the array
4977 *
4978 * This function returns the list of authorized children to insert
4979 * within an existing tree while respecting the validity constraints
4980 * forced by the Dtd. The insertion point is defined using @prev and
4981 * @next in the following ways:
4982 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4983 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4984 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4985 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4986 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4987 *
4988 * pointers to the element names are inserted at the beginning of the array
4989 * and do not need to be freed.
4990 *
4991 * returns the number of element in the list, or -1 in case of error. If
4992 * the function returns the value @max the caller is invited to grow the
4993 * receiving array and retry.
4994 */
4995
4996int
4997xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4998 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004999 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005000 int nb_valid_elements = 0;
5001 const xmlChar *elements[256];
5002 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005003 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005004
5005 xmlNode *ref_node;
5006 xmlNode *parent;
5007 xmlNode *test_node;
5008
5009 xmlNode *prev_next;
5010 xmlNode *next_prev;
5011 xmlNode *parent_childs;
5012 xmlNode *parent_last;
5013
5014 xmlElement *element_desc;
5015
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005016 vctxt.userData = NULL;
5017 vctxt.error = NULL;
5018 vctxt.warning = NULL;
5019
Owen Taylor3473f882001-02-23 17:55:21 +00005020 if (prev == NULL && next == NULL)
5021 return(-1);
5022
5023 if (list == NULL) return(-1);
5024 if (max <= 0) return(-1);
5025
5026 nb_valid_elements = 0;
5027 ref_node = prev ? prev : next;
5028 parent = ref_node->parent;
5029
5030 /*
5031 * Retrieves the parent element declaration
5032 */
5033 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5034 parent->name);
5035 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5036 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5037 parent->name);
5038 if (element_desc == NULL) return(-1);
5039
5040 /*
5041 * Do a backup of the current tree structure
5042 */
5043 prev_next = prev ? prev->next : NULL;
5044 next_prev = next ? next->prev : NULL;
5045 parent_childs = parent->children;
5046 parent_last = parent->last;
5047
5048 /*
5049 * Creates a dummy node and insert it into the tree
5050 */
5051 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5052 test_node->doc = ref_node->doc;
5053 test_node->parent = parent;
5054 test_node->prev = prev;
5055 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005056 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005057
5058 if (prev) prev->next = test_node;
5059 else parent->children = test_node;
5060
5061 if (next) next->prev = test_node;
5062 else parent->last = test_node;
5063
5064 /*
5065 * Insert each potential child node and check if the parent is
5066 * still valid
5067 */
5068 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5069 elements, &nb_elements, 256);
5070
5071 for (i = 0;i < nb_elements;i++) {
5072 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005073 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005074 int j;
5075
5076 for (j = 0; j < nb_valid_elements;j++)
5077 if (xmlStrEqual(elements[i], list[j])) break;
5078 list[nb_valid_elements++] = elements[i];
5079 if (nb_valid_elements >= max) break;
5080 }
5081 }
5082
5083 /*
5084 * Restore the tree structure
5085 */
5086 if (prev) prev->next = prev_next;
5087 if (next) next->prev = next_prev;
5088 parent->children = parent_childs;
5089 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005090
5091 /*
5092 * Free up the dummy node
5093 */
5094 test_node->name = name;
5095 xmlFreeNode(test_node);
5096
Owen Taylor3473f882001-02-23 17:55:21 +00005097 return(nb_valid_elements);
5098}