blob: 55b8212a4f03dc25df632676cf0e2adfbd54c0bc [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) &&
1359 (xmlScanIDAttributeDecl(NULL, elemDef) != 0))
1360 VERROR(ctxt->userData,
1361 "Element %s has too may ID attributes defined : %s\n",
1362 elem, name);
Daniel Veillard48da9102001-08-07 01:10:10 +00001363 /*
1364 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001365 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001366 */
1367 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1368 ((ret->prefix != NULL &&
1369 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1370 ret->nexth = elemDef->attributes;
1371 elemDef->attributes = ret;
1372 } else {
1373 xmlAttributePtr tmp = elemDef->attributes;
1374
1375 while ((tmp != NULL) &&
1376 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1377 ((ret->prefix != NULL &&
1378 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1379 if (tmp->nexth == NULL)
1380 break;
1381 tmp = tmp->nexth;
1382 }
1383 if (tmp != NULL) {
1384 ret->nexth = tmp->nexth;
1385 tmp->nexth = ret;
1386 } else {
1387 ret->nexth = elemDef->attributes;
1388 elemDef->attributes = ret;
1389 }
1390 }
Owen Taylor3473f882001-02-23 17:55:21 +00001391 }
1392
1393 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001394 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001395 */
1396 ret->parent = dtd;
1397 ret->doc = dtd->doc;
1398 if (dtd->last == NULL) {
1399 dtd->children = dtd->last = (xmlNodePtr) ret;
1400 } else {
1401 dtd->last->next = (xmlNodePtr) ret;
1402 ret->prev = dtd->last;
1403 dtd->last = (xmlNodePtr) ret;
1404 }
1405 return(ret);
1406}
1407
1408/**
1409 * xmlFreeAttributeTable:
1410 * @table: An attribute table
1411 *
1412 * Deallocate the memory used by an entities hash table.
1413 */
1414void
1415xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1416 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1417}
1418
1419/**
1420 * xmlCopyAttribute:
1421 * @attr: An attribute
1422 *
1423 * Build a copy of an attribute.
1424 *
1425 * Returns the new xmlAttributePtr or NULL in case of error.
1426 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001427static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001428xmlCopyAttribute(xmlAttributePtr attr) {
1429 xmlAttributePtr cur;
1430
1431 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1432 if (cur == NULL) {
1433 xmlGenericError(xmlGenericErrorContext,
1434 "xmlCopyAttribute: out of memory !\n");
1435 return(NULL);
1436 }
1437 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001438 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001439 cur->atype = attr->atype;
1440 cur->def = attr->def;
1441 cur->tree = xmlCopyEnumeration(attr->tree);
1442 if (attr->elem != NULL)
1443 cur->elem = xmlStrdup(attr->elem);
1444 if (attr->name != NULL)
1445 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001446 if (attr->prefix != NULL)
1447 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001448 if (attr->defaultValue != NULL)
1449 cur->defaultValue = xmlStrdup(attr->defaultValue);
1450 return(cur);
1451}
1452
1453/**
1454 * xmlCopyAttributeTable:
1455 * @table: An attribute table
1456 *
1457 * Build a copy of an attribute table.
1458 *
1459 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1460 */
1461xmlAttributeTablePtr
1462xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1463 return((xmlAttributeTablePtr) xmlHashCopy(table,
1464 (xmlHashCopier) xmlCopyAttribute));
1465}
1466
1467/**
1468 * xmlDumpAttributeDecl:
1469 * @buf: the XML buffer output
1470 * @attr: An attribute declaration
1471 *
1472 * This will dump the content of the attribute declaration as an XML
1473 * DTD definition
1474 */
1475void
1476xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1477 xmlBufferWriteChar(buf, "<!ATTLIST ");
1478 xmlBufferWriteCHAR(buf, attr->elem);
1479 xmlBufferWriteChar(buf, " ");
1480 if (attr->prefix != NULL) {
1481 xmlBufferWriteCHAR(buf, attr->prefix);
1482 xmlBufferWriteChar(buf, ":");
1483 }
1484 xmlBufferWriteCHAR(buf, attr->name);
1485 switch (attr->atype) {
1486 case XML_ATTRIBUTE_CDATA:
1487 xmlBufferWriteChar(buf, " CDATA");
1488 break;
1489 case XML_ATTRIBUTE_ID:
1490 xmlBufferWriteChar(buf, " ID");
1491 break;
1492 case XML_ATTRIBUTE_IDREF:
1493 xmlBufferWriteChar(buf, " IDREF");
1494 break;
1495 case XML_ATTRIBUTE_IDREFS:
1496 xmlBufferWriteChar(buf, " IDREFS");
1497 break;
1498 case XML_ATTRIBUTE_ENTITY:
1499 xmlBufferWriteChar(buf, " ENTITY");
1500 break;
1501 case XML_ATTRIBUTE_ENTITIES:
1502 xmlBufferWriteChar(buf, " ENTITIES");
1503 break;
1504 case XML_ATTRIBUTE_NMTOKEN:
1505 xmlBufferWriteChar(buf, " NMTOKEN");
1506 break;
1507 case XML_ATTRIBUTE_NMTOKENS:
1508 xmlBufferWriteChar(buf, " NMTOKENS");
1509 break;
1510 case XML_ATTRIBUTE_ENUMERATION:
1511 xmlBufferWriteChar(buf, " (");
1512 xmlDumpEnumeration(buf, attr->tree);
1513 break;
1514 case XML_ATTRIBUTE_NOTATION:
1515 xmlBufferWriteChar(buf, " NOTATION (");
1516 xmlDumpEnumeration(buf, attr->tree);
1517 break;
1518 default:
1519 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001520 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001521 attr->atype);
1522 }
1523 switch (attr->def) {
1524 case XML_ATTRIBUTE_NONE:
1525 break;
1526 case XML_ATTRIBUTE_REQUIRED:
1527 xmlBufferWriteChar(buf, " #REQUIRED");
1528 break;
1529 case XML_ATTRIBUTE_IMPLIED:
1530 xmlBufferWriteChar(buf, " #IMPLIED");
1531 break;
1532 case XML_ATTRIBUTE_FIXED:
1533 xmlBufferWriteChar(buf, " #FIXED");
1534 break;
1535 default:
1536 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001537 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001538 attr->def);
1539 }
1540 if (attr->defaultValue != NULL) {
1541 xmlBufferWriteChar(buf, " ");
1542 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1543 }
1544 xmlBufferWriteChar(buf, ">\n");
1545}
1546
1547/**
1548 * xmlDumpAttributeTable:
1549 * @buf: the XML buffer output
1550 * @table: An attribute table
1551 *
1552 * This will dump the content of the attribute table as an XML DTD definition
1553 */
1554void
1555xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1556 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1557}
1558
1559/************************************************************************
1560 * *
1561 * NOTATIONs *
1562 * *
1563 ************************************************************************/
1564/**
1565 * xmlCreateNotationTable:
1566 *
1567 * create and initialize an empty notation hash table.
1568 *
1569 * Returns the xmlNotationTablePtr just created or NULL in case
1570 * of error.
1571 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001572static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001573xmlCreateNotationTable(void) {
1574 return(xmlHashCreate(0));
1575}
1576
1577/**
1578 * xmlFreeNotation:
1579 * @not: A notation
1580 *
1581 * Deallocate the memory used by an notation definition
1582 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001583static void
Owen Taylor3473f882001-02-23 17:55:21 +00001584xmlFreeNotation(xmlNotationPtr nota) {
1585 if (nota == NULL) return;
1586 if (nota->name != NULL)
1587 xmlFree((xmlChar *) nota->name);
1588 if (nota->PublicID != NULL)
1589 xmlFree((xmlChar *) nota->PublicID);
1590 if (nota->SystemID != NULL)
1591 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001592 xmlFree(nota);
1593}
1594
1595
1596/**
1597 * xmlAddNotationDecl:
1598 * @dtd: pointer to the DTD
1599 * @ctxt: the validation context
1600 * @name: the entity name
1601 * @PublicID: the public identifier or NULL
1602 * @SystemID: the system identifier or NULL
1603 *
1604 * Register a new notation declaration
1605 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001606 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001607 */
1608xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001609xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001610 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001611 const xmlChar *PublicID, const xmlChar *SystemID) {
1612 xmlNotationPtr ret;
1613 xmlNotationTablePtr table;
1614
1615 if (dtd == NULL) {
1616 xmlGenericError(xmlGenericErrorContext,
1617 "xmlAddNotationDecl: dtd == NULL\n");
1618 return(NULL);
1619 }
1620 if (name == NULL) {
1621 xmlGenericError(xmlGenericErrorContext,
1622 "xmlAddNotationDecl: name == NULL\n");
1623 return(NULL);
1624 }
1625 if ((PublicID == NULL) && (SystemID == NULL)) {
1626 xmlGenericError(xmlGenericErrorContext,
1627 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
1628 }
1629
1630 /*
1631 * Create the Notation table if needed.
1632 */
1633 table = (xmlNotationTablePtr) dtd->notations;
1634 if (table == NULL)
1635 dtd->notations = table = xmlCreateNotationTable();
1636 if (table == NULL) {
1637 xmlGenericError(xmlGenericErrorContext,
1638 "xmlAddNotationDecl: Table creation failed!\n");
1639 return(NULL);
1640 }
1641
1642 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1643 if (ret == NULL) {
1644 xmlGenericError(xmlGenericErrorContext,
1645 "xmlAddNotationDecl: out of memory\n");
1646 return(NULL);
1647 }
1648 memset(ret, 0, sizeof(xmlNotation));
1649
1650 /*
1651 * fill the structure.
1652 */
1653 ret->name = xmlStrdup(name);
1654 if (SystemID != NULL)
1655 ret->SystemID = xmlStrdup(SystemID);
1656 if (PublicID != NULL)
1657 ret->PublicID = xmlStrdup(PublicID);
1658
1659 /*
1660 * Validity Check:
1661 * Check the DTD for previous declarations of the ATTLIST
1662 */
1663 if (xmlHashAddEntry(table, name, ret)) {
1664 xmlGenericError(xmlGenericErrorContext,
1665 "xmlAddNotationDecl: %s already defined\n", name);
1666 xmlFreeNotation(ret);
1667 return(NULL);
1668 }
1669 return(ret);
1670}
1671
1672/**
1673 * xmlFreeNotationTable:
1674 * @table: An notation table
1675 *
1676 * Deallocate the memory used by an entities hash table.
1677 */
1678void
1679xmlFreeNotationTable(xmlNotationTablePtr table) {
1680 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1681}
1682
1683/**
1684 * xmlCopyNotation:
1685 * @nota: A notation
1686 *
1687 * Build a copy of a notation.
1688 *
1689 * Returns the new xmlNotationPtr or NULL in case of error.
1690 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001691static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001692xmlCopyNotation(xmlNotationPtr nota) {
1693 xmlNotationPtr cur;
1694
1695 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1696 if (cur == NULL) {
1697 xmlGenericError(xmlGenericErrorContext,
1698 "xmlCopyNotation: out of memory !\n");
1699 return(NULL);
1700 }
1701 if (nota->name != NULL)
1702 cur->name = xmlStrdup(nota->name);
1703 else
1704 cur->name = NULL;
1705 if (nota->PublicID != NULL)
1706 cur->PublicID = xmlStrdup(nota->PublicID);
1707 else
1708 cur->PublicID = NULL;
1709 if (nota->SystemID != NULL)
1710 cur->SystemID = xmlStrdup(nota->SystemID);
1711 else
1712 cur->SystemID = NULL;
1713 return(cur);
1714}
1715
1716/**
1717 * xmlCopyNotationTable:
1718 * @table: A notation table
1719 *
1720 * Build a copy of a notation table.
1721 *
1722 * Returns the new xmlNotationTablePtr or NULL in case of error.
1723 */
1724xmlNotationTablePtr
1725xmlCopyNotationTable(xmlNotationTablePtr table) {
1726 return((xmlNotationTablePtr) xmlHashCopy(table,
1727 (xmlHashCopier) xmlCopyNotation));
1728}
1729
1730/**
1731 * xmlDumpNotationDecl:
1732 * @buf: the XML buffer output
1733 * @nota: A notation declaration
1734 *
1735 * This will dump the content the notation declaration as an XML DTD definition
1736 */
1737void
1738xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1739 xmlBufferWriteChar(buf, "<!NOTATION ");
1740 xmlBufferWriteCHAR(buf, nota->name);
1741 if (nota->PublicID != NULL) {
1742 xmlBufferWriteChar(buf, " PUBLIC ");
1743 xmlBufferWriteQuotedString(buf, nota->PublicID);
1744 if (nota->SystemID != NULL) {
1745 xmlBufferWriteChar(buf, " ");
1746 xmlBufferWriteCHAR(buf, nota->SystemID);
1747 }
1748 } else {
1749 xmlBufferWriteChar(buf, " SYSTEM ");
1750 xmlBufferWriteCHAR(buf, nota->SystemID);
1751 }
1752 xmlBufferWriteChar(buf, " >\n");
1753}
1754
1755/**
1756 * xmlDumpNotationTable:
1757 * @buf: the XML buffer output
1758 * @table: A notation table
1759 *
1760 * This will dump the content of the notation table as an XML DTD definition
1761 */
1762void
1763xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1764 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1765}
1766
1767/************************************************************************
1768 * *
1769 * IDs *
1770 * *
1771 ************************************************************************/
1772/**
1773 * xmlCreateIDTable:
1774 *
1775 * create and initialize an empty id hash table.
1776 *
1777 * Returns the xmlIDTablePtr just created or NULL in case
1778 * of error.
1779 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001780static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001781xmlCreateIDTable(void) {
1782 return(xmlHashCreate(0));
1783}
1784
1785/**
1786 * xmlFreeID:
1787 * @not: A id
1788 *
1789 * Deallocate the memory used by an id definition
1790 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001791static void
Owen Taylor3473f882001-02-23 17:55:21 +00001792xmlFreeID(xmlIDPtr id) {
1793 if (id == NULL) return;
1794 if (id->value != NULL)
1795 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001796 xmlFree(id);
1797}
1798
1799/**
1800 * xmlAddID:
1801 * @ctxt: the validation context
1802 * @doc: pointer to the document
1803 * @value: the value name
1804 * @attr: the attribute holding the ID
1805 *
1806 * Register a new id declaration
1807 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001808 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001809 */
1810xmlIDPtr
1811xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1812 xmlAttrPtr attr) {
1813 xmlIDPtr ret;
1814 xmlIDTablePtr table;
1815
1816 if (doc == NULL) {
1817 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001818 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001819 return(NULL);
1820 }
1821 if (value == NULL) {
1822 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001823 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001824 return(NULL);
1825 }
1826 if (attr == NULL) {
1827 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001828 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001829 return(NULL);
1830 }
1831
1832 /*
1833 * Create the ID table if needed.
1834 */
1835 table = (xmlIDTablePtr) doc->ids;
1836 if (table == NULL)
1837 doc->ids = table = xmlCreateIDTable();
1838 if (table == NULL) {
1839 xmlGenericError(xmlGenericErrorContext,
1840 "xmlAddID: Table creation failed!\n");
1841 return(NULL);
1842 }
1843
1844 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1845 if (ret == NULL) {
1846 xmlGenericError(xmlGenericErrorContext,
1847 "xmlAddID: out of memory\n");
1848 return(NULL);
1849 }
1850
1851 /*
1852 * fill the structure.
1853 */
1854 ret->value = xmlStrdup(value);
1855 ret->attr = attr;
1856
1857 if (xmlHashAddEntry(table, value, ret) < 0) {
1858 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001859 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001860 */
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00001861 if (ctxt != NULL)
1862 VERROR(ctxt->userData, "ID %s already defined\n", value);
Owen Taylor3473f882001-02-23 17:55:21 +00001863 xmlFreeID(ret);
1864 return(NULL);
1865 }
1866 return(ret);
1867}
1868
1869/**
1870 * xmlFreeIDTable:
1871 * @table: An id table
1872 *
1873 * Deallocate the memory used by an ID hash table.
1874 */
1875void
1876xmlFreeIDTable(xmlIDTablePtr table) {
1877 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1878}
1879
1880/**
1881 * xmlIsID:
1882 * @doc: the document
1883 * @elem: the element carrying the attribute
1884 * @attr: the attribute
1885 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001886 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00001887 * then this is simple, otherwise we use an heuristic: name ID (upper
1888 * or lowercase).
1889 *
1890 * Returns 0 or 1 depending on the lookup result
1891 */
1892int
1893xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1894 if (doc == NULL) return(0);
1895 if (attr == NULL) return(0);
1896 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1897 return(0);
1898 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1899 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1900 (xmlStrEqual(BAD_CAST "name", attr->name)))
1901 return(1);
1902 return(0);
1903 } else {
1904 xmlAttributePtr attrDecl;
1905
1906 if (elem == NULL) return(0);
1907 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1908 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1909 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1910 attr->name);
1911
1912 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1913 return(1);
1914 }
1915 return(0);
1916}
1917
1918/**
1919 * xmlRemoveID
1920 * @doc: the document
1921 * @attr: the attribute
1922 *
1923 * Remove the given attribute from the ID table maintained internally.
1924 *
1925 * Returns -1 if the lookup failed and 0 otherwise
1926 */
1927int
1928xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1929 xmlAttrPtr cur;
1930 xmlIDTablePtr table;
1931 xmlChar *ID;
1932
1933 if (doc == NULL) return(-1);
1934 if (attr == NULL) return(-1);
1935 table = (xmlIDTablePtr) doc->ids;
1936 if (table == NULL)
1937 return(-1);
1938
1939 if (attr == NULL)
1940 return(-1);
1941 ID = xmlNodeListGetString(doc, attr->children, 1);
1942 if (ID == NULL)
1943 return(-1);
1944 cur = xmlHashLookup(table, ID);
1945 if (cur != attr) {
1946 xmlFree(ID);
1947 return(-1);
1948 }
1949 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1950 xmlFree(ID);
1951 return(0);
1952}
1953
1954/**
1955 * xmlGetID:
1956 * @doc: pointer to the document
1957 * @ID: the ID value
1958 *
1959 * Search the attribute declaring the given ID
1960 *
1961 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1962 */
1963xmlAttrPtr
1964xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
1965 xmlIDTablePtr table;
1966 xmlIDPtr id;
1967
1968 if (doc == NULL) {
1969 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
1970 return(NULL);
1971 }
1972
1973 if (ID == NULL) {
1974 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
1975 return(NULL);
1976 }
1977
1978 table = (xmlIDTablePtr) doc->ids;
1979 if (table == NULL)
1980 return(NULL);
1981
1982 id = xmlHashLookup(table, ID);
1983 if (id == NULL)
1984 return(NULL);
1985 return(id->attr);
1986}
1987
1988/************************************************************************
1989 * *
1990 * Refs *
1991 * *
1992 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00001993typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00001994{
1995 xmlListPtr l;
1996 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00001997} xmlRemoveMemo;
1998
1999typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2000
2001typedef struct xmlValidateMemo_t
2002{
2003 xmlValidCtxtPtr ctxt;
2004 const xmlChar *name;
2005} xmlValidateMemo;
2006
2007typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002008
2009/**
2010 * xmlCreateRefTable:
2011 *
2012 * create and initialize an empty ref hash table.
2013 *
2014 * Returns the xmlRefTablePtr just created or NULL in case
2015 * of error.
2016 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002017static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002018xmlCreateRefTable(void) {
2019 return(xmlHashCreate(0));
2020}
2021
2022/**
2023 * xmlFreeRef:
2024 * @lk: A list link
2025 *
2026 * Deallocate the memory used by a ref definition
2027 */
2028static void
2029xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002030 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2031 if (ref == NULL) return;
2032 if (ref->value != NULL)
2033 xmlFree((xmlChar *)ref->value);
2034 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002035}
2036
2037/**
2038 * xmlFreeRefList:
2039 * @list_ref: A list of references.
2040 *
2041 * Deallocate the memory used by a list of references
2042 */
2043static void
2044xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002045 if (list_ref == NULL) return;
2046 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002047}
2048
2049/**
2050 * xmlWalkRemoveRef:
2051 * @data: Contents of current link
2052 * @user: Value supplied by the user
2053 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002054 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002055 */
2056static int
2057xmlWalkRemoveRef(const void *data, const void *user)
2058{
Daniel Veillard37721922001-05-04 15:21:12 +00002059 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2060 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2061 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002062
Daniel Veillard37721922001-05-04 15:21:12 +00002063 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2064 xmlListRemoveFirst(ref_list, (void *)data);
2065 return 0;
2066 }
2067 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002068}
2069
2070/**
2071 * xmlAddRef:
2072 * @ctxt: the validation context
2073 * @doc: pointer to the document
2074 * @value: the value name
2075 * @attr: the attribute holding the Ref
2076 *
2077 * Register a new ref declaration
2078 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002079 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002080 */
2081xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002082xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002083 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002084 xmlRefPtr ret;
2085 xmlRefTablePtr table;
2086 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002087
Daniel Veillard37721922001-05-04 15:21:12 +00002088 if (doc == NULL) {
2089 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002090 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002091 return(NULL);
2092 }
2093 if (value == NULL) {
2094 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002095 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002096 return(NULL);
2097 }
2098 if (attr == NULL) {
2099 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002100 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002101 return(NULL);
2102 }
Owen Taylor3473f882001-02-23 17:55:21 +00002103
Daniel Veillard37721922001-05-04 15:21:12 +00002104 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002105 * Create the Ref table if needed.
2106 */
Daniel Veillard37721922001-05-04 15:21:12 +00002107 table = (xmlRefTablePtr) doc->refs;
2108 if (table == NULL)
2109 doc->refs = table = xmlCreateRefTable();
2110 if (table == NULL) {
2111 xmlGenericError(xmlGenericErrorContext,
2112 "xmlAddRef: Table creation failed!\n");
2113 return(NULL);
2114 }
Owen Taylor3473f882001-02-23 17:55:21 +00002115
Daniel Veillard37721922001-05-04 15:21:12 +00002116 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2117 if (ret == NULL) {
2118 xmlGenericError(xmlGenericErrorContext,
2119 "xmlAddRef: out of memory\n");
2120 return(NULL);
2121 }
Owen Taylor3473f882001-02-23 17:55:21 +00002122
Daniel Veillard37721922001-05-04 15:21:12 +00002123 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002124 * fill the structure.
2125 */
Daniel Veillard37721922001-05-04 15:21:12 +00002126 ret->value = xmlStrdup(value);
2127 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002128
Daniel Veillard37721922001-05-04 15:21:12 +00002129 /* To add a reference :-
2130 * References are maintained as a list of references,
2131 * Lookup the entry, if no entry create new nodelist
2132 * Add the owning node to the NodeList
2133 * Return the ref
2134 */
Owen Taylor3473f882001-02-23 17:55:21 +00002135
Daniel Veillard37721922001-05-04 15:21:12 +00002136 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2137 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2138 xmlGenericError(xmlGenericErrorContext,
2139 "xmlAddRef: Reference list creation failed!\n");
2140 return(NULL);
2141 }
2142 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2143 xmlListDelete(ref_list);
2144 xmlGenericError(xmlGenericErrorContext,
2145 "xmlAddRef: Reference list insertion failed!\n");
2146 return(NULL);
2147 }
2148 }
2149 xmlListInsert(ref_list, ret);
2150 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002151}
2152
2153/**
2154 * xmlFreeRefTable:
2155 * @table: An ref table
2156 *
2157 * Deallocate the memory used by an Ref hash table.
2158 */
2159void
2160xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002161 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002162}
2163
2164/**
2165 * xmlIsRef:
2166 * @doc: the document
2167 * @elem: the element carrying the attribute
2168 * @attr: the attribute
2169 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002170 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002171 * then this is simple, otherwise we use an heuristic: name Ref (upper
2172 * or lowercase).
2173 *
2174 * Returns 0 or 1 depending on the lookup result
2175 */
2176int
2177xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002178 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2179 return(0);
2180 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2181 /* TODO @@@ */
2182 return(0);
2183 } else {
2184 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002185
Daniel Veillard37721922001-05-04 15:21:12 +00002186 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2187 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2188 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2189 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002190
Daniel Veillard37721922001-05-04 15:21:12 +00002191 if ((attrDecl != NULL) &&
2192 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2193 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2194 return(1);
2195 }
2196 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002197}
2198
2199/**
2200 * xmlRemoveRef
2201 * @doc: the document
2202 * @attr: the attribute
2203 *
2204 * Remove the given attribute from the Ref table maintained internally.
2205 *
2206 * Returns -1 if the lookup failed and 0 otherwise
2207 */
2208int
2209xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002210 xmlListPtr ref_list;
2211 xmlRefTablePtr table;
2212 xmlChar *ID;
2213 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002214
Daniel Veillard37721922001-05-04 15:21:12 +00002215 if (doc == NULL) return(-1);
2216 if (attr == NULL) return(-1);
2217 table = (xmlRefTablePtr) doc->refs;
2218 if (table == NULL)
2219 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002220
Daniel Veillard37721922001-05-04 15:21:12 +00002221 if (attr == NULL)
2222 return(-1);
2223 ID = xmlNodeListGetString(doc, attr->children, 1);
2224 if (ID == NULL)
2225 return(-1);
2226 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002227
Daniel Veillard37721922001-05-04 15:21:12 +00002228 if(ref_list == NULL) {
2229 xmlFree(ID);
2230 return (-1);
2231 }
2232 /* At this point, ref_list refers to a list of references which
2233 * have the same key as the supplied attr. Our list of references
2234 * is ordered by reference address and we don't have that information
2235 * here to use when removing. We'll have to walk the list and
2236 * check for a matching attribute, when we find one stop the walk
2237 * and remove the entry.
2238 * The list is ordered by reference, so that means we don't have the
2239 * key. Passing the list and the reference to the walker means we
2240 * will have enough data to be able to remove the entry.
2241 */
2242 target.l = ref_list;
2243 target.ap = attr;
2244
2245 /* Remove the supplied attr from our list */
2246 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002247
Daniel Veillard37721922001-05-04 15:21:12 +00002248 /*If the list is empty then remove the list entry in the hash */
2249 if (xmlListEmpty(ref_list))
2250 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2251 xmlFreeRefList);
2252 xmlFree(ID);
2253 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002254}
2255
2256/**
2257 * xmlGetRefs:
2258 * @doc: pointer to the document
2259 * @ID: the ID value
2260 *
2261 * Find the set of references for the supplied ID.
2262 *
2263 * Returns NULL if not found, otherwise node set for the ID.
2264 */
2265xmlListPtr
2266xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002267 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002268
Daniel Veillard37721922001-05-04 15:21:12 +00002269 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002270 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002271 return(NULL);
2272 }
Owen Taylor3473f882001-02-23 17:55:21 +00002273
Daniel Veillard37721922001-05-04 15:21:12 +00002274 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002275 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002276 return(NULL);
2277 }
Owen Taylor3473f882001-02-23 17:55:21 +00002278
Daniel Veillard37721922001-05-04 15:21:12 +00002279 table = (xmlRefTablePtr) doc->refs;
2280 if (table == NULL)
2281 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002282
Daniel Veillard37721922001-05-04 15:21:12 +00002283 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002284}
2285
2286/************************************************************************
2287 * *
2288 * Routines for validity checking *
2289 * *
2290 ************************************************************************/
2291
2292/**
2293 * xmlGetDtdElementDesc:
2294 * @dtd: a pointer to the DtD to search
2295 * @name: the element name
2296 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002297 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002298 *
2299 * returns the xmlElementPtr if found or NULL
2300 */
2301
2302xmlElementPtr
2303xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2304 xmlElementTablePtr table;
2305 xmlElementPtr cur;
2306 xmlChar *uqname = NULL, *prefix = NULL;
2307
2308 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002309 if (dtd->elements == NULL)
2310 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002311 table = (xmlElementTablePtr) dtd->elements;
2312
2313 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002314 if (uqname != NULL)
2315 name = uqname;
2316 cur = xmlHashLookup2(table, name, prefix);
2317 if (prefix != NULL) xmlFree(prefix);
2318 if (uqname != NULL) xmlFree(uqname);
2319 return(cur);
2320}
2321/**
2322 * xmlGetDtdElementDesc2:
2323 * @dtd: a pointer to the DtD to search
2324 * @name: the element name
2325 * @create: create an empty description if not found
2326 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002327 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002328 *
2329 * returns the xmlElementPtr if found or NULL
2330 */
2331
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002332static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002333xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2334 xmlElementTablePtr table;
2335 xmlElementPtr cur;
2336 xmlChar *uqname = NULL, *prefix = NULL;
2337
2338 if (dtd == NULL) return(NULL);
2339 if (dtd->elements == NULL) {
2340 if (!create)
2341 return(NULL);
2342 /*
2343 * Create the Element table if needed.
2344 */
2345 table = (xmlElementTablePtr) dtd->elements;
2346 if (table == NULL) {
2347 table = xmlCreateElementTable();
2348 dtd->elements = (void *) table;
2349 }
2350 if (table == NULL) {
2351 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002352 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002353 return(NULL);
2354 }
2355 }
2356 table = (xmlElementTablePtr) dtd->elements;
2357
2358 uqname = xmlSplitQName2(name, &prefix);
2359 if (uqname != NULL)
2360 name = uqname;
2361 cur = xmlHashLookup2(table, name, prefix);
2362 if ((cur == NULL) && (create)) {
2363 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2364 if (cur == NULL) {
2365 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002366 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002367 return(NULL);
2368 }
2369 memset(cur, 0, sizeof(xmlElement));
2370 cur->type = XML_ELEMENT_DECL;
2371
2372 /*
2373 * fill the structure.
2374 */
2375 cur->name = xmlStrdup(name);
2376 cur->prefix = xmlStrdup(prefix);
2377 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2378
2379 xmlHashAddEntry2(table, name, prefix, cur);
2380 }
2381 if (prefix != NULL) xmlFree(prefix);
2382 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002383 return(cur);
2384}
2385
2386/**
2387 * xmlGetDtdQElementDesc:
2388 * @dtd: a pointer to the DtD to search
2389 * @name: the element name
2390 * @prefix: the element namespace prefix
2391 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002392 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002393 *
2394 * returns the xmlElementPtr if found or NULL
2395 */
2396
Daniel Veillard48da9102001-08-07 01:10:10 +00002397xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002398xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2399 const xmlChar *prefix) {
2400 xmlElementTablePtr table;
2401
2402 if (dtd == NULL) return(NULL);
2403 if (dtd->elements == NULL) return(NULL);
2404 table = (xmlElementTablePtr) dtd->elements;
2405
2406 return(xmlHashLookup2(table, name, prefix));
2407}
2408
2409/**
2410 * xmlGetDtdAttrDesc:
2411 * @dtd: a pointer to the DtD to search
2412 * @elem: the element name
2413 * @name: the attribute name
2414 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002415 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002416 * this element.
2417 *
2418 * returns the xmlAttributePtr if found or NULL
2419 */
2420
2421xmlAttributePtr
2422xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2423 xmlAttributeTablePtr table;
2424 xmlAttributePtr cur;
2425 xmlChar *uqname = NULL, *prefix = NULL;
2426
2427 if (dtd == NULL) return(NULL);
2428 if (dtd->attributes == NULL) return(NULL);
2429
2430 table = (xmlAttributeTablePtr) dtd->attributes;
2431 if (table == NULL)
2432 return(NULL);
2433
2434 uqname = xmlSplitQName2(name, &prefix);
2435
2436 if (uqname != NULL) {
2437 cur = xmlHashLookup3(table, uqname, prefix, elem);
2438 if (prefix != NULL) xmlFree(prefix);
2439 if (uqname != NULL) xmlFree(uqname);
2440 } else
2441 cur = xmlHashLookup3(table, name, NULL, elem);
2442 return(cur);
2443}
2444
2445/**
2446 * xmlGetDtdQAttrDesc:
2447 * @dtd: a pointer to the DtD to search
2448 * @elem: the element name
2449 * @name: the attribute name
2450 * @prefix: the attribute namespace prefix
2451 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002452 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002453 * this element.
2454 *
2455 * returns the xmlAttributePtr if found or NULL
2456 */
2457
Daniel Veillard48da9102001-08-07 01:10:10 +00002458xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002459xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2460 const xmlChar *prefix) {
2461 xmlAttributeTablePtr table;
2462
2463 if (dtd == NULL) return(NULL);
2464 if (dtd->attributes == NULL) return(NULL);
2465 table = (xmlAttributeTablePtr) dtd->attributes;
2466
2467 return(xmlHashLookup3(table, name, prefix, elem));
2468}
2469
2470/**
2471 * xmlGetDtdNotationDesc:
2472 * @dtd: a pointer to the DtD to search
2473 * @name: the notation name
2474 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002475 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002476 *
2477 * returns the xmlNotationPtr if found or NULL
2478 */
2479
2480xmlNotationPtr
2481xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2482 xmlNotationTablePtr table;
2483
2484 if (dtd == NULL) return(NULL);
2485 if (dtd->notations == NULL) return(NULL);
2486 table = (xmlNotationTablePtr) dtd->notations;
2487
2488 return(xmlHashLookup(table, name));
2489}
2490
2491/**
2492 * xmlValidateNotationUse:
2493 * @ctxt: the validation context
2494 * @doc: the document
2495 * @notationName: the notation name to check
2496 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002497 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002498 * - [ VC: Notation Declared ]
2499 *
2500 * returns 1 if valid or 0 otherwise
2501 */
2502
2503int
2504xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2505 const xmlChar *notationName) {
2506 xmlNotationPtr notaDecl;
2507 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2508
2509 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2510 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2511 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2512
2513 if (notaDecl == NULL) {
2514 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2515 notationName);
2516 return(0);
2517 }
2518 return(1);
2519}
2520
2521/**
2522 * xmlIsMixedElement
2523 * @doc: the document
2524 * @name: the element name
2525 *
2526 * Search in the DtDs whether an element accept Mixed content (or ANY)
2527 * basically if it is supposed to accept text childs
2528 *
2529 * returns 0 if no, 1 if yes, and -1 if no element description is available
2530 */
2531
2532int
2533xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2534 xmlElementPtr elemDecl;
2535
2536 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2537
2538 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2539 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2540 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2541 if (elemDecl == NULL) return(-1);
2542 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002543 case XML_ELEMENT_TYPE_UNDEFINED:
2544 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002545 case XML_ELEMENT_TYPE_ELEMENT:
2546 return(0);
2547 case XML_ELEMENT_TYPE_EMPTY:
2548 /*
2549 * return 1 for EMPTY since we want VC error to pop up
2550 * on <empty> </empty> for example
2551 */
2552 case XML_ELEMENT_TYPE_ANY:
2553 case XML_ELEMENT_TYPE_MIXED:
2554 return(1);
2555 }
2556 return(1);
2557}
2558
2559/**
2560 * xmlValidateNameValue:
2561 * @value: an Name value
2562 *
2563 * Validate that the given value match Name production
2564 *
2565 * returns 1 if valid or 0 otherwise
2566 */
2567
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002568static int
Owen Taylor3473f882001-02-23 17:55:21 +00002569xmlValidateNameValue(const xmlChar *value) {
2570 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002571 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002572
2573 if (value == NULL) return(0);
2574 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002575 val = xmlStringCurrentChar(NULL, cur, &len);
2576 cur += len;
2577 if (!IS_LETTER(val) && (val != '_') &&
2578 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002579 return(0);
2580 }
2581
Daniel Veillardd8224e02002-01-13 15:43:22 +00002582 val = xmlStringCurrentChar(NULL, cur, &len);
2583 cur += len;
2584 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2585 (val == '.') || (val == '-') ||
2586 (val == '_') || (val == ':') ||
2587 (IS_COMBINING(val)) ||
2588 (IS_EXTENDER(val))) {
2589 val = xmlStringCurrentChar(NULL, cur, &len);
2590 cur += len;
2591 }
Owen Taylor3473f882001-02-23 17:55:21 +00002592
Daniel Veillardd8224e02002-01-13 15:43:22 +00002593 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002594
2595 return(1);
2596}
2597
2598/**
2599 * xmlValidateNamesValue:
2600 * @value: an Names value
2601 *
2602 * Validate that the given value match Names production
2603 *
2604 * returns 1 if valid or 0 otherwise
2605 */
2606
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002607static int
Owen Taylor3473f882001-02-23 17:55:21 +00002608xmlValidateNamesValue(const xmlChar *value) {
2609 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002610 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002611
2612 if (value == NULL) return(0);
2613 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002614 val = xmlStringCurrentChar(NULL, cur, &len);
2615 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002616
Daniel Veillardd8224e02002-01-13 15:43:22 +00002617 if (!IS_LETTER(val) && (val != '_') &&
2618 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002619 return(0);
2620 }
2621
Daniel Veillardd8224e02002-01-13 15:43:22 +00002622 val = xmlStringCurrentChar(NULL, cur, &len);
2623 cur += len;
2624 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2625 (val == '.') || (val == '-') ||
2626 (val == '_') || (val == ':') ||
2627 (IS_COMBINING(val)) ||
2628 (IS_EXTENDER(val))) {
2629 val = xmlStringCurrentChar(NULL, cur, &len);
2630 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002631 }
2632
Daniel Veillardd8224e02002-01-13 15:43:22 +00002633 while (IS_BLANK(val)) {
2634 while (IS_BLANK(val)) {
2635 val = xmlStringCurrentChar(NULL, cur, &len);
2636 cur += len;
2637 }
2638
2639 if (!IS_LETTER(val) && (val != '_') &&
2640 (val != ':')) {
2641 return(0);
2642 }
2643 val = xmlStringCurrentChar(NULL, cur, &len);
2644 cur += len;
2645
2646 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2647 (val == '.') || (val == '-') ||
2648 (val == '_') || (val == ':') ||
2649 (IS_COMBINING(val)) ||
2650 (IS_EXTENDER(val))) {
2651 val = xmlStringCurrentChar(NULL, cur, &len);
2652 cur += len;
2653 }
2654 }
2655
2656 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002657
2658 return(1);
2659}
2660
2661/**
2662 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002663 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002664 *
2665 * Validate that the given value match Nmtoken production
2666 *
2667 * [ VC: Name Token ]
2668 *
2669 * returns 1 if valid or 0 otherwise
2670 */
2671
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002672static int
Owen Taylor3473f882001-02-23 17:55:21 +00002673xmlValidateNmtokenValue(const xmlChar *value) {
2674 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002675 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002676
2677 if (value == NULL) return(0);
2678 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002679 val = xmlStringCurrentChar(NULL, cur, &len);
2680 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002681
Daniel Veillardd8224e02002-01-13 15:43:22 +00002682 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2683 (val != '.') && (val != '-') &&
2684 (val != '_') && (val != ':') &&
2685 (!IS_COMBINING(val)) &&
2686 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002687 return(0);
2688
Daniel Veillardd8224e02002-01-13 15:43:22 +00002689 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2690 (val == '.') || (val == '-') ||
2691 (val == '_') || (val == ':') ||
2692 (IS_COMBINING(val)) ||
2693 (IS_EXTENDER(val))) {
2694 val = xmlStringCurrentChar(NULL, cur, &len);
2695 cur += len;
2696 }
Owen Taylor3473f882001-02-23 17:55:21 +00002697
Daniel Veillardd8224e02002-01-13 15:43:22 +00002698 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002699
2700 return(1);
2701}
2702
2703/**
2704 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002705 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00002706 *
2707 * Validate that the given value match Nmtokens production
2708 *
2709 * [ VC: Name Token ]
2710 *
2711 * returns 1 if valid or 0 otherwise
2712 */
2713
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002714static int
Owen Taylor3473f882001-02-23 17:55:21 +00002715xmlValidateNmtokensValue(const xmlChar *value) {
2716 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002717 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002718
2719 if (value == NULL) return(0);
2720 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002721 val = xmlStringCurrentChar(NULL, cur, &len);
2722 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002723
Daniel Veillardd8224e02002-01-13 15:43:22 +00002724 while (IS_BLANK(val)) {
2725 val = xmlStringCurrentChar(NULL, cur, &len);
2726 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002727 }
2728
Daniel Veillardd8224e02002-01-13 15:43:22 +00002729 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2730 (val != '.') && (val != '-') &&
2731 (val != '_') && (val != ':') &&
2732 (!IS_COMBINING(val)) &&
2733 (!IS_EXTENDER(val)))
2734 return(0);
2735
2736 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2737 (val == '.') || (val == '-') ||
2738 (val == '_') || (val == ':') ||
2739 (IS_COMBINING(val)) ||
2740 (IS_EXTENDER(val))) {
2741 val = xmlStringCurrentChar(NULL, cur, &len);
2742 cur += len;
2743 }
2744
2745 while (IS_BLANK(val)) {
2746 while (IS_BLANK(val)) {
2747 val = xmlStringCurrentChar(NULL, cur, &len);
2748 cur += len;
2749 }
2750 if (val == 0) return(1);
2751
2752 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2753 (val != '.') && (val != '-') &&
2754 (val != '_') && (val != ':') &&
2755 (!IS_COMBINING(val)) &&
2756 (!IS_EXTENDER(val)))
2757 return(0);
2758
2759 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2760 (val == '.') || (val == '-') ||
2761 (val == '_') || (val == ':') ||
2762 (IS_COMBINING(val)) ||
2763 (IS_EXTENDER(val))) {
2764 val = xmlStringCurrentChar(NULL, cur, &len);
2765 cur += len;
2766 }
2767 }
2768
2769 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002770
2771 return(1);
2772}
2773
2774/**
2775 * xmlValidateNotationDecl:
2776 * @ctxt: the validation context
2777 * @doc: a document instance
2778 * @nota: a notation definition
2779 *
2780 * Try to validate a single notation definition
2781 * basically it does the following checks as described by the
2782 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002783 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00002784 * But this function get called anyway ...
2785 *
2786 * returns 1 if valid or 0 otherwise
2787 */
2788
2789int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002790xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2791 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002792 int ret = 1;
2793
2794 return(ret);
2795}
2796
2797/**
2798 * xmlValidateAttributeValue:
2799 * @type: an attribute type
2800 * @value: an attribute value
2801 *
2802 * Validate that the given attribute value match the proper production
2803 *
2804 * [ VC: ID ]
2805 * Values of type ID must match the Name production....
2806 *
2807 * [ VC: IDREF ]
2808 * Values of type IDREF must match the Name production, and values
2809 * of type IDREFS must match Names ...
2810 *
2811 * [ VC: Entity Name ]
2812 * Values of type ENTITY must match the Name production, values
2813 * of type ENTITIES must match Names ...
2814 *
2815 * [ VC: Name Token ]
2816 * Values of type NMTOKEN must match the Nmtoken production; values
2817 * of type NMTOKENS must match Nmtokens.
2818 *
2819 * returns 1 if valid or 0 otherwise
2820 */
2821
2822int
2823xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2824 switch (type) {
2825 case XML_ATTRIBUTE_ENTITIES:
2826 case XML_ATTRIBUTE_IDREFS:
2827 return(xmlValidateNamesValue(value));
2828 case XML_ATTRIBUTE_ENTITY:
2829 case XML_ATTRIBUTE_IDREF:
2830 case XML_ATTRIBUTE_ID:
2831 case XML_ATTRIBUTE_NOTATION:
2832 return(xmlValidateNameValue(value));
2833 case XML_ATTRIBUTE_NMTOKENS:
2834 case XML_ATTRIBUTE_ENUMERATION:
2835 return(xmlValidateNmtokensValue(value));
2836 case XML_ATTRIBUTE_NMTOKEN:
2837 return(xmlValidateNmtokenValue(value));
2838 case XML_ATTRIBUTE_CDATA:
2839 break;
2840 }
2841 return(1);
2842}
2843
2844/**
2845 * xmlValidateAttributeValue2:
2846 * @ctxt: the validation context
2847 * @doc: the document
2848 * @name: the attribute name (used for error reporting only)
2849 * @type: the attribute type
2850 * @value: the attribute value
2851 *
2852 * Validate that the given attribute value match a given type.
2853 * This typically cannot be done before having finished parsing
2854 * the subsets.
2855 *
2856 * [ VC: IDREF ]
2857 * Values of type IDREF must match one of the declared IDs
2858 * Values of type IDREFS must match a sequence of the declared IDs
2859 * each Name must match the value of an ID attribute on some element
2860 * in the XML document; i.e. IDREF values must match the value of
2861 * some ID attribute
2862 *
2863 * [ VC: Entity Name ]
2864 * Values of type ENTITY must match one declared entity
2865 * Values of type ENTITIES must match a sequence of declared entities
2866 *
2867 * [ VC: Notation Attributes ]
2868 * all notation names in the declaration must be declared.
2869 *
2870 * returns 1 if valid or 0 otherwise
2871 */
2872
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002873static int
Owen Taylor3473f882001-02-23 17:55:21 +00002874xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2875 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2876 int ret = 1;
2877 switch (type) {
2878 case XML_ATTRIBUTE_IDREFS:
2879 case XML_ATTRIBUTE_IDREF:
2880 case XML_ATTRIBUTE_ID:
2881 case XML_ATTRIBUTE_NMTOKENS:
2882 case XML_ATTRIBUTE_ENUMERATION:
2883 case XML_ATTRIBUTE_NMTOKEN:
2884 case XML_ATTRIBUTE_CDATA:
2885 break;
2886 case XML_ATTRIBUTE_ENTITY: {
2887 xmlEntityPtr ent;
2888
2889 ent = xmlGetDocEntity(doc, value);
2890 if (ent == NULL) {
2891 VERROR(ctxt->userData,
2892 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2893 name, value);
2894 ret = 0;
2895 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2896 VERROR(ctxt->userData,
2897 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2898 name, value);
2899 ret = 0;
2900 }
2901 break;
2902 }
2903 case XML_ATTRIBUTE_ENTITIES: {
2904 xmlChar *dup, *nam = NULL, *cur, save;
2905 xmlEntityPtr ent;
2906
2907 dup = xmlStrdup(value);
2908 if (dup == NULL)
2909 return(0);
2910 cur = dup;
2911 while (*cur != 0) {
2912 nam = cur;
2913 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2914 save = *cur;
2915 *cur = 0;
2916 ent = xmlGetDocEntity(doc, nam);
2917 if (ent == NULL) {
2918 VERROR(ctxt->userData,
2919 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2920 name, nam);
2921 ret = 0;
2922 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2923 VERROR(ctxt->userData,
2924 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2925 name, nam);
2926 ret = 0;
2927 }
2928 if (save == 0)
2929 break;
2930 *cur = save;
2931 while (IS_BLANK(*cur)) cur++;
2932 }
2933 xmlFree(dup);
2934 break;
2935 }
2936 case XML_ATTRIBUTE_NOTATION: {
2937 xmlNotationPtr nota;
2938
2939 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2940 if ((nota == NULL) && (doc->extSubset != NULL))
2941 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2942
2943 if (nota == NULL) {
2944 VERROR(ctxt->userData,
2945 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2946 name, value);
2947 ret = 0;
2948 }
2949 break;
2950 }
2951 }
2952 return(ret);
2953}
2954
2955/**
2956 * xmlValidNormalizeAttributeValue:
2957 * @doc: the document
2958 * @elem: the parent
2959 * @name: the attribute name
2960 * @value: the attribute value
2961 *
2962 * Does the validation related extra step of the normalization of attribute
2963 * values:
2964 *
2965 * If the declared value is not CDATA, then the XML processor must further
2966 * process the normalized attribute value by discarding any leading and
2967 * trailing space (#x20) characters, and by replacing sequences of space
2968 * (#x20) characters by single space (#x20) character.
2969 *
2970 * returns a new normalized string if normalization is needed, NULL otherwise
2971 * the caller must free the returned value.
2972 */
2973
2974xmlChar *
2975xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
2976 const xmlChar *name, const xmlChar *value) {
2977 xmlChar *ret, *dst;
2978 const xmlChar *src;
2979 xmlAttributePtr attrDecl = NULL;
2980
2981 if (doc == NULL) return(NULL);
2982 if (elem == NULL) return(NULL);
2983 if (name == NULL) return(NULL);
2984 if (value == NULL) return(NULL);
2985
2986 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2987 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00002988 snprintf((char *) qname, sizeof(qname), "%s:%s",
2989 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002990 qname[sizeof(qname) - 1] = 0;
2991 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
2992 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2993 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
2994 }
2995 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
2996 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2997 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
2998
2999 if (attrDecl == NULL)
3000 return(NULL);
3001 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3002 return(NULL);
3003
3004 ret = xmlStrdup(value);
3005 if (ret == NULL)
3006 return(NULL);
3007 src = value;
3008 dst = ret;
3009 while (*src == 0x20) src++;
3010 while (*src != 0) {
3011 if (*src == 0x20) {
3012 while (*src == 0x20) src++;
3013 if (*src != 0)
3014 *dst++ = 0x20;
3015 } else {
3016 *dst++ = *src++;
3017 }
3018 }
3019 *dst = 0;
3020 return(ret);
3021}
3022
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003023static void
Owen Taylor3473f882001-02-23 17:55:21 +00003024xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003025 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003026 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3027}
3028
3029/**
3030 * xmlValidateAttributeDecl:
3031 * @ctxt: the validation context
3032 * @doc: a document instance
3033 * @attr: an attribute definition
3034 *
3035 * Try to validate a single attribute definition
3036 * basically it does the following checks as described by the
3037 * XML-1.0 recommendation:
3038 * - [ VC: Attribute Default Legal ]
3039 * - [ VC: Enumeration ]
3040 * - [ VC: ID Attribute Default ]
3041 *
3042 * The ID/IDREF uniqueness and matching are done separately
3043 *
3044 * returns 1 if valid or 0 otherwise
3045 */
3046
3047int
3048xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3049 xmlAttributePtr attr) {
3050 int ret = 1;
3051 int val;
3052 CHECK_DTD;
3053 if(attr == NULL) return(1);
3054
3055 /* Attribute Default Legal */
3056 /* Enumeration */
3057 if (attr->defaultValue != NULL) {
3058 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3059 if (val == 0) {
3060 VERROR(ctxt->userData,
3061 "Syntax of default value for attribute %s on %s is not valid\n",
3062 attr->name, attr->elem);
3063 }
3064 ret &= val;
3065 }
3066
3067 /* ID Attribute Default */
3068 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3069 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3070 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3071 VERROR(ctxt->userData,
3072 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
3073 attr->name, attr->elem);
3074 ret = 0;
3075 }
3076
3077 /* One ID per Element Type */
3078 if (attr->atype == XML_ATTRIBUTE_ID) {
3079 int nbId;
3080
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003081 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003082 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3083 attr->elem);
3084 if (elem != NULL) {
3085 nbId = xmlScanIDAttributeDecl(NULL, elem);
3086 } else {
3087 xmlAttributeTablePtr table;
3088
3089 /*
3090 * The attribute may be declared in the internal subset and the
3091 * element in the external subset.
3092 */
3093 nbId = 0;
3094 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3095 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3096 xmlValidateAttributeIdCallback, &nbId);
3097 }
3098 if (nbId > 1) {
3099 VERROR(ctxt->userData,
3100 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3101 attr->elem, nbId, attr->name);
3102 } else if (doc->extSubset != NULL) {
3103 int extId = 0;
3104 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3105 if (elem != NULL) {
3106 extId = xmlScanIDAttributeDecl(NULL, elem);
3107 }
3108 if (extId > 1) {
3109 VERROR(ctxt->userData,
3110 "Element %s has %d ID attribute defined in the external subset : %s\n",
3111 attr->elem, extId, attr->name);
3112 } else if (extId + nbId > 1) {
3113 VERROR(ctxt->userData,
3114"Element %s has ID attributes defined in the internal and external subset : %s\n",
3115 attr->elem, attr->name);
3116 }
3117 }
3118 }
3119
3120 /* Validity Constraint: Enumeration */
3121 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3122 xmlEnumerationPtr tree = attr->tree;
3123 while (tree != NULL) {
3124 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3125 tree = tree->next;
3126 }
3127 if (tree == NULL) {
3128 VERROR(ctxt->userData,
3129"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3130 attr->defaultValue, attr->name, attr->elem);
3131 ret = 0;
3132 }
3133 }
3134
3135 return(ret);
3136}
3137
3138/**
3139 * xmlValidateElementDecl:
3140 * @ctxt: the validation context
3141 * @doc: a document instance
3142 * @elem: an element definition
3143 *
3144 * Try to validate a single element definition
3145 * basically it does the following checks as described by the
3146 * XML-1.0 recommendation:
3147 * - [ VC: One ID per Element Type ]
3148 * - [ VC: No Duplicate Types ]
3149 * - [ VC: Unique Element Type Declaration ]
3150 *
3151 * returns 1 if valid or 0 otherwise
3152 */
3153
3154int
3155xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3156 xmlElementPtr elem) {
3157 int ret = 1;
3158 xmlElementPtr tst;
3159
3160 CHECK_DTD;
3161
3162 if (elem == NULL) return(1);
3163
3164 /* No Duplicate Types */
3165 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3166 xmlElementContentPtr cur, next;
3167 const xmlChar *name;
3168
3169 cur = elem->content;
3170 while (cur != NULL) {
3171 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3172 if (cur->c1 == NULL) break;
3173 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3174 name = cur->c1->name;
3175 next = cur->c2;
3176 while (next != NULL) {
3177 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3178 if (xmlStrEqual(next->name, name)) {
3179 VERROR(ctxt->userData,
3180 "Definition of %s has duplicate references of %s\n",
3181 elem->name, name);
3182 ret = 0;
3183 }
3184 break;
3185 }
3186 if (next->c1 == NULL) break;
3187 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3188 if (xmlStrEqual(next->c1->name, name)) {
3189 VERROR(ctxt->userData,
3190 "Definition of %s has duplicate references of %s\n",
3191 elem->name, name);
3192 ret = 0;
3193 }
3194 next = next->c2;
3195 }
3196 }
3197 cur = cur->c2;
3198 }
3199 }
3200
3201 /* VC: Unique Element Type Declaration */
3202 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003203 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003204 ((tst->prefix == elem->prefix) ||
3205 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003206 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003207 VERROR(ctxt->userData, "Redefinition of element %s\n",
3208 elem->name);
3209 ret = 0;
3210 }
3211 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003212 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003213 ((tst->prefix == elem->prefix) ||
3214 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003215 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003216 VERROR(ctxt->userData, "Redefinition of element %s\n",
3217 elem->name);
3218 ret = 0;
3219 }
3220
Daniel Veillarda10efa82001-04-18 13:09:01 +00003221 /* One ID per Element Type
3222 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003223 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3224 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003225 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003226 return(ret);
3227}
3228
3229/**
3230 * xmlValidateOneAttribute:
3231 * @ctxt: the validation context
3232 * @doc: a document instance
3233 * @elem: an element instance
3234 * @attr: an attribute instance
3235 * @value: the attribute value (without entities processing)
3236 *
3237 * Try to validate a single attribute for an element
3238 * basically it does the following checks as described by the
3239 * XML-1.0 recommendation:
3240 * - [ VC: Attribute Value Type ]
3241 * - [ VC: Fixed Attribute Default ]
3242 * - [ VC: Entity Name ]
3243 * - [ VC: Name Token ]
3244 * - [ VC: ID ]
3245 * - [ VC: IDREF ]
3246 * - [ VC: Entity Name ]
3247 * - [ VC: Notation Attributes ]
3248 *
3249 * The ID/IDREF uniqueness and matching are done separately
3250 *
3251 * returns 1 if valid or 0 otherwise
3252 */
3253
3254int
3255xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3256 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3257 /* xmlElementPtr elemDecl; */
3258 xmlAttributePtr attrDecl = NULL;
3259 int val;
3260 int ret = 1;
3261
3262 CHECK_DTD;
3263 if ((elem == NULL) || (elem->name == NULL)) return(0);
3264 if ((attr == NULL) || (attr->name == NULL)) return(0);
3265
3266 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3267 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003268 snprintf((char *) qname, sizeof(qname), "%s:%s",
3269 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003270 qname[sizeof(qname) - 1] = 0;
3271 if (attr->ns != NULL) {
3272 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3273 attr->name, attr->ns->prefix);
3274 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3275 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3276 attr->name, attr->ns->prefix);
3277 } else {
3278 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3279 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3280 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3281 qname, attr->name);
3282 }
3283 }
3284 if (attrDecl == NULL) {
3285 if (attr->ns != NULL) {
3286 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3287 attr->name, attr->ns->prefix);
3288 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3289 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3290 attr->name, attr->ns->prefix);
3291 } else {
3292 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3293 elem->name, attr->name);
3294 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3295 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3296 elem->name, attr->name);
3297 }
3298 }
3299
3300
3301 /* Validity Constraint: Attribute Value Type */
3302 if (attrDecl == NULL) {
3303 VERROR(ctxt->userData,
3304 "No declaration for attribute %s on element %s\n",
3305 attr->name, elem->name);
3306 return(0);
3307 }
3308 attr->atype = attrDecl->atype;
3309
3310 val = xmlValidateAttributeValue(attrDecl->atype, value);
3311 if (val == 0) {
3312 VERROR(ctxt->userData,
3313 "Syntax of value for attribute %s on %s is not valid\n",
3314 attr->name, elem->name);
3315 ret = 0;
3316 }
3317
3318 /* Validity constraint: Fixed Attribute Default */
3319 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3320 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3321 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003322 "Value for attribute %s on %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003323 attr->name, elem->name, attrDecl->defaultValue);
3324 ret = 0;
3325 }
3326 }
3327
3328 /* Validity Constraint: ID uniqueness */
3329 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3330 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3331 ret = 0;
3332 }
3333
3334 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3335 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3336 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3337 ret = 0;
3338 }
3339
3340 /* Validity Constraint: Notation Attributes */
3341 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3342 xmlEnumerationPtr tree = attrDecl->tree;
3343 xmlNotationPtr nota;
3344
3345 /* First check that the given NOTATION was declared */
3346 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3347 if (nota == NULL)
3348 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3349
3350 if (nota == NULL) {
3351 VERROR(ctxt->userData,
3352 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
3353 value, attr->name, elem->name);
3354 ret = 0;
3355 }
3356
3357 /* Second, verify that it's among the list */
3358 while (tree != NULL) {
3359 if (xmlStrEqual(tree->name, value)) break;
3360 tree = tree->next;
3361 }
3362 if (tree == NULL) {
3363 VERROR(ctxt->userData,
3364"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
3365 value, attr->name, elem->name);
3366 ret = 0;
3367 }
3368 }
3369
3370 /* Validity Constraint: Enumeration */
3371 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3372 xmlEnumerationPtr tree = attrDecl->tree;
3373 while (tree != NULL) {
3374 if (xmlStrEqual(tree->name, value)) break;
3375 tree = tree->next;
3376 }
3377 if (tree == NULL) {
3378 VERROR(ctxt->userData,
3379 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3380 value, attr->name, elem->name);
3381 ret = 0;
3382 }
3383 }
3384
3385 /* Fixed Attribute Default */
3386 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3387 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3388 VERROR(ctxt->userData,
3389 "Value for attribute %s on %s must be \"%s\"\n",
3390 attr->name, elem->name, attrDecl->defaultValue);
3391 ret = 0;
3392 }
3393
3394 /* Extra check for the attribute value */
3395 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3396 attrDecl->atype, value);
3397
3398 return(ret);
3399}
3400
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003401/**
3402 * xmlValidateSkipIgnorable:
3403 * @ctxt: the validation context
3404 * @child: the child list
3405 *
3406 * Skip ignorable elements w.r.t. the validation process
3407 *
3408 * returns the first element to consider for validation of the content model
3409 */
3410
3411static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003412xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003413 while (child != NULL) {
3414 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003415 /* These things are ignored (skipped) during validation. */
3416 case XML_PI_NODE:
3417 case XML_COMMENT_NODE:
3418 case XML_XINCLUDE_START:
3419 case XML_XINCLUDE_END:
3420 child = child->next;
3421 break;
3422 case XML_TEXT_NODE:
3423 if (xmlIsBlankNode(child))
3424 child = child->next;
3425 else
3426 return(child);
3427 break;
3428 /* keep current node */
3429 default:
3430 return(child);
3431 }
3432 }
3433 return(child);
3434}
3435
3436/**
3437 * xmlValidateElementType:
3438 * @ctxt: the validation context
3439 *
3440 * Try to validate the content model of an element internal function
3441 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003442 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3443 * reference is found and -3 if the validation succeeded but
3444 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003445 */
3446
3447static int
3448xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003449 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003450 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003451
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003452 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003453 if ((NODE == NULL) && (CONT == NULL))
3454 return(1);
3455 if ((NODE == NULL) &&
3456 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3457 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3458 return(1);
3459 }
3460 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003461 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003462 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003463
3464 /*
3465 * We arrive here when more states need to be examined
3466 */
3467cont:
3468
3469 /*
3470 * We just recovered from a rollback generated by a possible
3471 * epsilon transition, go directly to the analysis phase
3472 */
3473 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003474 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003475 DEBUG_VALID_STATE(NODE, CONT)
3476 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003477 goto analyze;
3478 }
3479
3480 DEBUG_VALID_STATE(NODE, CONT)
3481 /*
3482 * we may have to save a backup state here. This is the equivalent
3483 * of handling epsilon transition in NFAs.
3484 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003485 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003486 ((CONT->parent == NULL) ||
3487 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003488 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003489 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003490 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003491 DEBUG_VALID_MSG("saving parent branch");
3492 vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
3493 }
3494
3495
3496 /*
3497 * Check first if the content matches
3498 */
3499 switch (CONT->type) {
3500 case XML_ELEMENT_CONTENT_PCDATA:
3501 if (NODE == NULL) {
3502 DEBUG_VALID_MSG("pcdata failed no node");
3503 ret = 0;
3504 break;
3505 }
3506 if (NODE->type == XML_TEXT_NODE) {
3507 DEBUG_VALID_MSG("pcdata found, skip to next");
3508 /*
3509 * go to next element in the content model
3510 * skipping ignorable elems
3511 */
3512 do {
3513 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003514 NODE = xmlValidateSkipIgnorable(NODE);
3515 if ((NODE != NULL) &&
3516 (NODE->type == XML_ENTITY_REF_NODE))
3517 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003518 } while ((NODE != NULL) &&
3519 ((NODE->type != XML_ELEMENT_NODE) &&
3520 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003521 ret = 1;
3522 break;
3523 } else {
3524 DEBUG_VALID_MSG("pcdata failed");
3525 ret = 0;
3526 break;
3527 }
3528 break;
3529 case XML_ELEMENT_CONTENT_ELEMENT:
3530 if (NODE == NULL) {
3531 DEBUG_VALID_MSG("element failed no node");
3532 ret = 0;
3533 break;
3534 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003535 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3536 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003537 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003538 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3539 ret = (CONT->prefix == NULL);
3540 } else if (CONT->prefix == NULL) {
3541 ret = 0;
3542 } else {
3543 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3544 }
3545 }
3546 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003547 DEBUG_VALID_MSG("element found, skip to next");
3548 /*
3549 * go to next element in the content model
3550 * skipping ignorable elems
3551 */
3552 do {
3553 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003554 NODE = xmlValidateSkipIgnorable(NODE);
3555 if ((NODE != NULL) &&
3556 (NODE->type == XML_ENTITY_REF_NODE))
3557 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003558 } while ((NODE != NULL) &&
3559 ((NODE->type != XML_ELEMENT_NODE) &&
3560 (NODE->type != XML_TEXT_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003561 } else {
3562 DEBUG_VALID_MSG("element failed");
3563 ret = 0;
3564 break;
3565 }
3566 break;
3567 case XML_ELEMENT_CONTENT_OR:
3568 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003569 * Small optimization.
3570 */
3571 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3572 if ((NODE == NULL) ||
3573 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3574 DEPTH++;
3575 CONT = CONT->c2;
3576 goto cont;
3577 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003578 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3579 ret = (CONT->c1->prefix == NULL);
3580 } else if (CONT->c1->prefix == NULL) {
3581 ret = 0;
3582 } else {
3583 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3584 }
3585 if (ret == 0) {
3586 DEPTH++;
3587 CONT = CONT->c2;
3588 goto cont;
3589 }
Daniel Veillard85349052001-04-20 13:48:21 +00003590 }
3591
3592 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003593 * save the second branch 'or' branch
3594 */
3595 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard268fd1b2001-08-26 18:46:36 +00003596 vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3597 OCCURS, ROLLBACK_OR);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003598
3599 DEPTH++;
3600 CONT = CONT->c1;
3601 goto cont;
3602 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003603 /*
3604 * Small optimization.
3605 */
3606 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3607 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3608 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3609 if ((NODE == NULL) ||
3610 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3611 DEPTH++;
3612 CONT = CONT->c2;
3613 goto cont;
3614 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003615 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3616 ret = (CONT->c1->prefix == NULL);
3617 } else if (CONT->c1->prefix == NULL) {
3618 ret = 0;
3619 } else {
3620 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3621 }
3622 if (ret == 0) {
3623 DEPTH++;
3624 CONT = CONT->c2;
3625 goto cont;
3626 }
Daniel Veillard1d047672001-06-09 16:41:01 +00003627 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003628 DEPTH++;
3629 CONT = CONT->c1;
3630 goto cont;
3631 }
3632
3633 /*
3634 * At this point handle going up in the tree
3635 */
3636 if (ret == -1) {
3637 DEBUG_VALID_MSG("error found returning");
3638 return(ret);
3639 }
3640analyze:
3641 while (CONT != NULL) {
3642 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003643 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003644 * this level.
3645 */
3646 if (ret == 0) {
3647 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003648 xmlNodePtr cur;
3649
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003650 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003651 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003652 DEBUG_VALID_MSG("Once branch failed, rollback");
3653 if (vstateVPop(ctxt) < 0 ) {
3654 DEBUG_VALID_MSG("exhaustion, failed");
3655 return(0);
3656 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003657 if (cur != ctxt->vstate->node)
3658 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003659 goto cont;
3660 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00003661 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003662 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003663 DEBUG_VALID_MSG("Plus branch failed, rollback");
3664 if (vstateVPop(ctxt) < 0 ) {
3665 DEBUG_VALID_MSG("exhaustion, failed");
3666 return(0);
3667 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003668 if (cur != ctxt->vstate->node)
3669 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003670 goto cont;
3671 }
3672 DEBUG_VALID_MSG("Plus branch found");
3673 ret = 1;
3674 break;
3675 case XML_ELEMENT_CONTENT_MULT:
3676#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00003677 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003678 DEBUG_VALID_MSG("Mult branch failed");
3679 } else {
3680 DEBUG_VALID_MSG("Mult branch found");
3681 }
3682#endif
3683 ret = 1;
3684 break;
3685 case XML_ELEMENT_CONTENT_OPT:
3686 DEBUG_VALID_MSG("Option branch failed");
3687 ret = 1;
3688 break;
3689 }
3690 } else {
3691 switch (CONT->ocur) {
3692 case XML_ELEMENT_CONTENT_OPT:
3693 DEBUG_VALID_MSG("Option branch succeeded");
3694 ret = 1;
3695 break;
3696 case XML_ELEMENT_CONTENT_ONCE:
3697 DEBUG_VALID_MSG("Once branch succeeded");
3698 ret = 1;
3699 break;
3700 case XML_ELEMENT_CONTENT_PLUS:
3701 if (STATE == ROLLBACK_PARENT) {
3702 DEBUG_VALID_MSG("Plus branch rollback");
3703 ret = 1;
3704 break;
3705 }
3706 if (NODE == NULL) {
3707 DEBUG_VALID_MSG("Plus branch exhausted");
3708 ret = 1;
3709 break;
3710 }
3711 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003712 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003713 goto cont;
3714 case XML_ELEMENT_CONTENT_MULT:
3715 if (STATE == ROLLBACK_PARENT) {
3716 DEBUG_VALID_MSG("Mult branch rollback");
3717 ret = 1;
3718 break;
3719 }
3720 if (NODE == NULL) {
3721 DEBUG_VALID_MSG("Mult branch exhausted");
3722 ret = 1;
3723 break;
3724 }
3725 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003726 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003727 goto cont;
3728 }
3729 }
3730 STATE = 0;
3731
3732 /*
3733 * Then act accordingly at the parent level
3734 */
Daniel Veillard5344c602001-12-31 16:37:34 +00003735 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003736 if (CONT->parent == NULL)
3737 break;
3738
3739 switch (CONT->parent->type) {
3740 case XML_ELEMENT_CONTENT_PCDATA:
3741 DEBUG_VALID_MSG("Error: parent pcdata");
3742 return(-1);
3743 case XML_ELEMENT_CONTENT_ELEMENT:
3744 DEBUG_VALID_MSG("Error: parent element");
3745 return(-1);
3746 case XML_ELEMENT_CONTENT_OR:
3747 if (ret == 1) {
3748 DEBUG_VALID_MSG("Or succeeded");
3749 CONT = CONT->parent;
3750 DEPTH--;
3751 } else {
3752 DEBUG_VALID_MSG("Or failed");
3753 CONT = CONT->parent;
3754 DEPTH--;
3755 }
3756 break;
3757 case XML_ELEMENT_CONTENT_SEQ:
3758 if (ret == 0) {
3759 DEBUG_VALID_MSG("Sequence failed");
3760 CONT = CONT->parent;
3761 DEPTH--;
3762 } else if (CONT == CONT->parent->c1) {
3763 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3764 CONT = CONT->parent->c2;
3765 goto cont;
3766 } else {
3767 DEBUG_VALID_MSG("Sequence succeeded");
3768 CONT = CONT->parent;
3769 DEPTH--;
3770 }
3771 }
3772 }
3773 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003774 xmlNodePtr cur;
3775
3776 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003777 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3778 if (vstateVPop(ctxt) < 0 ) {
3779 DEBUG_VALID_MSG("exhaustion, failed");
3780 return(0);
3781 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003782 if (cur != ctxt->vstate->node)
3783 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003784 goto cont;
3785 }
3786 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003787 xmlNodePtr cur;
3788
3789 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003790 DEBUG_VALID_MSG("Failure, rollback");
3791 if (vstateVPop(ctxt) < 0 ) {
3792 DEBUG_VALID_MSG("exhaustion, failed");
3793 return(0);
3794 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003795 if (cur != ctxt->vstate->node)
3796 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003797 goto cont;
3798 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003799 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003800}
3801
3802/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00003803 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003804 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00003805 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003806 * @content: An element
3807 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3808 *
3809 * This will dump the list of elements to the buffer
3810 * Intended just for the debug routine
3811 */
3812static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00003813xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003814 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00003815 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003816
3817 if (node == NULL) return;
3818 if (glob) strcat(buf, "(");
3819 cur = node;
3820 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00003821 len = strlen(buf);
3822 if (size - len < 50) {
3823 if ((size - len > 4) && (buf[len - 1] != '.'))
3824 strcat(buf, " ...");
3825 return;
3826 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003827 switch (cur->type) {
3828 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003829 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
3830 if (size - len < xmlStrlen(cur->ns->prefix + 10)) {
3831 if ((size - len > 4) && (buf[len - 1] != '.'))
3832 strcat(buf, " ...");
3833 return;
3834 }
3835 strcat(buf, (char *) cur->ns->prefix);
3836 strcat(buf, ":");
3837 }
Daniel Veillardd3d06722001-08-15 12:06:36 +00003838 if (size - len < xmlStrlen(cur->name + 10)) {
3839 if ((size - len > 4) && (buf[len - 1] != '.'))
3840 strcat(buf, " ...");
3841 return;
3842 }
3843 strcat(buf, (char *) cur->name);
3844 if (cur->next != NULL)
3845 strcat(buf, " ");
3846 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003847 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003848 if (xmlIsBlankNode(cur))
3849 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003850 case XML_CDATA_SECTION_NODE:
3851 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003852 strcat(buf, "CDATA");
3853 if (cur->next != NULL)
3854 strcat(buf, " ");
3855 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003856 case XML_ATTRIBUTE_NODE:
3857 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003858#ifdef LIBXML_DOCB_ENABLED
3859 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003860#endif
3861 case XML_HTML_DOCUMENT_NODE:
3862 case XML_DOCUMENT_TYPE_NODE:
3863 case XML_DOCUMENT_FRAG_NODE:
3864 case XML_NOTATION_NODE:
3865 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003866 strcat(buf, "???");
3867 if (cur->next != NULL)
3868 strcat(buf, " ");
3869 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003870 case XML_ENTITY_NODE:
3871 case XML_PI_NODE:
3872 case XML_DTD_NODE:
3873 case XML_COMMENT_NODE:
3874 case XML_ELEMENT_DECL:
3875 case XML_ATTRIBUTE_DECL:
3876 case XML_ENTITY_DECL:
3877 case XML_XINCLUDE_START:
3878 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003879 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003880 }
3881 cur = cur->next;
3882 }
3883 if (glob) strcat(buf, ")");
3884}
3885
3886/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003887 * xmlValidateElementContent:
3888 * @ctxt: the validation context
3889 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003890 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003891 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003892 *
3893 * Try to validate the content model of an element
3894 *
3895 * returns 1 if valid or 0 if not and -1 in case of error
3896 */
3897
3898static int
3899xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003900 xmlElementPtr elemDecl, int warn) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003901 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003902 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003903 xmlElementContentPtr cont;
3904 const xmlChar *name;
3905
3906 if (elemDecl == NULL)
3907 return(-1);
3908 cont = elemDecl->content;
3909 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003910
3911 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003912 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003913 */
3914 ctxt->vstateMax = 8;
3915 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
3916 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
3917 if (ctxt->vstateTab == NULL) {
3918 xmlGenericError(xmlGenericErrorContext,
3919 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003920 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003921 }
3922 /*
3923 * The first entry in the stack is reserved to the current state
3924 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00003925 ctxt->nodeMax = 0;
3926 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00003927 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003928 ctxt->vstate = &ctxt->vstateTab[0];
3929 ctxt->vstateNr = 1;
3930 CONT = cont;
3931 NODE = child;
3932 DEPTH = 0;
3933 OCCURS = 0;
3934 STATE = 0;
3935 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003936 if ((ret == -3) && (warn)) {
3937 VWARNING(ctxt->userData,
3938 "Element %s content model is ambiguous\n", name);
3939 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003940 /*
3941 * An entities reference appeared at this level.
3942 * Buid a minimal representation of this node content
3943 * sufficient to run the validation process on it
3944 */
3945 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003946 cur = child;
3947 while (cur != NULL) {
3948 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003949 case XML_ENTITY_REF_NODE:
3950 /*
3951 * Push the current node to be able to roll back
3952 * and process within the entity
3953 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003954 if ((cur->children != NULL) &&
3955 (cur->children->children != NULL)) {
3956 nodeVPush(ctxt, cur);
3957 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003958 continue;
3959 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00003960 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003961 case XML_TEXT_NODE:
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003962 case XML_CDATA_SECTION_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003963 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003964 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00003965 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003966 case XML_ELEMENT_NODE:
3967 /*
3968 * Allocate a new node and minimally fills in
3969 * what's required
3970 */
3971 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
3972 if (tmp == NULL) {
3973 xmlGenericError(xmlGenericErrorContext,
3974 "xmlValidateElementContent : malloc failed\n");
3975 xmlFreeNodeList(repl);
3976 ret = -1;
3977 goto done;
3978 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003979 tmp->type = cur->type;
3980 tmp->name = cur->name;
3981 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003982 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00003983 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003984 if (repl == NULL)
3985 repl = last = tmp;
3986 else {
3987 last->next = tmp;
3988 last = tmp;
3989 }
3990 break;
3991 default:
3992 break;
3993 }
3994 /*
3995 * Switch to next element
3996 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003997 cur = cur->next;
3998 while (cur == NULL) {
3999 cur = nodeVPop(ctxt);
4000 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004001 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004002 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004003 }
4004 }
4005
4006 /*
4007 * Relaunch the validation
4008 */
4009 ctxt->vstate = &ctxt->vstateTab[0];
4010 ctxt->vstateNr = 1;
4011 CONT = cont;
4012 NODE = repl;
4013 DEPTH = 0;
4014 OCCURS = 0;
4015 STATE = 0;
4016 ret = xmlValidateElementType(ctxt);
4017 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004018 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004019 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4020 char expr[5000];
4021 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004022
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004023 expr[0] = 0;
4024 xmlSnprintfElementContent(expr, 5000, cont, 1);
4025 list[0] = 0;
4026 if (repl != NULL)
4027 xmlSnprintfElements(list, 5000, repl, 1);
4028 else
4029 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004030
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004031 if (name != NULL) {
4032 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004033 "Element %s content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004034 name, expr, list);
4035 } else {
4036 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004037 "Element content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004038 expr, list);
4039 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004040 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004041 if (name != NULL) {
4042 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004043 "Element %s content doesn't follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004044 name);
4045 } else {
4046 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004047 "Element content doesn't follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004048 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004049 }
4050 ret = 0;
4051 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004052 if (ret == -3)
4053 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004054
4055
4056done:
4057 /*
4058 * Deallocate the copy if done, and free up the validation stack
4059 */
4060 while (repl != NULL) {
4061 tmp = repl->next;
4062 xmlFree(repl);
4063 repl = tmp;
4064 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004065 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004066 if (ctxt->vstateTab != NULL) {
4067 xmlFree(ctxt->vstateTab);
4068 ctxt->vstateTab = NULL;
4069 }
4070 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004071 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004072 if (ctxt->nodeTab != NULL) {
4073 xmlFree(ctxt->nodeTab);
4074 ctxt->nodeTab = NULL;
4075 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004076 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004077
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004078}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004079
Owen Taylor3473f882001-02-23 17:55:21 +00004080/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004081 * xmlValidateCdataElement:
4082 * @ctxt: the validation context
4083 * @doc: a document instance
4084 * @elem: an element instance
4085 *
4086 * Check that an element follows #CDATA
4087 *
4088 * returns 1 if valid or 0 otherwise
4089 */
4090static int
4091xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4092 xmlNodePtr elem) {
4093 int ret = 1;
4094 xmlNodePtr cur, child;
4095
4096 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4097 return(0);
4098
4099 child = elem->children;
4100
4101 cur = child;
4102 while (cur != NULL) {
4103 switch (cur->type) {
4104 case XML_ENTITY_REF_NODE:
4105 /*
4106 * Push the current node to be able to roll back
4107 * and process within the entity
4108 */
4109 if ((cur->children != NULL) &&
4110 (cur->children->children != NULL)) {
4111 nodeVPush(ctxt, cur);
4112 cur = cur->children->children;
4113 continue;
4114 }
4115 break;
4116 case XML_COMMENT_NODE:
4117 case XML_PI_NODE:
4118 case XML_TEXT_NODE:
4119 case XML_CDATA_SECTION_NODE:
4120 break;
4121 default:
4122 ret = 0;
4123 goto done;
4124 }
4125 /*
4126 * Switch to next element
4127 */
4128 cur = cur->next;
4129 while (cur == NULL) {
4130 cur = nodeVPop(ctxt);
4131 if (cur == NULL)
4132 break;
4133 cur = cur->next;
4134 }
4135 }
4136done:
4137 ctxt->nodeMax = 0;
4138 ctxt->nodeNr = 0;
4139 if (ctxt->nodeTab != NULL) {
4140 xmlFree(ctxt->nodeTab);
4141 ctxt->nodeTab = NULL;
4142 }
4143 return(ret);
4144}
4145
4146/**
Owen Taylor3473f882001-02-23 17:55:21 +00004147 * xmlValidateOneElement:
4148 * @ctxt: the validation context
4149 * @doc: a document instance
4150 * @elem: an element instance
4151 *
4152 * Try to validate a single element and it's attributes,
4153 * basically it does the following checks as described by the
4154 * XML-1.0 recommendation:
4155 * - [ VC: Element Valid ]
4156 * - [ VC: Required Attribute ]
4157 * Then call xmlValidateOneAttribute() for each attribute present.
4158 *
4159 * The ID/IDREF checkings are done separately
4160 *
4161 * returns 1 if valid or 0 otherwise
4162 */
4163
4164int
4165xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4166 xmlNodePtr elem) {
4167 xmlElementPtr elemDecl = NULL;
4168 xmlElementContentPtr cont;
4169 xmlAttributePtr attr;
4170 xmlNodePtr child;
4171 int ret = 1;
4172 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004173 const xmlChar *prefix = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +00004174
4175 CHECK_DTD;
4176
4177 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004178 switch (elem->type) {
4179 case XML_ATTRIBUTE_NODE:
4180 VERROR(ctxt->userData,
4181 "Attribute element not expected here\n");
4182 return(0);
4183 case XML_TEXT_NODE:
4184 if (elem->children != NULL) {
4185 VERROR(ctxt->userData, "Text element has childs !\n");
4186 return(0);
4187 }
4188 if (elem->properties != NULL) {
4189 VERROR(ctxt->userData, "Text element has attributes !\n");
4190 return(0);
4191 }
4192 if (elem->ns != NULL) {
4193 VERROR(ctxt->userData, "Text element has namespace !\n");
4194 return(0);
4195 }
4196 if (elem->nsDef != NULL) {
4197 VERROR(ctxt->userData,
4198 "Text element carries namespace definitions !\n");
4199 return(0);
4200 }
4201 if (elem->content == NULL) {
4202 VERROR(ctxt->userData,
4203 "Text element has no content !\n");
4204 return(0);
4205 }
4206 return(1);
4207 case XML_XINCLUDE_START:
4208 case XML_XINCLUDE_END:
4209 return(1);
4210 case XML_CDATA_SECTION_NODE:
4211 case XML_ENTITY_REF_NODE:
4212 case XML_PI_NODE:
4213 case XML_COMMENT_NODE:
4214 return(1);
4215 case XML_ENTITY_NODE:
4216 VERROR(ctxt->userData,
4217 "Entity element not expected here\n");
4218 return(0);
4219 case XML_NOTATION_NODE:
4220 VERROR(ctxt->userData,
4221 "Notation element not expected here\n");
4222 return(0);
4223 case XML_DOCUMENT_NODE:
4224 case XML_DOCUMENT_TYPE_NODE:
4225 case XML_DOCUMENT_FRAG_NODE:
4226 VERROR(ctxt->userData,
4227 "Document element not expected here\n");
4228 return(0);
4229 case XML_HTML_DOCUMENT_NODE:
4230 VERROR(ctxt->userData,
4231 "\n");
4232 return(0);
4233 case XML_ELEMENT_NODE:
4234 break;
4235 default:
4236 VERROR(ctxt->userData,
4237 "unknown element type %d\n", elem->type);
4238 return(0);
4239 }
4240 if (elem->name == NULL) return(0);
4241
4242 /*
4243 * Fetch the declaration for the qualified name
4244 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004245 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4246 prefix = elem->ns->prefix;
4247
4248 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004249 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004250 elem->name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00004251 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4252 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004253 elem->name, prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00004254 }
4255
4256 /*
4257 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004258 * This is "non-strict" validation should be done on the
4259 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004260 */
4261 if (elemDecl == NULL) {
4262 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
4263 if ((elemDecl == NULL) && (doc->extSubset != NULL))
4264 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
4265 }
4266 if (elemDecl == NULL) {
4267 VERROR(ctxt->userData, "No declaration for element %s\n",
4268 elem->name);
4269 return(0);
4270 }
4271
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004272 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004273 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004274 case XML_ELEMENT_TYPE_UNDEFINED:
4275 VERROR(ctxt->userData, "No declaration for element %s\n",
4276 elem->name);
4277 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004278 case XML_ELEMENT_TYPE_EMPTY:
4279 if (elem->children != NULL) {
4280 VERROR(ctxt->userData,
4281 "Element %s was declared EMPTY this one has content\n",
4282 elem->name);
4283 ret = 0;
4284 }
4285 break;
4286 case XML_ELEMENT_TYPE_ANY:
4287 /* I don't think anything is required then */
4288 break;
4289 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004290 /* simple case of declared as #PCDATA */
4291 if ((elemDecl->content != NULL) &&
4292 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4293 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4294 if (!ret) {
4295 VERROR(ctxt->userData,
4296 "Element %s was declared #PCDATA but contains non text nodes\n",
4297 elem->name);
4298 }
4299 break;
4300 }
Owen Taylor3473f882001-02-23 17:55:21 +00004301 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004302 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004303 while (child != NULL) {
4304 if (child->type == XML_ELEMENT_NODE) {
4305 name = child->name;
4306 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4307 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004308 snprintf((char *) qname, sizeof(qname), "%s:%s",
4309 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004310 qname[sizeof(qname) - 1] = 0;
4311 cont = elemDecl->content;
4312 while (cont != NULL) {
4313 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4314 if (xmlStrEqual(cont->name, qname)) break;
4315 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4316 (cont->c1 != NULL) &&
4317 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4318 if (xmlStrEqual(cont->c1->name, qname)) break;
4319 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4320 (cont->c1 == NULL) ||
4321 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4322 /* Internal error !!! */
4323 xmlGenericError(xmlGenericErrorContext,
4324 "Internal: MIXED struct bad\n");
4325 break;
4326 }
4327 cont = cont->c2;
4328 }
4329 if (cont != NULL)
4330 goto child_ok;
4331 }
4332 cont = elemDecl->content;
4333 while (cont != NULL) {
4334 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4335 if (xmlStrEqual(cont->name, name)) break;
4336 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4337 (cont->c1 != NULL) &&
4338 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4339 if (xmlStrEqual(cont->c1->name, name)) break;
4340 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4341 (cont->c1 == NULL) ||
4342 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4343 /* Internal error !!! */
4344 xmlGenericError(xmlGenericErrorContext,
4345 "Internal: MIXED struct bad\n");
4346 break;
4347 }
4348 cont = cont->c2;
4349 }
4350 if (cont == NULL) {
4351 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004352 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004353 name, elem->name);
4354 ret = 0;
4355 }
4356 }
4357child_ok:
4358 child = child->next;
4359 }
4360 break;
4361 case XML_ELEMENT_TYPE_ELEMENT:
4362 child = elem->children;
4363 cont = elemDecl->content;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004364 ret = xmlValidateElementContent(ctxt, child, elemDecl, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00004365 break;
4366 }
4367
4368 /* [ VC: Required Attribute ] */
4369 attr = elemDecl->attributes;
4370 while (attr != NULL) {
4371 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004372 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00004373
Daniel Veillarde4301c82002-02-13 13:32:35 +00004374 if ((attr->prefix == NULL) &&
4375 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4376 xmlNsPtr ns;
4377
4378 ns = elem->nsDef;
4379 while (ns != NULL) {
4380 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00004381 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004382 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00004383 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004384 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4385 xmlNsPtr ns;
4386
4387 ns = elem->nsDef;
4388 while (ns != NULL) {
4389 if (xmlStrEqual(attr->name, ns->prefix))
4390 goto found;
4391 ns = ns->next;
4392 }
4393 } else {
4394 xmlAttrPtr attrib;
4395
4396 attrib = elem->properties;
4397 while (attrib != NULL) {
4398 if (xmlStrEqual(attrib->name, attr->name)) {
4399 if (attr->prefix != NULL) {
4400 xmlNsPtr nameSpace = attrib->ns;
4401
4402 if (nameSpace == NULL)
4403 nameSpace = elem->ns;
4404 /*
4405 * qualified names handling is problematic, having a
4406 * different prefix should be possible but DTDs don't
4407 * allow to define the URI instead of the prefix :-(
4408 */
4409 if (nameSpace == NULL) {
4410 if (qualified < 0)
4411 qualified = 0;
4412 } else if (!xmlStrEqual(nameSpace->prefix,
4413 attr->prefix)) {
4414 if (qualified < 1)
4415 qualified = 1;
4416 } else
4417 goto found;
4418 } else {
4419 /*
4420 * We should allow applications to define namespaces
4421 * for their application even if the DTD doesn't
4422 * carry one, otherwise, basically we would always
4423 * break.
4424 */
4425 goto found;
4426 }
4427 }
4428 attrib = attrib->next;
4429 }
Owen Taylor3473f882001-02-23 17:55:21 +00004430 }
4431 if (qualified == -1) {
4432 if (attr->prefix == NULL) {
4433 VERROR(ctxt->userData,
4434 "Element %s doesn't carry attribute %s\n",
4435 elem->name, attr->name);
4436 ret = 0;
4437 } else {
4438 VERROR(ctxt->userData,
4439 "Element %s doesn't carry attribute %s:%s\n",
4440 elem->name, attr->prefix,attr->name);
4441 ret = 0;
4442 }
4443 } else if (qualified == 0) {
4444 VWARNING(ctxt->userData,
4445 "Element %s required attribute %s:%s has no prefix\n",
4446 elem->name, attr->prefix,attr->name);
4447 } else if (qualified == 1) {
4448 VWARNING(ctxt->userData,
4449 "Element %s required attribute %s:%s has different prefix\n",
4450 elem->name, attr->prefix,attr->name);
4451 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004452 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
4453 /*
4454 * Special tests checking #FIXED namespace declarations
4455 * have the right value since this is not done as an
4456 * attribute checking
4457 */
4458 if ((attr->prefix == NULL) &&
4459 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4460 xmlNsPtr ns;
4461
4462 ns = elem->nsDef;
4463 while (ns != NULL) {
4464 if (ns->prefix == NULL) {
4465 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
4466 VERROR(ctxt->userData,
4467 "Element %s namespace name for default namespace does not match the DTD\n",
4468 elem->name);
4469 }
4470 goto found;
4471 }
4472 ns = ns->next;
4473 }
4474 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4475 xmlNsPtr ns;
4476
4477 ns = elem->nsDef;
4478 while (ns != NULL) {
4479 if (xmlStrEqual(attr->name, ns->prefix)) {
4480 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
4481 VERROR(ctxt->userData,
4482 "Element %s namespace name for %s doesn't match the DTD\n",
4483 elem->name, ns->prefix);
4484 }
4485 goto found;
4486 }
4487 ns = ns->next;
4488 }
4489 }
Owen Taylor3473f882001-02-23 17:55:21 +00004490 }
4491found:
4492 attr = attr->nexth;
4493 }
4494 return(ret);
4495}
4496
4497/**
4498 * xmlValidateRoot:
4499 * @ctxt: the validation context
4500 * @doc: a document instance
4501 *
4502 * Try to validate a the root element
4503 * basically it does the following check as described by the
4504 * XML-1.0 recommendation:
4505 * - [ VC: Root Element Type ]
4506 * it doesn't try to recurse or apply other check to the element
4507 *
4508 * returns 1 if valid or 0 otherwise
4509 */
4510
4511int
4512xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4513 xmlNodePtr root;
4514 if (doc == NULL) return(0);
4515
4516 root = xmlDocGetRootElement(doc);
4517 if ((root == NULL) || (root->name == NULL)) {
4518 VERROR(ctxt->userData, "Not valid: no root element\n");
4519 return(0);
4520 }
4521
4522 /*
4523 * When doing post validation against a separate DTD, those may
4524 * no internal subset has been generated
4525 */
4526 if ((doc->intSubset != NULL) &&
4527 (doc->intSubset->name != NULL)) {
4528 /*
4529 * Check first the document root against the NQName
4530 */
4531 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4532 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4533 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004534 snprintf((char *) qname, sizeof(qname), "%s:%s",
4535 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004536 qname[sizeof(qname) - 1] = 0;
4537 if (xmlStrEqual(doc->intSubset->name, qname))
4538 goto name_ok;
4539 }
4540 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4541 (xmlStrEqual(root->name, BAD_CAST "html")))
4542 goto name_ok;
4543 VERROR(ctxt->userData,
4544 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4545 root->name, doc->intSubset->name);
4546 return(0);
4547
4548 }
4549 }
4550name_ok:
4551 return(1);
4552}
4553
4554
4555/**
4556 * xmlValidateElement:
4557 * @ctxt: the validation context
4558 * @doc: a document instance
4559 * @elem: an element instance
4560 *
4561 * Try to validate the subtree under an element
4562 *
4563 * returns 1 if valid or 0 otherwise
4564 */
4565
4566int
4567xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4568 xmlNodePtr child;
4569 xmlAttrPtr attr;
4570 xmlChar *value;
4571 int ret = 1;
4572
4573 if (elem == NULL) return(0);
4574
4575 /*
4576 * XInclude elements were added after parsing in the infoset,
4577 * they don't really mean anything validation wise.
4578 */
4579 if ((elem->type == XML_XINCLUDE_START) ||
4580 (elem->type == XML_XINCLUDE_END))
4581 return(1);
4582
4583 CHECK_DTD;
4584
Daniel Veillard10ea86c2001-06-20 13:55:33 +00004585 /*
4586 * Entities references have to be handled separately
4587 */
4588 if (elem->type == XML_ENTITY_REF_NODE) {
4589 return(1);
4590 }
4591
Owen Taylor3473f882001-02-23 17:55:21 +00004592 ret &= xmlValidateOneElement(ctxt, doc, elem);
4593 attr = elem->properties;
4594 while(attr != NULL) {
4595 value = xmlNodeListGetString(doc, attr->children, 0);
4596 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4597 if (value != NULL)
4598 xmlFree(value);
4599 attr= attr->next;
4600 }
4601 child = elem->children;
4602 while (child != NULL) {
4603 ret &= xmlValidateElement(ctxt, doc, child);
4604 child = child->next;
4605 }
4606
4607 return(ret);
4608}
4609
Daniel Veillard8730c562001-02-26 10:49:57 +00004610/**
4611 * xmlValidateRef:
4612 * @ref: A reference to be validated
4613 * @ctxt: Validation context
4614 * @name: Name of ID we are searching for
4615 *
4616 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004617static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004618xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004619 const xmlChar *name) {
4620 xmlAttrPtr id;
4621 xmlAttrPtr attr;
4622
4623 if (ref == NULL)
4624 return;
4625 attr = ref->attr;
4626 if (attr == NULL)
4627 return;
4628 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4629 id = xmlGetID(ctxt->doc, name);
4630 if (id == NULL) {
4631 VERROR(ctxt->userData,
4632 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4633 attr->name, name);
4634 ctxt->valid = 0;
4635 }
4636 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4637 xmlChar *dup, *str = NULL, *cur, save;
4638
4639 dup = xmlStrdup(name);
4640 if (dup == NULL) {
4641 ctxt->valid = 0;
4642 return;
4643 }
4644 cur = dup;
4645 while (*cur != 0) {
4646 str = cur;
4647 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4648 save = *cur;
4649 *cur = 0;
4650 id = xmlGetID(ctxt->doc, str);
4651 if (id == NULL) {
4652 VERROR(ctxt->userData,
4653 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4654 attr->name, str);
4655 ctxt->valid = 0;
4656 }
4657 if (save == 0)
4658 break;
4659 *cur = save;
4660 while (IS_BLANK(*cur)) cur++;
4661 }
4662 xmlFree(dup);
4663 }
4664}
4665
4666/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004667 * xmlWalkValidateList:
4668 * @data: Contents of current link
4669 * @user: Value supplied by the user
4670 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004671 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00004672 */
4673static int
4674xmlWalkValidateList(const void *data, const void *user)
4675{
4676 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4677 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4678 return 1;
4679}
4680
4681/**
4682 * xmlValidateCheckRefCallback:
4683 * @ref_list: List of references
4684 * @ctxt: Validation context
4685 * @name: Name of ID we are searching for
4686 *
4687 */
4688static void
4689xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4690 const xmlChar *name) {
4691 xmlValidateMemo memo;
4692
4693 if (ref_list == NULL)
4694 return;
4695 memo.ctxt = ctxt;
4696 memo.name = name;
4697
4698 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4699
4700}
4701
4702/**
Owen Taylor3473f882001-02-23 17:55:21 +00004703 * xmlValidateDocumentFinal:
4704 * @ctxt: the validation context
4705 * @doc: a document instance
4706 *
4707 * Does the final step for the document validation once all the
4708 * incremental validation steps have been completed
4709 *
4710 * basically it does the following checks described by the XML Rec
4711 *
4712 *
4713 * returns 1 if valid or 0 otherwise
4714 */
4715
4716int
4717xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4718 xmlRefTablePtr table;
4719
4720 if (doc == NULL) {
4721 xmlGenericError(xmlGenericErrorContext,
4722 "xmlValidateDocumentFinal: doc == NULL\n");
4723 return(0);
4724 }
4725
4726 /*
4727 * Check all the NOTATION/NOTATIONS attributes
4728 */
4729 /*
4730 * Check all the ENTITY/ENTITIES attributes definition for validity
4731 */
4732 /*
4733 * Check all the IDREF/IDREFS attributes definition for validity
4734 */
4735 table = (xmlRefTablePtr) doc->refs;
4736 ctxt->doc = doc;
4737 ctxt->valid = 1;
4738 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4739 return(ctxt->valid);
4740}
4741
4742/**
4743 * xmlValidateDtd:
4744 * @ctxt: the validation context
4745 * @doc: a document instance
4746 * @dtd: a dtd instance
4747 *
4748 * Try to validate the document against the dtd instance
4749 *
4750 * basically it does check all the definitions in the DtD.
4751 *
4752 * returns 1 if valid or 0 otherwise
4753 */
4754
4755int
4756xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4757 int ret;
4758 xmlDtdPtr oldExt;
4759 xmlNodePtr root;
4760
4761 if (dtd == NULL) return(0);
4762 if (doc == NULL) return(0);
4763 oldExt = doc->extSubset;
4764 doc->extSubset = dtd;
4765 ret = xmlValidateRoot(ctxt, doc);
4766 if (ret == 0) {
4767 doc->extSubset = oldExt;
4768 return(ret);
4769 }
4770 if (doc->ids != NULL) {
4771 xmlFreeIDTable(doc->ids);
4772 doc->ids = NULL;
4773 }
4774 if (doc->refs != NULL) {
4775 xmlFreeRefTable(doc->refs);
4776 doc->refs = NULL;
4777 }
4778 root = xmlDocGetRootElement(doc);
4779 ret = xmlValidateElement(ctxt, doc, root);
4780 ret &= xmlValidateDocumentFinal(ctxt, doc);
4781 doc->extSubset = oldExt;
4782 return(ret);
4783}
4784
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004785static void
Owen Taylor3473f882001-02-23 17:55:21 +00004786xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004787 const xmlChar *name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004788 if (cur == NULL)
4789 return;
4790 switch (cur->atype) {
4791 case XML_ATTRIBUTE_CDATA:
4792 case XML_ATTRIBUTE_ID:
4793 case XML_ATTRIBUTE_IDREF :
4794 case XML_ATTRIBUTE_IDREFS:
4795 case XML_ATTRIBUTE_NMTOKEN:
4796 case XML_ATTRIBUTE_NMTOKENS:
4797 case XML_ATTRIBUTE_ENUMERATION:
4798 break;
4799 case XML_ATTRIBUTE_ENTITY:
4800 case XML_ATTRIBUTE_ENTITIES:
4801 case XML_ATTRIBUTE_NOTATION:
4802 if (cur->defaultValue != NULL) {
4803 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4804 cur->name, cur->atype, cur->defaultValue);
4805 }
4806 if (cur->tree != NULL) {
4807 xmlEnumerationPtr tree = cur->tree;
4808 while (tree != NULL) {
4809 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
4810 cur->name, cur->atype, tree->name);
4811 tree = tree->next;
4812 }
4813 }
4814 }
4815}
4816
4817/**
4818 * xmlValidateDtdFinal:
4819 * @ctxt: the validation context
4820 * @doc: a document instance
4821 *
4822 * Does the final step for the dtds validation once all the
4823 * subsets have been parsed
4824 *
4825 * basically it does the following checks described by the XML Rec
4826 * - check that ENTITY and ENTITIES type attributes default or
4827 * possible values matches one of the defined entities.
4828 * - check that NOTATION type attributes default or
4829 * possible values matches one of the defined notations.
4830 *
4831 * returns 1 if valid or 0 otherwise
4832 */
4833
4834int
4835xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4836 int ret = 1;
4837 xmlDtdPtr dtd;
4838 xmlAttributeTablePtr table;
4839
4840 if (doc == NULL) return(0);
4841 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4842 return(0);
4843 ctxt->doc = doc;
4844 ctxt->valid = ret;
4845 dtd = doc->intSubset;
4846 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4847 table = (xmlAttributeTablePtr) dtd->attributes;
4848 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4849 }
4850 dtd = doc->extSubset;
4851 if ((dtd != NULL) && (dtd->attributes != NULL)) {
4852 table = (xmlAttributeTablePtr) dtd->attributes;
4853 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
4854 }
4855 return(ctxt->valid);
4856}
4857
4858/**
4859 * xmlValidateDocument:
4860 * @ctxt: the validation context
4861 * @doc: a document instance
4862 *
4863 * Try to validate the document instance
4864 *
4865 * basically it does the all the checks described by the XML Rec
4866 * i.e. validates the internal and external subset (if present)
4867 * and validate the document tree.
4868 *
4869 * returns 1 if valid or 0 otherwise
4870 */
4871
4872int
4873xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4874 int ret;
4875 xmlNodePtr root;
4876
4877 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
4878 return(0);
4879 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
4880 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
4881 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
4882 doc->intSubset->SystemID);
4883 if (doc->extSubset == NULL) {
4884 if (doc->intSubset->SystemID != NULL) {
4885 VERROR(ctxt->userData,
4886 "Could not load the external subset \"%s\"\n",
4887 doc->intSubset->SystemID);
4888 } else {
4889 VERROR(ctxt->userData,
4890 "Could not load the external subset \"%s\"\n",
4891 doc->intSubset->ExternalID);
4892 }
4893 return(0);
4894 }
4895 }
4896
4897 if (doc->ids != NULL) {
4898 xmlFreeIDTable(doc->ids);
4899 doc->ids = NULL;
4900 }
4901 if (doc->refs != NULL) {
4902 xmlFreeRefTable(doc->refs);
4903 doc->refs = NULL;
4904 }
4905 ret = xmlValidateDtdFinal(ctxt, doc);
4906 if (!xmlValidateRoot(ctxt, doc)) return(0);
4907
4908 root = xmlDocGetRootElement(doc);
4909 ret &= xmlValidateElement(ctxt, doc, root);
4910 ret &= xmlValidateDocumentFinal(ctxt, doc);
4911 return(ret);
4912}
4913
4914
4915/************************************************************************
4916 * *
4917 * Routines for dynamic validation editing *
4918 * *
4919 ************************************************************************/
4920
4921/**
4922 * xmlValidGetPotentialChildren:
4923 * @ctree: an element content tree
4924 * @list: an array to store the list of child names
4925 * @len: a pointer to the number of element in the list
4926 * @max: the size of the array
4927 *
4928 * Build/extend a list of potential children allowed by the content tree
4929 *
4930 * returns the number of element in the list, or -1 in case of error.
4931 */
4932
4933int
4934xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
4935 int *len, int max) {
4936 int i;
4937
4938 if ((ctree == NULL) || (list == NULL) || (len == NULL))
4939 return(-1);
4940 if (*len >= max) return(*len);
4941
4942 switch (ctree->type) {
4943 case XML_ELEMENT_CONTENT_PCDATA:
4944 for (i = 0; i < *len;i++)
4945 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
4946 list[(*len)++] = BAD_CAST "#PCDATA";
4947 break;
4948 case XML_ELEMENT_CONTENT_ELEMENT:
4949 for (i = 0; i < *len;i++)
4950 if (xmlStrEqual(ctree->name, list[i])) return(*len);
4951 list[(*len)++] = ctree->name;
4952 break;
4953 case XML_ELEMENT_CONTENT_SEQ:
4954 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4955 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4956 break;
4957 case XML_ELEMENT_CONTENT_OR:
4958 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
4959 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
4960 break;
4961 }
4962
4963 return(*len);
4964}
4965
4966/**
4967 * xmlValidGetValidElements:
4968 * @prev: an element to insert after
4969 * @next: an element to insert next
4970 * @list: an array to store the list of child names
4971 * @max: the size of the array
4972 *
4973 * This function returns the list of authorized children to insert
4974 * within an existing tree while respecting the validity constraints
4975 * forced by the Dtd. The insertion point is defined using @prev and
4976 * @next in the following ways:
4977 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
4978 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
4979 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
4980 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
4981 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
4982 *
4983 * pointers to the element names are inserted at the beginning of the array
4984 * and do not need to be freed.
4985 *
4986 * returns the number of element in the list, or -1 in case of error. If
4987 * the function returns the value @max the caller is invited to grow the
4988 * receiving array and retry.
4989 */
4990
4991int
4992xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
4993 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00004994 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00004995 int nb_valid_elements = 0;
4996 const xmlChar *elements[256];
4997 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004998 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00004999
5000 xmlNode *ref_node;
5001 xmlNode *parent;
5002 xmlNode *test_node;
5003
5004 xmlNode *prev_next;
5005 xmlNode *next_prev;
5006 xmlNode *parent_childs;
5007 xmlNode *parent_last;
5008
5009 xmlElement *element_desc;
5010
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005011 vctxt.userData = NULL;
5012 vctxt.error = NULL;
5013 vctxt.warning = NULL;
5014
Owen Taylor3473f882001-02-23 17:55:21 +00005015 if (prev == NULL && next == NULL)
5016 return(-1);
5017
5018 if (list == NULL) return(-1);
5019 if (max <= 0) return(-1);
5020
5021 nb_valid_elements = 0;
5022 ref_node = prev ? prev : next;
5023 parent = ref_node->parent;
5024
5025 /*
5026 * Retrieves the parent element declaration
5027 */
5028 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5029 parent->name);
5030 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5031 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5032 parent->name);
5033 if (element_desc == NULL) return(-1);
5034
5035 /*
5036 * Do a backup of the current tree structure
5037 */
5038 prev_next = prev ? prev->next : NULL;
5039 next_prev = next ? next->prev : NULL;
5040 parent_childs = parent->children;
5041 parent_last = parent->last;
5042
5043 /*
5044 * Creates a dummy node and insert it into the tree
5045 */
5046 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5047 test_node->doc = ref_node->doc;
5048 test_node->parent = parent;
5049 test_node->prev = prev;
5050 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005051 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005052
5053 if (prev) prev->next = test_node;
5054 else parent->children = test_node;
5055
5056 if (next) next->prev = test_node;
5057 else parent->last = test_node;
5058
5059 /*
5060 * Insert each potential child node and check if the parent is
5061 * still valid
5062 */
5063 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5064 elements, &nb_elements, 256);
5065
5066 for (i = 0;i < nb_elements;i++) {
5067 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005068 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005069 int j;
5070
5071 for (j = 0; j < nb_valid_elements;j++)
5072 if (xmlStrEqual(elements[i], list[j])) break;
5073 list[nb_valid_elements++] = elements[i];
5074 if (nb_valid_elements >= max) break;
5075 }
5076 }
5077
5078 /*
5079 * Restore the tree structure
5080 */
5081 if (prev) prev->next = prev_next;
5082 if (next) next->prev = next_prev;
5083 parent->children = parent_childs;
5084 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005085
5086 /*
5087 * Free up the dummy node
5088 */
5089 test_node->name = name;
5090 xmlFreeNode(test_node);
5091
Owen Taylor3473f882001-02-23 17:55:21 +00005092 return(nb_valid_elements);
5093}