blob: c6db9e48b656cc8d5c6a80c04eb22a30f1619da0 [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;
Daniel Veillardd01fd3e2002-02-18 22:27:47 +00001298 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00001299 }
1300
1301 /*
1302 * Create the Attribute table if needed.
1303 */
1304 table = (xmlAttributeTablePtr) dtd->attributes;
1305 if (table == NULL) {
1306 table = xmlCreateAttributeTable();
1307 dtd->attributes = (void *) table;
1308 }
1309 if (table == NULL) {
1310 xmlGenericError(xmlGenericErrorContext,
1311 "xmlAddAttributeDecl: Table creation failed!\n");
1312 return(NULL);
1313 }
1314
1315
1316 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1317 if (ret == NULL) {
1318 xmlGenericError(xmlGenericErrorContext,
1319 "xmlAddAttributeDecl: out of memory\n");
1320 return(NULL);
1321 }
1322 memset(ret, 0, sizeof(xmlAttribute));
1323 ret->type = XML_ATTRIBUTE_DECL;
1324
1325 /*
1326 * fill the structure.
1327 */
1328 ret->atype = type;
1329 ret->name = xmlStrdup(name);
1330 ret->prefix = xmlStrdup(ns);
1331 ret->elem = xmlStrdup(elem);
1332 ret->def = def;
1333 ret->tree = tree;
1334 if (defaultValue != NULL)
1335 ret->defaultValue = xmlStrdup(defaultValue);
1336
1337 /*
1338 * Validity Check:
1339 * Search the DTD for previous declarations of the ATTLIST
1340 */
1341 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1342 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001343 * The attribute is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001344 */
1345 VWARNING(ctxt->userData,
1346 "Attribute %s on %s: already defined\n",
1347 name, elem);
1348 xmlFreeAttribute(ret);
1349 return(NULL);
1350 }
1351
1352 /*
1353 * Validity Check:
1354 * Multiple ID per element
1355 */
Daniel Veillarda10efa82001-04-18 13:09:01 +00001356 elemDef = xmlGetDtdElementDesc2(dtd, elem, 1);
Owen Taylor3473f882001-02-23 17:55:21 +00001357 if (elemDef != NULL) {
Daniel Veillard48da9102001-08-07 01:10:10 +00001358
Owen Taylor3473f882001-02-23 17:55:21 +00001359 if ((type == XML_ATTRIBUTE_ID) &&
Daniel Veillardc7612992002-02-17 22:47:37 +00001360 (xmlScanIDAttributeDecl(NULL, elemDef) != 0)) {
Owen Taylor3473f882001-02-23 17:55:21 +00001361 VERROR(ctxt->userData,
1362 "Element %s has too may ID attributes defined : %s\n",
1363 elem, name);
Daniel Veillardc7612992002-02-17 22:47:37 +00001364 ctxt->valid = 0;
1365 }
1366
Daniel Veillard48da9102001-08-07 01:10:10 +00001367 /*
1368 * Insert namespace default def first they need to be
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001369 * processed first.
Daniel Veillard48da9102001-08-07 01:10:10 +00001370 */
1371 if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) ||
1372 ((ret->prefix != NULL &&
1373 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) {
1374 ret->nexth = elemDef->attributes;
1375 elemDef->attributes = ret;
1376 } else {
1377 xmlAttributePtr tmp = elemDef->attributes;
1378
1379 while ((tmp != NULL) &&
1380 ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) ||
1381 ((ret->prefix != NULL &&
1382 (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) {
1383 if (tmp->nexth == NULL)
1384 break;
1385 tmp = tmp->nexth;
1386 }
1387 if (tmp != NULL) {
1388 ret->nexth = tmp->nexth;
1389 tmp->nexth = ret;
1390 } else {
1391 ret->nexth = elemDef->attributes;
1392 elemDef->attributes = ret;
1393 }
1394 }
Owen Taylor3473f882001-02-23 17:55:21 +00001395 }
1396
1397 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001398 * Link it to the DTD
Owen Taylor3473f882001-02-23 17:55:21 +00001399 */
1400 ret->parent = dtd;
1401 ret->doc = dtd->doc;
1402 if (dtd->last == NULL) {
1403 dtd->children = dtd->last = (xmlNodePtr) ret;
1404 } else {
1405 dtd->last->next = (xmlNodePtr) ret;
1406 ret->prev = dtd->last;
1407 dtd->last = (xmlNodePtr) ret;
1408 }
1409 return(ret);
1410}
1411
1412/**
1413 * xmlFreeAttributeTable:
1414 * @table: An attribute table
1415 *
1416 * Deallocate the memory used by an entities hash table.
1417 */
1418void
1419xmlFreeAttributeTable(xmlAttributeTablePtr table) {
1420 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1421}
1422
1423/**
1424 * xmlCopyAttribute:
1425 * @attr: An attribute
1426 *
1427 * Build a copy of an attribute.
1428 *
1429 * Returns the new xmlAttributePtr or NULL in case of error.
1430 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001431static xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001432xmlCopyAttribute(xmlAttributePtr attr) {
1433 xmlAttributePtr cur;
1434
1435 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1436 if (cur == NULL) {
1437 xmlGenericError(xmlGenericErrorContext,
1438 "xmlCopyAttribute: out of memory !\n");
1439 return(NULL);
1440 }
1441 memset(cur, 0, sizeof(xmlAttribute));
Daniel Veillard36065812002-01-24 15:02:46 +00001442 cur->type = XML_ATTRIBUTE_DECL;
Owen Taylor3473f882001-02-23 17:55:21 +00001443 cur->atype = attr->atype;
1444 cur->def = attr->def;
1445 cur->tree = xmlCopyEnumeration(attr->tree);
1446 if (attr->elem != NULL)
1447 cur->elem = xmlStrdup(attr->elem);
1448 if (attr->name != NULL)
1449 cur->name = xmlStrdup(attr->name);
Daniel Veillard36065812002-01-24 15:02:46 +00001450 if (attr->prefix != NULL)
1451 cur->prefix = xmlStrdup(attr->prefix);
Owen Taylor3473f882001-02-23 17:55:21 +00001452 if (attr->defaultValue != NULL)
1453 cur->defaultValue = xmlStrdup(attr->defaultValue);
1454 return(cur);
1455}
1456
1457/**
1458 * xmlCopyAttributeTable:
1459 * @table: An attribute table
1460 *
1461 * Build a copy of an attribute table.
1462 *
1463 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1464 */
1465xmlAttributeTablePtr
1466xmlCopyAttributeTable(xmlAttributeTablePtr table) {
1467 return((xmlAttributeTablePtr) xmlHashCopy(table,
1468 (xmlHashCopier) xmlCopyAttribute));
1469}
1470
1471/**
1472 * xmlDumpAttributeDecl:
1473 * @buf: the XML buffer output
1474 * @attr: An attribute declaration
1475 *
1476 * This will dump the content of the attribute declaration as an XML
1477 * DTD definition
1478 */
1479void
1480xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1481 xmlBufferWriteChar(buf, "<!ATTLIST ");
1482 xmlBufferWriteCHAR(buf, attr->elem);
1483 xmlBufferWriteChar(buf, " ");
1484 if (attr->prefix != NULL) {
1485 xmlBufferWriteCHAR(buf, attr->prefix);
1486 xmlBufferWriteChar(buf, ":");
1487 }
1488 xmlBufferWriteCHAR(buf, attr->name);
1489 switch (attr->atype) {
1490 case XML_ATTRIBUTE_CDATA:
1491 xmlBufferWriteChar(buf, " CDATA");
1492 break;
1493 case XML_ATTRIBUTE_ID:
1494 xmlBufferWriteChar(buf, " ID");
1495 break;
1496 case XML_ATTRIBUTE_IDREF:
1497 xmlBufferWriteChar(buf, " IDREF");
1498 break;
1499 case XML_ATTRIBUTE_IDREFS:
1500 xmlBufferWriteChar(buf, " IDREFS");
1501 break;
1502 case XML_ATTRIBUTE_ENTITY:
1503 xmlBufferWriteChar(buf, " ENTITY");
1504 break;
1505 case XML_ATTRIBUTE_ENTITIES:
1506 xmlBufferWriteChar(buf, " ENTITIES");
1507 break;
1508 case XML_ATTRIBUTE_NMTOKEN:
1509 xmlBufferWriteChar(buf, " NMTOKEN");
1510 break;
1511 case XML_ATTRIBUTE_NMTOKENS:
1512 xmlBufferWriteChar(buf, " NMTOKENS");
1513 break;
1514 case XML_ATTRIBUTE_ENUMERATION:
1515 xmlBufferWriteChar(buf, " (");
1516 xmlDumpEnumeration(buf, attr->tree);
1517 break;
1518 case XML_ATTRIBUTE_NOTATION:
1519 xmlBufferWriteChar(buf, " NOTATION (");
1520 xmlDumpEnumeration(buf, attr->tree);
1521 break;
1522 default:
1523 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001524 "xmlDumpAttributeDecl: internal: unknown type %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001525 attr->atype);
1526 }
1527 switch (attr->def) {
1528 case XML_ATTRIBUTE_NONE:
1529 break;
1530 case XML_ATTRIBUTE_REQUIRED:
1531 xmlBufferWriteChar(buf, " #REQUIRED");
1532 break;
1533 case XML_ATTRIBUTE_IMPLIED:
1534 xmlBufferWriteChar(buf, " #IMPLIED");
1535 break;
1536 case XML_ATTRIBUTE_FIXED:
1537 xmlBufferWriteChar(buf, " #FIXED");
1538 break;
1539 default:
1540 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001541 "xmlDumpAttributeDecl: internal: unknown default %d\n",
Owen Taylor3473f882001-02-23 17:55:21 +00001542 attr->def);
1543 }
1544 if (attr->defaultValue != NULL) {
1545 xmlBufferWriteChar(buf, " ");
1546 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1547 }
1548 xmlBufferWriteChar(buf, ">\n");
1549}
1550
1551/**
1552 * xmlDumpAttributeTable:
1553 * @buf: the XML buffer output
1554 * @table: An attribute table
1555 *
1556 * This will dump the content of the attribute table as an XML DTD definition
1557 */
1558void
1559xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
1560 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
1561}
1562
1563/************************************************************************
1564 * *
1565 * NOTATIONs *
1566 * *
1567 ************************************************************************/
1568/**
1569 * xmlCreateNotationTable:
1570 *
1571 * create and initialize an empty notation hash table.
1572 *
1573 * Returns the xmlNotationTablePtr just created or NULL in case
1574 * of error.
1575 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001576static xmlNotationTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001577xmlCreateNotationTable(void) {
1578 return(xmlHashCreate(0));
1579}
1580
1581/**
1582 * xmlFreeNotation:
1583 * @not: A notation
1584 *
1585 * Deallocate the memory used by an notation definition
1586 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001587static void
Owen Taylor3473f882001-02-23 17:55:21 +00001588xmlFreeNotation(xmlNotationPtr nota) {
1589 if (nota == NULL) return;
1590 if (nota->name != NULL)
1591 xmlFree((xmlChar *) nota->name);
1592 if (nota->PublicID != NULL)
1593 xmlFree((xmlChar *) nota->PublicID);
1594 if (nota->SystemID != NULL)
1595 xmlFree((xmlChar *) nota->SystemID);
Owen Taylor3473f882001-02-23 17:55:21 +00001596 xmlFree(nota);
1597}
1598
1599
1600/**
1601 * xmlAddNotationDecl:
1602 * @dtd: pointer to the DTD
1603 * @ctxt: the validation context
1604 * @name: the entity name
1605 * @PublicID: the public identifier or NULL
1606 * @SystemID: the system identifier or NULL
1607 *
1608 * Register a new notation declaration
1609 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001610 * Returns NULL if not, otherwise the entity
Owen Taylor3473f882001-02-23 17:55:21 +00001611 */
1612xmlNotationPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00001613xmlAddNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDtdPtr dtd,
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001614 const xmlChar *name,
Owen Taylor3473f882001-02-23 17:55:21 +00001615 const xmlChar *PublicID, const xmlChar *SystemID) {
1616 xmlNotationPtr ret;
1617 xmlNotationTablePtr table;
1618
1619 if (dtd == NULL) {
1620 xmlGenericError(xmlGenericErrorContext,
1621 "xmlAddNotationDecl: dtd == NULL\n");
1622 return(NULL);
1623 }
1624 if (name == NULL) {
1625 xmlGenericError(xmlGenericErrorContext,
1626 "xmlAddNotationDecl: name == NULL\n");
1627 return(NULL);
1628 }
1629 if ((PublicID == NULL) && (SystemID == NULL)) {
1630 xmlGenericError(xmlGenericErrorContext,
1631 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard7aea52d2002-02-17 23:07:47 +00001632 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00001633 }
1634
1635 /*
1636 * Create the Notation table if needed.
1637 */
1638 table = (xmlNotationTablePtr) dtd->notations;
1639 if (table == NULL)
1640 dtd->notations = table = xmlCreateNotationTable();
1641 if (table == NULL) {
1642 xmlGenericError(xmlGenericErrorContext,
1643 "xmlAddNotationDecl: Table creation failed!\n");
1644 return(NULL);
1645 }
1646
1647 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1648 if (ret == NULL) {
1649 xmlGenericError(xmlGenericErrorContext,
1650 "xmlAddNotationDecl: out of memory\n");
1651 return(NULL);
1652 }
1653 memset(ret, 0, sizeof(xmlNotation));
1654
1655 /*
1656 * fill the structure.
1657 */
1658 ret->name = xmlStrdup(name);
1659 if (SystemID != NULL)
1660 ret->SystemID = xmlStrdup(SystemID);
1661 if (PublicID != NULL)
1662 ret->PublicID = xmlStrdup(PublicID);
1663
1664 /*
1665 * Validity Check:
1666 * Check the DTD for previous declarations of the ATTLIST
1667 */
1668 if (xmlHashAddEntry(table, name, ret)) {
1669 xmlGenericError(xmlGenericErrorContext,
1670 "xmlAddNotationDecl: %s already defined\n", name);
1671 xmlFreeNotation(ret);
1672 return(NULL);
1673 }
1674 return(ret);
1675}
1676
1677/**
1678 * xmlFreeNotationTable:
1679 * @table: An notation table
1680 *
1681 * Deallocate the memory used by an entities hash table.
1682 */
1683void
1684xmlFreeNotationTable(xmlNotationTablePtr table) {
1685 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1686}
1687
1688/**
1689 * xmlCopyNotation:
1690 * @nota: A notation
1691 *
1692 * Build a copy of a notation.
1693 *
1694 * Returns the new xmlNotationPtr or NULL in case of error.
1695 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001696static xmlNotationPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001697xmlCopyNotation(xmlNotationPtr nota) {
1698 xmlNotationPtr cur;
1699
1700 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1701 if (cur == NULL) {
1702 xmlGenericError(xmlGenericErrorContext,
1703 "xmlCopyNotation: out of memory !\n");
1704 return(NULL);
1705 }
1706 if (nota->name != NULL)
1707 cur->name = xmlStrdup(nota->name);
1708 else
1709 cur->name = NULL;
1710 if (nota->PublicID != NULL)
1711 cur->PublicID = xmlStrdup(nota->PublicID);
1712 else
1713 cur->PublicID = NULL;
1714 if (nota->SystemID != NULL)
1715 cur->SystemID = xmlStrdup(nota->SystemID);
1716 else
1717 cur->SystemID = NULL;
1718 return(cur);
1719}
1720
1721/**
1722 * xmlCopyNotationTable:
1723 * @table: A notation table
1724 *
1725 * Build a copy of a notation table.
1726 *
1727 * Returns the new xmlNotationTablePtr or NULL in case of error.
1728 */
1729xmlNotationTablePtr
1730xmlCopyNotationTable(xmlNotationTablePtr table) {
1731 return((xmlNotationTablePtr) xmlHashCopy(table,
1732 (xmlHashCopier) xmlCopyNotation));
1733}
1734
1735/**
1736 * xmlDumpNotationDecl:
1737 * @buf: the XML buffer output
1738 * @nota: A notation declaration
1739 *
1740 * This will dump the content the notation declaration as an XML DTD definition
1741 */
1742void
1743xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1744 xmlBufferWriteChar(buf, "<!NOTATION ");
1745 xmlBufferWriteCHAR(buf, nota->name);
1746 if (nota->PublicID != NULL) {
1747 xmlBufferWriteChar(buf, " PUBLIC ");
1748 xmlBufferWriteQuotedString(buf, nota->PublicID);
1749 if (nota->SystemID != NULL) {
1750 xmlBufferWriteChar(buf, " ");
1751 xmlBufferWriteCHAR(buf, nota->SystemID);
1752 }
1753 } else {
1754 xmlBufferWriteChar(buf, " SYSTEM ");
1755 xmlBufferWriteCHAR(buf, nota->SystemID);
1756 }
1757 xmlBufferWriteChar(buf, " >\n");
1758}
1759
1760/**
1761 * xmlDumpNotationTable:
1762 * @buf: the XML buffer output
1763 * @table: A notation table
1764 *
1765 * This will dump the content of the notation table as an XML DTD definition
1766 */
1767void
1768xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
1769 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
1770}
1771
1772/************************************************************************
1773 * *
1774 * IDs *
1775 * *
1776 ************************************************************************/
1777/**
1778 * xmlCreateIDTable:
1779 *
1780 * create and initialize an empty id hash table.
1781 *
1782 * Returns the xmlIDTablePtr just created or NULL in case
1783 * of error.
1784 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001785static xmlIDTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00001786xmlCreateIDTable(void) {
1787 return(xmlHashCreate(0));
1788}
1789
1790/**
1791 * xmlFreeID:
1792 * @not: A id
1793 *
1794 * Deallocate the memory used by an id definition
1795 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00001796static void
Owen Taylor3473f882001-02-23 17:55:21 +00001797xmlFreeID(xmlIDPtr id) {
1798 if (id == NULL) return;
1799 if (id->value != NULL)
1800 xmlFree((xmlChar *) id->value);
Owen Taylor3473f882001-02-23 17:55:21 +00001801 xmlFree(id);
1802}
1803
1804/**
1805 * xmlAddID:
1806 * @ctxt: the validation context
1807 * @doc: pointer to the document
1808 * @value: the value name
1809 * @attr: the attribute holding the ID
1810 *
1811 * Register a new id declaration
1812 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001813 * Returns NULL if not, otherwise the new xmlIDPtr
Owen Taylor3473f882001-02-23 17:55:21 +00001814 */
1815xmlIDPtr
1816xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
1817 xmlAttrPtr attr) {
1818 xmlIDPtr ret;
1819 xmlIDTablePtr table;
1820
1821 if (doc == NULL) {
1822 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001823 "xmlAddID: doc == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001824 return(NULL);
1825 }
1826 if (value == NULL) {
1827 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001828 "xmlAddID: value == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001829 return(NULL);
1830 }
1831 if (attr == NULL) {
1832 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001833 "xmlAddID: attr == NULL\n");
Owen Taylor3473f882001-02-23 17:55:21 +00001834 return(NULL);
1835 }
1836
1837 /*
1838 * Create the ID table if needed.
1839 */
1840 table = (xmlIDTablePtr) doc->ids;
1841 if (table == NULL)
1842 doc->ids = table = xmlCreateIDTable();
1843 if (table == NULL) {
1844 xmlGenericError(xmlGenericErrorContext,
1845 "xmlAddID: Table creation failed!\n");
1846 return(NULL);
1847 }
1848
1849 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
1850 if (ret == NULL) {
1851 xmlGenericError(xmlGenericErrorContext,
1852 "xmlAddID: out of memory\n");
1853 return(NULL);
1854 }
1855
1856 /*
1857 * fill the structure.
1858 */
1859 ret->value = xmlStrdup(value);
1860 ret->attr = attr;
1861
1862 if (xmlHashAddEntry(table, value, ret) < 0) {
1863 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001864 * The id is already defined in this DTD.
Owen Taylor3473f882001-02-23 17:55:21 +00001865 */
Daniel Veillardc5f05ad2002-02-10 11:57:22 +00001866 if (ctxt != NULL)
1867 VERROR(ctxt->userData, "ID %s already defined\n", value);
Owen Taylor3473f882001-02-23 17:55:21 +00001868 xmlFreeID(ret);
1869 return(NULL);
1870 }
1871 return(ret);
1872}
1873
1874/**
1875 * xmlFreeIDTable:
1876 * @table: An id table
1877 *
1878 * Deallocate the memory used by an ID hash table.
1879 */
1880void
1881xmlFreeIDTable(xmlIDTablePtr table) {
1882 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
1883}
1884
1885/**
1886 * xmlIsID:
1887 * @doc: the document
1888 * @elem: the element carrying the attribute
1889 * @attr: the attribute
1890 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00001891 * Determine whether an attribute is of type ID. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00001892 * then this is simple, otherwise we use an heuristic: name ID (upper
1893 * or lowercase).
1894 *
1895 * Returns 0 or 1 depending on the lookup result
1896 */
1897int
1898xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1899 if (doc == NULL) return(0);
1900 if (attr == NULL) return(0);
1901 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1902 return(0);
1903 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1904 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1905 (xmlStrEqual(BAD_CAST "name", attr->name)))
1906 return(1);
1907 return(0);
1908 } else {
1909 xmlAttributePtr attrDecl;
1910
1911 if (elem == NULL) return(0);
1912 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1913 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1914 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1915 attr->name);
1916
1917 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
1918 return(1);
1919 }
1920 return(0);
1921}
1922
1923/**
1924 * xmlRemoveID
1925 * @doc: the document
1926 * @attr: the attribute
1927 *
1928 * Remove the given attribute from the ID table maintained internally.
1929 *
1930 * Returns -1 if the lookup failed and 0 otherwise
1931 */
1932int
1933xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
1934 xmlAttrPtr cur;
1935 xmlIDTablePtr table;
1936 xmlChar *ID;
1937
1938 if (doc == NULL) return(-1);
1939 if (attr == NULL) return(-1);
1940 table = (xmlIDTablePtr) doc->ids;
1941 if (table == NULL)
1942 return(-1);
1943
1944 if (attr == NULL)
1945 return(-1);
1946 ID = xmlNodeListGetString(doc, attr->children, 1);
1947 if (ID == NULL)
1948 return(-1);
1949 cur = xmlHashLookup(table, ID);
1950 if (cur != attr) {
1951 xmlFree(ID);
1952 return(-1);
1953 }
1954 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1955 xmlFree(ID);
1956 return(0);
1957}
1958
1959/**
1960 * xmlGetID:
1961 * @doc: pointer to the document
1962 * @ID: the ID value
1963 *
1964 * Search the attribute declaring the given ID
1965 *
1966 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1967 */
1968xmlAttrPtr
1969xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
1970 xmlIDTablePtr table;
1971 xmlIDPtr id;
1972
1973 if (doc == NULL) {
1974 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
1975 return(NULL);
1976 }
1977
1978 if (ID == NULL) {
1979 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
1980 return(NULL);
1981 }
1982
1983 table = (xmlIDTablePtr) doc->ids;
1984 if (table == NULL)
1985 return(NULL);
1986
1987 id = xmlHashLookup(table, ID);
1988 if (id == NULL)
1989 return(NULL);
1990 return(id->attr);
1991}
1992
1993/************************************************************************
1994 * *
1995 * Refs *
1996 * *
1997 ************************************************************************/
Daniel Veillard8730c562001-02-26 10:49:57 +00001998typedef struct xmlRemoveMemo_t
Owen Taylor3473f882001-02-23 17:55:21 +00001999{
2000 xmlListPtr l;
2001 xmlAttrPtr ap;
Daniel Veillard8730c562001-02-26 10:49:57 +00002002} xmlRemoveMemo;
2003
2004typedef xmlRemoveMemo *xmlRemoveMemoPtr;
2005
2006typedef struct xmlValidateMemo_t
2007{
2008 xmlValidCtxtPtr ctxt;
2009 const xmlChar *name;
2010} xmlValidateMemo;
2011
2012typedef xmlValidateMemo *xmlValidateMemoPtr;
Owen Taylor3473f882001-02-23 17:55:21 +00002013
2014/**
2015 * xmlCreateRefTable:
2016 *
2017 * create and initialize an empty ref hash table.
2018 *
2019 * Returns the xmlRefTablePtr just created or NULL in case
2020 * of error.
2021 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002022static xmlRefTablePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002023xmlCreateRefTable(void) {
2024 return(xmlHashCreate(0));
2025}
2026
2027/**
2028 * xmlFreeRef:
2029 * @lk: A list link
2030 *
2031 * Deallocate the memory used by a ref definition
2032 */
2033static void
2034xmlFreeRef(xmlLinkPtr lk) {
Daniel Veillard37721922001-05-04 15:21:12 +00002035 xmlRefPtr ref = (xmlRefPtr)xmlLinkGetData(lk);
2036 if (ref == NULL) return;
2037 if (ref->value != NULL)
2038 xmlFree((xmlChar *)ref->value);
2039 xmlFree(ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002040}
2041
2042/**
2043 * xmlFreeRefList:
2044 * @list_ref: A list of references.
2045 *
2046 * Deallocate the memory used by a list of references
2047 */
2048static void
2049xmlFreeRefList(xmlListPtr list_ref) {
Daniel Veillard37721922001-05-04 15:21:12 +00002050 if (list_ref == NULL) return;
2051 xmlListDelete(list_ref);
Owen Taylor3473f882001-02-23 17:55:21 +00002052}
2053
2054/**
2055 * xmlWalkRemoveRef:
2056 * @data: Contents of current link
2057 * @user: Value supplied by the user
2058 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002059 * Returns 0 to abort the walk or 1 to continue
Owen Taylor3473f882001-02-23 17:55:21 +00002060 */
2061static int
2062xmlWalkRemoveRef(const void *data, const void *user)
2063{
Daniel Veillard37721922001-05-04 15:21:12 +00002064 xmlAttrPtr attr0 = ((xmlRefPtr)data)->attr;
2065 xmlAttrPtr attr1 = ((xmlRemoveMemoPtr)user)->ap;
2066 xmlListPtr ref_list = ((xmlRemoveMemoPtr)user)->l;
Owen Taylor3473f882001-02-23 17:55:21 +00002067
Daniel Veillard37721922001-05-04 15:21:12 +00002068 if (attr0 == attr1) { /* Matched: remove and terminate walk */
2069 xmlListRemoveFirst(ref_list, (void *)data);
2070 return 0;
2071 }
2072 return 1;
Owen Taylor3473f882001-02-23 17:55:21 +00002073}
2074
2075/**
2076 * xmlAddRef:
2077 * @ctxt: the validation context
2078 * @doc: pointer to the document
2079 * @value: the value name
2080 * @attr: the attribute holding the Ref
2081 *
2082 * Register a new ref declaration
2083 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002084 * Returns NULL if not, otherwise the new xmlRefPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002085 */
2086xmlRefPtr
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002087xmlAddRef(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc, const xmlChar *value,
Owen Taylor3473f882001-02-23 17:55:21 +00002088 xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002089 xmlRefPtr ret;
2090 xmlRefTablePtr table;
2091 xmlListPtr ref_list;
Owen Taylor3473f882001-02-23 17:55:21 +00002092
Daniel Veillard37721922001-05-04 15:21:12 +00002093 if (doc == NULL) {
2094 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002095 "xmlAddRef: doc == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002096 return(NULL);
2097 }
2098 if (value == NULL) {
2099 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002100 "xmlAddRef: value == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002101 return(NULL);
2102 }
2103 if (attr == NULL) {
2104 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002105 "xmlAddRef: attr == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002106 return(NULL);
2107 }
Owen Taylor3473f882001-02-23 17:55:21 +00002108
Daniel Veillard37721922001-05-04 15:21:12 +00002109 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002110 * Create the Ref table if needed.
2111 */
Daniel Veillard37721922001-05-04 15:21:12 +00002112 table = (xmlRefTablePtr) doc->refs;
2113 if (table == NULL)
2114 doc->refs = table = xmlCreateRefTable();
2115 if (table == NULL) {
2116 xmlGenericError(xmlGenericErrorContext,
2117 "xmlAddRef: Table creation failed!\n");
2118 return(NULL);
2119 }
Owen Taylor3473f882001-02-23 17:55:21 +00002120
Daniel Veillard37721922001-05-04 15:21:12 +00002121 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
2122 if (ret == NULL) {
2123 xmlGenericError(xmlGenericErrorContext,
2124 "xmlAddRef: out of memory\n");
2125 return(NULL);
2126 }
Owen Taylor3473f882001-02-23 17:55:21 +00002127
Daniel Veillard37721922001-05-04 15:21:12 +00002128 /*
Owen Taylor3473f882001-02-23 17:55:21 +00002129 * fill the structure.
2130 */
Daniel Veillard37721922001-05-04 15:21:12 +00002131 ret->value = xmlStrdup(value);
2132 ret->attr = attr;
Owen Taylor3473f882001-02-23 17:55:21 +00002133
Daniel Veillard37721922001-05-04 15:21:12 +00002134 /* To add a reference :-
2135 * References are maintained as a list of references,
2136 * Lookup the entry, if no entry create new nodelist
2137 * Add the owning node to the NodeList
2138 * Return the ref
2139 */
Owen Taylor3473f882001-02-23 17:55:21 +00002140
Daniel Veillard37721922001-05-04 15:21:12 +00002141 if (NULL == (ref_list = xmlHashLookup(table, value))) {
2142 if (NULL == (ref_list = xmlListCreate(xmlFreeRef, NULL))) {
2143 xmlGenericError(xmlGenericErrorContext,
2144 "xmlAddRef: Reference list creation failed!\n");
2145 return(NULL);
2146 }
2147 if (xmlHashAddEntry(table, value, ref_list) < 0) {
2148 xmlListDelete(ref_list);
2149 xmlGenericError(xmlGenericErrorContext,
2150 "xmlAddRef: Reference list insertion failed!\n");
2151 return(NULL);
2152 }
2153 }
2154 xmlListInsert(ref_list, ret);
2155 return(ret);
Owen Taylor3473f882001-02-23 17:55:21 +00002156}
2157
2158/**
2159 * xmlFreeRefTable:
2160 * @table: An ref table
2161 *
2162 * Deallocate the memory used by an Ref hash table.
2163 */
2164void
2165xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard37721922001-05-04 15:21:12 +00002166 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRefList);
Owen Taylor3473f882001-02-23 17:55:21 +00002167}
2168
2169/**
2170 * xmlIsRef:
2171 * @doc: the document
2172 * @elem: the element carrying the attribute
2173 * @attr: the attribute
2174 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002175 * Determine whether an attribute is of type Ref. In case we have DTD(s)
Owen Taylor3473f882001-02-23 17:55:21 +00002176 * then this is simple, otherwise we use an heuristic: name Ref (upper
2177 * or lowercase).
2178 *
2179 * Returns 0 or 1 depending on the lookup result
2180 */
2181int
2182xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002183 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
2184 return(0);
2185 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
2186 /* TODO @@@ */
2187 return(0);
2188 } else {
2189 xmlAttributePtr attrDecl;
Owen Taylor3473f882001-02-23 17:55:21 +00002190
Daniel Veillard37721922001-05-04 15:21:12 +00002191 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
2192 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2193 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2194 elem->name, attr->name);
Owen Taylor3473f882001-02-23 17:55:21 +00002195
Daniel Veillard37721922001-05-04 15:21:12 +00002196 if ((attrDecl != NULL) &&
2197 (attrDecl->atype == XML_ATTRIBUTE_IDREF ||
2198 attrDecl->atype == XML_ATTRIBUTE_IDREFS))
2199 return(1);
2200 }
2201 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002202}
2203
2204/**
2205 * xmlRemoveRef
2206 * @doc: the document
2207 * @attr: the attribute
2208 *
2209 * Remove the given attribute from the Ref table maintained internally.
2210 *
2211 * Returns -1 if the lookup failed and 0 otherwise
2212 */
2213int
2214xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard37721922001-05-04 15:21:12 +00002215 xmlListPtr ref_list;
2216 xmlRefTablePtr table;
2217 xmlChar *ID;
2218 xmlRemoveMemo target;
Owen Taylor3473f882001-02-23 17:55:21 +00002219
Daniel Veillard37721922001-05-04 15:21:12 +00002220 if (doc == NULL) return(-1);
2221 if (attr == NULL) return(-1);
2222 table = (xmlRefTablePtr) doc->refs;
2223 if (table == NULL)
2224 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002225
Daniel Veillard37721922001-05-04 15:21:12 +00002226 if (attr == NULL)
2227 return(-1);
2228 ID = xmlNodeListGetString(doc, attr->children, 1);
2229 if (ID == NULL)
2230 return(-1);
2231 ref_list = xmlHashLookup(table, ID);
Owen Taylor3473f882001-02-23 17:55:21 +00002232
Daniel Veillard37721922001-05-04 15:21:12 +00002233 if(ref_list == NULL) {
2234 xmlFree(ID);
2235 return (-1);
2236 }
2237 /* At this point, ref_list refers to a list of references which
2238 * have the same key as the supplied attr. Our list of references
2239 * is ordered by reference address and we don't have that information
2240 * here to use when removing. We'll have to walk the list and
2241 * check for a matching attribute, when we find one stop the walk
2242 * and remove the entry.
2243 * The list is ordered by reference, so that means we don't have the
2244 * key. Passing the list and the reference to the walker means we
2245 * will have enough data to be able to remove the entry.
2246 */
2247 target.l = ref_list;
2248 target.ap = attr;
2249
2250 /* Remove the supplied attr from our list */
2251 xmlListWalk(ref_list, xmlWalkRemoveRef, &target);
Owen Taylor3473f882001-02-23 17:55:21 +00002252
Daniel Veillard37721922001-05-04 15:21:12 +00002253 /*If the list is empty then remove the list entry in the hash */
2254 if (xmlListEmpty(ref_list))
2255 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator)
2256 xmlFreeRefList);
2257 xmlFree(ID);
2258 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002259}
2260
2261/**
2262 * xmlGetRefs:
2263 * @doc: pointer to the document
2264 * @ID: the ID value
2265 *
2266 * Find the set of references for the supplied ID.
2267 *
2268 * Returns NULL if not found, otherwise node set for the ID.
2269 */
2270xmlListPtr
2271xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillard37721922001-05-04 15:21:12 +00002272 xmlRefTablePtr table;
Owen Taylor3473f882001-02-23 17:55:21 +00002273
Daniel Veillard37721922001-05-04 15:21:12 +00002274 if (doc == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002275 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: doc == 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 if (ID == NULL) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002280 xmlGenericError(xmlGenericErrorContext, "xmlGetRefs: ID == NULL\n");
Daniel Veillard37721922001-05-04 15:21:12 +00002281 return(NULL);
2282 }
Owen Taylor3473f882001-02-23 17:55:21 +00002283
Daniel Veillard37721922001-05-04 15:21:12 +00002284 table = (xmlRefTablePtr) doc->refs;
2285 if (table == NULL)
2286 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002287
Daniel Veillard37721922001-05-04 15:21:12 +00002288 return (xmlHashLookup(table, ID));
Owen Taylor3473f882001-02-23 17:55:21 +00002289}
2290
2291/************************************************************************
2292 * *
2293 * Routines for validity checking *
2294 * *
2295 ************************************************************************/
2296
2297/**
2298 * xmlGetDtdElementDesc:
2299 * @dtd: a pointer to the DtD to search
2300 * @name: the element name
2301 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002302 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002303 *
2304 * returns the xmlElementPtr if found or NULL
2305 */
2306
2307xmlElementPtr
2308xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
2309 xmlElementTablePtr table;
2310 xmlElementPtr cur;
2311 xmlChar *uqname = NULL, *prefix = NULL;
2312
2313 if (dtd == NULL) return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002314 if (dtd->elements == NULL)
2315 return(NULL);
Owen Taylor3473f882001-02-23 17:55:21 +00002316 table = (xmlElementTablePtr) dtd->elements;
2317
2318 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillarda10efa82001-04-18 13:09:01 +00002319 if (uqname != NULL)
2320 name = uqname;
2321 cur = xmlHashLookup2(table, name, prefix);
2322 if (prefix != NULL) xmlFree(prefix);
2323 if (uqname != NULL) xmlFree(uqname);
2324 return(cur);
2325}
2326/**
2327 * xmlGetDtdElementDesc2:
2328 * @dtd: a pointer to the DtD to search
2329 * @name: the element name
2330 * @create: create an empty description if not found
2331 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002332 * Search the DTD for the description of this element
Daniel Veillarda10efa82001-04-18 13:09:01 +00002333 *
2334 * returns the xmlElementPtr if found or NULL
2335 */
2336
Daniel Veillard86fd5a72001-12-13 14:55:21 +00002337static xmlElementPtr
Daniel Veillarda10efa82001-04-18 13:09:01 +00002338xmlGetDtdElementDesc2(xmlDtdPtr dtd, const xmlChar *name, int create) {
2339 xmlElementTablePtr table;
2340 xmlElementPtr cur;
2341 xmlChar *uqname = NULL, *prefix = NULL;
2342
2343 if (dtd == NULL) return(NULL);
2344 if (dtd->elements == NULL) {
2345 if (!create)
2346 return(NULL);
2347 /*
2348 * Create the Element table if needed.
2349 */
2350 table = (xmlElementTablePtr) dtd->elements;
2351 if (table == NULL) {
2352 table = xmlCreateElementTable();
2353 dtd->elements = (void *) table;
2354 }
2355 if (table == NULL) {
2356 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002357 "xmlGetDtdElementDesc2: Table creation failed!\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002358 return(NULL);
2359 }
2360 }
2361 table = (xmlElementTablePtr) dtd->elements;
2362
2363 uqname = xmlSplitQName2(name, &prefix);
2364 if (uqname != NULL)
2365 name = uqname;
2366 cur = xmlHashLookup2(table, name, prefix);
2367 if ((cur == NULL) && (create)) {
2368 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
2369 if (cur == NULL) {
2370 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002371 "xmlGetDtdElementDesc2: out of memory\n");
Daniel Veillarda10efa82001-04-18 13:09:01 +00002372 return(NULL);
2373 }
2374 memset(cur, 0, sizeof(xmlElement));
2375 cur->type = XML_ELEMENT_DECL;
2376
2377 /*
2378 * fill the structure.
2379 */
2380 cur->name = xmlStrdup(name);
2381 cur->prefix = xmlStrdup(prefix);
2382 cur->etype = XML_ELEMENT_TYPE_UNDEFINED;
2383
2384 xmlHashAddEntry2(table, name, prefix, cur);
2385 }
2386 if (prefix != NULL) xmlFree(prefix);
2387 if (uqname != NULL) xmlFree(uqname);
Owen Taylor3473f882001-02-23 17:55:21 +00002388 return(cur);
2389}
2390
2391/**
2392 * xmlGetDtdQElementDesc:
2393 * @dtd: a pointer to the DtD to search
2394 * @name: the element name
2395 * @prefix: the element namespace prefix
2396 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002397 * Search the DTD for the description of this element
Owen Taylor3473f882001-02-23 17:55:21 +00002398 *
2399 * returns the xmlElementPtr if found or NULL
2400 */
2401
Daniel Veillard48da9102001-08-07 01:10:10 +00002402xmlElementPtr
Owen Taylor3473f882001-02-23 17:55:21 +00002403xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
2404 const xmlChar *prefix) {
2405 xmlElementTablePtr table;
2406
2407 if (dtd == NULL) return(NULL);
2408 if (dtd->elements == NULL) return(NULL);
2409 table = (xmlElementTablePtr) dtd->elements;
2410
2411 return(xmlHashLookup2(table, name, prefix));
2412}
2413
2414/**
2415 * xmlGetDtdAttrDesc:
2416 * @dtd: a pointer to the DtD to search
2417 * @elem: the element name
2418 * @name: the attribute name
2419 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002420 * Search the DTD for the description of this attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002421 * this element.
2422 *
2423 * returns the xmlAttributePtr if found or NULL
2424 */
2425
2426xmlAttributePtr
2427xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
2428 xmlAttributeTablePtr table;
2429 xmlAttributePtr cur;
2430 xmlChar *uqname = NULL, *prefix = NULL;
2431
2432 if (dtd == NULL) return(NULL);
2433 if (dtd->attributes == NULL) return(NULL);
2434
2435 table = (xmlAttributeTablePtr) dtd->attributes;
2436 if (table == NULL)
2437 return(NULL);
2438
2439 uqname = xmlSplitQName2(name, &prefix);
2440
2441 if (uqname != NULL) {
2442 cur = xmlHashLookup3(table, uqname, prefix, elem);
2443 if (prefix != NULL) xmlFree(prefix);
2444 if (uqname != NULL) xmlFree(uqname);
2445 } else
2446 cur = xmlHashLookup3(table, name, NULL, elem);
2447 return(cur);
2448}
2449
2450/**
2451 * xmlGetDtdQAttrDesc:
2452 * @dtd: a pointer to the DtD to search
2453 * @elem: the element name
2454 * @name: the attribute name
2455 * @prefix: the attribute namespace prefix
2456 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002457 * Search the DTD for the description of this qualified attribute on
Owen Taylor3473f882001-02-23 17:55:21 +00002458 * this element.
2459 *
2460 * returns the xmlAttributePtr if found or NULL
2461 */
2462
Daniel Veillard48da9102001-08-07 01:10:10 +00002463xmlAttributePtr
Owen Taylor3473f882001-02-23 17:55:21 +00002464xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2465 const xmlChar *prefix) {
2466 xmlAttributeTablePtr table;
2467
2468 if (dtd == NULL) return(NULL);
2469 if (dtd->attributes == NULL) return(NULL);
2470 table = (xmlAttributeTablePtr) dtd->attributes;
2471
2472 return(xmlHashLookup3(table, name, prefix, elem));
2473}
2474
2475/**
2476 * xmlGetDtdNotationDesc:
2477 * @dtd: a pointer to the DtD to search
2478 * @name: the notation name
2479 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002480 * Search the DTD for the description of this notation
Owen Taylor3473f882001-02-23 17:55:21 +00002481 *
2482 * returns the xmlNotationPtr if found or NULL
2483 */
2484
2485xmlNotationPtr
2486xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
2487 xmlNotationTablePtr table;
2488
2489 if (dtd == NULL) return(NULL);
2490 if (dtd->notations == NULL) return(NULL);
2491 table = (xmlNotationTablePtr) dtd->notations;
2492
2493 return(xmlHashLookup(table, name));
2494}
2495
2496/**
2497 * xmlValidateNotationUse:
2498 * @ctxt: the validation context
2499 * @doc: the document
2500 * @notationName: the notation name to check
2501 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002502 * Validate that the given name match a notation declaration.
Owen Taylor3473f882001-02-23 17:55:21 +00002503 * - [ VC: Notation Declared ]
2504 *
2505 * returns 1 if valid or 0 otherwise
2506 */
2507
2508int
2509xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2510 const xmlChar *notationName) {
2511 xmlNotationPtr notaDecl;
2512 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2513
2514 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2515 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2516 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2517
2518 if (notaDecl == NULL) {
2519 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2520 notationName);
2521 return(0);
2522 }
2523 return(1);
2524}
2525
2526/**
2527 * xmlIsMixedElement
2528 * @doc: the document
2529 * @name: the element name
2530 *
2531 * Search in the DtDs whether an element accept Mixed content (or ANY)
2532 * basically if it is supposed to accept text childs
2533 *
2534 * returns 0 if no, 1 if yes, and -1 if no element description is available
2535 */
2536
2537int
2538xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
2539 xmlElementPtr elemDecl;
2540
2541 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2542
2543 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2544 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2545 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2546 if (elemDecl == NULL) return(-1);
2547 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00002548 case XML_ELEMENT_TYPE_UNDEFINED:
2549 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +00002550 case XML_ELEMENT_TYPE_ELEMENT:
2551 return(0);
2552 case XML_ELEMENT_TYPE_EMPTY:
2553 /*
2554 * return 1 for EMPTY since we want VC error to pop up
2555 * on <empty> </empty> for example
2556 */
2557 case XML_ELEMENT_TYPE_ANY:
2558 case XML_ELEMENT_TYPE_MIXED:
2559 return(1);
2560 }
2561 return(1);
2562}
2563
2564/**
2565 * xmlValidateNameValue:
2566 * @value: an Name value
2567 *
2568 * Validate that the given value match Name production
2569 *
2570 * returns 1 if valid or 0 otherwise
2571 */
2572
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002573static int
Owen Taylor3473f882001-02-23 17:55:21 +00002574xmlValidateNameValue(const xmlChar *value) {
2575 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002576 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002577
2578 if (value == NULL) return(0);
2579 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002580 val = xmlStringCurrentChar(NULL, cur, &len);
2581 cur += len;
2582 if (!IS_LETTER(val) && (val != '_') &&
2583 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002584 return(0);
2585 }
2586
Daniel Veillardd8224e02002-01-13 15:43:22 +00002587 val = xmlStringCurrentChar(NULL, cur, &len);
2588 cur += len;
2589 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2590 (val == '.') || (val == '-') ||
2591 (val == '_') || (val == ':') ||
2592 (IS_COMBINING(val)) ||
2593 (IS_EXTENDER(val))) {
2594 val = xmlStringCurrentChar(NULL, cur, &len);
2595 cur += len;
2596 }
Owen Taylor3473f882001-02-23 17:55:21 +00002597
Daniel Veillardd8224e02002-01-13 15:43:22 +00002598 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002599
2600 return(1);
2601}
2602
2603/**
2604 * xmlValidateNamesValue:
2605 * @value: an Names value
2606 *
2607 * Validate that the given value match Names production
2608 *
2609 * returns 1 if valid or 0 otherwise
2610 */
2611
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002612static int
Owen Taylor3473f882001-02-23 17:55:21 +00002613xmlValidateNamesValue(const xmlChar *value) {
2614 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002615 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002616
2617 if (value == NULL) return(0);
2618 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002619 val = xmlStringCurrentChar(NULL, cur, &len);
2620 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002621
Daniel Veillardd8224e02002-01-13 15:43:22 +00002622 if (!IS_LETTER(val) && (val != '_') &&
2623 (val != ':')) {
Owen Taylor3473f882001-02-23 17:55:21 +00002624 return(0);
2625 }
2626
Daniel Veillardd8224e02002-01-13 15:43:22 +00002627 val = xmlStringCurrentChar(NULL, cur, &len);
2628 cur += len;
2629 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2630 (val == '.') || (val == '-') ||
2631 (val == '_') || (val == ':') ||
2632 (IS_COMBINING(val)) ||
2633 (IS_EXTENDER(val))) {
2634 val = xmlStringCurrentChar(NULL, cur, &len);
2635 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002636 }
2637
Daniel Veillardd8224e02002-01-13 15:43:22 +00002638 while (IS_BLANK(val)) {
2639 while (IS_BLANK(val)) {
2640 val = xmlStringCurrentChar(NULL, cur, &len);
2641 cur += len;
2642 }
2643
2644 if (!IS_LETTER(val) && (val != '_') &&
2645 (val != ':')) {
2646 return(0);
2647 }
2648 val = xmlStringCurrentChar(NULL, cur, &len);
2649 cur += len;
2650
2651 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2652 (val == '.') || (val == '-') ||
2653 (val == '_') || (val == ':') ||
2654 (IS_COMBINING(val)) ||
2655 (IS_EXTENDER(val))) {
2656 val = xmlStringCurrentChar(NULL, cur, &len);
2657 cur += len;
2658 }
2659 }
2660
2661 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002662
2663 return(1);
2664}
2665
2666/**
2667 * xmlValidateNmtokenValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002668 * @value: an Nmtoken value
Owen Taylor3473f882001-02-23 17:55:21 +00002669 *
2670 * Validate that the given value match Nmtoken production
2671 *
2672 * [ VC: Name Token ]
2673 *
2674 * returns 1 if valid or 0 otherwise
2675 */
2676
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002677static int
Owen Taylor3473f882001-02-23 17:55:21 +00002678xmlValidateNmtokenValue(const xmlChar *value) {
2679 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002680 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002681
2682 if (value == NULL) return(0);
2683 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002684 val = xmlStringCurrentChar(NULL, cur, &len);
2685 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002686
Daniel Veillardd8224e02002-01-13 15:43:22 +00002687 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2688 (val != '.') && (val != '-') &&
2689 (val != '_') && (val != ':') &&
2690 (!IS_COMBINING(val)) &&
2691 (!IS_EXTENDER(val)))
Owen Taylor3473f882001-02-23 17:55:21 +00002692 return(0);
2693
Daniel Veillardd8224e02002-01-13 15:43:22 +00002694 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2695 (val == '.') || (val == '-') ||
2696 (val == '_') || (val == ':') ||
2697 (IS_COMBINING(val)) ||
2698 (IS_EXTENDER(val))) {
2699 val = xmlStringCurrentChar(NULL, cur, &len);
2700 cur += len;
2701 }
Owen Taylor3473f882001-02-23 17:55:21 +00002702
Daniel Veillardd8224e02002-01-13 15:43:22 +00002703 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002704
2705 return(1);
2706}
2707
2708/**
2709 * xmlValidateNmtokensValue:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002710 * @value: an Nmtokens value
Owen Taylor3473f882001-02-23 17:55:21 +00002711 *
2712 * Validate that the given value match Nmtokens production
2713 *
2714 * [ VC: Name Token ]
2715 *
2716 * returns 1 if valid or 0 otherwise
2717 */
2718
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002719static int
Owen Taylor3473f882001-02-23 17:55:21 +00002720xmlValidateNmtokensValue(const xmlChar *value) {
2721 const xmlChar *cur;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002722 int val, len;
Owen Taylor3473f882001-02-23 17:55:21 +00002723
2724 if (value == NULL) return(0);
2725 cur = value;
Daniel Veillardd8224e02002-01-13 15:43:22 +00002726 val = xmlStringCurrentChar(NULL, cur, &len);
2727 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002728
Daniel Veillardd8224e02002-01-13 15:43:22 +00002729 while (IS_BLANK(val)) {
2730 val = xmlStringCurrentChar(NULL, cur, &len);
2731 cur += len;
Owen Taylor3473f882001-02-23 17:55:21 +00002732 }
2733
Daniel Veillardd8224e02002-01-13 15:43:22 +00002734 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2735 (val != '.') && (val != '-') &&
2736 (val != '_') && (val != ':') &&
2737 (!IS_COMBINING(val)) &&
2738 (!IS_EXTENDER(val)))
2739 return(0);
2740
2741 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2742 (val == '.') || (val == '-') ||
2743 (val == '_') || (val == ':') ||
2744 (IS_COMBINING(val)) ||
2745 (IS_EXTENDER(val))) {
2746 val = xmlStringCurrentChar(NULL, cur, &len);
2747 cur += len;
2748 }
2749
2750 while (IS_BLANK(val)) {
2751 while (IS_BLANK(val)) {
2752 val = xmlStringCurrentChar(NULL, cur, &len);
2753 cur += len;
2754 }
2755 if (val == 0) return(1);
2756
2757 if (!IS_LETTER(val) && !IS_DIGIT(val) &&
2758 (val != '.') && (val != '-') &&
2759 (val != '_') && (val != ':') &&
2760 (!IS_COMBINING(val)) &&
2761 (!IS_EXTENDER(val)))
2762 return(0);
2763
2764 while ((IS_LETTER(val)) || (IS_DIGIT(val)) ||
2765 (val == '.') || (val == '-') ||
2766 (val == '_') || (val == ':') ||
2767 (IS_COMBINING(val)) ||
2768 (IS_EXTENDER(val))) {
2769 val = xmlStringCurrentChar(NULL, cur, &len);
2770 cur += len;
2771 }
2772 }
2773
2774 if (val != 0) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00002775
2776 return(1);
2777}
2778
2779/**
2780 * xmlValidateNotationDecl:
2781 * @ctxt: the validation context
2782 * @doc: a document instance
2783 * @nota: a notation definition
2784 *
2785 * Try to validate a single notation definition
2786 * basically it does the following checks as described by the
2787 * XML-1.0 recommendation:
Daniel Veillardcbaf3992001-12-31 16:16:02 +00002788 * - it seems that no validity constraint exists on notation declarations
Owen Taylor3473f882001-02-23 17:55:21 +00002789 * But this function get called anyway ...
2790 *
2791 * returns 1 if valid or 0 otherwise
2792 */
2793
2794int
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00002795xmlValidateNotationDecl(xmlValidCtxtPtr ctxt ATTRIBUTE_UNUSED, xmlDocPtr doc ATTRIBUTE_UNUSED,
2796 xmlNotationPtr nota ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00002797 int ret = 1;
2798
2799 return(ret);
2800}
2801
2802/**
2803 * xmlValidateAttributeValue:
2804 * @type: an attribute type
2805 * @value: an attribute value
2806 *
2807 * Validate that the given attribute value match the proper production
2808 *
2809 * [ VC: ID ]
2810 * Values of type ID must match the Name production....
2811 *
2812 * [ VC: IDREF ]
2813 * Values of type IDREF must match the Name production, and values
2814 * of type IDREFS must match Names ...
2815 *
2816 * [ VC: Entity Name ]
2817 * Values of type ENTITY must match the Name production, values
2818 * of type ENTITIES must match Names ...
2819 *
2820 * [ VC: Name Token ]
2821 * Values of type NMTOKEN must match the Nmtoken production; values
2822 * of type NMTOKENS must match Nmtokens.
2823 *
2824 * returns 1 if valid or 0 otherwise
2825 */
2826
2827int
2828xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
2829 switch (type) {
2830 case XML_ATTRIBUTE_ENTITIES:
2831 case XML_ATTRIBUTE_IDREFS:
2832 return(xmlValidateNamesValue(value));
2833 case XML_ATTRIBUTE_ENTITY:
2834 case XML_ATTRIBUTE_IDREF:
2835 case XML_ATTRIBUTE_ID:
2836 case XML_ATTRIBUTE_NOTATION:
2837 return(xmlValidateNameValue(value));
2838 case XML_ATTRIBUTE_NMTOKENS:
2839 case XML_ATTRIBUTE_ENUMERATION:
2840 return(xmlValidateNmtokensValue(value));
2841 case XML_ATTRIBUTE_NMTOKEN:
2842 return(xmlValidateNmtokenValue(value));
2843 case XML_ATTRIBUTE_CDATA:
2844 break;
2845 }
2846 return(1);
2847}
2848
2849/**
2850 * xmlValidateAttributeValue2:
2851 * @ctxt: the validation context
2852 * @doc: the document
2853 * @name: the attribute name (used for error reporting only)
2854 * @type: the attribute type
2855 * @value: the attribute value
2856 *
2857 * Validate that the given attribute value match a given type.
2858 * This typically cannot be done before having finished parsing
2859 * the subsets.
2860 *
2861 * [ VC: IDREF ]
2862 * Values of type IDREF must match one of the declared IDs
2863 * Values of type IDREFS must match a sequence of the declared IDs
2864 * each Name must match the value of an ID attribute on some element
2865 * in the XML document; i.e. IDREF values must match the value of
2866 * some ID attribute
2867 *
2868 * [ VC: Entity Name ]
2869 * Values of type ENTITY must match one declared entity
2870 * Values of type ENTITIES must match a sequence of declared entities
2871 *
2872 * [ VC: Notation Attributes ]
2873 * all notation names in the declaration must be declared.
2874 *
2875 * returns 1 if valid or 0 otherwise
2876 */
2877
Daniel Veillard56a4cb82001-03-24 17:00:36 +00002878static int
Owen Taylor3473f882001-02-23 17:55:21 +00002879xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2880 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2881 int ret = 1;
2882 switch (type) {
2883 case XML_ATTRIBUTE_IDREFS:
2884 case XML_ATTRIBUTE_IDREF:
2885 case XML_ATTRIBUTE_ID:
2886 case XML_ATTRIBUTE_NMTOKENS:
2887 case XML_ATTRIBUTE_ENUMERATION:
2888 case XML_ATTRIBUTE_NMTOKEN:
2889 case XML_ATTRIBUTE_CDATA:
2890 break;
2891 case XML_ATTRIBUTE_ENTITY: {
2892 xmlEntityPtr ent;
2893
2894 ent = xmlGetDocEntity(doc, value);
Daniel Veillard878eab02002-02-19 13:46:09 +00002895 if ((ent == NULL) && (doc->standalone == 1)) {
2896 doc->standalone = 0;
2897 ent = xmlGetDocEntity(doc, value);
2898 if (ent != NULL) {
2899 VERROR(ctxt->userData,
2900"standalone problem: attribute %s reference entity \"%s\" in external subset\n",
2901 name, value);
2902 /* WAIT to get answer from the Core WG on this
2903 ret = 0;
2904 */
2905 }
2906 }
Owen Taylor3473f882001-02-23 17:55:21 +00002907 if (ent == NULL) {
2908 VERROR(ctxt->userData,
2909 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2910 name, value);
2911 ret = 0;
2912 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2913 VERROR(ctxt->userData,
2914 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2915 name, value);
2916 ret = 0;
2917 }
2918 break;
2919 }
2920 case XML_ATTRIBUTE_ENTITIES: {
2921 xmlChar *dup, *nam = NULL, *cur, save;
2922 xmlEntityPtr ent;
2923
2924 dup = xmlStrdup(value);
2925 if (dup == NULL)
2926 return(0);
2927 cur = dup;
2928 while (*cur != 0) {
2929 nam = cur;
2930 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2931 save = *cur;
2932 *cur = 0;
2933 ent = xmlGetDocEntity(doc, nam);
2934 if (ent == NULL) {
2935 VERROR(ctxt->userData,
2936 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2937 name, nam);
2938 ret = 0;
2939 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2940 VERROR(ctxt->userData,
2941 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2942 name, nam);
2943 ret = 0;
2944 }
2945 if (save == 0)
2946 break;
2947 *cur = save;
2948 while (IS_BLANK(*cur)) cur++;
2949 }
2950 xmlFree(dup);
2951 break;
2952 }
2953 case XML_ATTRIBUTE_NOTATION: {
2954 xmlNotationPtr nota;
2955
2956 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2957 if ((nota == NULL) && (doc->extSubset != NULL))
2958 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2959
2960 if (nota == NULL) {
2961 VERROR(ctxt->userData,
2962 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2963 name, value);
2964 ret = 0;
2965 }
2966 break;
2967 }
2968 }
2969 return(ret);
2970}
2971
2972/**
Daniel Veillard8dc16a62002-02-19 21:08:48 +00002973 * xmlValidCtxtNormalizeAttributeValue:
2974 * @ctxt: the validation context
2975 * @doc: the document
2976 * @elem: the parent
2977 * @name: the attribute name
2978 * @value: the attribute value
2979 * @ctxt: the validation context or NULL
2980 *
2981 * Does the validation related extra step of the normalization of attribute
2982 * values:
2983 *
2984 * If the declared value is not CDATA, then the XML processor must further
2985 * process the normalized attribute value by discarding any leading and
2986 * trailing space (#x20) characters, and by replacing sequences of space
2987 * (#x20) characters by single space (#x20) character.
2988 *
2989 * Also check VC: Standalone Document Declaration in P32, and update
2990 * ctxt->valid accordingly
2991 *
2992 * returns a new normalized string if normalization is needed, NULL otherwise
2993 * the caller must free the returned value.
2994 */
2995
2996xmlChar *
2997xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2998 xmlNodePtr elem, const xmlChar *name, const xmlChar *value) {
2999 xmlChar *ret, *dst;
3000 const xmlChar *src;
3001 xmlAttributePtr attrDecl = NULL;
3002 int extsubset = 0;
3003
3004 if (doc == NULL) return(NULL);
3005 if (elem == NULL) return(NULL);
3006 if (name == NULL) return(NULL);
3007 if (value == NULL) return(NULL);
3008
3009 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3010 xmlChar qname[500];
3011 snprintf((char *) qname, sizeof(qname), "%s:%s",
3012 elem->ns->prefix, elem->name);
3013 qname[sizeof(qname) - 1] = 0;
3014 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3015 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3016 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3017 if (attrDecl != NULL)
3018 extsubset = 1;
3019 }
3020 }
3021 if ((attrDecl == NULL) && (doc->intSubset != NULL))
3022 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3023 if ((attrDecl == NULL) && (doc->extSubset != NULL)) {
3024 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3025 if (attrDecl != NULL)
3026 extsubset = 1;
3027 }
3028
3029 if (attrDecl == NULL)
3030 return(NULL);
3031 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3032 return(NULL);
3033
3034 ret = xmlStrdup(value);
3035 if (ret == NULL)
3036 return(NULL);
3037 src = value;
3038 dst = ret;
3039 while (*src == 0x20) src++;
3040 while (*src != 0) {
3041 if (*src == 0x20) {
3042 while (*src == 0x20) src++;
3043 if (*src != 0)
3044 *dst++ = 0x20;
3045 } else {
3046 *dst++ = *src++;
3047 }
3048 }
3049 *dst = 0;
3050 if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) {
3051 VERROR(ctxt->userData,
3052"standalone: %s on %s value had to be normalized based on external subset declaration\n",
3053 name, elem->name);
3054 ctxt->valid = 0;
3055 }
3056 return(ret);
3057}
3058
3059/**
Owen Taylor3473f882001-02-23 17:55:21 +00003060 * xmlValidNormalizeAttributeValue:
3061 * @doc: the document
3062 * @elem: the parent
3063 * @name: the attribute name
3064 * @value: the attribute value
Daniel Veillard8dc16a62002-02-19 21:08:48 +00003065 * @ctxt: the validation context or NULL
Owen Taylor3473f882001-02-23 17:55:21 +00003066 *
3067 * Does the validation related extra step of the normalization of attribute
3068 * values:
3069 *
3070 * If the declared value is not CDATA, then the XML processor must further
3071 * process the normalized attribute value by discarding any leading and
3072 * trailing space (#x20) characters, and by replacing sequences of space
3073 * (#x20) characters by single space (#x20) character.
3074 *
3075 * returns a new normalized string if normalization is needed, NULL otherwise
3076 * the caller must free the returned value.
3077 */
3078
3079xmlChar *
3080xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
3081 const xmlChar *name, const xmlChar *value) {
3082 xmlChar *ret, *dst;
3083 const xmlChar *src;
3084 xmlAttributePtr attrDecl = NULL;
3085
3086 if (doc == NULL) return(NULL);
3087 if (elem == NULL) return(NULL);
3088 if (name == NULL) return(NULL);
3089 if (value == NULL) return(NULL);
3090
3091 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3092 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003093 snprintf((char *) qname, sizeof(qname), "%s:%s",
3094 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003095 qname[sizeof(qname) - 1] = 0;
3096 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
3097 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3098 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
3099 }
3100 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
3101 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3102 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
3103
3104 if (attrDecl == NULL)
3105 return(NULL);
3106 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
3107 return(NULL);
3108
3109 ret = xmlStrdup(value);
3110 if (ret == NULL)
3111 return(NULL);
3112 src = value;
3113 dst = ret;
3114 while (*src == 0x20) src++;
3115 while (*src != 0) {
3116 if (*src == 0x20) {
3117 while (*src == 0x20) src++;
3118 if (*src != 0)
3119 *dst++ = 0x20;
3120 } else {
3121 *dst++ = *src++;
3122 }
3123 }
3124 *dst = 0;
3125 return(ret);
3126}
3127
Daniel Veillard56a4cb82001-03-24 17:00:36 +00003128static void
Owen Taylor3473f882001-02-23 17:55:21 +00003129xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00003130 const xmlChar* name ATTRIBUTE_UNUSED) {
Owen Taylor3473f882001-02-23 17:55:21 +00003131 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
3132}
3133
3134/**
3135 * xmlValidateAttributeDecl:
3136 * @ctxt: the validation context
3137 * @doc: a document instance
3138 * @attr: an attribute definition
3139 *
3140 * Try to validate a single attribute definition
3141 * basically it does the following checks as described by the
3142 * XML-1.0 recommendation:
3143 * - [ VC: Attribute Default Legal ]
3144 * - [ VC: Enumeration ]
3145 * - [ VC: ID Attribute Default ]
3146 *
3147 * The ID/IDREF uniqueness and matching are done separately
3148 *
3149 * returns 1 if valid or 0 otherwise
3150 */
3151
3152int
3153xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3154 xmlAttributePtr attr) {
3155 int ret = 1;
3156 int val;
3157 CHECK_DTD;
3158 if(attr == NULL) return(1);
3159
3160 /* Attribute Default Legal */
3161 /* Enumeration */
3162 if (attr->defaultValue != NULL) {
3163 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
3164 if (val == 0) {
3165 VERROR(ctxt->userData,
3166 "Syntax of default value for attribute %s on %s is not valid\n",
3167 attr->name, attr->elem);
3168 }
3169 ret &= val;
3170 }
3171
3172 /* ID Attribute Default */
3173 if ((attr->atype == XML_ATTRIBUTE_ID)&&
3174 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
3175 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
3176 VERROR(ctxt->userData,
3177 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
3178 attr->name, attr->elem);
3179 ret = 0;
3180 }
3181
3182 /* One ID per Element Type */
3183 if (attr->atype == XML_ATTRIBUTE_ID) {
3184 int nbId;
3185
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003186 /* the trick is that we parse DtD as their own internal subset */
Owen Taylor3473f882001-02-23 17:55:21 +00003187 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
3188 attr->elem);
3189 if (elem != NULL) {
3190 nbId = xmlScanIDAttributeDecl(NULL, elem);
3191 } else {
3192 xmlAttributeTablePtr table;
3193
3194 /*
3195 * The attribute may be declared in the internal subset and the
3196 * element in the external subset.
3197 */
3198 nbId = 0;
3199 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
3200 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
3201 xmlValidateAttributeIdCallback, &nbId);
3202 }
3203 if (nbId > 1) {
3204 VERROR(ctxt->userData,
3205 "Element %s has %d ID attribute defined in the internal subset : %s\n",
3206 attr->elem, nbId, attr->name);
3207 } else if (doc->extSubset != NULL) {
3208 int extId = 0;
3209 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
3210 if (elem != NULL) {
3211 extId = xmlScanIDAttributeDecl(NULL, elem);
3212 }
3213 if (extId > 1) {
3214 VERROR(ctxt->userData,
3215 "Element %s has %d ID attribute defined in the external subset : %s\n",
3216 attr->elem, extId, attr->name);
3217 } else if (extId + nbId > 1) {
3218 VERROR(ctxt->userData,
3219"Element %s has ID attributes defined in the internal and external subset : %s\n",
3220 attr->elem, attr->name);
3221 }
3222 }
3223 }
3224
3225 /* Validity Constraint: Enumeration */
3226 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
3227 xmlEnumerationPtr tree = attr->tree;
3228 while (tree != NULL) {
3229 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
3230 tree = tree->next;
3231 }
3232 if (tree == NULL) {
3233 VERROR(ctxt->userData,
3234"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3235 attr->defaultValue, attr->name, attr->elem);
3236 ret = 0;
3237 }
3238 }
3239
3240 return(ret);
3241}
3242
3243/**
3244 * xmlValidateElementDecl:
3245 * @ctxt: the validation context
3246 * @doc: a document instance
3247 * @elem: an element definition
3248 *
3249 * Try to validate a single element definition
3250 * basically it does the following checks as described by the
3251 * XML-1.0 recommendation:
3252 * - [ VC: One ID per Element Type ]
3253 * - [ VC: No Duplicate Types ]
3254 * - [ VC: Unique Element Type Declaration ]
3255 *
3256 * returns 1 if valid or 0 otherwise
3257 */
3258
3259int
3260xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3261 xmlElementPtr elem) {
3262 int ret = 1;
3263 xmlElementPtr tst;
3264
3265 CHECK_DTD;
3266
3267 if (elem == NULL) return(1);
3268
3269 /* No Duplicate Types */
3270 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
3271 xmlElementContentPtr cur, next;
3272 const xmlChar *name;
3273
3274 cur = elem->content;
3275 while (cur != NULL) {
3276 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
3277 if (cur->c1 == NULL) break;
3278 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3279 name = cur->c1->name;
3280 next = cur->c2;
3281 while (next != NULL) {
3282 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
3283 if (xmlStrEqual(next->name, name)) {
3284 VERROR(ctxt->userData,
3285 "Definition of %s has duplicate references of %s\n",
3286 elem->name, name);
3287 ret = 0;
3288 }
3289 break;
3290 }
3291 if (next->c1 == NULL) break;
3292 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
3293 if (xmlStrEqual(next->c1->name, name)) {
3294 VERROR(ctxt->userData,
3295 "Definition of %s has duplicate references of %s\n",
3296 elem->name, name);
3297 ret = 0;
3298 }
3299 next = next->c2;
3300 }
3301 }
3302 cur = cur->c2;
3303 }
3304 }
3305
3306 /* VC: Unique Element Type Declaration */
3307 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003308 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003309 ((tst->prefix == elem->prefix) ||
3310 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003311 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003312 VERROR(ctxt->userData, "Redefinition of element %s\n",
3313 elem->name);
3314 ret = 0;
3315 }
3316 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillarda10efa82001-04-18 13:09:01 +00003317 if ((tst != NULL ) && (tst != elem) &&
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003318 ((tst->prefix == elem->prefix) ||
3319 (xmlStrEqual(tst->prefix, elem->prefix))) &&
Daniel Veillarda10efa82001-04-18 13:09:01 +00003320 (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) {
Owen Taylor3473f882001-02-23 17:55:21 +00003321 VERROR(ctxt->userData, "Redefinition of element %s\n",
3322 elem->name);
3323 ret = 0;
3324 }
Daniel Veillarda10efa82001-04-18 13:09:01 +00003325 /* One ID per Element Type
3326 * already done when registering the attribute
Owen Taylor3473f882001-02-23 17:55:21 +00003327 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
3328 ret = 0;
Daniel Veillarda10efa82001-04-18 13:09:01 +00003329 } */
Owen Taylor3473f882001-02-23 17:55:21 +00003330 return(ret);
3331}
3332
3333/**
3334 * xmlValidateOneAttribute:
3335 * @ctxt: the validation context
3336 * @doc: a document instance
3337 * @elem: an element instance
3338 * @attr: an attribute instance
3339 * @value: the attribute value (without entities processing)
3340 *
3341 * Try to validate a single attribute for an element
3342 * basically it does the following checks as described by the
3343 * XML-1.0 recommendation:
3344 * - [ VC: Attribute Value Type ]
3345 * - [ VC: Fixed Attribute Default ]
3346 * - [ VC: Entity Name ]
3347 * - [ VC: Name Token ]
3348 * - [ VC: ID ]
3349 * - [ VC: IDREF ]
3350 * - [ VC: Entity Name ]
3351 * - [ VC: Notation Attributes ]
3352 *
3353 * The ID/IDREF uniqueness and matching are done separately
3354 *
3355 * returns 1 if valid or 0 otherwise
3356 */
3357
3358int
3359xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3360 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
3361 /* xmlElementPtr elemDecl; */
3362 xmlAttributePtr attrDecl = NULL;
3363 int val;
3364 int ret = 1;
3365
3366 CHECK_DTD;
3367 if ((elem == NULL) || (elem->name == NULL)) return(0);
3368 if ((attr == NULL) || (attr->name == NULL)) return(0);
3369
3370 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3371 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00003372 snprintf((char *) qname, sizeof(qname), "%s:%s",
3373 elem->ns->prefix, elem->name);
Owen Taylor3473f882001-02-23 17:55:21 +00003374 qname[sizeof(qname) - 1] = 0;
3375 if (attr->ns != NULL) {
3376 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
3377 attr->name, attr->ns->prefix);
3378 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3379 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
3380 attr->name, attr->ns->prefix);
3381 } else {
3382 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
3383 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3384 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3385 qname, attr->name);
3386 }
3387 }
3388 if (attrDecl == NULL) {
3389 if (attr->ns != NULL) {
3390 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
3391 attr->name, attr->ns->prefix);
3392 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3393 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
3394 attr->name, attr->ns->prefix);
3395 } else {
3396 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
3397 elem->name, attr->name);
3398 if ((attrDecl == NULL) && (doc->extSubset != NULL))
3399 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
3400 elem->name, attr->name);
3401 }
3402 }
3403
3404
3405 /* Validity Constraint: Attribute Value Type */
3406 if (attrDecl == NULL) {
3407 VERROR(ctxt->userData,
3408 "No declaration for attribute %s on element %s\n",
3409 attr->name, elem->name);
3410 return(0);
3411 }
3412 attr->atype = attrDecl->atype;
3413
3414 val = xmlValidateAttributeValue(attrDecl->atype, value);
3415 if (val == 0) {
3416 VERROR(ctxt->userData,
3417 "Syntax of value for attribute %s on %s is not valid\n",
3418 attr->name, elem->name);
3419 ret = 0;
3420 }
3421
3422 /* Validity constraint: Fixed Attribute Default */
3423 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
3424 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
3425 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003426 "Value for attribute %s on %s is different from default \"%s\"\n",
Owen Taylor3473f882001-02-23 17:55:21 +00003427 attr->name, elem->name, attrDecl->defaultValue);
3428 ret = 0;
3429 }
3430 }
3431
3432 /* Validity Constraint: ID uniqueness */
3433 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
3434 if (xmlAddID(ctxt, doc, value, attr) == NULL)
3435 ret = 0;
3436 }
3437
3438 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
3439 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
3440 if (xmlAddRef(ctxt, doc, value, attr) == NULL)
3441 ret = 0;
3442 }
3443
3444 /* Validity Constraint: Notation Attributes */
3445 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
3446 xmlEnumerationPtr tree = attrDecl->tree;
3447 xmlNotationPtr nota;
3448
3449 /* First check that the given NOTATION was declared */
3450 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
3451 if (nota == NULL)
3452 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
3453
3454 if (nota == NULL) {
3455 VERROR(ctxt->userData,
3456 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
3457 value, attr->name, elem->name);
3458 ret = 0;
3459 }
3460
3461 /* Second, verify that it's among the list */
3462 while (tree != NULL) {
3463 if (xmlStrEqual(tree->name, value)) break;
3464 tree = tree->next;
3465 }
3466 if (tree == NULL) {
3467 VERROR(ctxt->userData,
3468"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
3469 value, attr->name, elem->name);
3470 ret = 0;
3471 }
3472 }
3473
3474 /* Validity Constraint: Enumeration */
3475 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
3476 xmlEnumerationPtr tree = attrDecl->tree;
3477 while (tree != NULL) {
3478 if (xmlStrEqual(tree->name, value)) break;
3479 tree = tree->next;
3480 }
3481 if (tree == NULL) {
3482 VERROR(ctxt->userData,
3483 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
3484 value, attr->name, elem->name);
3485 ret = 0;
3486 }
3487 }
3488
3489 /* Fixed Attribute Default */
3490 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
3491 (!xmlStrEqual(attrDecl->defaultValue, value))) {
3492 VERROR(ctxt->userData,
3493 "Value for attribute %s on %s must be \"%s\"\n",
3494 attr->name, elem->name, attrDecl->defaultValue);
3495 ret = 0;
3496 }
3497
3498 /* Extra check for the attribute value */
3499 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
3500 attrDecl->atype, value);
3501
3502 return(ret);
3503}
3504
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003505/**
3506 * xmlValidateSkipIgnorable:
3507 * @ctxt: the validation context
3508 * @child: the child list
3509 *
3510 * Skip ignorable elements w.r.t. the validation process
3511 *
3512 * returns the first element to consider for validation of the content model
3513 */
3514
3515static xmlNodePtr
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003516xmlValidateSkipIgnorable(xmlNodePtr child) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003517 while (child != NULL) {
3518 switch (child->type) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003519 /* These things are ignored (skipped) during validation. */
3520 case XML_PI_NODE:
3521 case XML_COMMENT_NODE:
3522 case XML_XINCLUDE_START:
3523 case XML_XINCLUDE_END:
3524 child = child->next;
3525 break;
3526 case XML_TEXT_NODE:
3527 if (xmlIsBlankNode(child))
3528 child = child->next;
3529 else
3530 return(child);
3531 break;
3532 /* keep current node */
3533 default:
3534 return(child);
3535 }
3536 }
3537 return(child);
3538}
3539
3540/**
3541 * xmlValidateElementType:
3542 * @ctxt: the validation context
3543 *
3544 * Try to validate the content model of an element internal function
3545 *
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003546 * returns 1 if valid or 0 ,-1 in case of error, -2 if an entity
3547 * reference is found and -3 if the validation succeeded but
3548 * the content model is not determinist.
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003549 */
3550
3551static int
3552xmlValidateElementType(xmlValidCtxtPtr ctxt) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00003553 int ret = -1;
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003554 int determinist = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003555
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003556 NODE = xmlValidateSkipIgnorable(NODE);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003557 if ((NODE == NULL) && (CONT == NULL))
3558 return(1);
3559 if ((NODE == NULL) &&
3560 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
3561 (CONT->ocur == XML_ELEMENT_CONTENT_OPT))) {
3562 return(1);
3563 }
3564 if (CONT == NULL) return(-1);
Daniel Veillard7533cc82001-04-24 15:52:00 +00003565 if ((NODE != NULL) && (NODE->type == XML_ENTITY_REF_NODE))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003566 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003567
3568 /*
3569 * We arrive here when more states need to be examined
3570 */
3571cont:
3572
3573 /*
3574 * We just recovered from a rollback generated by a possible
3575 * epsilon transition, go directly to the analysis phase
3576 */
3577 if (STATE == ROLLBACK_PARENT) {
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003578 DEBUG_VALID_MSG("restored parent branch");
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003579 DEBUG_VALID_STATE(NODE, CONT)
3580 ret = 1;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003581 goto analyze;
3582 }
3583
3584 DEBUG_VALID_STATE(NODE, CONT)
3585 /*
3586 * we may have to save a backup state here. This is the equivalent
3587 * of handling epsilon transition in NFAs.
3588 */
Daniel Veillarde62d36c2001-05-15 08:53:16 +00003589 if ((CONT != NULL) &&
Daniel Veillardce2c2f02001-10-18 14:57:24 +00003590 ((CONT->parent == NULL) ||
3591 (CONT->parent->type != XML_ELEMENT_CONTENT_OR)) &&
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003592 ((CONT->ocur == XML_ELEMENT_CONTENT_MULT) ||
Daniel Veillardca1f1722001-04-20 15:47:35 +00003593 (CONT->ocur == XML_ELEMENT_CONTENT_OPT) ||
Daniel Veillard5344c602001-12-31 16:37:34 +00003594 ((CONT->ocur == XML_ELEMENT_CONTENT_PLUS) && (OCCURRENCE)))) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003595 DEBUG_VALID_MSG("saving parent branch");
3596 vstateVPush(ctxt, CONT, NODE, DEPTH, OCCURS, ROLLBACK_PARENT);
3597 }
3598
3599
3600 /*
3601 * Check first if the content matches
3602 */
3603 switch (CONT->type) {
3604 case XML_ELEMENT_CONTENT_PCDATA:
3605 if (NODE == NULL) {
3606 DEBUG_VALID_MSG("pcdata failed no node");
3607 ret = 0;
3608 break;
3609 }
3610 if (NODE->type == XML_TEXT_NODE) {
3611 DEBUG_VALID_MSG("pcdata found, skip to next");
3612 /*
3613 * go to next element in the content model
3614 * skipping ignorable elems
3615 */
3616 do {
3617 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003618 NODE = xmlValidateSkipIgnorable(NODE);
3619 if ((NODE != NULL) &&
3620 (NODE->type == XML_ENTITY_REF_NODE))
3621 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003622 } while ((NODE != NULL) &&
3623 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003624 (NODE->type != XML_TEXT_NODE) &&
3625 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003626 ret = 1;
3627 break;
3628 } else {
3629 DEBUG_VALID_MSG("pcdata failed");
3630 ret = 0;
3631 break;
3632 }
3633 break;
3634 case XML_ELEMENT_CONTENT_ELEMENT:
3635 if (NODE == NULL) {
3636 DEBUG_VALID_MSG("element failed no node");
3637 ret = 0;
3638 break;
3639 }
Daniel Veillard8bdd2202001-06-11 12:47:59 +00003640 ret = ((NODE->type == XML_ELEMENT_NODE) &&
3641 (xmlStrEqual(NODE->name, CONT->name)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003642 if (ret == 1) {
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003643 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3644 ret = (CONT->prefix == NULL);
3645 } else if (CONT->prefix == NULL) {
3646 ret = 0;
3647 } else {
3648 ret = xmlStrEqual(NODE->ns->prefix, CONT->prefix);
3649 }
3650 }
3651 if (ret == 1) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003652 DEBUG_VALID_MSG("element found, skip to next");
3653 /*
3654 * go to next element in the content model
3655 * skipping ignorable elems
3656 */
3657 do {
3658 NODE = NODE->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00003659 NODE = xmlValidateSkipIgnorable(NODE);
3660 if ((NODE != NULL) &&
3661 (NODE->type == XML_ENTITY_REF_NODE))
3662 return(-2);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003663 } while ((NODE != NULL) &&
3664 ((NODE->type != XML_ELEMENT_NODE) &&
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00003665 (NODE->type != XML_TEXT_NODE) &&
3666 (NODE->type != XML_CDATA_SECTION_NODE)));
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003667 } else {
3668 DEBUG_VALID_MSG("element failed");
3669 ret = 0;
3670 break;
3671 }
3672 break;
3673 case XML_ELEMENT_CONTENT_OR:
3674 /*
Daniel Veillard85349052001-04-20 13:48:21 +00003675 * Small optimization.
3676 */
3677 if (CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
3678 if ((NODE == NULL) ||
3679 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3680 DEPTH++;
3681 CONT = CONT->c2;
3682 goto cont;
3683 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003684 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3685 ret = (CONT->c1->prefix == NULL);
3686 } else if (CONT->c1->prefix == NULL) {
3687 ret = 0;
3688 } else {
3689 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3690 }
3691 if (ret == 0) {
3692 DEPTH++;
3693 CONT = CONT->c2;
3694 goto cont;
3695 }
Daniel Veillard85349052001-04-20 13:48:21 +00003696 }
3697
3698 /*
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003699 * save the second branch 'or' branch
3700 */
3701 DEBUG_VALID_MSG("saving 'or' branch");
Daniel Veillard268fd1b2001-08-26 18:46:36 +00003702 vstateVPush(ctxt, CONT->c2, NODE, (unsigned char)(DEPTH + 1),
3703 OCCURS, ROLLBACK_OR);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003704
3705 DEPTH++;
3706 CONT = CONT->c1;
3707 goto cont;
3708 case XML_ELEMENT_CONTENT_SEQ:
Daniel Veillard1d047672001-06-09 16:41:01 +00003709 /*
3710 * Small optimization.
3711 */
3712 if ((CONT->c1->type == XML_ELEMENT_CONTENT_ELEMENT) &&
3713 ((CONT->c1->ocur == XML_ELEMENT_CONTENT_OPT) ||
3714 (CONT->c1->ocur == XML_ELEMENT_CONTENT_MULT))) {
3715 if ((NODE == NULL) ||
3716 (!xmlStrEqual(NODE->name, CONT->c1->name))) {
3717 DEPTH++;
3718 CONT = CONT->c2;
3719 goto cont;
3720 }
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003721 if ((NODE->ns == NULL) || (NODE->ns->prefix == NULL)) {
3722 ret = (CONT->c1->prefix == NULL);
3723 } else if (CONT->c1->prefix == NULL) {
3724 ret = 0;
3725 } else {
3726 ret = xmlStrEqual(NODE->ns->prefix, CONT->c1->prefix);
3727 }
3728 if (ret == 0) {
3729 DEPTH++;
3730 CONT = CONT->c2;
3731 goto cont;
3732 }
Daniel Veillard1d047672001-06-09 16:41:01 +00003733 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003734 DEPTH++;
3735 CONT = CONT->c1;
3736 goto cont;
3737 }
3738
3739 /*
3740 * At this point handle going up in the tree
3741 */
3742 if (ret == -1) {
3743 DEBUG_VALID_MSG("error found returning");
3744 return(ret);
3745 }
3746analyze:
3747 while (CONT != NULL) {
3748 /*
Daniel Veillardcbaf3992001-12-31 16:16:02 +00003749 * First do the analysis depending on the occurrence model at
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003750 * this level.
3751 */
3752 if (ret == 0) {
3753 switch (CONT->ocur) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003754 xmlNodePtr cur;
3755
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003756 case XML_ELEMENT_CONTENT_ONCE:
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003757 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003758 DEBUG_VALID_MSG("Once branch failed, rollback");
3759 if (vstateVPop(ctxt) < 0 ) {
3760 DEBUG_VALID_MSG("exhaustion, failed");
3761 return(0);
3762 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003763 if (cur != ctxt->vstate->node)
3764 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003765 goto cont;
3766 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5344c602001-12-31 16:37:34 +00003767 if (OCCURRENCE == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003768 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003769 DEBUG_VALID_MSG("Plus branch failed, rollback");
3770 if (vstateVPop(ctxt) < 0 ) {
3771 DEBUG_VALID_MSG("exhaustion, failed");
3772 return(0);
3773 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003774 if (cur != ctxt->vstate->node)
3775 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003776 goto cont;
3777 }
3778 DEBUG_VALID_MSG("Plus branch found");
3779 ret = 1;
3780 break;
3781 case XML_ELEMENT_CONTENT_MULT:
3782#ifdef DEBUG_VALID_ALGO
Daniel Veillard5344c602001-12-31 16:37:34 +00003783 if (OCCURRENCE == 0) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003784 DEBUG_VALID_MSG("Mult branch failed");
3785 } else {
3786 DEBUG_VALID_MSG("Mult branch found");
3787 }
3788#endif
3789 ret = 1;
3790 break;
3791 case XML_ELEMENT_CONTENT_OPT:
3792 DEBUG_VALID_MSG("Option branch failed");
3793 ret = 1;
3794 break;
3795 }
3796 } else {
3797 switch (CONT->ocur) {
3798 case XML_ELEMENT_CONTENT_OPT:
3799 DEBUG_VALID_MSG("Option branch succeeded");
3800 ret = 1;
3801 break;
3802 case XML_ELEMENT_CONTENT_ONCE:
3803 DEBUG_VALID_MSG("Once branch succeeded");
3804 ret = 1;
3805 break;
3806 case XML_ELEMENT_CONTENT_PLUS:
3807 if (STATE == ROLLBACK_PARENT) {
3808 DEBUG_VALID_MSG("Plus branch rollback");
3809 ret = 1;
3810 break;
3811 }
3812 if (NODE == NULL) {
3813 DEBUG_VALID_MSG("Plus branch exhausted");
3814 ret = 1;
3815 break;
3816 }
3817 DEBUG_VALID_MSG("Plus branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003818 SET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003819 goto cont;
3820 case XML_ELEMENT_CONTENT_MULT:
3821 if (STATE == ROLLBACK_PARENT) {
3822 DEBUG_VALID_MSG("Mult branch rollback");
3823 ret = 1;
3824 break;
3825 }
3826 if (NODE == NULL) {
3827 DEBUG_VALID_MSG("Mult branch exhausted");
3828 ret = 1;
3829 break;
3830 }
3831 DEBUG_VALID_MSG("Mult branch succeeded, continuing");
Daniel Veillard5344c602001-12-31 16:37:34 +00003832 /* SET_OCCURRENCE; */
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003833 goto cont;
3834 }
3835 }
3836 STATE = 0;
3837
3838 /*
3839 * Then act accordingly at the parent level
3840 */
Daniel Veillard5344c602001-12-31 16:37:34 +00003841 RESET_OCCURRENCE;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003842 if (CONT->parent == NULL)
3843 break;
3844
3845 switch (CONT->parent->type) {
3846 case XML_ELEMENT_CONTENT_PCDATA:
3847 DEBUG_VALID_MSG("Error: parent pcdata");
3848 return(-1);
3849 case XML_ELEMENT_CONTENT_ELEMENT:
3850 DEBUG_VALID_MSG("Error: parent element");
3851 return(-1);
3852 case XML_ELEMENT_CONTENT_OR:
3853 if (ret == 1) {
3854 DEBUG_VALID_MSG("Or succeeded");
3855 CONT = CONT->parent;
3856 DEPTH--;
3857 } else {
3858 DEBUG_VALID_MSG("Or failed");
3859 CONT = CONT->parent;
3860 DEPTH--;
3861 }
3862 break;
3863 case XML_ELEMENT_CONTENT_SEQ:
3864 if (ret == 0) {
3865 DEBUG_VALID_MSG("Sequence failed");
3866 CONT = CONT->parent;
3867 DEPTH--;
3868 } else if (CONT == CONT->parent->c1) {
3869 DEBUG_VALID_MSG("Sequence testing 2nd branch");
3870 CONT = CONT->parent->c2;
3871 goto cont;
3872 } else {
3873 DEBUG_VALID_MSG("Sequence succeeded");
3874 CONT = CONT->parent;
3875 DEPTH--;
3876 }
3877 }
3878 }
3879 if (NODE != NULL) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003880 xmlNodePtr cur;
3881
3882 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003883 DEBUG_VALID_MSG("Failed, remaining input, rollback");
3884 if (vstateVPop(ctxt) < 0 ) {
3885 DEBUG_VALID_MSG("exhaustion, failed");
3886 return(0);
3887 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003888 if (cur != ctxt->vstate->node)
3889 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003890 goto cont;
3891 }
3892 if (ret == 0) {
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003893 xmlNodePtr cur;
3894
3895 cur = ctxt->vstate->node;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003896 DEBUG_VALID_MSG("Failure, rollback");
3897 if (vstateVPop(ctxt) < 0 ) {
3898 DEBUG_VALID_MSG("exhaustion, failed");
3899 return(0);
3900 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003901 if (cur != ctxt->vstate->node)
3902 determinist = -3;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003903 goto cont;
3904 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00003905 return(determinist);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003906}
3907
3908/**
Daniel Veillardd3d06722001-08-15 12:06:36 +00003909 * xmlSnprintfElements:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003910 * @buf: an output buffer
Daniel Veillardd3d06722001-08-15 12:06:36 +00003911 * @size: the size of the buffer
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003912 * @content: An element
3913 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3914 *
3915 * This will dump the list of elements to the buffer
3916 * Intended just for the debug routine
3917 */
3918static void
Daniel Veillardd3d06722001-08-15 12:06:36 +00003919xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) {
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003920 xmlNodePtr cur;
Daniel Veillardd3d06722001-08-15 12:06:36 +00003921 int len;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003922
3923 if (node == NULL) return;
3924 if (glob) strcat(buf, "(");
3925 cur = node;
3926 while (cur != NULL) {
Daniel Veillardd3d06722001-08-15 12:06:36 +00003927 len = strlen(buf);
3928 if (size - len < 50) {
3929 if ((size - len > 4) && (buf[len - 1] != '.'))
3930 strcat(buf, " ...");
3931 return;
3932 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003933 switch (cur->type) {
3934 case XML_ELEMENT_NODE:
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003935 if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) {
3936 if (size - len < xmlStrlen(cur->ns->prefix + 10)) {
3937 if ((size - len > 4) && (buf[len - 1] != '.'))
3938 strcat(buf, " ...");
3939 return;
3940 }
3941 strcat(buf, (char *) cur->ns->prefix);
3942 strcat(buf, ":");
3943 }
Daniel Veillardd3d06722001-08-15 12:06:36 +00003944 if (size - len < xmlStrlen(cur->name + 10)) {
3945 if ((size - len > 4) && (buf[len - 1] != '.'))
3946 strcat(buf, " ...");
3947 return;
3948 }
3949 strcat(buf, (char *) cur->name);
3950 if (cur->next != NULL)
3951 strcat(buf, " ");
3952 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003953 case XML_TEXT_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003954 if (xmlIsBlankNode(cur))
3955 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003956 case XML_CDATA_SECTION_NODE:
3957 case XML_ENTITY_REF_NODE:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003958 strcat(buf, "CDATA");
3959 if (cur->next != NULL)
3960 strcat(buf, " ");
3961 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003962 case XML_ATTRIBUTE_NODE:
3963 case XML_DOCUMENT_NODE:
Daniel Veillardeae522a2001-04-23 13:41:34 +00003964#ifdef LIBXML_DOCB_ENABLED
3965 case XML_DOCB_DOCUMENT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003966#endif
3967 case XML_HTML_DOCUMENT_NODE:
3968 case XML_DOCUMENT_TYPE_NODE:
3969 case XML_DOCUMENT_FRAG_NODE:
3970 case XML_NOTATION_NODE:
3971 case XML_NAMESPACE_DECL:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003972 strcat(buf, "???");
3973 if (cur->next != NULL)
3974 strcat(buf, " ");
3975 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003976 case XML_ENTITY_NODE:
3977 case XML_PI_NODE:
3978 case XML_DTD_NODE:
3979 case XML_COMMENT_NODE:
3980 case XML_ELEMENT_DECL:
3981 case XML_ATTRIBUTE_DECL:
3982 case XML_ENTITY_DECL:
3983 case XML_XINCLUDE_START:
3984 case XML_XINCLUDE_END:
Daniel Veillardd3d06722001-08-15 12:06:36 +00003985 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003986 }
3987 cur = cur->next;
3988 }
3989 if (glob) strcat(buf, ")");
3990}
3991
3992/**
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003993 * xmlValidateElementContent:
3994 * @ctxt: the validation context
3995 * @child: the child list
Daniel Veillardbe480fb2001-11-08 23:36:42 +00003996 * @elemDecl: pointer to the element declaration
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00003997 * @warn: emit the error message
Daniel Veillarddab4cb32001-04-20 13:03:48 +00003998 *
3999 * Try to validate the content model of an element
4000 *
4001 * returns 1 if valid or 0 if not and -1 in case of error
4002 */
4003
4004static int
4005xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004006 xmlElementPtr elemDecl, int warn) {
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004007 int ret;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004008 xmlNodePtr repl = NULL, last = NULL, cur, tmp;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004009 xmlElementContentPtr cont;
4010 const xmlChar *name;
4011
4012 if (elemDecl == NULL)
4013 return(-1);
4014 cont = elemDecl->content;
4015 name = elemDecl->name;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004016
4017 /*
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004018 * Allocate the stack
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004019 */
4020 ctxt->vstateMax = 8;
4021 ctxt->vstateTab = (xmlValidState *) xmlMalloc(
4022 ctxt->vstateMax * sizeof(ctxt->vstateTab[0]));
4023 if (ctxt->vstateTab == NULL) {
4024 xmlGenericError(xmlGenericErrorContext,
4025 "malloc failed !n");
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004026 return(-1);
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004027 }
4028 /*
4029 * The first entry in the stack is reserved to the current state
4030 */
Daniel Veillarda9142e72001-06-19 11:07:54 +00004031 ctxt->nodeMax = 0;
4032 ctxt->nodeNr = 0;
Daniel Veillard61b33d52001-04-24 13:55:12 +00004033 ctxt->nodeTab = NULL;
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004034 ctxt->vstate = &ctxt->vstateTab[0];
4035 ctxt->vstateNr = 1;
4036 CONT = cont;
4037 NODE = child;
4038 DEPTH = 0;
4039 OCCURS = 0;
4040 STATE = 0;
4041 ret = xmlValidateElementType(ctxt);
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004042 if ((ret == -3) && (warn)) {
4043 VWARNING(ctxt->userData,
4044 "Element %s content model is ambiguous\n", name);
4045 } else if (ret == -2) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004046 /*
4047 * An entities reference appeared at this level.
4048 * Buid a minimal representation of this node content
4049 * sufficient to run the validation process on it
4050 */
4051 DEBUG_VALID_MSG("Found an entity reference, linearizing");
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004052 cur = child;
4053 while (cur != NULL) {
4054 switch (cur->type) {
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004055 case XML_ENTITY_REF_NODE:
4056 /*
4057 * Push the current node to be able to roll back
4058 * and process within the entity
4059 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004060 if ((cur->children != NULL) &&
4061 (cur->children->children != NULL)) {
4062 nodeVPush(ctxt, cur);
4063 cur = cur->children->children;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004064 continue;
4065 }
Daniel Veillard64b98c02001-06-17 17:20:21 +00004066 break;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004067 case XML_TEXT_NODE:
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004068 if (xmlIsBlankNode(cur))
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004069 break;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004070 /* no break on purpose */
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004071 case XML_CDATA_SECTION_NODE:
4072 /* no break on purpose */
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004073 case XML_ELEMENT_NODE:
4074 /*
4075 * Allocate a new node and minimally fills in
4076 * what's required
4077 */
4078 tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode));
4079 if (tmp == NULL) {
4080 xmlGenericError(xmlGenericErrorContext,
4081 "xmlValidateElementContent : malloc failed\n");
4082 xmlFreeNodeList(repl);
4083 ret = -1;
4084 goto done;
4085 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004086 tmp->type = cur->type;
4087 tmp->name = cur->name;
4088 tmp->ns = cur->ns;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004089 tmp->next = NULL;
Daniel Veillarded472f32001-12-13 08:48:14 +00004090 tmp->content = NULL;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004091 if (repl == NULL)
4092 repl = last = tmp;
4093 else {
4094 last->next = tmp;
4095 last = tmp;
4096 }
Daniel Veillardd6dc4cb2002-02-19 14:18:08 +00004097 if (cur->type == XML_CDATA_SECTION_NODE) {
4098 /*
4099 * E59 spaces in CDATA does not match the
4100 * nonterminal S
4101 */
4102 tmp->content = xmlStrdup(BAD_CAST "CDATA");
4103 }
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004104 break;
4105 default:
4106 break;
4107 }
4108 /*
4109 * Switch to next element
4110 */
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004111 cur = cur->next;
4112 while (cur == NULL) {
4113 cur = nodeVPop(ctxt);
4114 if (cur == NULL)
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004115 break;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004116 cur = cur->next;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004117 }
4118 }
4119
4120 /*
4121 * Relaunch the validation
4122 */
4123 ctxt->vstate = &ctxt->vstateTab[0];
4124 ctxt->vstateNr = 1;
4125 CONT = cont;
4126 NODE = repl;
4127 DEPTH = 0;
4128 OCCURS = 0;
4129 STATE = 0;
4130 ret = xmlValidateElementType(ctxt);
4131 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004132 if ((warn) && ((ret != 1) && (ret != -3))) {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004133 if ((ctxt != NULL) && (ctxt->warning != NULL)) {
4134 char expr[5000];
4135 char list[5000];
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004136
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004137 expr[0] = 0;
4138 xmlSnprintfElementContent(expr, 5000, cont, 1);
4139 list[0] = 0;
4140 if (repl != NULL)
4141 xmlSnprintfElements(list, 5000, repl, 1);
4142 else
4143 xmlSnprintfElements(list, 5000, child, 1);
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004144
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004145 if (name != NULL) {
4146 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004147 "Element %s content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004148 name, expr, list);
4149 } else {
4150 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004151 "Element content doesn't follow the DTD\nExpecting %s, got %s\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004152 expr, list);
4153 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004154 } else {
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004155 if (name != NULL) {
4156 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004157 "Element %s content doesn't follow the DTD\n",
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004158 name);
4159 } else {
4160 VERROR(ctxt->userData,
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004161 "Element content doesn't follow the DTD\n");
Daniel Veillardb4545fd2001-11-20 09:37:09 +00004162 }
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004163 }
4164 ret = 0;
4165 }
Daniel Veillard4de4d3b2001-05-07 20:50:47 +00004166 if (ret == -3)
4167 ret = 1;
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004168
4169
4170done:
4171 /*
4172 * Deallocate the copy if done, and free up the validation stack
4173 */
4174 while (repl != NULL) {
4175 tmp = repl->next;
4176 xmlFree(repl);
4177 repl = tmp;
4178 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004179 ctxt->vstateMax = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004180 if (ctxt->vstateTab != NULL) {
4181 xmlFree(ctxt->vstateTab);
4182 ctxt->vstateTab = NULL;
4183 }
4184 ctxt->nodeMax = 0;
Daniel Veillarda9142e72001-06-19 11:07:54 +00004185 ctxt->nodeNr = 0;
Daniel Veillard34b1b3a2001-04-21 14:16:10 +00004186 if (ctxt->nodeTab != NULL) {
4187 xmlFree(ctxt->nodeTab);
4188 ctxt->nodeTab = NULL;
4189 }
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004190 return(ret);
Daniel Veillard1c14b8d2001-04-21 10:28:59 +00004191
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004192}
Daniel Veillarddab4cb32001-04-20 13:03:48 +00004193
Owen Taylor3473f882001-02-23 17:55:21 +00004194/**
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004195 * xmlValidateCdataElement:
4196 * @ctxt: the validation context
4197 * @doc: a document instance
4198 * @elem: an element instance
4199 *
4200 * Check that an element follows #CDATA
4201 *
4202 * returns 1 if valid or 0 otherwise
4203 */
4204static int
4205xmlValidateOneCdataElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4206 xmlNodePtr elem) {
4207 int ret = 1;
4208 xmlNodePtr cur, child;
4209
4210 if ((ctxt == NULL) || (doc == NULL) | (elem == NULL))
4211 return(0);
4212
4213 child = elem->children;
4214
4215 cur = child;
4216 while (cur != NULL) {
4217 switch (cur->type) {
4218 case XML_ENTITY_REF_NODE:
4219 /*
4220 * Push the current node to be able to roll back
4221 * and process within the entity
4222 */
4223 if ((cur->children != NULL) &&
4224 (cur->children->children != NULL)) {
4225 nodeVPush(ctxt, cur);
4226 cur = cur->children->children;
4227 continue;
4228 }
4229 break;
4230 case XML_COMMENT_NODE:
4231 case XML_PI_NODE:
4232 case XML_TEXT_NODE:
4233 case XML_CDATA_SECTION_NODE:
4234 break;
4235 default:
4236 ret = 0;
4237 goto done;
4238 }
4239 /*
4240 * Switch to next element
4241 */
4242 cur = cur->next;
4243 while (cur == NULL) {
4244 cur = nodeVPop(ctxt);
4245 if (cur == NULL)
4246 break;
4247 cur = cur->next;
4248 }
4249 }
4250done:
4251 ctxt->nodeMax = 0;
4252 ctxt->nodeNr = 0;
4253 if (ctxt->nodeTab != NULL) {
4254 xmlFree(ctxt->nodeTab);
4255 ctxt->nodeTab = NULL;
4256 }
4257 return(ret);
4258}
4259
4260/**
Owen Taylor3473f882001-02-23 17:55:21 +00004261 * xmlValidateOneElement:
4262 * @ctxt: the validation context
4263 * @doc: a document instance
4264 * @elem: an element instance
4265 *
4266 * Try to validate a single element and it's attributes,
4267 * basically it does the following checks as described by the
4268 * XML-1.0 recommendation:
4269 * - [ VC: Element Valid ]
4270 * - [ VC: Required Attribute ]
4271 * Then call xmlValidateOneAttribute() for each attribute present.
4272 *
4273 * The ID/IDREF checkings are done separately
4274 *
4275 * returns 1 if valid or 0 otherwise
4276 */
4277
4278int
4279xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
4280 xmlNodePtr elem) {
4281 xmlElementPtr elemDecl = NULL;
4282 xmlElementContentPtr cont;
4283 xmlAttributePtr attr;
4284 xmlNodePtr child;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004285 int ret = 1, tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004286 const xmlChar *name;
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004287 const xmlChar *prefix = NULL;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004288 int extsubset = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004289
4290 CHECK_DTD;
4291
4292 if (elem == NULL) return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004293 switch (elem->type) {
4294 case XML_ATTRIBUTE_NODE:
4295 VERROR(ctxt->userData,
4296 "Attribute element not expected here\n");
4297 return(0);
4298 case XML_TEXT_NODE:
4299 if (elem->children != NULL) {
4300 VERROR(ctxt->userData, "Text element has childs !\n");
4301 return(0);
4302 }
4303 if (elem->properties != NULL) {
4304 VERROR(ctxt->userData, "Text element has attributes !\n");
4305 return(0);
4306 }
4307 if (elem->ns != NULL) {
4308 VERROR(ctxt->userData, "Text element has namespace !\n");
4309 return(0);
4310 }
4311 if (elem->nsDef != NULL) {
4312 VERROR(ctxt->userData,
4313 "Text element carries namespace definitions !\n");
4314 return(0);
4315 }
4316 if (elem->content == NULL) {
4317 VERROR(ctxt->userData,
4318 "Text element has no content !\n");
4319 return(0);
4320 }
4321 return(1);
4322 case XML_XINCLUDE_START:
4323 case XML_XINCLUDE_END:
4324 return(1);
4325 case XML_CDATA_SECTION_NODE:
4326 case XML_ENTITY_REF_NODE:
4327 case XML_PI_NODE:
4328 case XML_COMMENT_NODE:
4329 return(1);
4330 case XML_ENTITY_NODE:
4331 VERROR(ctxt->userData,
4332 "Entity element not expected here\n");
4333 return(0);
4334 case XML_NOTATION_NODE:
4335 VERROR(ctxt->userData,
4336 "Notation element not expected here\n");
4337 return(0);
4338 case XML_DOCUMENT_NODE:
4339 case XML_DOCUMENT_TYPE_NODE:
4340 case XML_DOCUMENT_FRAG_NODE:
4341 VERROR(ctxt->userData,
4342 "Document element not expected here\n");
4343 return(0);
4344 case XML_HTML_DOCUMENT_NODE:
4345 VERROR(ctxt->userData,
4346 "\n");
4347 return(0);
4348 case XML_ELEMENT_NODE:
4349 break;
4350 default:
4351 VERROR(ctxt->userData,
4352 "unknown element type %d\n", elem->type);
4353 return(0);
4354 }
4355 if (elem->name == NULL) return(0);
4356
4357 /*
4358 * Fetch the declaration for the qualified name
4359 */
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004360 if ((elem->ns != NULL) && (elem->ns->prefix != NULL))
4361 prefix = elem->ns->prefix;
4362
4363 if (prefix != NULL) {
Owen Taylor3473f882001-02-23 17:55:21 +00004364 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004365 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004366 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004367 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004368 elem->name, prefix);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004369 if (elemDecl != NULL)
4370 extsubset = 1;
4371 }
Owen Taylor3473f882001-02-23 17:55:21 +00004372 }
4373
4374 /*
4375 * Fetch the declaration for the non qualified name
Daniel Veillardbe480fb2001-11-08 23:36:42 +00004376 * This is "non-strict" validation should be done on the
4377 * full QName but in that case being flexible makes sense.
Owen Taylor3473f882001-02-23 17:55:21 +00004378 */
4379 if (elemDecl == NULL) {
4380 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004381 if ((elemDecl == NULL) && (doc->extSubset != NULL)) {
Owen Taylor3473f882001-02-23 17:55:21 +00004382 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004383 if (elemDecl != NULL)
4384 extsubset = 1;
4385 }
Owen Taylor3473f882001-02-23 17:55:21 +00004386 }
4387 if (elemDecl == NULL) {
4388 VERROR(ctxt->userData, "No declaration for element %s\n",
4389 elem->name);
4390 return(0);
4391 }
4392
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004393 /* Check that the element content matches the definition */
Owen Taylor3473f882001-02-23 17:55:21 +00004394 switch (elemDecl->etype) {
Daniel Veillarda10efa82001-04-18 13:09:01 +00004395 case XML_ELEMENT_TYPE_UNDEFINED:
4396 VERROR(ctxt->userData, "No declaration for element %s\n",
4397 elem->name);
4398 return(0);
Owen Taylor3473f882001-02-23 17:55:21 +00004399 case XML_ELEMENT_TYPE_EMPTY:
4400 if (elem->children != NULL) {
4401 VERROR(ctxt->userData,
4402 "Element %s was declared EMPTY this one has content\n",
4403 elem->name);
4404 ret = 0;
4405 }
4406 break;
4407 case XML_ELEMENT_TYPE_ANY:
4408 /* I don't think anything is required then */
4409 break;
4410 case XML_ELEMENT_TYPE_MIXED:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004411
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004412 /* simple case of declared as #PCDATA */
4413 if ((elemDecl->content != NULL) &&
4414 (elemDecl->content->type == XML_ELEMENT_CONTENT_PCDATA)) {
4415 ret = xmlValidateOneCdataElement(ctxt, doc, elem);
4416 if (!ret) {
4417 VERROR(ctxt->userData,
4418 "Element %s was declared #PCDATA but contains non text nodes\n",
4419 elem->name);
4420 }
4421 break;
4422 }
Owen Taylor3473f882001-02-23 17:55:21 +00004423 child = elem->children;
Daniel Veillard04e2dae2001-07-09 20:07:25 +00004424 /* Hum, this start to get messy */
Owen Taylor3473f882001-02-23 17:55:21 +00004425 while (child != NULL) {
4426 if (child->type == XML_ELEMENT_NODE) {
4427 name = child->name;
4428 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
4429 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004430 snprintf((char *) qname, sizeof(qname), "%s:%s",
4431 child->ns->prefix, child->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004432 qname[sizeof(qname) - 1] = 0;
4433 cont = elemDecl->content;
4434 while (cont != NULL) {
4435 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4436 if (xmlStrEqual(cont->name, qname)) break;
4437 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4438 (cont->c1 != NULL) &&
4439 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
4440 if (xmlStrEqual(cont->c1->name, qname)) break;
4441 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4442 (cont->c1 == NULL) ||
4443 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
4444 /* Internal error !!! */
4445 xmlGenericError(xmlGenericErrorContext,
4446 "Internal: MIXED struct bad\n");
4447 break;
4448 }
4449 cont = cont->c2;
4450 }
4451 if (cont != NULL)
4452 goto child_ok;
4453 }
4454 cont = elemDecl->content;
4455 while (cont != NULL) {
4456 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
4457 if (xmlStrEqual(cont->name, name)) break;
4458 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
4459 (cont->c1 != NULL) &&
4460 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
4461 if (xmlStrEqual(cont->c1->name, name)) break;
4462 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
4463 (cont->c1 == NULL) ||
4464 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
4465 /* Internal error !!! */
4466 xmlGenericError(xmlGenericErrorContext,
4467 "Internal: MIXED struct bad\n");
4468 break;
4469 }
4470 cont = cont->c2;
4471 }
4472 if (cont == NULL) {
4473 VERROR(ctxt->userData,
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00004474 "Element %s is not declared in %s list of possible children\n",
Owen Taylor3473f882001-02-23 17:55:21 +00004475 name, elem->name);
4476 ret = 0;
4477 }
4478 }
4479child_ok:
4480 child = child->next;
4481 }
4482 break;
4483 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004484 if ((doc->standalone == 1) && (extsubset == 1)) {
4485 /*
4486 * VC: Standalone Document Declaration
4487 * - element types with element content, if white space
4488 * occurs directly within any instance of those types.
4489 */
4490 child = elem->children;
4491 while (child != NULL) {
4492 if (child->type == XML_TEXT_NODE) {
4493 const xmlChar *content = child->content;
4494
4495 while (IS_BLANK(*content))
4496 content++;
4497 if (*content == 0) {
4498 VERROR(ctxt->userData,
4499"standalone: %s declared in the external subset contains white spaces nodes\n",
4500 elem->name);
4501 ret = 0;
4502 break;
4503 }
4504 }
4505 child =child->next;
4506 }
4507 }
Owen Taylor3473f882001-02-23 17:55:21 +00004508 child = elem->children;
4509 cont = elemDecl->content;
Daniel Veillard8dc16a62002-02-19 21:08:48 +00004510 tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1);
4511 if (tmp <= 0)
4512 ret = tmp;
Owen Taylor3473f882001-02-23 17:55:21 +00004513 break;
4514 }
4515
4516 /* [ VC: Required Attribute ] */
4517 attr = elemDecl->attributes;
4518 while (attr != NULL) {
4519 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
Owen Taylor3473f882001-02-23 17:55:21 +00004520 int qualified = -1;
Owen Taylor3473f882001-02-23 17:55:21 +00004521
Daniel Veillarde4301c82002-02-13 13:32:35 +00004522 if ((attr->prefix == NULL) &&
4523 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4524 xmlNsPtr ns;
4525
4526 ns = elem->nsDef;
4527 while (ns != NULL) {
4528 if (ns->prefix == NULL)
Owen Taylor3473f882001-02-23 17:55:21 +00004529 goto found;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004530 ns = ns->next;
Owen Taylor3473f882001-02-23 17:55:21 +00004531 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004532 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4533 xmlNsPtr ns;
4534
4535 ns = elem->nsDef;
4536 while (ns != NULL) {
4537 if (xmlStrEqual(attr->name, ns->prefix))
4538 goto found;
4539 ns = ns->next;
4540 }
4541 } else {
4542 xmlAttrPtr attrib;
4543
4544 attrib = elem->properties;
4545 while (attrib != NULL) {
4546 if (xmlStrEqual(attrib->name, attr->name)) {
4547 if (attr->prefix != NULL) {
4548 xmlNsPtr nameSpace = attrib->ns;
4549
4550 if (nameSpace == NULL)
4551 nameSpace = elem->ns;
4552 /*
4553 * qualified names handling is problematic, having a
4554 * different prefix should be possible but DTDs don't
4555 * allow to define the URI instead of the prefix :-(
4556 */
4557 if (nameSpace == NULL) {
4558 if (qualified < 0)
4559 qualified = 0;
4560 } else if (!xmlStrEqual(nameSpace->prefix,
4561 attr->prefix)) {
4562 if (qualified < 1)
4563 qualified = 1;
4564 } else
4565 goto found;
4566 } else {
4567 /*
4568 * We should allow applications to define namespaces
4569 * for their application even if the DTD doesn't
4570 * carry one, otherwise, basically we would always
4571 * break.
4572 */
4573 goto found;
4574 }
4575 }
4576 attrib = attrib->next;
4577 }
Owen Taylor3473f882001-02-23 17:55:21 +00004578 }
4579 if (qualified == -1) {
4580 if (attr->prefix == NULL) {
4581 VERROR(ctxt->userData,
4582 "Element %s doesn't carry attribute %s\n",
4583 elem->name, attr->name);
4584 ret = 0;
4585 } else {
4586 VERROR(ctxt->userData,
4587 "Element %s doesn't carry attribute %s:%s\n",
4588 elem->name, attr->prefix,attr->name);
4589 ret = 0;
4590 }
4591 } else if (qualified == 0) {
4592 VWARNING(ctxt->userData,
4593 "Element %s required attribute %s:%s has no prefix\n",
4594 elem->name, attr->prefix,attr->name);
4595 } else if (qualified == 1) {
4596 VWARNING(ctxt->userData,
4597 "Element %s required attribute %s:%s has different prefix\n",
4598 elem->name, attr->prefix,attr->name);
4599 }
Daniel Veillarde4301c82002-02-13 13:32:35 +00004600 } else if (attr->def == XML_ATTRIBUTE_FIXED) {
4601 /*
4602 * Special tests checking #FIXED namespace declarations
4603 * have the right value since this is not done as an
4604 * attribute checking
4605 */
4606 if ((attr->prefix == NULL) &&
4607 (xmlStrEqual(attr->name, BAD_CAST "xmlns"))) {
4608 xmlNsPtr ns;
4609
4610 ns = elem->nsDef;
4611 while (ns != NULL) {
4612 if (ns->prefix == NULL) {
4613 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
4614 VERROR(ctxt->userData,
4615 "Element %s namespace name for default namespace does not match the DTD\n",
4616 elem->name);
Daniel Veillardc7612992002-02-17 22:47:37 +00004617 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004618 }
4619 goto found;
4620 }
4621 ns = ns->next;
4622 }
4623 } else if (xmlStrEqual(attr->prefix, BAD_CAST "xmlns")) {
4624 xmlNsPtr ns;
4625
4626 ns = elem->nsDef;
4627 while (ns != NULL) {
4628 if (xmlStrEqual(attr->name, ns->prefix)) {
4629 if (!xmlStrEqual(attr->defaultValue, ns->href)) {
4630 VERROR(ctxt->userData,
4631 "Element %s namespace name for %s doesn't match the DTD\n",
4632 elem->name, ns->prefix);
Daniel Veillardc7612992002-02-17 22:47:37 +00004633 ret = 0;
Daniel Veillarde4301c82002-02-13 13:32:35 +00004634 }
4635 goto found;
4636 }
4637 ns = ns->next;
4638 }
4639 }
Owen Taylor3473f882001-02-23 17:55:21 +00004640 }
4641found:
4642 attr = attr->nexth;
4643 }
4644 return(ret);
4645}
4646
4647/**
4648 * xmlValidateRoot:
4649 * @ctxt: the validation context
4650 * @doc: a document instance
4651 *
4652 * Try to validate a the root element
4653 * basically it does the following check as described by the
4654 * XML-1.0 recommendation:
4655 * - [ VC: Root Element Type ]
4656 * it doesn't try to recurse or apply other check to the element
4657 *
4658 * returns 1 if valid or 0 otherwise
4659 */
4660
4661int
4662xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4663 xmlNodePtr root;
4664 if (doc == NULL) return(0);
4665
4666 root = xmlDocGetRootElement(doc);
4667 if ((root == NULL) || (root->name == NULL)) {
4668 VERROR(ctxt->userData, "Not valid: no root element\n");
4669 return(0);
4670 }
4671
4672 /*
4673 * When doing post validation against a separate DTD, those may
4674 * no internal subset has been generated
4675 */
4676 if ((doc->intSubset != NULL) &&
4677 (doc->intSubset->name != NULL)) {
4678 /*
4679 * Check first the document root against the NQName
4680 */
4681 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
4682 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
4683 xmlChar qname[500];
Owen Taylor3473f882001-02-23 17:55:21 +00004684 snprintf((char *) qname, sizeof(qname), "%s:%s",
4685 root->ns->prefix, root->name);
Owen Taylor3473f882001-02-23 17:55:21 +00004686 qname[sizeof(qname) - 1] = 0;
4687 if (xmlStrEqual(doc->intSubset->name, qname))
4688 goto name_ok;
4689 }
4690 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
4691 (xmlStrEqual(root->name, BAD_CAST "html")))
4692 goto name_ok;
4693 VERROR(ctxt->userData,
4694 "Not valid: root and DtD name do not match '%s' and '%s'\n",
4695 root->name, doc->intSubset->name);
4696 return(0);
4697
4698 }
4699 }
4700name_ok:
4701 return(1);
4702}
4703
4704
4705/**
4706 * xmlValidateElement:
4707 * @ctxt: the validation context
4708 * @doc: a document instance
4709 * @elem: an element instance
4710 *
4711 * Try to validate the subtree under an element
4712 *
4713 * returns 1 if valid or 0 otherwise
4714 */
4715
4716int
4717xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
4718 xmlNodePtr child;
4719 xmlAttrPtr attr;
4720 xmlChar *value;
4721 int ret = 1;
4722
4723 if (elem == NULL) return(0);
4724
4725 /*
4726 * XInclude elements were added after parsing in the infoset,
4727 * they don't really mean anything validation wise.
4728 */
4729 if ((elem->type == XML_XINCLUDE_START) ||
4730 (elem->type == XML_XINCLUDE_END))
4731 return(1);
4732
4733 CHECK_DTD;
4734
Daniel Veillard10ea86c2001-06-20 13:55:33 +00004735 /*
4736 * Entities references have to be handled separately
4737 */
4738 if (elem->type == XML_ENTITY_REF_NODE) {
4739 return(1);
4740 }
4741
Owen Taylor3473f882001-02-23 17:55:21 +00004742 ret &= xmlValidateOneElement(ctxt, doc, elem);
4743 attr = elem->properties;
4744 while(attr != NULL) {
4745 value = xmlNodeListGetString(doc, attr->children, 0);
4746 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
4747 if (value != NULL)
4748 xmlFree(value);
4749 attr= attr->next;
4750 }
4751 child = elem->children;
4752 while (child != NULL) {
4753 ret &= xmlValidateElement(ctxt, doc, child);
4754 child = child->next;
4755 }
4756
4757 return(ret);
4758}
4759
Daniel Veillard8730c562001-02-26 10:49:57 +00004760/**
4761 * xmlValidateRef:
4762 * @ref: A reference to be validated
4763 * @ctxt: Validation context
4764 * @name: Name of ID we are searching for
4765 *
4766 */
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004767static void
Daniel Veillard8730c562001-02-26 10:49:57 +00004768xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
Owen Taylor3473f882001-02-23 17:55:21 +00004769 const xmlChar *name) {
4770 xmlAttrPtr id;
4771 xmlAttrPtr attr;
4772
4773 if (ref == NULL)
4774 return;
4775 attr = ref->attr;
4776 if (attr == NULL)
4777 return;
4778 if (attr->atype == XML_ATTRIBUTE_IDREF) {
4779 id = xmlGetID(ctxt->doc, name);
4780 if (id == NULL) {
4781 VERROR(ctxt->userData,
4782 "IDREF attribute %s reference an unknown ID \"%s\"\n",
4783 attr->name, name);
4784 ctxt->valid = 0;
4785 }
4786 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
4787 xmlChar *dup, *str = NULL, *cur, save;
4788
4789 dup = xmlStrdup(name);
4790 if (dup == NULL) {
4791 ctxt->valid = 0;
4792 return;
4793 }
4794 cur = dup;
4795 while (*cur != 0) {
4796 str = cur;
4797 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
4798 save = *cur;
4799 *cur = 0;
4800 id = xmlGetID(ctxt->doc, str);
4801 if (id == NULL) {
4802 VERROR(ctxt->userData,
4803 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
4804 attr->name, str);
4805 ctxt->valid = 0;
4806 }
4807 if (save == 0)
4808 break;
4809 *cur = save;
4810 while (IS_BLANK(*cur)) cur++;
4811 }
4812 xmlFree(dup);
4813 }
4814}
4815
4816/**
Daniel Veillard8730c562001-02-26 10:49:57 +00004817 * xmlWalkValidateList:
4818 * @data: Contents of current link
4819 * @user: Value supplied by the user
4820 *
Daniel Veillardcbaf3992001-12-31 16:16:02 +00004821 * Returns 0 to abort the walk or 1 to continue
Daniel Veillard8730c562001-02-26 10:49:57 +00004822 */
4823static int
4824xmlWalkValidateList(const void *data, const void *user)
4825{
4826 xmlValidateMemoPtr memo = (xmlValidateMemoPtr)user;
4827 xmlValidateRef((xmlRefPtr)data, memo->ctxt, memo->name);
4828 return 1;
4829}
4830
4831/**
4832 * xmlValidateCheckRefCallback:
4833 * @ref_list: List of references
4834 * @ctxt: Validation context
4835 * @name: Name of ID we are searching for
4836 *
4837 */
4838static void
4839xmlValidateCheckRefCallback(xmlListPtr ref_list, xmlValidCtxtPtr ctxt,
4840 const xmlChar *name) {
4841 xmlValidateMemo memo;
4842
4843 if (ref_list == NULL)
4844 return;
4845 memo.ctxt = ctxt;
4846 memo.name = name;
4847
4848 xmlListWalk(ref_list, xmlWalkValidateList, &memo);
4849
4850}
4851
4852/**
Owen Taylor3473f882001-02-23 17:55:21 +00004853 * xmlValidateDocumentFinal:
4854 * @ctxt: the validation context
4855 * @doc: a document instance
4856 *
4857 * Does the final step for the document validation once all the
4858 * incremental validation steps have been completed
4859 *
4860 * basically it does the following checks described by the XML Rec
4861 *
4862 *
4863 * returns 1 if valid or 0 otherwise
4864 */
4865
4866int
4867xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
4868 xmlRefTablePtr table;
4869
4870 if (doc == NULL) {
4871 xmlGenericError(xmlGenericErrorContext,
4872 "xmlValidateDocumentFinal: doc == NULL\n");
4873 return(0);
4874 }
4875
4876 /*
4877 * Check all the NOTATION/NOTATIONS attributes
4878 */
4879 /*
4880 * Check all the ENTITY/ENTITIES attributes definition for validity
4881 */
4882 /*
4883 * Check all the IDREF/IDREFS attributes definition for validity
4884 */
4885 table = (xmlRefTablePtr) doc->refs;
4886 ctxt->doc = doc;
4887 ctxt->valid = 1;
4888 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
4889 return(ctxt->valid);
4890}
4891
4892/**
4893 * xmlValidateDtd:
4894 * @ctxt: the validation context
4895 * @doc: a document instance
4896 * @dtd: a dtd instance
4897 *
4898 * Try to validate the document against the dtd instance
4899 *
4900 * basically it does check all the definitions in the DtD.
4901 *
4902 * returns 1 if valid or 0 otherwise
4903 */
4904
4905int
4906xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
4907 int ret;
4908 xmlDtdPtr oldExt;
4909 xmlNodePtr root;
4910
4911 if (dtd == NULL) return(0);
4912 if (doc == NULL) return(0);
4913 oldExt = doc->extSubset;
4914 doc->extSubset = dtd;
4915 ret = xmlValidateRoot(ctxt, doc);
4916 if (ret == 0) {
4917 doc->extSubset = oldExt;
4918 return(ret);
4919 }
4920 if (doc->ids != NULL) {
4921 xmlFreeIDTable(doc->ids);
4922 doc->ids = NULL;
4923 }
4924 if (doc->refs != NULL) {
4925 xmlFreeRefTable(doc->refs);
4926 doc->refs = NULL;
4927 }
4928 root = xmlDocGetRootElement(doc);
4929 ret = xmlValidateElement(ctxt, doc, root);
4930 ret &= xmlValidateDocumentFinal(ctxt, doc);
4931 doc->extSubset = oldExt;
4932 return(ret);
4933}
4934
Daniel Veillard56a4cb82001-03-24 17:00:36 +00004935static void
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004936xmlValidateNotationCallback(xmlEntityPtr cur, xmlValidCtxtPtr ctxt,
4937 const xmlChar *name ATTRIBUTE_UNUSED) {
4938 if (cur == NULL)
4939 return;
4940 if (cur->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
4941 xmlChar *notation = cur->content;
4942
Daniel Veillard878eab02002-02-19 13:46:09 +00004943 if (notation != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004944 int ret;
4945
4946 ret = xmlValidateNotationUse(ctxt, cur->doc, notation);
4947 if (ret != 1) {
Daniel Veillard878eab02002-02-19 13:46:09 +00004948 ctxt->valid = 0;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004949 }
4950 }
4951 }
4952}
4953
4954static void
Owen Taylor3473f882001-02-23 17:55:21 +00004955xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
Daniel Veillardc86a4fa2001-03-26 16:28:29 +00004956 const xmlChar *name ATTRIBUTE_UNUSED) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004957 int ret;
Daniel Veillard878eab02002-02-19 13:46:09 +00004958 xmlDocPtr doc;
4959 xmlElementPtr elem;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004960
Owen Taylor3473f882001-02-23 17:55:21 +00004961 if (cur == NULL)
4962 return;
4963 switch (cur->atype) {
4964 case XML_ATTRIBUTE_CDATA:
4965 case XML_ATTRIBUTE_ID:
4966 case XML_ATTRIBUTE_IDREF :
4967 case XML_ATTRIBUTE_IDREFS:
4968 case XML_ATTRIBUTE_NMTOKEN:
4969 case XML_ATTRIBUTE_NMTOKENS:
4970 case XML_ATTRIBUTE_ENUMERATION:
4971 break;
4972 case XML_ATTRIBUTE_ENTITY:
4973 case XML_ATTRIBUTE_ENTITIES:
4974 case XML_ATTRIBUTE_NOTATION:
4975 if (cur->defaultValue != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004976
4977 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc, cur->name,
4978 cur->atype, cur->defaultValue);
4979 if ((ret == 0) && (ctxt->valid == 1))
4980 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004981 }
4982 if (cur->tree != NULL) {
4983 xmlEnumerationPtr tree = cur->tree;
4984 while (tree != NULL) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004985 ret = xmlValidateAttributeValue2(ctxt, ctxt->doc,
Owen Taylor3473f882001-02-23 17:55:21 +00004986 cur->name, cur->atype, tree->name);
Daniel Veillard8ab0f582002-02-18 18:31:38 +00004987 if ((ret == 0) && (ctxt->valid == 1))
4988 ctxt->valid = 0;
Owen Taylor3473f882001-02-23 17:55:21 +00004989 tree = tree->next;
4990 }
4991 }
4992 }
Daniel Veillard878eab02002-02-19 13:46:09 +00004993 if (cur->atype == XML_ATTRIBUTE_NOTATION) {
4994 doc = cur->doc;
4995 if ((doc == NULL) || (cur->elem == NULL)) {
4996 VERROR(ctxt->userData,
4997 "xmlValidateAttributeCallback(%s): internal error\n",
4998 cur->name);
4999 return;
5000 }
5001 elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem);
5002 if (elem == NULL)
5003 elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem);
5004 if (elem == NULL) {
5005 VERROR(ctxt->userData,
5006 "attribute %s: could not find decl for element %s\n",
5007 cur->name, cur->elem);
5008 return;
5009 }
5010 if (elem->etype == XML_ELEMENT_TYPE_EMPTY) {
5011 VERROR(ctxt->userData,
5012 "NOTATION attribute %s declared on EMPTY element %s\n",
5013 cur->name, cur->elem);
5014 ctxt->valid = 0;
5015 }
5016 }
Owen Taylor3473f882001-02-23 17:55:21 +00005017}
5018
5019/**
5020 * xmlValidateDtdFinal:
5021 * @ctxt: the validation context
5022 * @doc: a document instance
5023 *
5024 * Does the final step for the dtds validation once all the
5025 * subsets have been parsed
5026 *
5027 * basically it does the following checks described by the XML Rec
5028 * - check that ENTITY and ENTITIES type attributes default or
5029 * possible values matches one of the defined entities.
5030 * - check that NOTATION type attributes default or
5031 * possible values matches one of the defined notations.
5032 *
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005033 * returns 1 if valid or 0 if invalid and -1 if not well-formed
Owen Taylor3473f882001-02-23 17:55:21 +00005034 */
5035
5036int
5037xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Owen Taylor3473f882001-02-23 17:55:21 +00005038 xmlDtdPtr dtd;
5039 xmlAttributeTablePtr table;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005040 xmlEntitiesTablePtr entities;
Owen Taylor3473f882001-02-23 17:55:21 +00005041
5042 if (doc == NULL) return(0);
5043 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5044 return(0);
5045 ctxt->doc = doc;
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005046 ctxt->valid = 1;
Owen Taylor3473f882001-02-23 17:55:21 +00005047 dtd = doc->intSubset;
5048 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5049 table = (xmlAttributeTablePtr) dtd->attributes;
5050 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005051 }
5052 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005053 entities = (xmlEntitiesTablePtr) dtd->entities;
5054 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5055 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005056 }
5057 dtd = doc->extSubset;
5058 if ((dtd != NULL) && (dtd->attributes != NULL)) {
5059 table = (xmlAttributeTablePtr) dtd->attributes;
5060 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillard878eab02002-02-19 13:46:09 +00005061 }
5062 if ((dtd != NULL) && (dtd->entities != NULL)) {
Daniel Veillard8ab0f582002-02-18 18:31:38 +00005063 entities = (xmlEntitiesTablePtr) dtd->entities;
5064 xmlHashScan(entities, (xmlHashScanner) xmlValidateNotationCallback,
5065 ctxt);
Owen Taylor3473f882001-02-23 17:55:21 +00005066 }
5067 return(ctxt->valid);
5068}
5069
5070/**
5071 * xmlValidateDocument:
5072 * @ctxt: the validation context
5073 * @doc: a document instance
5074 *
5075 * Try to validate the document instance
5076 *
5077 * basically it does the all the checks described by the XML Rec
5078 * i.e. validates the internal and external subset (if present)
5079 * and validate the document tree.
5080 *
5081 * returns 1 if valid or 0 otherwise
5082 */
5083
5084int
5085xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
5086 int ret;
5087 xmlNodePtr root;
5088
5089 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
5090 return(0);
5091 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
5092 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
5093 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
5094 doc->intSubset->SystemID);
5095 if (doc->extSubset == NULL) {
5096 if (doc->intSubset->SystemID != NULL) {
5097 VERROR(ctxt->userData,
5098 "Could not load the external subset \"%s\"\n",
5099 doc->intSubset->SystemID);
5100 } else {
5101 VERROR(ctxt->userData,
5102 "Could not load the external subset \"%s\"\n",
5103 doc->intSubset->ExternalID);
5104 }
5105 return(0);
5106 }
5107 }
5108
5109 if (doc->ids != NULL) {
5110 xmlFreeIDTable(doc->ids);
5111 doc->ids = NULL;
5112 }
5113 if (doc->refs != NULL) {
5114 xmlFreeRefTable(doc->refs);
5115 doc->refs = NULL;
5116 }
5117 ret = xmlValidateDtdFinal(ctxt, doc);
5118 if (!xmlValidateRoot(ctxt, doc)) return(0);
5119
5120 root = xmlDocGetRootElement(doc);
5121 ret &= xmlValidateElement(ctxt, doc, root);
5122 ret &= xmlValidateDocumentFinal(ctxt, doc);
5123 return(ret);
5124}
5125
5126
5127/************************************************************************
5128 * *
5129 * Routines for dynamic validation editing *
5130 * *
5131 ************************************************************************/
5132
5133/**
5134 * xmlValidGetPotentialChildren:
5135 * @ctree: an element content tree
5136 * @list: an array to store the list of child names
5137 * @len: a pointer to the number of element in the list
5138 * @max: the size of the array
5139 *
5140 * Build/extend a list of potential children allowed by the content tree
5141 *
5142 * returns the number of element in the list, or -1 in case of error.
5143 */
5144
5145int
5146xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
5147 int *len, int max) {
5148 int i;
5149
5150 if ((ctree == NULL) || (list == NULL) || (len == NULL))
5151 return(-1);
5152 if (*len >= max) return(*len);
5153
5154 switch (ctree->type) {
5155 case XML_ELEMENT_CONTENT_PCDATA:
5156 for (i = 0; i < *len;i++)
5157 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
5158 list[(*len)++] = BAD_CAST "#PCDATA";
5159 break;
5160 case XML_ELEMENT_CONTENT_ELEMENT:
5161 for (i = 0; i < *len;i++)
5162 if (xmlStrEqual(ctree->name, list[i])) return(*len);
5163 list[(*len)++] = ctree->name;
5164 break;
5165 case XML_ELEMENT_CONTENT_SEQ:
5166 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5167 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5168 break;
5169 case XML_ELEMENT_CONTENT_OR:
5170 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
5171 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
5172 break;
5173 }
5174
5175 return(*len);
5176}
5177
5178/**
5179 * xmlValidGetValidElements:
5180 * @prev: an element to insert after
5181 * @next: an element to insert next
5182 * @list: an array to store the list of child names
5183 * @max: the size of the array
5184 *
5185 * This function returns the list of authorized children to insert
5186 * within an existing tree while respecting the validity constraints
5187 * forced by the Dtd. The insertion point is defined using @prev and
5188 * @next in the following ways:
5189 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
5190 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
5191 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
5192 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
5193 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
5194 *
5195 * pointers to the element names are inserted at the beginning of the array
5196 * and do not need to be freed.
5197 *
5198 * returns the number of element in the list, or -1 in case of error. If
5199 * the function returns the value @max the caller is invited to grow the
5200 * receiving array and retry.
5201 */
5202
5203int
5204xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
5205 int max) {
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005206 xmlValidCtxt vctxt;
Owen Taylor3473f882001-02-23 17:55:21 +00005207 int nb_valid_elements = 0;
5208 const xmlChar *elements[256];
5209 int nb_elements = 0, i;
Daniel Veillard5e5c2d02002-02-09 18:03:01 +00005210 const xmlChar *name;
Owen Taylor3473f882001-02-23 17:55:21 +00005211
5212 xmlNode *ref_node;
5213 xmlNode *parent;
5214 xmlNode *test_node;
5215
5216 xmlNode *prev_next;
5217 xmlNode *next_prev;
5218 xmlNode *parent_childs;
5219 xmlNode *parent_last;
5220
5221 xmlElement *element_desc;
5222
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005223 vctxt.userData = NULL;
5224 vctxt.error = NULL;
5225 vctxt.warning = NULL;
5226
Owen Taylor3473f882001-02-23 17:55:21 +00005227 if (prev == NULL && next == NULL)
5228 return(-1);
5229
5230 if (list == NULL) return(-1);
5231 if (max <= 0) return(-1);
5232
5233 nb_valid_elements = 0;
5234 ref_node = prev ? prev : next;
5235 parent = ref_node->parent;
5236
5237 /*
5238 * Retrieves the parent element declaration
5239 */
5240 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
5241 parent->name);
5242 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
5243 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
5244 parent->name);
5245 if (element_desc == NULL) return(-1);
5246
5247 /*
5248 * Do a backup of the current tree structure
5249 */
5250 prev_next = prev ? prev->next : NULL;
5251 next_prev = next ? next->prev : NULL;
5252 parent_childs = parent->children;
5253 parent_last = parent->last;
5254
5255 /*
5256 * Creates a dummy node and insert it into the tree
5257 */
5258 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
5259 test_node->doc = ref_node->doc;
5260 test_node->parent = parent;
5261 test_node->prev = prev;
5262 test_node->next = next;
Daniel Veillardd455d792002-02-08 13:37:46 +00005263 name = test_node->name;
Owen Taylor3473f882001-02-23 17:55:21 +00005264
5265 if (prev) prev->next = test_node;
5266 else parent->children = test_node;
5267
5268 if (next) next->prev = test_node;
5269 else parent->last = test_node;
5270
5271 /*
5272 * Insert each potential child node and check if the parent is
5273 * still valid
5274 */
5275 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
5276 elements, &nb_elements, 256);
5277
5278 for (i = 0;i < nb_elements;i++) {
5279 test_node->name = elements[i];
Daniel Veillardf69bb4b2001-05-19 13:24:56 +00005280 if (xmlValidateOneElement(&vctxt, parent->doc, parent)) {
Owen Taylor3473f882001-02-23 17:55:21 +00005281 int j;
5282
5283 for (j = 0; j < nb_valid_elements;j++)
5284 if (xmlStrEqual(elements[i], list[j])) break;
5285 list[nb_valid_elements++] = elements[i];
5286 if (nb_valid_elements >= max) break;
5287 }
5288 }
5289
5290 /*
5291 * Restore the tree structure
5292 */
5293 if (prev) prev->next = prev_next;
5294 if (next) next->prev = next_prev;
5295 parent->children = parent_childs;
5296 parent->last = parent_last;
Daniel Veillardd455d792002-02-08 13:37:46 +00005297
5298 /*
5299 * Free up the dummy node
5300 */
5301 test_node->name = name;
5302 xmlFreeNode(test_node);
5303
Owen Taylor3473f882001-02-23 17:55:21 +00005304 return(nb_valid_elements);
5305}