blob: 323575518293d178edaece9c5c2bfb6c69fd994f [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");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001631 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001632 }
1633
1634 /*
1635 * Create the Notation table if needed.
1636 */
1637 table = (xmlNotationTablePtr) dtd->notations;
1638 if (table == NULL)
1639 dtd->notations = table = xmlCreateNotationTable();
1640 if (table == NULL) {
1641 xmlGenericError(xmlGenericErrorContext,
1642 "xmlAddNotationDecl: Table creation failed!\n");
1643 return(NULL);
1644 }
1645
1646 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1647 if (ret == NULL) {
1648 xmlGenericError(xmlGenericErrorContext,
1649 "xmlAddNotationDecl: out of memory\n");
1650 return(NULL);
1651 }
1652 memset(ret, 0, sizeof(xmlNotation));
1653
1654 /*
1655 * fill the structure.
1656 */
1657 ret->name = xmlStrdup(name);
1658 if (SystemID != NULL)
1659 ret->SystemID = xmlStrdup(SystemID);
1660 if (PublicID != NULL)
1661 ret->PublicID = xmlStrdup(PublicID);
1662
1663 /*
1664 * Validity Check:
1665 * Check the DTD for previous declarations of the ATTLIST
1666 */
1667 if (xmlHashAddEntry(table, name, ret)) {
1668 xmlGenericError(xmlGenericErrorContext,
1669 "xmlAddNotationDecl: %s already defined\n", name);
1670 xmlFreeNotation(ret);
1671 return(NULL);
1672 }
1673 return(ret);
1674}
1675
1676/**
1677 * xmlFreeNotationTable:
1678 * @table: An notation table
1679 *
1680 * Deallocate the memory used by an entities hash table.
1681 */
1682void
1683xmlFreeNotationTable(xmlNotationTablePtr table) {
1684 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1685}
1686
1687/**
1688 * xmlCopyNotation:
1689 * @nota: A notation
1690 *
1691 * Build a copy of a notation.
1692 *
1693 * Returns the new xmlNotationPtr or NULL in case of error.
1694 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001695static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001696xmlCopyNotation(xmlNotationPtr nota) {
1697 xmlNotationPtr cur;
1698
1699 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1700 if (cur == NULL) {
1701 xmlGenericError(xmlGenericErrorContext,
1702 "xmlCopyNotation: out of memory !\n");
1703 return(NULL);
1704 }
1705 if (nota->name != NULL)
1706 cur->name = xmlStrdup(nota->name);
1707 else
1708 cur->name = NULL;
1709 if (nota->PublicID != NULL)
1710 cur->PublicID = xmlStrdup(nota->PublicID);
1711 else
1712 cur->PublicID = NULL;
1713 if (nota->SystemID != NULL)
1714 cur->SystemID = xmlStrdup(nota->SystemID);
1715 else
1716 cur->SystemID = NULL;
1717 return(cur);
1718}
1719
1720/**
1721 * xmlCopyNotationTable:
1722 * @table: A notation table
1723 *
1724 * Build a copy of a notation table.
1725 *
1726 * Returns the new xmlNotationTablePtr or NULL in case of error.
1727 */
1728xmlNotationTablePtr
1729xmlCopyNotationTable(xmlNotationTablePtr table) {
1730 return((xmlNotationTablePtr) xmlHashCopy(table,
1731 (xmlHashCopier) xmlCopyNotation));
1732}
1733
1734/**
1735 * xmlDumpNotationDecl:
1736 * @buf: the XML buffer output
1737 * @nota: A notation declaration
1738 *
1739 * This will dump the content the notation declaration as an XML DTD definition
1740 */
1741void
1742xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1743 xmlBufferWriteChar(buf, "<!NOTATION ");
1744 xmlBufferWriteCHAR(buf, nota->name);
1745 if (nota->PublicID != NULL) {
1746 xmlBufferWriteChar(buf, " PUBLIC ");
1747 xmlBufferWriteQuotedString(buf, nota->PublicID);
1748 if (nota->SystemID != NULL) {
1749 xmlBufferWriteChar(buf, " ");
1750 xmlBufferWriteCHAR(buf, nota->SystemID);
1751 }
1752 } else {
1753 xmlBufferWriteChar(buf, " SYSTEM ");
1754 xmlBufferWriteCHAR(buf, nota->SystemID);
1755 }
1756 xmlBufferWriteChar(buf, " >\n");
1757}
1758
1759/**
1760 * xmlDumpNotationTable:
1761 * @buf: the XML buffer output
1762 * @table: A notation table
1763 *
1764 * This will dump the content of the notation table as an XML DTD definition
1765 */
1766void
1767xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1768 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1769}
1770
1771/************************************************************************
1772 * *
1773 * IDs *
1774 * *
1775 ************************************************************************/
1776/**
1777 * xmlCreateIDTable:
1778 *
1779 * create and initialize an empty id hash table.
1780 *
1781 * Returns the xmlIDTablePtr just created or NULL in case
1782 * of error.
1783 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001784static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001785xmlCreateIDTable(void) {
1786 return(xmlHashCreate(0));
1787}
1788
1789/**
1790 * xmlFreeID:
1791 * @not: A id
1792 *
1793 * Deallocate the memory used by an id definition
1794 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001795static void
Owen Taylor3473f882001-02-23 17:55:21 +00001796xmlFreeID(xmlIDPtr id) {
1797 if (id == NULL) return;
1798 if (id->value != NULL)
1799 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001800 xmlFree(id);
1801}
1802
1803/**
1804 * xmlAddID:
1805 * @ctxt: the validation context
1806 * @doc: pointer to the document
1807 * @value: the value name
1808 * @attr: the attribute holding the ID
1809 *
1810 * Register a new id declaration
1811 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001812 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001813 */
1814xmlIDPtr
1815xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1816 xmlAttrPtr attr) {
1817 xmlIDPtr ret;
1818 xmlIDTablePtr table;
1819
1820 if (doc == NULL) {
1821 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001822 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001823 return(NULL);
1824 }
1825 if (value == NULL) {
1826 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001827 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001828 return(NULL);
1829 }
1830 if (attr == NULL) {
1831 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001832 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001833 return(NULL);
1834 }
1835
1836 /*
1837 * Create the ID table if needed.
1838 */
1839 table = (xmlIDTablePtr) doc->ids;
1840 if (table == NULL)
1841 doc->ids = table = xmlCreateIDTable();
1842 if (table == NULL) {
1843 xmlGenericError(xmlGenericErrorContext,
1844 "xmlAddID: Table creation failed!\n");
1845 return(NULL);
1846 }
1847
1848 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1849 if (ret == NULL) {
1850 xmlGenericError(xmlGenericErrorContext,
1851 "xmlAddID: out of memory\n");
1852 return(NULL);
1853 }
1854
1855 /*
1856 * fill the structure.
1857 */
1858 ret->value = xmlStrdup(value);
1859 ret->attr = attr;
1860
1861 if (xmlHashAddEntry(table, value, ret) < 0) {
1862 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001863 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001864 */
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00001865 if (ctxt != NULL)
1866 VERROR(ctxt->userData, "ID %s already defined\n", value);
Owen Taylor3473f882001-02-23 17:55:21 +00001867 xmlFreeID(ret);
1868 return(NULL);
1869 }
1870 return(ret);
1871}
1872
1873/**
1874 * xmlFreeIDTable:
1875 * @table: An id table
1876 *
1877 * Deallocate the memory used by an ID hash table.
1878 */
1879void
1880xmlFreeIDTable(xmlIDTablePtr table) {
1881 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1882}
1883
1884/**
1885 * xmlIsID:
1886 * @doc: the document
1887 * @elem: the element carrying the attribute
1888 * @attr: the attribute
1889 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001890 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00001891 * then this is simple, otherwise we use an heuristic: name ID (upper
1892 * or lowercase).
1893 *
1894 * Returns 0 or 1 depending on the lookup result
1895 */
1896int
1897xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1898 if (doc == NULL) return(0);
1899 if (attr == NULL) return(0);
1900 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1901 return(0);
1902 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1903 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1904 (xmlStrEqual(BAD_CAST "name", attr->name)))
1905 return(1);
1906 return(0);
1907 } else {
1908 xmlAttributePtr attrDecl;
1909
1910 if (elem == NULL) return(0);
1911 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1912 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1913 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1914 attr->name);
1915
1916 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1917 return(1);
1918 }
1919 return(0);
1920}
1921
1922/**
1923 * xmlRemoveID
1924 * @doc: the document
1925 * @attr: the attribute
1926 *
1927 * Remove the given attribute from the ID table maintained internally.
1928 *
1929 * Returns -1 if the lookup failed and 0 otherwise
1930 */
1931int
1932xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1933 xmlAttrPtr cur;
1934 xmlIDTablePtr table;
1935 xmlChar *ID;
1936
1937 if (doc == NULL) return(-1);
1938 if (attr == NULL) return(-1);
1939 table = (xmlIDTablePtr) doc->ids;
1940 if (table == NULL)
1941 return(-1);
1942
1943 if (attr == NULL)
1944 return(-1);
1945 ID = xmlNodeListGetString(doc, attr->children, 1);
1946 if (ID == NULL)
1947 return(-1);
1948 cur = xmlHashLookup(table, ID);
1949 if (cur != attr) {
1950 xmlFree(ID);
1951 return(-1);
1952 }
1953 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1954 xmlFree(ID);
1955 return(0);
1956}
1957
1958/**
1959 * xmlGetID:
1960 * @doc: pointer to the document
1961 * @ID: the ID value
1962 *
1963 * Search the attribute declaring the given ID
1964 *
1965 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1966 */
1967xmlAttrPtr
1968xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
1969 xmlIDTablePtr table;
1970 xmlIDPtr id;
1971
1972 if (doc == NULL) {
1973 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
1974 return(NULL);
1975 }
1976
1977 if (ID == NULL) {
1978 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
1979 return(NULL);
1980 }
1981
1982 table = (xmlIDTablePtr) doc->ids;
1983 if (table == NULL)
1984 return(NULL);
1985
1986 id = xmlHashLookup(table, ID);
1987 if (id == NULL)
1988 return(NULL);
1989 return(id->attr);
1990}
1991
1992/************************************************************************
1993 * *
1994 * Refs *
1995 * *
1996 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00001997typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00001998{
1999 xmlListPtr l;
2000 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002001} xmlRemoveMemo;
2002
2003typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2004
2005typedef struct xmlValidateMemo_t
2006{
2007 xmlValidCtxtPtr ctxt;
2008 const xmlChar *name;
2009} xmlValidateMemo;
2010
2011typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002012
2013/**
2014 * xmlCreateRefTable:
2015 *
2016 * create and initialize an empty ref hash table.
2017 *
2018 * Returns the xmlRefTablePtr just created or NULL in case
2019 * of error.
2020 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002021static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002022xmlCreateRefTable(void) {
2023 return(xmlHashCreate(0));
2024}
2025
2026/**
2027 * xmlFreeRef:
2028 * @lk: A list link
2029 *
2030 * Deallocate the memory used by a ref definition
2031 */
2032static void
2033xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002034 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2035 if (ref == NULL) return;
2036 if (ref->value != NULL)
2037 xmlFree((xmlChar *)ref->value);
2038 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002039}
2040
2041/**
2042 * xmlFreeRefList:
2043 * @list_ref: A list of references.
2044 *
2045 * Deallocate the memory used by a list of references
2046 */
2047static void
2048xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002049 if (list_ref == NULL) return;
2050 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002051}
2052
2053/**
2054 * xmlWalkRemoveRef:
2055 * @data: Contents of current link
2056 * @user: Value supplied by the user
2057 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002058 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002059 */
2060static int
2061xmlWalkRemoveRef(const void *data, const void *user)
2062{
Daniel Veillard37721922001-05-04 15:21:12 +00002063 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2064 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2065 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002066
Daniel Veillard37721922001-05-04 15:21:12 +00002067 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2068 xmlListRemoveFirst(ref_list, (void *)data);
2069 return 0;
2070 }
2071 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002072}
2073
2074/**
2075 * xmlAddRef:
2076 * @ctxt: the validation context
2077 * @doc: pointer to the document
2078 * @value: the value name
2079 * @attr: the attribute holding the Ref
2080 *
2081 * Register a new ref declaration
2082 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002083 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002084 */
2085xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002086xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002087 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002088 xmlRefPtr ret;
2089 xmlRefTablePtr table;
2090 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002091
Daniel Veillard37721922001-05-04 15:21:12 +00002092 if (doc == NULL) {
2093 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002094 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002095 return(NULL);
2096 }
2097 if (value == NULL) {
2098 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002099 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002100 return(NULL);
2101 }
2102 if (attr == NULL) {
2103 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002104 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002105 return(NULL);
2106 }
Owen Taylor3473f882001-02-23 17:55:21 +00002107
Daniel Veillard37721922001-05-04 15:21:12 +00002108 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002109 * Create the Ref table if needed.
2110 */
Daniel Veillard37721922001-05-04 15:21:12 +00002111 table = (xmlRefTablePtr) doc->refs;
2112 if (table == NULL)
2113 doc->refs = table = xmlCreateRefTable();
2114 if (table == NULL) {
2115 xmlGenericError(xmlGenericErrorContext,
2116 "xmlAddRef: Table creation failed!\n");
2117 return(NULL);
2118 }
Owen Taylor3473f882001-02-23 17:55:21 +00002119
Daniel Veillard37721922001-05-04 15:21:12 +00002120 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2121 if (ret == NULL) {
2122 xmlGenericError(xmlGenericErrorContext,
2123 "xmlAddRef: out of memory\n");
2124 return(NULL);
2125 }
Owen Taylor3473f882001-02-23 17:55:21 +00002126
Daniel Veillard37721922001-05-04 15:21:12 +00002127 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002128 * fill the structure.
2129 */
Daniel Veillard37721922001-05-04 15:21:12 +00002130 ret->value = xmlStrdup(value);
2131 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002132
Daniel Veillard37721922001-05-04 15:21:12 +00002133 /* To add a reference :-
2134 * References are maintained as a list of references,
2135 * Lookup the entry, if no entry create new nodelist
2136 * Add the owning node to the NodeList
2137 * Return the ref
2138 */
Owen Taylor3473f882001-02-23 17:55:21 +00002139
Daniel Veillard37721922001-05-04 15:21:12 +00002140 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2141 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2142 xmlGenericError(xmlGenericErrorContext,
2143 "xmlAddRef: Reference list creation failed!\n");
2144 return(NULL);
2145 }
2146 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2147 xmlListDelete(ref_list);
2148 xmlGenericError(xmlGenericErrorContext,
2149 "xmlAddRef: Reference list insertion failed!\n");
2150 return(NULL);
2151 }
2152 }
2153 xmlListInsert(ref_list, ret);
2154 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002155}
2156
2157/**
2158 * xmlFreeRefTable:
2159 * @table: An ref table
2160 *
2161 * Deallocate the memory used by an Ref hash table.
2162 */
2163void
2164xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002165 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002166}
2167
2168/**
2169 * xmlIsRef:
2170 * @doc: the document
2171 * @elem: the element carrying the attribute
2172 * @attr: the attribute
2173 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002174 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002175 * then this is simple, otherwise we use an heuristic: name Ref (upper
2176 * or lowercase).
2177 *
2178 * Returns 0 or 1 depending on the lookup result
2179 */
2180int
2181xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002182 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2183 return(0);
2184 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2185 /* TODO @@@ */
2186 return(0);
2187 } else {
2188 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002189
Daniel Veillard37721922001-05-04 15:21:12 +00002190 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2191 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2192 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2193 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002194
Daniel Veillard37721922001-05-04 15:21:12 +00002195 if ((attrDecl != NULL) &&
2196 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2197 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2198 return(1);
2199 }
2200 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002201}
2202
2203/**
2204 * xmlRemoveRef
2205 * @doc: the document
2206 * @attr: the attribute
2207 *
2208 * Remove the given attribute from the Ref table maintained internally.
2209 *
2210 * Returns -1 if the lookup failed and 0 otherwise
2211 */
2212int
2213xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002214 xmlListPtr ref_list;
2215 xmlRefTablePtr table;
2216 xmlChar *ID;
2217 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002218
Daniel Veillard37721922001-05-04 15:21:12 +00002219 if (doc == NULL) return(-1);
2220 if (attr == NULL) return(-1);
2221 table = (xmlRefTablePtr) doc->refs;
2222 if (table == NULL)
2223 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002224
Daniel Veillard37721922001-05-04 15:21:12 +00002225 if (attr == NULL)
2226 return(-1);
2227 ID = xmlNodeListGetString(doc, attr->children, 1);
2228 if (ID == NULL)
2229 return(-1);
2230 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002231
Daniel Veillard37721922001-05-04 15:21:12 +00002232 if(ref_list == NULL) {
2233 xmlFree(ID);
2234 return (-1);
2235 }
2236 /* At this point, ref_list refers to a list of references which
2237 * have the same key as the supplied attr. Our list of references
2238 * is ordered by reference address and we don't have that information
2239 * here to use when removing. We'll have to walk the list and
2240 * check for a matching attribute, when we find one stop the walk
2241 * and remove the entry.
2242 * The list is ordered by reference, so that means we don't have the
2243 * key. Passing the list and the reference to the walker means we
2244 * will have enough data to be able to remove the entry.
2245 */
2246 target.l = ref_list;
2247 target.ap = attr;
2248
2249 /* Remove the supplied attr from our list */
2250 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002251
Daniel Veillard37721922001-05-04 15:21:12 +00002252 /*If the list is empty then remove the list entry in the hash */
2253 if (xmlListEmpty(ref_list))
2254 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2255 xmlFreeRefList);
2256 xmlFree(ID);
2257 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002258}
2259
2260/**
2261 * xmlGetRefs:
2262 * @doc: pointer to the document
2263 * @ID: the ID value
2264 *
2265 * Find the set of references for the supplied ID.
2266 *
2267 * Returns NULL if not found, otherwise node set for the ID.
2268 */
2269xmlListPtr
2270xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002271 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002272
Daniel Veillard37721922001-05-04 15:21:12 +00002273 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002274 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002275 return(NULL);
2276 }
Owen Taylor3473f882001-02-23 17:55:21 +00002277
Daniel Veillard37721922001-05-04 15:21:12 +00002278 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002279 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002280 return(NULL);
2281 }
Owen Taylor3473f882001-02-23 17:55:21 +00002282
Daniel Veillard37721922001-05-04 15:21:12 +00002283 table = (xmlRefTablePtr) doc->refs;
2284 if (table == NULL)
2285 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002286
Daniel Veillard37721922001-05-04 15:21:12 +00002287 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002288}
2289
2290/************************************************************************
2291 * *
2292 * Routines for validity checking *
2293 * *
2294 ************************************************************************/
2295
2296/**
2297 * xmlGetDtdElementDesc:
2298 * @dtd: a pointer to the DtD to search
2299 * @name: the element name
2300 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002301 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002302 *
2303 * returns the xmlElementPtr if found or NULL
2304 */
2305
2306xmlElementPtr
2307xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2308 xmlElementTablePtr table;
2309 xmlElementPtr cur;
2310 xmlChar *uqname = NULL, *prefix = NULL;
2311
2312 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002313 if (dtd->elements == NULL)
2314 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002315 table = (xmlElementTablePtr) dtd->elements;
2316
2317 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002318 if (uqname != NULL)
2319 name = uqname;
2320 cur = xmlHashLookup2(table, name, prefix);
2321 if (prefix != NULL) xmlFree(prefix);
2322 if (uqname != NULL) xmlFree(uqname);
2323 return(cur);
2324}
2325/**
2326 * xmlGetDtdElementDesc2:
2327 * @dtd: a pointer to the DtD to search
2328 * @name: the element name
2329 * @create: create an empty description if not found
2330 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002331 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002332 *
2333 * returns the xmlElementPtr if found or NULL
2334 */
2335
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002336static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002337xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2338 xmlElementTablePtr table;
2339 xmlElementPtr cur;
2340 xmlChar *uqname = NULL, *prefix = NULL;
2341
2342 if (dtd == NULL) return(NULL);
2343 if (dtd->elements == NULL) {
2344 if (!create)
2345 return(NULL);
2346 /*
2347 * Create the Element table if needed.
2348 */
2349 table = (xmlElementTablePtr) dtd->elements;
2350 if (table == NULL) {
2351 table = xmlCreateElementTable();
2352 dtd->elements = (void *) table;
2353 }
2354 if (table == NULL) {
2355 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002356 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002357 return(NULL);
2358 }
2359 }
2360 table = (xmlElementTablePtr) dtd->elements;
2361
2362 uqname = xmlSplitQName2(name, &prefix);
2363 if (uqname != NULL)
2364 name = uqname;
2365 cur = xmlHashLookup2(table, name, prefix);
2366 if ((cur == NULL) && (create)) {
2367 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2368 if (cur == NULL) {
2369 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002370 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002371 return(NULL);
2372 }
2373 memset(cur, 0, sizeof(xmlElement));
2374 cur->type = XML_ELEMENT_DECL;
2375
2376 /*
2377 * fill the structure.
2378 */
2379 cur->name = xmlStrdup(name);
2380 cur->prefix = xmlStrdup(prefix);
2381 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2382
2383 xmlHashAddEntry2(table, name, prefix, cur);
2384 }
2385 if (prefix != NULL) xmlFree(prefix);
2386 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002387 return(cur);
2388}
2389
2390/**
2391 * xmlGetDtdQElementDesc:
2392 * @dtd: a pointer to the DtD to search
2393 * @name: the element name
2394 * @prefix: the element namespace prefix
2395 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002396 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002397 *
2398 * returns the xmlElementPtr if found or NULL
2399 */
2400
Daniel Veillard48da9102001-08-07 01:10:10 +00002401xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002402xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2403 const xmlChar *prefix) {
2404 xmlElementTablePtr table;
2405
2406 if (dtd == NULL) return(NULL);
2407 if (dtd->elements == NULL) return(NULL);
2408 table = (xmlElementTablePtr) dtd->elements;
2409
2410 return(xmlHashLookup2(table, name, prefix));
2411}
2412
2413/**
2414 * xmlGetDtdAttrDesc:
2415 * @dtd: a pointer to the DtD to search
2416 * @elem: the element name
2417 * @name: the attribute name
2418 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002419 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002420 * this element.
2421 *
2422 * returns the xmlAttributePtr if found or NULL
2423 */
2424
2425xmlAttributePtr
2426xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2427 xmlAttributeTablePtr table;
2428 xmlAttributePtr cur;
2429 xmlChar *uqname = NULL, *prefix = NULL;
2430
2431 if (dtd == NULL) return(NULL);
2432 if (dtd->attributes == NULL) return(NULL);
2433
2434 table = (xmlAttributeTablePtr) dtd->attributes;
2435 if (table == NULL)
2436 return(NULL);
2437
2438 uqname = xmlSplitQName2(name, &prefix);
2439
2440 if (uqname != NULL) {
2441 cur = xmlHashLookup3(table, uqname, prefix, elem);
2442 if (prefix != NULL) xmlFree(prefix);
2443 if (uqname != NULL) xmlFree(uqname);
2444 } else
2445 cur = xmlHashLookup3(table, name, NULL, elem);
2446 return(cur);
2447}
2448
2449/**
2450 * xmlGetDtdQAttrDesc:
2451 * @dtd: a pointer to the DtD to search
2452 * @elem: the element name
2453 * @name: the attribute name
2454 * @prefix: the attribute namespace prefix
2455 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002456 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002457 * this element.
2458 *
2459 * returns the xmlAttributePtr if found or NULL
2460 */
2461
Daniel Veillard48da9102001-08-07 01:10:10 +00002462xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002463xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2464 const xmlChar *prefix) {
2465 xmlAttributeTablePtr table;
2466
2467 if (dtd == NULL) return(NULL);
2468 if (dtd->attributes == NULL) return(NULL);
2469 table = (xmlAttributeTablePtr) dtd->attributes;
2470
2471 return(xmlHashLookup3(table, name, prefix, elem));
2472}
2473
2474/**
2475 * xmlGetDtdNotationDesc:
2476 * @dtd: a pointer to the DtD to search
2477 * @name: the notation name
2478 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002479 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002480 *
2481 * returns the xmlNotationPtr if found or NULL
2482 */
2483
2484xmlNotationPtr
2485xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2486 xmlNotationTablePtr table;
2487
2488 if (dtd == NULL) return(NULL);
2489 if (dtd->notations == NULL) return(NULL);
2490 table = (xmlNotationTablePtr) dtd->notations;
2491
2492 return(xmlHashLookup(table, name));
2493}
2494
2495/**
2496 * xmlValidateNotationUse:
2497 * @ctxt: the validation context
2498 * @doc: the document
2499 * @notationName: the notation name to check
2500 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002501 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002502 * - [ VC: Notation Declared ]
2503 *
2504 * returns 1 if valid or 0 otherwise
2505 */
2506
2507int
2508xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2509 const xmlChar *notationName) {
2510 xmlNotationPtr notaDecl;
2511 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2512
2513 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2514 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2515 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2516
2517 if (notaDecl == NULL) {
2518 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2519 notationName);
2520 return(0);
2521 }
2522 return(1);
2523}
2524
2525/**
2526 * xmlIsMixedElement
2527 * @doc: the document
2528 * @name: the element name
2529 *
2530 * Search in the DtDs whether an element accept Mixed content (or ANY)
2531 * basically if it is supposed to accept text childs
2532 *
2533 * returns 0 if no, 1 if yes, and -1 if no element description is available
2534 */
2535
2536int
2537xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2538 xmlElementPtr elemDecl;
2539
2540 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2541
2542 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2543 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2544 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2545 if (elemDecl == NULL) return(-1);
2546 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002547 case XML_ELEMENT_TYPE_UNDEFINED:
2548 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002549 case XML_ELEMENT_TYPE_ELEMENT:
2550 return(0);
2551 case XML_ELEMENT_TYPE_EMPTY:
2552 /*
2553 * return 1 for EMPTY since we want VC error to pop up
2554 * on <empty> </empty> for example
2555 */
2556 case XML_ELEMENT_TYPE_ANY:
2557 case XML_ELEMENT_TYPE_MIXED:
2558 return(1);
2559 }
2560 return(1);
2561}
2562
2563/**
2564 * xmlValidateNameValue:
2565 * @value: an Name value
2566 *
2567 * Validate that the given value match Name production
2568 *
2569 * returns 1 if valid or 0 otherwise
2570 */
2571
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002572static int
Owen Taylor3473f882001-02-23 17:55:21 +00002573xmlValidateNameValue(const xmlChar *value) {
2574 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002575 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002576
2577 if (value == NULL) return(0);
2578 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002579 val = xmlStringCurrentChar(NULL, cur, &len);
2580 cur += len;
2581 if (!IS_LETTER(val) && (val != '_') &&
2582 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002583 return(0);
2584 }
2585
Daniel Veillardd8224e02002-01-13 15:43:22 +00002586 val = xmlStringCurrentChar(NULL, cur, &len);
2587 cur += len;
2588 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2589 (val == '.') || (val == '-') ||
2590 (val == '_') || (val == ':') ||
2591 (IS_COMBINING(val)) ||
2592 (IS_EXTENDER(val))) {
2593 val = xmlStringCurrentChar(NULL, cur, &len);
2594 cur += len;
2595 }
Owen Taylor3473f882001-02-23 17:55:21 +00002596
Daniel Veillardd8224e02002-01-13 15:43:22 +00002597 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002598
2599 return(1);
2600}
2601
2602/**
2603 * xmlValidateNamesValue:
2604 * @value: an Names value
2605 *
2606 * Validate that the given value match Names production
2607 *
2608 * returns 1 if valid or 0 otherwise
2609 */
2610
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002611static int
Owen Taylor3473f882001-02-23 17:55:21 +00002612xmlValidateNamesValue(const xmlChar *value) {
2613 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002614 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002615
2616 if (value == NULL) return(0);
2617 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002618 val = xmlStringCurrentChar(NULL, cur, &len);
2619 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002620
Daniel Veillardd8224e02002-01-13 15:43:22 +00002621 if (!IS_LETTER(val) && (val != '_') &&
2622 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002623 return(0);
2624 }
2625
Daniel Veillardd8224e02002-01-13 15:43:22 +00002626 val = xmlStringCurrentChar(NULL, cur, &len);
2627 cur += len;
2628 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2629 (val == '.') || (val == '-') ||
2630 (val == '_') || (val == ':') ||
2631 (IS_COMBINING(val)) ||
2632 (IS_EXTENDER(val))) {
2633 val = xmlStringCurrentChar(NULL, cur, &len);
2634 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002635 }
2636
Daniel Veillardd8224e02002-01-13 15:43:22 +00002637 while (IS_BLANK(val)) {
2638 while (IS_BLANK(val)) {
2639 val = xmlStringCurrentChar(NULL, cur, &len);
2640 cur += len;
2641 }
2642
2643 if (!IS_LETTER(val) && (val != '_') &&
2644 (val != ':')) {
2645 return(0);
2646 }
2647 val = xmlStringCurrentChar(NULL, cur, &len);
2648 cur += len;
2649
2650 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2651 (val == '.') || (val == '-') ||
2652 (val == '_') || (val == ':') ||
2653 (IS_COMBINING(val)) ||
2654 (IS_EXTENDER(val))) {
2655 val = xmlStringCurrentChar(NULL, cur, &len);
2656 cur += len;
2657 }
2658 }
2659
2660 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002661
2662 return(1);
2663}
2664
2665/**
2666 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002667 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002668 *
2669 * Validate that the given value match Nmtoken production
2670 *
2671 * [ VC: Name Token ]
2672 *
2673 * returns 1 if valid or 0 otherwise
2674 */
2675
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002676static int
Owen Taylor3473f882001-02-23 17:55:21 +00002677xmlValidateNmtokenValue(const xmlChar *value) {
2678 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002679 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002680
2681 if (value == NULL) return(0);
2682 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002683 val = xmlStringCurrentChar(NULL, cur, &len);
2684 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002685
Daniel Veillardd8224e02002-01-13 15:43:22 +00002686 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2687 (val != '.') && (val != '-') &&
2688 (val != '_') && (val != ':') &&
2689 (!IS_COMBINING(val)) &&
2690 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002691 return(0);
2692
Daniel Veillardd8224e02002-01-13 15:43:22 +00002693 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2694 (val == '.') || (val == '-') ||
2695 (val == '_') || (val == ':') ||
2696 (IS_COMBINING(val)) ||
2697 (IS_EXTENDER(val))) {
2698 val = xmlStringCurrentChar(NULL, cur, &len);
2699 cur += len;
2700 }
Owen Taylor3473f882001-02-23 17:55:21 +00002701
Daniel Veillardd8224e02002-01-13 15:43:22 +00002702 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002703
2704 return(1);
2705}
2706
2707/**
2708 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002709 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00002710 *
2711 * Validate that the given value match Nmtokens production
2712 *
2713 * [ VC: Name Token ]
2714 *
2715 * returns 1 if valid or 0 otherwise
2716 */
2717
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002718static int
Owen Taylor3473f882001-02-23 17:55:21 +00002719xmlValidateNmtokensValue(const xmlChar *value) {
2720 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002721 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002722
2723 if (value == NULL) return(0);
2724 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002725 val = xmlStringCurrentChar(NULL, cur, &len);
2726 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002727
Daniel Veillardd8224e02002-01-13 15:43:22 +00002728 while (IS_BLANK(val)) {
2729 val = xmlStringCurrentChar(NULL, cur, &len);
2730 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002731 }
2732
Daniel Veillardd8224e02002-01-13 15:43:22 +00002733 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2734 (val != '.') && (val != '-') &&
2735 (val != '_') && (val != ':') &&
2736 (!IS_COMBINING(val)) &&
2737 (!IS_EXTENDER(val)))
2738 return(0);
2739
2740 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2741 (val == '.') || (val == '-') ||
2742 (val == '_') || (val == ':') ||
2743 (IS_COMBINING(val)) ||
2744 (IS_EXTENDER(val))) {
2745 val = xmlStringCurrentChar(NULL, cur, &len);
2746 cur += len;
2747 }
2748
2749 while (IS_BLANK(val)) {
2750 while (IS_BLANK(val)) {
2751 val = xmlStringCurrentChar(NULL, cur, &len);
2752 cur += len;
2753 }
2754 if (val == 0) return(1);
2755
2756 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2757 (val != '.') && (val != '-') &&
2758 (val != '_') && (val != ':') &&
2759 (!IS_COMBINING(val)) &&
2760 (!IS_EXTENDER(val)))
2761 return(0);
2762
2763 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2764 (val == '.') || (val == '-') ||
2765 (val == '_') || (val == ':') ||
2766 (IS_COMBINING(val)) ||
2767 (IS_EXTENDER(val))) {
2768 val = xmlStringCurrentChar(NULL, cur, &len);
2769 cur += len;
2770 }
2771 }
2772
2773 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002774
2775 return(1);
2776}
2777
2778/**
2779 * xmlValidateNotationDecl:
2780 * @ctxt: the validation context
2781 * @doc: a document instance
2782 * @nota: a notation definition
2783 *
2784 * Try to validate a single notation definition
2785 * basically it does the following checks as described by the
2786 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002787 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00002788 * But this function get called anyway ...
2789 *
2790 * returns 1 if valid or 0 otherwise
2791 */
2792
2793int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002794xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2795 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002796 int ret = 1;
2797
2798 return(ret);
2799}
2800
2801/**
2802 * xmlValidateAttributeValue:
2803 * @type: an attribute type
2804 * @value: an attribute value
2805 *
2806 * Validate that the given attribute value match the proper production
2807 *
2808 * [ VC: ID ]
2809 * Values of type ID must match the Name production....
2810 *
2811 * [ VC: IDREF ]
2812 * Values of type IDREF must match the Name production, and values
2813 * of type IDREFS must match Names ...
2814 *
2815 * [ VC: Entity Name ]
2816 * Values of type ENTITY must match the Name production, values
2817 * of type ENTITIES must match Names ...
2818 *
2819 * [ VC: Name Token ]
2820 * Values of type NMTOKEN must match the Nmtoken production; values
2821 * of type NMTOKENS must match Nmtokens.
2822 *
2823 * returns 1 if valid or 0 otherwise
2824 */
2825
2826int
2827xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2828 switch (type) {
2829 case XML_ATTRIBUTE_ENTITIES:
2830 case XML_ATTRIBUTE_IDREFS:
2831 return(xmlValidateNamesValue(value));
2832 case XML_ATTRIBUTE_ENTITY:
2833 case XML_ATTRIBUTE_IDREF:
2834 case XML_ATTRIBUTE_ID:
2835 case XML_ATTRIBUTE_NOTATION:
2836 return(xmlValidateNameValue(value));
2837 case XML_ATTRIBUTE_NMTOKENS:
2838 case XML_ATTRIBUTE_ENUMERATION:
2839 return(xmlValidateNmtokensValue(value));
2840 case XML_ATTRIBUTE_NMTOKEN:
2841 return(xmlValidateNmtokenValue(value));
2842 case XML_ATTRIBUTE_CDATA:
2843 break;
2844 }
2845 return(1);
2846}
2847
2848/**
2849 * xmlValidateAttributeValue2:
2850 * @ctxt: the validation context
2851 * @doc: the document
2852 * @name: the attribute name (used for error reporting only)
2853 * @type: the attribute type
2854 * @value: the attribute value
2855 *
2856 * Validate that the given attribute value match a given type.
2857 * This typically cannot be done before having finished parsing
2858 * the subsets.
2859 *
2860 * [ VC: IDREF ]
2861 * Values of type IDREF must match one of the declared IDs
2862 * Values of type IDREFS must match a sequence of the declared IDs
2863 * each Name must match the value of an ID attribute on some element
2864 * in the XML document; i.e. IDREF values must match the value of
2865 * some ID attribute
2866 *
2867 * [ VC: Entity Name ]
2868 * Values of type ENTITY must match one declared entity
2869 * Values of type ENTITIES must match a sequence of declared entities
2870 *
2871 * [ VC: Notation Attributes ]
2872 * all notation names in the declaration must be declared.
2873 *
2874 * returns 1 if valid or 0 otherwise
2875 */
2876
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002877static int
Owen Taylor3473f882001-02-23 17:55:21 +00002878xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2879 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2880 int ret = 1;
2881 switch (type) {
2882 case XML_ATTRIBUTE_IDREFS:
2883 case XML_ATTRIBUTE_IDREF:
2884 case XML_ATTRIBUTE_ID:
2885 case XML_ATTRIBUTE_NMTOKENS:
2886 case XML_ATTRIBUTE_ENUMERATION:
2887 case XML_ATTRIBUTE_NMTOKEN:
2888 case XML_ATTRIBUTE_CDATA:
2889 break;
2890 case XML_ATTRIBUTE_ENTITY: {
2891 xmlEntityPtr ent;
2892
2893 ent = xmlGetDocEntity(doc, value);
2894 if (ent == NULL) {
2895 VERROR(ctxt->userData,
2896 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2897 name, value);
2898 ret = 0;
2899 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2900 VERROR(ctxt->userData,
2901 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2902 name, value);
2903 ret = 0;
2904 }
2905 break;
2906 }
2907 case XML_ATTRIBUTE_ENTITIES: {
2908 xmlChar *dup, *nam = NULL, *cur, save;
2909 xmlEntityPtr ent;
2910
2911 dup = xmlStrdup(value);
2912 if (dup == NULL)
2913 return(0);
2914 cur = dup;
2915 while (*cur != 0) {
2916 nam = cur;
2917 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2918 save = *cur;
2919 *cur = 0;
2920 ent = xmlGetDocEntity(doc, nam);
2921 if (ent == NULL) {
2922 VERROR(ctxt->userData,
2923 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2924 name, nam);
2925 ret = 0;
2926 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2927 VERROR(ctxt->userData,
2928 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2929 name, nam);
2930 ret = 0;
2931 }
2932 if (save == 0)
2933 break;
2934 *cur = save;
2935 while (IS_BLANK(*cur)) cur++;
2936 }
2937 xmlFree(dup);
2938 break;
2939 }
2940 case XML_ATTRIBUTE_NOTATION: {
2941 xmlNotationPtr nota;
2942
2943 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2944 if ((nota == NULL) && (doc->extSubset != NULL))
2945 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2946
2947 if (nota == NULL) {
2948 VERROR(ctxt->userData,
2949 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2950 name, value);
2951 ret = 0;
2952 }
2953 break;
2954 }
2955 }
2956 return(ret);
2957}
2958
2959/**
2960 * xmlValidNormalizeAttributeValue:
2961 * @doc: the document
2962 * @elem: the parent
2963 * @name: the attribute name
2964 * @value: the attribute value
2965 *
2966 * Does the validation related extra step of the normalization of attribute
2967 * values:
2968 *
2969 * If the declared value is not CDATA, then the XML processor must further
2970 * process the normalized attribute value by discarding any leading and
2971 * trailing space (#x20) characters, and by replacing sequences of space
2972 * (#x20) characters by single space (#x20) character.
2973 *
2974 * returns a new normalized string if normalization is needed, NULL otherwise
2975 * the caller must free the returned value.
2976 */
2977
2978xmlChar *
2979xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
2980 const xmlChar *name, const xmlChar *value) {
2981 xmlChar *ret, *dst;
2982 const xmlChar *src;
2983 xmlAttributePtr attrDecl = NULL;
2984
2985 if (doc == NULL) return(NULL);
2986 if (elem == NULL) return(NULL);
2987 if (name == NULL) return(NULL);
2988 if (value == NULL) return(NULL);
2989
2990 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2991 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00002992 snprintf((char *) qname, sizeof(qname), "%s:%s",
2993 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002994 qname[sizeof(qname) - 1] = 0;
2995 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
2996 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2997 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
2998 }
2999 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3000 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3001 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3002
3003 if (attrDecl == NULL)
3004 return(NULL);
3005 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3006 return(NULL);
3007
3008 ret = xmlStrdup(value);
3009 if (ret == NULL)
3010 return(NULL);
3011 src = value;
3012 dst = ret;
3013 while (*src == 0x20) src++;
3014 while (*src != 0) {
3015 if (*src == 0x20) {
3016 while (*src == 0x20) src++;
3017 if (*src != 0)
3018 *dst++ = 0x20;
3019 } else {
3020 *dst++ = *src++;
3021 }
3022 }
3023 *dst = 0;
3024 return(ret);
3025}
3026
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003027static void
Owen Taylor3473f882001-02-23 17:55:21 +00003028xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003029 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003030 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3031}
3032
3033/**
3034 * xmlValidateAttributeDecl:
3035 * @ctxt: the validation context
3036 * @doc: a document instance
3037 * @attr: an attribute definition
3038 *
3039 * Try to validate a single attribute definition
3040 * basically it does the following checks as described by the
3041 * XML-1.0 recommendation:
3042 * - [ VC: Attribute Default Legal ]
3043 * - [ VC: Enumeration ]
3044 * - [ VC: ID Attribute Default ]
3045 *
3046 * The ID/IDREF uniqueness and matching are done separately
3047 *
3048 * returns 1 if valid or 0 otherwise
3049 */
3050
3051int
3052xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3053 xmlAttributePtr attr) {
3054 int ret = 1;
3055 int val;
3056 CHECK_DTD;
3057 if(attr == NULL) return(1);
3058
3059 /* Attribute Default Legal */
3060 /* Enumeration */
3061 if (attr->defaultValue != NULL) {
3062 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3063 if (val == 0) {
3064 VERROR(ctxt->userData,
3065 "Syntax of default value for attribute %s on %s is not valid\n",
3066 attr->name, attr->elem);
3067 }
3068 ret &= val;
3069 }
3070
3071 /* ID Attribute Default */
3072 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3073 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3074 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3075 VERROR(ctxt->userData,
3076 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
3077 attr->name, attr->elem);
3078 ret = 0;
3079 }
3080
3081 /* One ID per Element Type */
3082 if (attr->atype == XML_ATTRIBUTE_ID) {
3083 int nbId;
3084
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003085 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003086 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3087 attr->elem);
3088 if (elem != NULL) {
3089 nbId = xmlScanIDAttributeDecl(NULL, elem);
3090 } else {
3091 xmlAttributeTablePtr table;
3092
3093 /*
3094 * The attribute may be declared in the internal subset and the
3095 * element in the external subset.
3096 */
3097 nbId = 0;
3098 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3099 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3100 xmlValidateAttributeIdCallback, &nbId);
3101 }
3102 if (nbId > 1) {
3103 VERROR(ctxt->userData,
3104 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3105 attr->elem, nbId, attr->name);
3106 } else if (doc->extSubset != NULL) {
3107 int extId = 0;
3108 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3109 if (elem != NULL) {
3110 extId = xmlScanIDAttributeDecl(NULL, elem);
3111 }
3112 if (extId > 1) {
3113 VERROR(ctxt->userData,
3114 "Element %s has %d ID attribute defined in the external subset : %s\n",
3115 attr->elem, extId, attr->name);
3116 } else if (extId + nbId > 1) {
3117 VERROR(ctxt->userData,
3118"Element %s has ID attributes defined in the internal and external subset : %s\n",
3119 attr->elem, attr->name);
3120 }
3121 }
3122 }
3123
3124 /* Validity Constraint: Enumeration */
3125 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3126 xmlEnumerationPtr tree = attr->tree;
3127 while (tree != NULL) {
3128 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3129 tree = tree->next;
3130 }
3131 if (tree == NULL) {
3132 VERROR(ctxt->userData,
3133"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3134 attr->defaultValue, attr->name, attr->elem);
3135 ret = 0;
3136 }
3137 }
3138
3139 return(ret);
3140}
3141
3142/**
3143 * xmlValidateElementDecl:
3144 * @ctxt: the validation context
3145 * @doc: a document instance
3146 * @elem: an element definition
3147 *
3148 * Try to validate a single element definition
3149 * basically it does the following checks as described by the
3150 * XML-1.0 recommendation:
3151 * - [ VC: One ID per Element Type ]
3152 * - [ VC: No Duplicate Types ]
3153 * - [ VC: Unique Element Type Declaration ]
3154 *
3155 * returns 1 if valid or 0 otherwise
3156 */
3157
3158int
3159xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3160 xmlElementPtr elem) {
3161 int ret = 1;
3162 xmlElementPtr tst;
3163
3164 CHECK_DTD;
3165
3166 if (elem == NULL) return(1);
3167
3168 /* No Duplicate Types */
3169 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3170 xmlElementContentPtr cur, next;
3171 const xmlChar *name;
3172
3173 cur = elem->content;
3174 while (cur != NULL) {
3175 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3176 if (cur->c1 == NULL) break;
3177 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3178 name = cur->c1->name;
3179 next = cur->c2;
3180 while (next != NULL) {
3181 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3182 if (xmlStrEqual(next->name, name)) {
3183 VERROR(ctxt->userData,
3184 "Definition of %s has duplicate references of %s\n",
3185 elem->name, name);
3186 ret = 0;
3187 }
3188 break;
3189 }
3190 if (next->c1 == NULL) break;
3191 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3192 if (xmlStrEqual(next->c1->name, name)) {
3193 VERROR(ctxt->userData,
3194 "Definition of %s has duplicate references of %s\n",
3195 elem->name, name);
3196 ret = 0;
3197 }
3198 next = next->c2;
3199 }
3200 }
3201 cur = cur->c2;
3202 }
3203 }
3204
3205 /* VC: Unique Element Type Declaration */
3206 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003207 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003208 ((tst->prefix == elem->prefix) ||
3209 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003210 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003211 VERROR(ctxt->userData, "Redefinition of element %s\n",
3212 elem->name);
3213 ret = 0;
3214 }
3215 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003216 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003217 ((tst->prefix == elem->prefix) ||
3218 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003219 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003220 VERROR(ctxt->userData, "Redefinition of element %s\n",
3221 elem->name);
3222 ret = 0;
3223 }
3224
Daniel Veillarda10efa82001-04-18 13:09:01 +00003225 /* One ID per Element Type
3226 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003227 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3228 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003229 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003230 return(ret);
3231}
3232
3233/**
3234 * xmlValidateOneAttribute:
3235 * @ctxt: the validation context
3236 * @doc: a document instance
3237 * @elem: an element instance
3238 * @attr: an attribute instance
3239 * @value: the attribute value (without entities processing)
3240 *
3241 * Try to validate a single attribute for an element
3242 * basically it does the following checks as described by the
3243 * XML-1.0 recommendation:
3244 * - [ VC: Attribute Value Type ]
3245 * - [ VC: Fixed Attribute Default ]
3246 * - [ VC: Entity Name ]
3247 * - [ VC: Name Token ]
3248 * - [ VC: ID ]
3249 * - [ VC: IDREF ]
3250 * - [ VC: Entity Name ]
3251 * - [ VC: Notation Attributes ]
3252 *
3253 * The ID/IDREF uniqueness and matching are done separately
3254 *
3255 * returns 1 if valid or 0 otherwise
3256 */
3257
3258int
3259xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3260 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3261 /* xmlElementPtr elemDecl; */
3262 xmlAttributePtr attrDecl = NULL;
3263 int val;
3264 int ret = 1;
3265
3266 CHECK_DTD;
3267 if ((elem == NULL) || (elem->name == NULL)) return(0);
3268 if ((attr == NULL) || (attr->name == NULL)) return(0);
3269
3270 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3271 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003272 snprintf((char *) qname, sizeof(qname), "%s:%s",
3273 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003274 qname[sizeof(qname) - 1] = 0;
3275 if (attr->ns != NULL) {
3276 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3277 attr->name, attr->ns->prefix);
3278 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3279 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3280 attr->name, attr->ns->prefix);
3281 } else {
3282 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3283 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3284 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3285 qname, attr->name);
3286 }
3287 }
3288 if (attrDecl == NULL) {
3289 if (attr->ns != NULL) {
3290 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3291 attr->name, attr->ns->prefix);
3292 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3293 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3294 attr->name, attr->ns->prefix);
3295 } else {
3296 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3297 elem->name, attr->name);
3298 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3299 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3300 elem->name, attr->name);
3301 }
3302 }
3303
3304
3305 /* Validity Constraint: Attribute Value Type */
3306 if (attrDecl == NULL) {
3307 VERROR(ctxt->userData,
3308 "No declaration for attribute %s on element %s\n",
3309 attr->name, elem->name);
3310 return(0);
3311 }
3312 attr->atype = attrDecl->atype;
3313
3314 val = xmlValidateAttributeValue(attrDecl->atype, value);
3315 if (val == 0) {
3316 VERROR(ctxt->userData,
3317 "Syntax of value for attribute %s on %s is not valid\n",
3318 attr->name, elem->name);
3319 ret = 0;
3320 }
3321
3322 /* Validity constraint: Fixed Attribute Default */
3323 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3324 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3325 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003326 "Value for attribute %s on %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003327 attr->name, elem->name, attrDecl->defaultValue);
3328 ret = 0;
3329 }
3330 }
3331
3332 /* Validity Constraint: ID uniqueness */
3333 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3334 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3335 ret = 0;
3336 }
3337
3338 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3339 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3340 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3341 ret = 0;
3342 }
3343
3344 /* Validity Constraint: Notation Attributes */
3345 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3346 xmlEnumerationPtr tree = attrDecl->tree;
3347 xmlNotationPtr nota;
3348
3349 /* First check that the given NOTATION was declared */
3350 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3351 if (nota == NULL)
3352 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3353
3354 if (nota == NULL) {
3355 VERROR(ctxt->userData,
3356 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
3357 value, attr->name, elem->name);
3358 ret = 0;
3359 }
3360
3361 /* Second, verify that it's among the list */
3362 while (tree != NULL) {
3363 if (xmlStrEqual(tree->name, value)) break;
3364 tree = tree->next;
3365 }
3366 if (tree == NULL) {
3367 VERROR(ctxt->userData,
3368"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
3369 value, attr->name, elem->name);
3370 ret = 0;
3371 }
3372 }
3373
3374 /* Validity Constraint: Enumeration */
3375 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3376 xmlEnumerationPtr tree = attrDecl->tree;
3377 while (tree != NULL) {
3378 if (xmlStrEqual(tree->name, value)) break;
3379 tree = tree->next;
3380 }
3381 if (tree == NULL) {
3382 VERROR(ctxt->userData,
3383 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3384 value, attr->name, elem->name);
3385 ret = 0;
3386 }
3387 }
3388
3389 /* Fixed Attribute Default */
3390 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3391 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3392 VERROR(ctxt->userData,
3393 "Value for attribute %s on %s must be \"%s\"\n",
3394 attr->name, elem->name, attrDecl->defaultValue);
3395 ret = 0;
3396 }
3397
3398 /* Extra check for the attribute value */
3399 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3400 attrDecl->atype, value);
3401
3402 return(ret);
3403}
3404
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003405/**
3406 * xmlValidateSkipIgnorable:
3407 * @ctxt: the validation context
3408 * @child: the child list
3409 *
3410 * Skip ignorable elements w.r.t. the validation process
3411 *
3412 * returns the first element to consider for validation of the content model
3413 */
3414
3415static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003416xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003417 while (child != NULL) {
3418 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003419 /* These things are ignored (skipped) during validation. */
3420 case XML_PI_NODE:
3421 case XML_COMMENT_NODE:
3422 case XML_XINCLUDE_START:
3423 case XML_XINCLUDE_END:
3424 child = child->next;
3425 break;
3426 case XML_TEXT_NODE:
3427 if (xmlIsBlankNode(child))
3428 child = child->next;
3429 else
3430 return(child);
3431 break;
3432 /* keep current node */
3433 default:
3434 return(child);
3435 }
3436 }
3437 return(child);
3438}
3439
3440/**
3441 * xmlValidateElementType:
3442 * @ctxt: the validation context
3443 *
3444 * Try to validate the content model of an element internal function
3445 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003446 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3447 * reference is found and -3 if the validation succeeded but
3448 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003449 */
3450
3451static int
3452xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003453 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003454 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003455
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003456 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003457 if ((NODE == NULL) && (CONT == NULL))
3458 return(1);
3459 if ((NODE == NULL) &&
3460 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3461 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3462 return(1);
3463 }
3464 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003465 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003466 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003467
3468 /*
3469 * We arrive here when more states need to be examined
3470 */
3471cont:
3472
3473 /*
3474 * We just recovered from a rollback generated by a possible
3475 * epsilon transition, go directly to the analysis phase
3476 */
3477 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003478 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003479 DEBUG_VALID_STATE(NODE, CONT)
3480 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003481 goto analyze;
3482 }
3483
3484 DEBUG_VALID_STATE(NODE, CONT)
3485 /*
3486 * we may have to save a backup state here. This is the equivalent
3487 * of handling epsilon transition in NFAs.
3488 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003489 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003490 ((CONT->parent == NULL) ||
3491 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003492 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003493 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003494 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003495 DEBUG_VALID_MSG("saving parent branch");
3496 vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
3497 }
3498
3499
3500 /*
3501 * Check first if the content matches
3502 */
3503 switch (CONT->type) {
3504 case XML_ELEMENT_CONTENT_PCDATA:
3505 if (NODE == NULL) {
3506 DEBUG_VALID_MSG("pcdata failed no node");
3507 ret = 0;
3508 break;
3509 }
3510 if (NODE->type == XML_TEXT_NODE) {
3511 DEBUG_VALID_MSG("pcdata found, skip to next");
3512 /*
3513 * go to next element in the content model
3514 * skipping ignorable elems
3515 */
3516 do {
3517 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003518 NODE = xmlValidateSkipIgnorable(NODE);
3519 if ((NODE != NULL) &&
3520 (NODE->type == XML_ENTITY_REF_NODE))
3521 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003522 } while ((NODE != NULL) &&
3523 ((NODE->type != XML_ELEMENT_NODE) &&
3524 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003525 ret = 1;
3526 break;
3527 } else {
3528 DEBUG_VALID_MSG("pcdata failed");
3529 ret = 0;
3530 break;
3531 }
3532 break;
3533 case XML_ELEMENT_CONTENT_ELEMENT:
3534 if (NODE == NULL) {
3535 DEBUG_VALID_MSG("element failed no node");
3536 ret = 0;
3537 break;
3538 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003539 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3540 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003541 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003542 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3543 ret = (CONT->prefix == NULL);
3544 } else if (CONT->prefix == NULL) {
3545 ret = 0;
3546 } else {
3547 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3548 }
3549 }
3550 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003551 DEBUG_VALID_MSG("element found, skip to next");
3552 /*
3553 * go to next element in the content model
3554 * skipping ignorable elems
3555 */
3556 do {
3557 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003558 NODE = xmlValidateSkipIgnorable(NODE);
3559 if ((NODE != NULL) &&
3560 (NODE->type == XML_ENTITY_REF_NODE))
3561 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003562 } while ((NODE != NULL) &&
3563 ((NODE->type != XML_ELEMENT_NODE) &&
3564 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003565 } else {
3566 DEBUG_VALID_MSG("element failed");
3567 ret = 0;
3568 break;
3569 }
3570 break;
3571 case XML_ELEMENT_CONTENT_OR:
3572 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003573 * Small optimization.
3574 */
3575 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3576 if ((NODE == NULL) ||
3577 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3578 DEPTH++;
3579 CONT = CONT->c2;
3580 goto cont;
3581 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003582 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3583 ret = (CONT->c1->prefix == NULL);
3584 } else if (CONT->c1->prefix == NULL) {
3585 ret = 0;
3586 } else {
3587 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3588 }
3589 if (ret == 0) {
3590 DEPTH++;
3591 CONT = CONT->c2;
3592 goto cont;
3593 }
Daniel Veillard85349052001-04-20 13:48:21 +00003594 }
3595
3596 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003597 * save the second branch 'or' branch
3598 */
3599 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard268fd1b2001-08-26 18:46:36 +00003600 vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3601 OCCURS, ROLLBACK_OR);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003602
3603 DEPTH++;
3604 CONT = CONT->c1;
3605 goto cont;
3606 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003607 /*
3608 * Small optimization.
3609 */
3610 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3611 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3612 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3613 if ((NODE == NULL) ||
3614 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3615 DEPTH++;
3616 CONT = CONT->c2;
3617 goto cont;
3618 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003619 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3620 ret = (CONT->c1->prefix == NULL);
3621 } else if (CONT->c1->prefix == NULL) {
3622 ret = 0;
3623 } else {
3624 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3625 }
3626 if (ret == 0) {
3627 DEPTH++;
3628 CONT = CONT->c2;
3629 goto cont;
3630 }
Daniel Veillard1d047672001-06-09 16:41:01 +00003631 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003632 DEPTH++;
3633 CONT = CONT->c1;
3634 goto cont;
3635 }
3636
3637 /*
3638 * At this point handle going up in the tree
3639 */
3640 if (ret == -1) {
3641 DEBUG_VALID_MSG("error found returning");
3642 return(ret);
3643 }
3644analyze:
3645 while (CONT != NULL) {
3646 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003647 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003648 * this level.
3649 */
3650 if (ret == 0) {
3651 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003652 xmlNodePtr cur;
3653
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003654 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003655 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003656 DEBUG_VALID_MSG("Once branch failed, rollback");
3657 if (vstateVPop(ctxt) < 0 ) {
3658 DEBUG_VALID_MSG("exhaustion, failed");
3659 return(0);
3660 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003661 if (cur != ctxt->vstate->node)
3662 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003663 goto cont;
3664 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00003665 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003666 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003667 DEBUG_VALID_MSG("Plus branch failed, rollback");
3668 if (vstateVPop(ctxt) < 0 ) {
3669 DEBUG_VALID_MSG("exhaustion, failed");
3670 return(0);
3671 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003672 if (cur != ctxt->vstate->node)
3673 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003674 goto cont;
3675 }
3676 DEBUG_VALID_MSG("Plus branch found");
3677 ret = 1;
3678 break;
3679 case XML_ELEMENT_CONTENT_MULT:
3680#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00003681 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003682 DEBUG_VALID_MSG("Mult branch failed");
3683 } else {
3684 DEBUG_VALID_MSG("Mult branch found");
3685 }
3686#endif
3687 ret = 1;
3688 break;
3689 case XML_ELEMENT_CONTENT_OPT:
3690 DEBUG_VALID_MSG("Option branch failed");
3691 ret = 1;
3692 break;
3693 }
3694 } else {
3695 switch (CONT->ocur) {
3696 case XML_ELEMENT_CONTENT_OPT:
3697 DEBUG_VALID_MSG("Option branch succeeded");
3698 ret = 1;
3699 break;
3700 case XML_ELEMENT_CONTENT_ONCE:
3701 DEBUG_VALID_MSG("Once branch succeeded");
3702 ret = 1;
3703 break;
3704 case XML_ELEMENT_CONTENT_PLUS:
3705 if (STATE == ROLLBACK_PARENT) {
3706 DEBUG_VALID_MSG("Plus branch rollback");
3707 ret = 1;
3708 break;
3709 }
3710 if (NODE == NULL) {
3711 DEBUG_VALID_MSG("Plus branch exhausted");
3712 ret = 1;
3713 break;
3714 }
3715 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003716 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003717 goto cont;
3718 case XML_ELEMENT_CONTENT_MULT:
3719 if (STATE == ROLLBACK_PARENT) {
3720 DEBUG_VALID_MSG("Mult branch rollback");
3721 ret = 1;
3722 break;
3723 }
3724 if (NODE == NULL) {
3725 DEBUG_VALID_MSG("Mult branch exhausted");
3726 ret = 1;
3727 break;
3728 }
3729 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003730 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003731 goto cont;
3732 }
3733 }
3734 STATE = 0;
3735
3736 /*
3737 * Then act accordingly at the parent level
3738 */
Daniel Veillard5344c602001-12-31 16:37:34 +00003739 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003740 if (CONT->parent == NULL)
3741 break;
3742
3743 switch (CONT->parent->type) {
3744 case XML_ELEMENT_CONTENT_PCDATA:
3745 DEBUG_VALID_MSG("Error: parent pcdata");
3746 return(-1);
3747 case XML_ELEMENT_CONTENT_ELEMENT:
3748 DEBUG_VALID_MSG("Error: parent element");
3749 return(-1);
3750 case XML_ELEMENT_CONTENT_OR:
3751 if (ret == 1) {
3752 DEBUG_VALID_MSG("Or succeeded");
3753 CONT = CONT->parent;
3754 DEPTH--;
3755 } else {
3756 DEBUG_VALID_MSG("Or failed");
3757 CONT = CONT->parent;
3758 DEPTH--;
3759 }
3760 break;
3761 case XML_ELEMENT_CONTENT_SEQ:
3762 if (ret == 0) {
3763 DEBUG_VALID_MSG("Sequence failed");
3764 CONT = CONT->parent;
3765 DEPTH--;
3766 } else if (CONT == CONT->parent->c1) {
3767 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3768 CONT = CONT->parent->c2;
3769 goto cont;
3770 } else {
3771 DEBUG_VALID_MSG("Sequence succeeded");
3772 CONT = CONT->parent;
3773 DEPTH--;
3774 }
3775 }
3776 }
3777 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003778 xmlNodePtr cur;
3779
3780 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003781 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3782 if (vstateVPop(ctxt) < 0 ) {
3783 DEBUG_VALID_MSG("exhaustion, failed");
3784 return(0);
3785 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003786 if (cur != ctxt->vstate->node)
3787 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003788 goto cont;
3789 }
3790 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003791 xmlNodePtr cur;
3792
3793 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003794 DEBUG_VALID_MSG("Failure, rollback");
3795 if (vstateVPop(ctxt) < 0 ) {
3796 DEBUG_VALID_MSG("exhaustion, failed");
3797 return(0);
3798 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003799 if (cur != ctxt->vstate->node)
3800 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003801 goto cont;
3802 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003803 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003804}
3805
3806/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00003807 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003808 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00003809 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003810 * @content: An element
3811 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3812 *
3813 * This will dump the list of elements to the buffer
3814 * Intended just for the debug routine
3815 */
3816static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00003817xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003818 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00003819 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003820
3821 if (node == NULL) return;
3822 if (glob) strcat(buf, "(");
3823 cur = node;
3824 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00003825 len = strlen(buf);
3826 if (size - len < 50) {
3827 if ((size - len > 4) && (buf[len - 1] != '.'))
3828 strcat(buf, " ...");
3829 return;
3830 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003831 switch (cur->type) {
3832 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003833 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
3834 if (size - len < xmlStrlen(cur->ns->prefix + 10)) {
3835 if ((size - len > 4) && (buf[len - 1] != '.'))
3836 strcat(buf, " ...");
3837 return;
3838 }
3839 strcat(buf, (char *) cur->ns->prefix);
3840 strcat(buf, ":");
3841 }
Daniel Veillardd3d06722001-08-15 12:06:36 +00003842 if (size - len < xmlStrlen(cur->name + 10)) {
3843 if ((size - len > 4) && (buf[len - 1] != '.'))
3844 strcat(buf, " ...");
3845 return;
3846 }
3847 strcat(buf, (char *) cur->name);
3848 if (cur->next != NULL)
3849 strcat(buf, " ");
3850 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003851 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003852 if (xmlIsBlankNode(cur))
3853 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003854 case XML_CDATA_SECTION_NODE:
3855 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003856 strcat(buf, "CDATA");
3857 if (cur->next != NULL)
3858 strcat(buf, " ");
3859 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003860 case XML_ATTRIBUTE_NODE:
3861 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003862#ifdef LIBXML_DOCB_ENABLED
3863 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003864#endif
3865 case XML_HTML_DOCUMENT_NODE:
3866 case XML_DOCUMENT_TYPE_NODE:
3867 case XML_DOCUMENT_FRAG_NODE:
3868 case XML_NOTATION_NODE:
3869 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003870 strcat(buf, "???");
3871 if (cur->next != NULL)
3872 strcat(buf, " ");
3873 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003874 case XML_ENTITY_NODE:
3875 case XML_PI_NODE:
3876 case XML_DTD_NODE:
3877 case XML_COMMENT_NODE:
3878 case XML_ELEMENT_DECL:
3879 case XML_ATTRIBUTE_DECL:
3880 case XML_ENTITY_DECL:
3881 case XML_XINCLUDE_START:
3882 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003883 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003884 }
3885 cur = cur->next;
3886 }
3887 if (glob) strcat(buf, ")");
3888}
3889
3890/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003891 * xmlValidateElementContent:
3892 * @ctxt: the validation context
3893 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003894 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003895 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003896 *
3897 * Try to validate the content model of an element
3898 *
3899 * returns 1 if valid or 0 if not and -1 in case of error
3900 */
3901
3902static int
3903xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003904 xmlElementPtr elemDecl, int warn) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003905 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003906 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003907 xmlElementContentPtr cont;
3908 const xmlChar *name;
3909
3910 if (elemDecl == NULL)
3911 return(-1);
3912 cont = elemDecl->content;
3913 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003914
3915 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003916 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003917 */
3918 ctxt->vstateMax = 8;
3919 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
3920 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
3921 if (ctxt->vstateTab == NULL) {
3922 xmlGenericError(xmlGenericErrorContext,
3923 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003924 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003925 }
3926 /*
3927 * The first entry in the stack is reserved to the current state
3928 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00003929 ctxt->nodeMax = 0;
3930 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00003931 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003932 ctxt->vstate = &ctxt->vstateTab[0];
3933 ctxt->vstateNr = 1;
3934 CONT = cont;
3935 NODE = child;
3936 DEPTH = 0;
3937 OCCURS = 0;
3938 STATE = 0;
3939 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003940 if ((ret == -3) && (warn)) {
3941 VWARNING(ctxt->userData,
3942 "Element %s content model is ambiguous\n", name);
3943 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003944 /*
3945 * An entities reference appeared at this level.
3946 * Buid a minimal representation of this node content
3947 * sufficient to run the validation process on it
3948 */
3949 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003950 cur = child;
3951 while (cur != NULL) {
3952 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003953 case XML_ENTITY_REF_NODE:
3954 /*
3955 * Push the current node to be able to roll back
3956 * and process within the entity
3957 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003958 if ((cur->children != NULL) &&
3959 (cur->children->children != NULL)) {
3960 nodeVPush(ctxt, cur);
3961 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003962 continue;
3963 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00003964 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003965 case XML_TEXT_NODE:
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003966 case XML_CDATA_SECTION_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003967 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003968 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003969 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003970 case XML_ELEMENT_NODE:
3971 /*
3972 * Allocate a new node and minimally fills in
3973 * what's required
3974 */
3975 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
3976 if (tmp == NULL) {
3977 xmlGenericError(xmlGenericErrorContext,
3978 "xmlValidateElementContent : malloc failed\n");
3979 xmlFreeNodeList(repl);
3980 ret = -1;
3981 goto done;
3982 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003983 tmp->type = cur->type;
3984 tmp->name = cur->name;
3985 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003986 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00003987 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003988 if (repl == NULL)
3989 repl = last = tmp;
3990 else {
3991 last->next = tmp;
3992 last = tmp;
3993 }
3994 break;
3995 default:
3996 break;
3997 }
3998 /*
3999 * Switch to next element
4000 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004001 cur = cur->next;
4002 while (cur == NULL) {
4003 cur = nodeVPop(ctxt);
4004 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004005 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004006 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004007 }
4008 }
4009
4010 /*
4011 * Relaunch the validation
4012 */
4013 ctxt->vstate = &ctxt->vstateTab[0];
4014 ctxt->vstateNr = 1;
4015 CONT = cont;
4016 NODE = repl;
4017 DEPTH = 0;
4018 OCCURS = 0;
4019 STATE = 0;
4020 ret = xmlValidateElementType(ctxt);
4021 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004022 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004023 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4024 char expr[5000];
4025 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004026
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004027 expr[0] = 0;
4028 xmlSnprintfElementContent(expr, 5000, cont, 1);
4029 list[0] = 0;
4030 if (repl != NULL)
4031 xmlSnprintfElements(list, 5000, repl, 1);
4032 else
4033 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004034
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004035 if (name != NULL) {
4036 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004037 "Element %s content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004038 name, expr, list);
4039 } else {
4040 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004041 "Element content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004042 expr, list);
4043 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004044 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004045 if (name != NULL) {
4046 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004047 "Element %s content doesn't follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004048 name);
4049 } else {
4050 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004051 "Element content doesn't follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004052 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004053 }
4054 ret = 0;
4055 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004056 if (ret == -3)
4057 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004058
4059
4060done:
4061 /*
4062 * Deallocate the copy if done, and free up the validation stack
4063 */
4064 while (repl != NULL) {
4065 tmp = repl->next;
4066 xmlFree(repl);
4067 repl = tmp;
4068 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004069 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004070 if (ctxt->vstateTab != NULL) {
4071 xmlFree(ctxt->vstateTab);
4072 ctxt->vstateTab = NULL;
4073 }
4074 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004075 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004076 if (ctxt->nodeTab != NULL) {
4077 xmlFree(ctxt->nodeTab);
4078 ctxt->nodeTab = NULL;
4079 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004080 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004081
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004082}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004083
Owen Taylor3473f882001-02-23 17:55:21 +00004084/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004085 * xmlValidateCdataElement:
4086 * @ctxt: the validation context
4087 * @doc: a document instance
4088 * @elem: an element instance
4089 *
4090 * Check that an element follows #CDATA
4091 *
4092 * returns 1 if valid or 0 otherwise
4093 */
4094static int
4095xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4096 xmlNodePtr elem) {
4097 int ret = 1;
4098 xmlNodePtr cur, child;
4099
4100 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4101 return(0);
4102
4103 child = elem->children;
4104
4105 cur = child;
4106 while (cur != NULL) {
4107 switch (cur->type) {
4108 case XML_ENTITY_REF_NODE:
4109 /*
4110 * Push the current node to be able to roll back
4111 * and process within the entity
4112 */
4113 if ((cur->children != NULL) &&
4114 (cur->children->children != NULL)) {
4115 nodeVPush(ctxt, cur);
4116 cur = cur->children->children;
4117 continue;
4118 }
4119 break;
4120 case XML_COMMENT_NODE:
4121 case XML_PI_NODE:
4122 case XML_TEXT_NODE:
4123 case XML_CDATA_SECTION_NODE:
4124 break;
4125 default:
4126 ret = 0;
4127 goto done;
4128 }
4129 /*
4130 * Switch to next element
4131 */
4132 cur = cur->next;
4133 while (cur == NULL) {
4134 cur = nodeVPop(ctxt);
4135 if (cur == NULL)
4136 break;
4137 cur = cur->next;
4138 }
4139 }
4140done:
4141 ctxt->nodeMax = 0;
4142 ctxt->nodeNr = 0;
4143 if (ctxt->nodeTab != NULL) {
4144 xmlFree(ctxt->nodeTab);
4145 ctxt->nodeTab = NULL;
4146 }
4147 return(ret);
4148}
4149
4150/**
Owen Taylor3473f882001-02-23 17:55:21 +00004151 * xmlValidateOneElement:
4152 * @ctxt: the validation context
4153 * @doc: a document instance
4154 * @elem: an element instance
4155 *
4156 * Try to validate a single element and it's attributes,
4157 * basically it does the following checks as described by the
4158 * XML-1.0 recommendation:
4159 * - [ VC: Element Valid ]
4160 * - [ VC: Required Attribute ]
4161 * Then call xmlValidateOneAttribute() for each attribute present.
4162 *
4163 * The ID/IDREF checkings are done separately
4164 *
4165 * returns 1 if valid or 0 otherwise
4166 */
4167
4168int
4169xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4170 xmlNodePtr elem) {
4171 xmlElementPtr elemDecl = NULL;
4172 xmlElementContentPtr cont;
4173 xmlAttributePtr attr;
4174 xmlNodePtr child;
4175 int ret = 1;
4176 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004177 const xmlChar *prefix = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00004178
4179 CHECK_DTD;
4180
4181 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004182 switch (elem->type) {
4183 case XML_ATTRIBUTE_NODE:
4184 VERROR(ctxt->userData,
4185 "Attribute element not expected here\n");
4186 return(0);
4187 case XML_TEXT_NODE:
4188 if (elem->children != NULL) {
4189 VERROR(ctxt->userData, "Text element has childs !\n");
4190 return(0);
4191 }
4192 if (elem->properties != NULL) {
4193 VERROR(ctxt->userData, "Text element has attributes !\n");
4194 return(0);
4195 }
4196 if (elem->ns != NULL) {
4197 VERROR(ctxt->userData, "Text element has namespace !\n");
4198 return(0);
4199 }
4200 if (elem->nsDef != NULL) {
4201 VERROR(ctxt->userData,
4202 "Text element carries namespace definitions !\n");
4203 return(0);
4204 }
4205 if (elem->content == NULL) {
4206 VERROR(ctxt->userData,
4207 "Text element has no content !\n");
4208 return(0);
4209 }
4210 return(1);
4211 case XML_XINCLUDE_START:
4212 case XML_XINCLUDE_END:
4213 return(1);
4214 case XML_CDATA_SECTION_NODE:
4215 case XML_ENTITY_REF_NODE:
4216 case XML_PI_NODE:
4217 case XML_COMMENT_NODE:
4218 return(1);
4219 case XML_ENTITY_NODE:
4220 VERROR(ctxt->userData,
4221 "Entity element not expected here\n");
4222 return(0);
4223 case XML_NOTATION_NODE:
4224 VERROR(ctxt->userData,
4225 "Notation element not expected here\n");
4226 return(0);
4227 case XML_DOCUMENT_NODE:
4228 case XML_DOCUMENT_TYPE_NODE:
4229 case XML_DOCUMENT_FRAG_NODE:
4230 VERROR(ctxt->userData,
4231 "Document element not expected here\n");
4232 return(0);
4233 case XML_HTML_DOCUMENT_NODE:
4234 VERROR(ctxt->userData,
4235 "\n");
4236 return(0);
4237 case XML_ELEMENT_NODE:
4238 break;
4239 default:
4240 VERROR(ctxt->userData,
4241 "unknown element type %d\n", elem->type);
4242 return(0);
4243 }
4244 if (elem->name == NULL) return(0);
4245
4246 /*
4247 * Fetch the declaration for the qualified name
4248 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004249 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4250 prefix = elem->ns->prefix;
4251
4252 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004253 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004254 elem->name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00004255 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4256 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004257 elem->name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00004258 }
4259
4260 /*
4261 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004262 * This is "non-strict" validation should be done on the
4263 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004264 */
4265 if (elemDecl == NULL) {
4266 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
4267 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4268 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
4269 }
4270 if (elemDecl == NULL) {
4271 VERROR(ctxt->userData, "No declaration for element %s\n",
4272 elem->name);
4273 return(0);
4274 }
4275
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004276 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004277 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004278 case XML_ELEMENT_TYPE_UNDEFINED:
4279 VERROR(ctxt->userData, "No declaration for element %s\n",
4280 elem->name);
4281 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004282 case XML_ELEMENT_TYPE_EMPTY:
4283 if (elem->children != NULL) {
4284 VERROR(ctxt->userData,
4285 "Element %s was declared EMPTY this one has content\n",
4286 elem->name);
4287 ret = 0;
4288 }
4289 break;
4290 case XML_ELEMENT_TYPE_ANY:
4291 /* I don't think anything is required then */
4292 break;
4293 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004294 /* simple case of declared as #PCDATA */
4295 if ((elemDecl->content != NULL) &&
4296 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4297 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4298 if (!ret) {
4299 VERROR(ctxt->userData,
4300 "Element %s was declared #PCDATA but contains non text nodes\n",
4301 elem->name);
4302 }
4303 break;
4304 }
Owen Taylor3473f882001-02-23 17:55:21 +00004305 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004306 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004307 while (child != NULL) {
4308 if (child->type == XML_ELEMENT_NODE) {
4309 name = child->name;
4310 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4311 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004312 snprintf((char *) qname, sizeof(qname), "%s:%s",
4313 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004314 qname[sizeof(qname) - 1] = 0;
4315 cont = elemDecl->content;
4316 while (cont != NULL) {
4317 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4318 if (xmlStrEqual(cont->name, qname)) break;
4319 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4320 (cont->c1 != NULL) &&
4321 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4322 if (xmlStrEqual(cont->c1->name, qname)) break;
4323 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4324 (cont->c1 == NULL) ||
4325 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4326 /* Internal error !!! */
4327 xmlGenericError(xmlGenericErrorContext,
4328 "Internal: MIXED struct bad\n");
4329 break;
4330 }
4331 cont = cont->c2;
4332 }
4333 if (cont != NULL)
4334 goto child_ok;
4335 }
4336 cont = elemDecl->content;
4337 while (cont != NULL) {
4338 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4339 if (xmlStrEqual(cont->name, name)) break;
4340 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4341 (cont->c1 != NULL) &&
4342 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4343 if (xmlStrEqual(cont->c1->name, name)) break;
4344 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4345 (cont->c1 == NULL) ||
4346 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4347 /* Internal error !!! */
4348 xmlGenericError(xmlGenericErrorContext,
4349 "Internal: MIXED struct bad\n");
4350 break;
4351 }
4352 cont = cont->c2;
4353 }
4354 if (cont == NULL) {
4355 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004356 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004357 name, elem->name);
4358 ret = 0;
4359 }
4360 }
4361child_ok:
4362 child = child->next;
4363 }
4364 break;
4365 case XML_ELEMENT_TYPE_ELEMENT:
4366 child = elem->children;
4367 cont = elemDecl->content;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004368 ret = xmlValidateElementContent(ctxt, child, elemDecl, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00004369 break;
4370 }
4371
4372 /* [ VC: Required Attribute ] */
4373 attr = elemDecl->attributes;
4374 while (attr != NULL) {
4375 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004376 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00004377
Daniel Veillarde4301c82002-02-13 13:32:35 +00004378 if ((attr->prefix == NULL) &&
4379 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4380 xmlNsPtr ns;
4381
4382 ns = elem->nsDef;
4383 while (ns != NULL) {
4384 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00004385 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004386 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00004387 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004388 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4389 xmlNsPtr ns;
4390
4391 ns = elem->nsDef;
4392 while (ns != NULL) {
4393 if (xmlStrEqual(attr->name, ns->prefix))
4394 goto found;
4395 ns = ns->next;
4396 }
4397 } else {
4398 xmlAttrPtr attrib;
4399
4400 attrib = elem->properties;
4401 while (attrib != NULL) {
4402 if (xmlStrEqual(attrib->name, attr->name)) {
4403 if (attr->prefix != NULL) {
4404 xmlNsPtr nameSpace = attrib->ns;
4405
4406 if (nameSpace == NULL)
4407 nameSpace = elem->ns;
4408 /*
4409 * qualified names handling is problematic, having a
4410 * different prefix should be possible but DTDs don't
4411 * allow to define the URI instead of the prefix :-(
4412 */
4413 if (nameSpace == NULL) {
4414 if (qualified < 0)
4415 qualified = 0;
4416 } else if (!xmlStrEqual(nameSpace->prefix,
4417 attr->prefix)) {
4418 if (qualified < 1)
4419 qualified = 1;
4420 } else
4421 goto found;
4422 } else {
4423 /*
4424 * We should allow applications to define namespaces
4425 * for their application even if the DTD doesn't
4426 * carry one, otherwise, basically we would always
4427 * break.
4428 */
4429 goto found;
4430 }
4431 }
4432 attrib = attrib->next;
4433 }
Owen Taylor3473f882001-02-23 17:55:21 +00004434 }
4435 if (qualified == -1) {
4436 if (attr->prefix == NULL) {
4437 VERROR(ctxt->userData,
4438 "Element %s doesn't carry attribute %s\n",
4439 elem->name, attr->name);
4440 ret = 0;
4441 } else {
4442 VERROR(ctxt->userData,
4443 "Element %s doesn't carry attribute %s:%s\n",
4444 elem->name, attr->prefix,attr->name);
4445 ret = 0;
4446 }
4447 } else if (qualified == 0) {
4448 VWARNING(ctxt->userData,
4449 "Element %s required attribute %s:%s has no prefix\n",
4450 elem->name, attr->prefix,attr->name);
4451 } else if (qualified == 1) {
4452 VWARNING(ctxt->userData,
4453 "Element %s required attribute %s:%s has different prefix\n",
4454 elem->name, attr->prefix,attr->name);
4455 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004456 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
4457 /*
4458 * Special tests checking #FIXED namespace declarations
4459 * have the right value since this is not done as an
4460 * attribute checking
4461 */
4462 if ((attr->prefix == NULL) &&
4463 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4464 xmlNsPtr ns;
4465
4466 ns = elem->nsDef;
4467 while (ns != NULL) {
4468 if (ns->prefix == NULL) {
4469 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
4470 VERROR(ctxt->userData,
4471 "Element %s namespace name for default namespace does not match the DTD\n",
4472 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00004473 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004474 }
4475 goto found;
4476 }
4477 ns = ns->next;
4478 }
4479 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4480 xmlNsPtr ns;
4481
4482 ns = elem->nsDef;
4483 while (ns != NULL) {
4484 if (xmlStrEqual(attr->name, ns->prefix)) {
4485 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
4486 VERROR(ctxt->userData,
4487 "Element %s namespace name for %s doesn't match the DTD\n",
4488 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00004489 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004490 }
4491 goto found;
4492 }
4493 ns = ns->next;
4494 }
4495 }
Owen Taylor3473f882001-02-23 17:55:21 +00004496 }
4497found:
4498 attr = attr->nexth;
4499 }
4500 return(ret);
4501}
4502
4503/**
4504 * xmlValidateRoot:
4505 * @ctxt: the validation context
4506 * @doc: a document instance
4507 *
4508 * Try to validate a the root element
4509 * basically it does the following check as described by the
4510 * XML-1.0 recommendation:
4511 * - [ VC: Root Element Type ]
4512 * it doesn't try to recurse or apply other check to the element
4513 *
4514 * returns 1 if valid or 0 otherwise
4515 */
4516
4517int
4518xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4519 xmlNodePtr root;
4520 if (doc == NULL) return(0);
4521
4522 root = xmlDocGetRootElement(doc);
4523 if ((root == NULL) || (root->name == NULL)) {
4524 VERROR(ctxt->userData, "Not valid: no root element\n");
4525 return(0);
4526 }
4527
4528 /*
4529 * When doing post validation against a separate DTD, those may
4530 * no internal subset has been generated
4531 */
4532 if ((doc->intSubset != NULL) &&
4533 (doc->intSubset->name != NULL)) {
4534 /*
4535 * Check first the document root against the NQName
4536 */
4537 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4538 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4539 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004540 snprintf((char *) qname, sizeof(qname), "%s:%s",
4541 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004542 qname[sizeof(qname) - 1] = 0;
4543 if (xmlStrEqual(doc->intSubset->name, qname))
4544 goto name_ok;
4545 }
4546 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4547 (xmlStrEqual(root->name, BAD_CAST "html")))
4548 goto name_ok;
4549 VERROR(ctxt->userData,
4550 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4551 root->name, doc->intSubset->name);
4552 return(0);
4553
4554 }
4555 }
4556name_ok:
4557 return(1);
4558}
4559
4560
4561/**
4562 * xmlValidateElement:
4563 * @ctxt: the validation context
4564 * @doc: a document instance
4565 * @elem: an element instance
4566 *
4567 * Try to validate the subtree under an element
4568 *
4569 * returns 1 if valid or 0 otherwise
4570 */
4571
4572int
4573xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4574 xmlNodePtr child;
4575 xmlAttrPtr attr;
4576 xmlChar *value;
4577 int ret = 1;
4578
4579 if (elem == NULL) return(0);
4580
4581 /*
4582 * XInclude elements were added after parsing in the infoset,
4583 * they don't really mean anything validation wise.
4584 */
4585 if ((elem->type == XML_XINCLUDE_START) ||
4586 (elem->type == XML_XINCLUDE_END))
4587 return(1);
4588
4589 CHECK_DTD;
4590
Daniel Veillard10ea86c2001-06-20 13:55:33 +00004591 /*
4592 * Entities references have to be handled separately
4593 */
4594 if (elem->type == XML_ENTITY_REF_NODE) {
4595 return(1);
4596 }
4597
Owen Taylor3473f882001-02-23 17:55:21 +00004598 ret &= xmlValidateOneElement(ctxt, doc, elem);
4599 attr = elem->properties;
4600 while(attr != NULL) {
4601 value = xmlNodeListGetString(doc, attr->children, 0);
4602 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4603 if (value != NULL)
4604 xmlFree(value);
4605 attr= attr->next;
4606 }
4607 child = elem->children;
4608 while (child != NULL) {
4609 ret &= xmlValidateElement(ctxt, doc, child);
4610 child = child->next;
4611 }
4612
4613 return(ret);
4614}
4615
Daniel Veillard8730c562001-02-26 10:49:57 +00004616/**
4617 * xmlValidateRef:
4618 * @ref: A reference to be validated
4619 * @ctxt: Validation context
4620 * @name: Name of ID we are searching for
4621 *
4622 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004623static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004624xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004625 const xmlChar *name) {
4626 xmlAttrPtr id;
4627 xmlAttrPtr attr;
4628
4629 if (ref == NULL)
4630 return;
4631 attr = ref->attr;
4632 if (attr == NULL)
4633 return;
4634 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4635 id = xmlGetID(ctxt->doc, name);
4636 if (id == NULL) {
4637 VERROR(ctxt->userData,
4638 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4639 attr->name, name);
4640 ctxt->valid = 0;
4641 }
4642 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4643 xmlChar *dup, *str = NULL, *cur, save;
4644
4645 dup = xmlStrdup(name);
4646 if (dup == NULL) {
4647 ctxt->valid = 0;
4648 return;
4649 }
4650 cur = dup;
4651 while (*cur != 0) {
4652 str = cur;
4653 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4654 save = *cur;
4655 *cur = 0;
4656 id = xmlGetID(ctxt->doc, str);
4657 if (id == NULL) {
4658 VERROR(ctxt->userData,
4659 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4660 attr->name, str);
4661 ctxt->valid = 0;
4662 }
4663 if (save == 0)
4664 break;
4665 *cur = save;
4666 while (IS_BLANK(*cur)) cur++;
4667 }
4668 xmlFree(dup);
4669 }
4670}
4671
4672/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004673 * xmlWalkValidateList:
4674 * @data: Contents of current link
4675 * @user: Value supplied by the user
4676 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004677 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00004678 */
4679static int
4680xmlWalkValidateList(const void *data, const void *user)
4681{
4682 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4683 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4684 return 1;
4685}
4686
4687/**
4688 * xmlValidateCheckRefCallback:
4689 * @ref_list: List of references
4690 * @ctxt: Validation context
4691 * @name: Name of ID we are searching for
4692 *
4693 */
4694static void
4695xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4696 const xmlChar *name) {
4697 xmlValidateMemo memo;
4698
4699 if (ref_list == NULL)
4700 return;
4701 memo.ctxt = ctxt;
4702 memo.name = name;
4703
4704 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4705
4706}
4707
4708/**
Owen Taylor3473f882001-02-23 17:55:21 +00004709 * xmlValidateDocumentFinal:
4710 * @ctxt: the validation context
4711 * @doc: a document instance
4712 *
4713 * Does the final step for the document validation once all the
4714 * incremental validation steps have been completed
4715 *
4716 * basically it does the following checks described by the XML Rec
4717 *
4718 *
4719 * returns 1 if valid or 0 otherwise
4720 */
4721
4722int
4723xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4724 xmlRefTablePtr table;
4725
4726 if (doc == NULL) {
4727 xmlGenericError(xmlGenericErrorContext,
4728 "xmlValidateDocumentFinal: doc == NULL\n");
4729 return(0);
4730 }
4731
4732 /*
4733 * Check all the NOTATION/NOTATIONS attributes
4734 */
4735 /*
4736 * Check all the ENTITY/ENTITIES attributes definition for validity
4737 */
4738 /*
4739 * Check all the IDREF/IDREFS attributes definition for validity
4740 */
4741 table = (xmlRefTablePtr) doc->refs;
4742 ctxt->doc = doc;
4743 ctxt->valid = 1;
4744 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4745 return(ctxt->valid);
4746}
4747
4748/**
4749 * xmlValidateDtd:
4750 * @ctxt: the validation context
4751 * @doc: a document instance
4752 * @dtd: a dtd instance
4753 *
4754 * Try to validate the document against the dtd instance
4755 *
4756 * basically it does check all the definitions in the DtD.
4757 *
4758 * returns 1 if valid or 0 otherwise
4759 */
4760
4761int
4762xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4763 int ret;
4764 xmlDtdPtr oldExt;
4765 xmlNodePtr root;
4766
4767 if (dtd == NULL) return(0);
4768 if (doc == NULL) return(0);
4769 oldExt = doc->extSubset;
4770 doc->extSubset = dtd;
4771 ret = xmlValidateRoot(ctxt, doc);
4772 if (ret == 0) {
4773 doc->extSubset = oldExt;
4774 return(ret);
4775 }
4776 if (doc->ids != NULL) {
4777 xmlFreeIDTable(doc->ids);
4778 doc->ids = NULL;
4779 }
4780 if (doc->refs != NULL) {
4781 xmlFreeRefTable(doc->refs);
4782 doc->refs = NULL;
4783 }
4784 root = xmlDocGetRootElement(doc);
4785 ret = xmlValidateElement(ctxt, doc, root);
4786 ret &= xmlValidateDocumentFinal(ctxt, doc);
4787 doc->extSubset = oldExt;
4788 return(ret);
4789}
4790
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004791static void
Owen Taylor3473f882001-02-23 17:55:21 +00004792xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004793 const xmlChar *name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004794 if (cur == NULL)
4795 return;
4796 switch (cur->atype) {
4797 case XML_ATTRIBUTE_CDATA:
4798 case XML_ATTRIBUTE_ID:
4799 case XML_ATTRIBUTE_IDREF :
4800 case XML_ATTRIBUTE_IDREFS:
4801 case XML_ATTRIBUTE_NMTOKEN:
4802 case XML_ATTRIBUTE_NMTOKENS:
4803 case XML_ATTRIBUTE_ENUMERATION:
4804 break;
4805 case XML_ATTRIBUTE_ENTITY:
4806 case XML_ATTRIBUTE_ENTITIES:
4807 case XML_ATTRIBUTE_NOTATION:
4808 if (cur->defaultValue != NULL) {
4809 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4810 cur->name, cur->atype, cur->defaultValue);
4811 }
4812 if (cur->tree != NULL) {
4813 xmlEnumerationPtr tree = cur->tree;
4814 while (tree != NULL) {
4815 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4816 cur->name, cur->atype, tree->name);
4817 tree = tree->next;
4818 }
4819 }
4820 }
4821}
4822
4823/**
4824 * xmlValidateDtdFinal:
4825 * @ctxt: the validation context
4826 * @doc: a document instance
4827 *
4828 * Does the final step for the dtds validation once all the
4829 * subsets have been parsed
4830 *
4831 * basically it does the following checks described by the XML Rec
4832 * - check that ENTITY and ENTITIES type attributes default or
4833 * possible values matches one of the defined entities.
4834 * - check that NOTATION type attributes default or
4835 * possible values matches one of the defined notations.
4836 *
4837 * returns 1 if valid or 0 otherwise
4838 */
4839
4840int
4841xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4842 int ret = 1;
4843 xmlDtdPtr dtd;
4844 xmlAttributeTablePtr table;
4845
4846 if (doc == NULL) return(0);
4847 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4848 return(0);
4849 ctxt->doc = doc;
4850 ctxt->valid = ret;
4851 dtd = doc->intSubset;
4852 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4853 table = (xmlAttributeTablePtr) dtd->attributes;
4854 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4855 }
4856 dtd = doc->extSubset;
4857 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4858 table = (xmlAttributeTablePtr) dtd->attributes;
4859 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4860 }
4861 return(ctxt->valid);
4862}
4863
4864/**
4865 * xmlValidateDocument:
4866 * @ctxt: the validation context
4867 * @doc: a document instance
4868 *
4869 * Try to validate the document instance
4870 *
4871 * basically it does the all the checks described by the XML Rec
4872 * i.e. validates the internal and external subset (if present)
4873 * and validate the document tree.
4874 *
4875 * returns 1 if valid or 0 otherwise
4876 */
4877
4878int
4879xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4880 int ret;
4881 xmlNodePtr root;
4882
4883 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4884 return(0);
4885 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4886 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4887 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4888 doc->intSubset->SystemID);
4889 if (doc->extSubset == NULL) {
4890 if (doc->intSubset->SystemID != NULL) {
4891 VERROR(ctxt->userData,
4892 "Could not load the external subset \"%s\"\n",
4893 doc->intSubset->SystemID);
4894 } else {
4895 VERROR(ctxt->userData,
4896 "Could not load the external subset \"%s\"\n",
4897 doc->intSubset->ExternalID);
4898 }
4899 return(0);
4900 }
4901 }
4902
4903 if (doc->ids != NULL) {
4904 xmlFreeIDTable(doc->ids);
4905 doc->ids = NULL;
4906 }
4907 if (doc->refs != NULL) {
4908 xmlFreeRefTable(doc->refs);
4909 doc->refs = NULL;
4910 }
4911 ret = xmlValidateDtdFinal(ctxt, doc);
4912 if (!xmlValidateRoot(ctxt, doc)) return(0);
4913
4914 root = xmlDocGetRootElement(doc);
4915 ret &= xmlValidateElement(ctxt, doc, root);
4916 ret &= xmlValidateDocumentFinal(ctxt, doc);
4917 return(ret);
4918}
4919
4920
4921/************************************************************************
4922 * *
4923 * Routines for dynamic validation editing *
4924 * *
4925 ************************************************************************/
4926
4927/**
4928 * xmlValidGetPotentialChildren:
4929 * @ctree: an element content tree
4930 * @list: an array to store the list of child names
4931 * @len: a pointer to the number of element in the list
4932 * @max: the size of the array
4933 *
4934 * Build/extend a list of potential children allowed by the content tree
4935 *
4936 * returns the number of element in the list, or -1 in case of error.
4937 */
4938
4939int
4940xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4941 int *len, int max) {
4942 int i;
4943
4944 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4945 return(-1);
4946 if (*len >= max) return(*len);
4947
4948 switch (ctree->type) {
4949 case XML_ELEMENT_CONTENT_PCDATA:
4950 for (i = 0; i < *len;i++)
4951 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4952 list[(*len)++] = BAD_CAST "#PCDATA";
4953 break;
4954 case XML_ELEMENT_CONTENT_ELEMENT:
4955 for (i = 0; i < *len;i++)
4956 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4957 list[(*len)++] = ctree->name;
4958 break;
4959 case XML_ELEMENT_CONTENT_SEQ:
4960 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4961 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4962 break;
4963 case XML_ELEMENT_CONTENT_OR:
4964 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4965 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4966 break;
4967 }
4968
4969 return(*len);
4970}
4971
4972/**
4973 * xmlValidGetValidElements:
4974 * @prev: an element to insert after
4975 * @next: an element to insert next
4976 * @list: an array to store the list of child names
4977 * @max: the size of the array
4978 *
4979 * This function returns the list of authorized children to insert
4980 * within an existing tree while respecting the validity constraints
4981 * forced by the Dtd. The insertion point is defined using @prev and
4982 * @next in the following ways:
4983 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4984 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4985 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4986 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4987 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4988 *
4989 * pointers to the element names are inserted at the beginning of the array
4990 * and do not need to be freed.
4991 *
4992 * returns the number of element in the list, or -1 in case of error. If
4993 * the function returns the value @max the caller is invited to grow the
4994 * receiving array and retry.
4995 */
4996
4997int
4998xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4999 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005000 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005001 int nb_valid_elements = 0;
5002 const xmlChar *elements[256];
5003 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005004 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005005
5006 xmlNode *ref_node;
5007 xmlNode *parent;
5008 xmlNode *test_node;
5009
5010 xmlNode *prev_next;
5011 xmlNode *next_prev;
5012 xmlNode *parent_childs;
5013 xmlNode *parent_last;
5014
5015 xmlElement *element_desc;
5016
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005017 vctxt.userData = NULL;
5018 vctxt.error = NULL;
5019 vctxt.warning = NULL;
5020
Owen Taylor3473f882001-02-23 17:55:21 +00005021 if (prev == NULL && next == NULL)
5022 return(-1);
5023
5024 if (list == NULL) return(-1);
5025 if (max <= 0) return(-1);
5026
5027 nb_valid_elements = 0;
5028 ref_node = prev ? prev : next;
5029 parent = ref_node->parent;
5030
5031 /*
5032 * Retrieves the parent element declaration
5033 */
5034 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5035 parent->name);
5036 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5037 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5038 parent->name);
5039 if (element_desc == NULL) return(-1);
5040
5041 /*
5042 * Do a backup of the current tree structure
5043 */
5044 prev_next = prev ? prev->next : NULL;
5045 next_prev = next ? next->prev : NULL;
5046 parent_childs = parent->children;
5047 parent_last = parent->last;
5048
5049 /*
5050 * Creates a dummy node and insert it into the tree
5051 */
5052 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5053 test_node->doc = ref_node->doc;
5054 test_node->parent = parent;
5055 test_node->prev = prev;
5056 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005057 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005058
5059 if (prev) prev->next = test_node;
5060 else parent->children = test_node;
5061
5062 if (next) next->prev = test_node;
5063 else parent->last = test_node;
5064
5065 /*
5066 * Insert each potential child node and check if the parent is
5067 * still valid
5068 */
5069 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5070 elements, &nb_elements, 256);
5071
5072 for (i = 0;i < nb_elements;i++) {
5073 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005074 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005075 int j;
5076
5077 for (j = 0; j < nb_valid_elements;j++)
5078 if (xmlStrEqual(elements[i], list[j])) break;
5079 list[nb_valid_elements++] = elements[i];
5080 if (nb_valid_elements >= max) break;
5081 }
5082 }
5083
5084 /*
5085 * Restore the tree structure
5086 */
5087 if (prev) prev->next = prev_next;
5088 if (next) next->prev = next_prev;
5089 parent->children = parent_childs;
5090 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005091
5092 /*
5093 * Free up the dummy node
5094 */
5095 test_node->name = name;
5096 xmlFreeNode(test_node);
5097
Owen Taylor3473f882001-02-23 17:55:21 +00005098 return(nb_valid_elements);
5099}