blob: 438c84d8ccde0c8024ee471425c0840500d86b00 [file] [log] [blame]
Daniel Veillard39a1f9a1999-01-17 19:11:59 +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 *
7 * Daniel.Veillard@w3.org
8 */
9
Daniel Veillard7f7d1111999-09-22 09:46:25 +000010#ifdef WIN32
Daniel Veillard3c558c31999-12-22 11:30:41 +000011#include "win32config.h"
Daniel Veillard7f7d1111999-09-22 09:46:25 +000012#else
13#include "config.h"
14#endif
15
Daniel Veillard39a1f9a1999-01-17 19:11:59 +000016#include <stdio.h>
Daniel Veillard39a1f9a1999-01-17 19:11:59 +000017#include <string.h>
Daniel Veillard7f7d1111999-09-22 09:46:25 +000018
19#ifdef HAVE_STDLIB_H
20#include <stdlib.h>
21#endif
22
Daniel Veillard361d8452000-04-03 19:48:13 +000023#include <libxml/xmlmemory.h>
Daniel Veillard126f2792000-10-24 17:10:12 +000024#include <libxml/hash.h>
Daniel Veillard361d8452000-04-03 19:48:13 +000025#include <libxml/valid.h>
26#include <libxml/parser.h>
27#include <libxml/parserInternals.h>
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +000028#include <libxml/xmlerror.h>
Daniel Veillardb05deb71999-08-10 19:04:08 +000029
Daniel Veillardcf461992000-03-14 18:30:20 +000030/*
31 * Generic function for accessing stacks in the Validity Context
32 */
33
34#define PUSH_AND_POP(scope, type, name) \
35scope int name##VPush(xmlValidCtxtPtr ctxt, type value) { \
36 if (ctxt->name##Nr >= ctxt->name##Max) { \
37 ctxt->name##Max *= 2; \
Daniel Veillard32bc74e2000-07-14 14:49:25 +000038 ctxt->name##Tab = (type *) xmlRealloc(ctxt->name##Tab, \
Daniel Veillardcf461992000-03-14 18:30:20 +000039 ctxt->name##Max * sizeof(ctxt->name##Tab[0])); \
40 if (ctxt->name##Tab == NULL) { \
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +000041 xmlGenericError(xmlGenericErrorContext, \
42 "realloc failed !\n"); \
Daniel Veillardcf461992000-03-14 18:30:20 +000043 return(0); \
44 } \
45 } \
46 ctxt->name##Tab[ctxt->name##Nr] = value; \
47 ctxt->name = value; \
48 return(ctxt->name##Nr++); \
49} \
50scope type name##VPop(xmlValidCtxtPtr ctxt) { \
51 type ret; \
52 if (ctxt->name##Nr <= 0) return(0); \
53 ctxt->name##Nr--; \
54 if (ctxt->name##Nr > 0) \
55 ctxt->name = ctxt->name##Tab[ctxt->name##Nr - 1]; \
56 else \
57 ctxt->name = NULL; \
58 ret = ctxt->name##Tab[ctxt->name##Nr]; \
59 ctxt->name##Tab[ctxt->name##Nr] = 0; \
60 return(ret); \
61} \
62
63PUSH_AND_POP(static, xmlNodePtr, node)
64
65/* #define DEBUG_VALID_ALGO */
66
67#ifdef DEBUG_VALID_ALGO
68void xmlValidPrintNodeList(xmlNodePtr cur) {
69 if (cur == NULL)
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +000070 xmlGenericError(xmlGenericErrorContext, "null ");
Daniel Veillardcf461992000-03-14 18:30:20 +000071 while (cur != NULL) {
72 switch (cur->type) {
73 case XML_ELEMENT_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +000074 xmlGenericError(xmlGenericErrorContext, "%s ", cur->name);
Daniel Veillardcf461992000-03-14 18:30:20 +000075 break;
76 case XML_TEXT_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +000077 xmlGenericError(xmlGenericErrorContext, "text ");
Daniel Veillardcf461992000-03-14 18:30:20 +000078 break;
79 case XML_CDATA_SECTION_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +000080 xmlGenericError(xmlGenericErrorContext, "cdata ");
Daniel Veillardcf461992000-03-14 18:30:20 +000081 break;
82 case XML_ENTITY_REF_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +000083 xmlGenericError(xmlGenericErrorContext, "&%s; ", cur->name);
Daniel Veillardcf461992000-03-14 18:30:20 +000084 break;
85 case XML_PI_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +000086 xmlGenericError(xmlGenericErrorContext, "pi(%s) ", cur->name);
Daniel Veillardcf461992000-03-14 18:30:20 +000087 break;
88 case XML_COMMENT_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +000089 xmlGenericError(xmlGenericErrorContext, "comment ");
Daniel Veillardcf461992000-03-14 18:30:20 +000090 break;
91 case XML_ATTRIBUTE_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +000092 xmlGenericError(xmlGenericErrorContext, "?attr? ");
Daniel Veillardcf461992000-03-14 18:30:20 +000093 break;
94 case XML_ENTITY_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +000095 xmlGenericError(xmlGenericErrorContext, "?ent? ");
Daniel Veillardcf461992000-03-14 18:30:20 +000096 break;
97 case XML_DOCUMENT_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +000098 xmlGenericError(xmlGenericErrorContext, "?doc? ");
Daniel Veillardcf461992000-03-14 18:30:20 +000099 break;
100 case XML_DOCUMENT_TYPE_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000101 xmlGenericError(xmlGenericErrorContext, "?doctype? ");
Daniel Veillardcf461992000-03-14 18:30:20 +0000102 break;
103 case XML_DOCUMENT_FRAG_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000104 xmlGenericError(xmlGenericErrorContext, "?frag? ");
Daniel Veillardcf461992000-03-14 18:30:20 +0000105 break;
106 case XML_NOTATION_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000107 xmlGenericError(xmlGenericErrorContext, "?nota? ");
Daniel Veillardcf461992000-03-14 18:30:20 +0000108 break;
109 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000110 xmlGenericError(xmlGenericErrorContext, "?html? ");
Daniel Veillardcf461992000-03-14 18:30:20 +0000111 break;
112 case XML_DTD_NODE:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000113 xmlGenericError(xmlGenericErrorContext, "?dtd? ");
Daniel Veillardcf461992000-03-14 18:30:20 +0000114 break;
115 case XML_ELEMENT_DECL:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000116 xmlGenericError(xmlGenericErrorContext, "?edecl? ");
Daniel Veillardcf461992000-03-14 18:30:20 +0000117 break;
118 case XML_ATTRIBUTE_DECL:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000119 xmlGenericError(xmlGenericErrorContext, "?adecl? ");
Daniel Veillardcf461992000-03-14 18:30:20 +0000120 break;
121 case XML_ENTITY_DECL:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000122 xmlGenericError(xmlGenericErrorContext, "?entdecl? ");
Daniel Veillardcf461992000-03-14 18:30:20 +0000123 break;
124 }
125 cur = cur->next;
126 }
127}
128
129void xmlValidDebug(xmlNodePtr cur, xmlElementContentPtr cont) {
130 char expr[1000];
131
132 expr[0] = 0;
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000133 xmlGenericError(xmlGenericErrorContext, "valid: ");
Daniel Veillardcf461992000-03-14 18:30:20 +0000134 xmlValidPrintNodeList(cur);
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000135 xmlGenericError(xmlGenericErrorContext, "against ");
Daniel Veillardcf461992000-03-14 18:30:20 +0000136 xmlSprintfElementContent(expr, cont, 0);
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000137 xmlGenericError(xmlGenericErrorContext, "%s\n", expr);
Daniel Veillardcf461992000-03-14 18:30:20 +0000138}
139
140#define DEBUG_VALID_STATE(n,c) xmlValidDebug(n,c);
141#else
142#define DEBUG_VALID_STATE(n,c)
143#endif
144
Daniel Veillarddbfd6411999-12-28 16:35:14 +0000145/* TODO: use hash table for accesses to elem and attribute dedinitions */
146
Daniel Veillardb05deb71999-08-10 19:04:08 +0000147#define VERROR \
148 if ((ctxt != NULL) && (ctxt->error != NULL)) ctxt->error
149
150#define VWARNING \
151 if ((ctxt != NULL) && (ctxt->warning != NULL)) ctxt->warning
152
153#define CHECK_DTD \
154 if (doc == NULL) return(0); \
Daniel Veillardcd429612000-10-11 15:57:05 +0000155 else if ((doc->intSubset == NULL) && \
156 (doc->extSubset == NULL)) return(0)
Daniel Veillardb05deb71999-08-10 19:04:08 +0000157
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000158xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name);
159xmlAttributePtr xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem);
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000160
Daniel Veillardbe803962000-06-28 23:40:59 +0000161/************************************************************************
162 * *
163 * QName handling helper *
164 * *
165 ************************************************************************/
166
167/**
168 * xmlSplitQName2:
169 * @name: an XML parser context
170 * @prefix: a xmlChar **
171 *
172 * parse an XML qualified name string
173 *
174 * [NS 5] QName ::= (Prefix ':')? LocalPart
175 *
176 * [NS 6] Prefix ::= NCName
177 *
178 * [NS 7] LocalPart ::= NCName
179 *
180 * Returns NULL if not a QName, otherwise the local part, and prefix
181 * is updated to get the Prefix if any.
182 */
183
184xmlChar *
185xmlSplitQName2(const xmlChar *name, xmlChar **prefix) {
186 int len = 0;
187 xmlChar *ret = NULL;
188
189 *prefix = NULL;
190
191 /* xml: prefix is not really a namespace */
192 if ((name[0] == 'x') && (name[1] == 'm') &&
193 (name[2] == 'l') && (name[3] == ':'))
194 return(NULL);
195
196 /* nasty but valid */
197 if (name[0] == ':')
198 return(NULL);
199
200 /*
201 * we are not trying to validate but just to cut, and yes it will
202 * work even if this is as set of UTF-8 encoded chars
203 */
204 while ((name[len] != 0) && (name[len] != ':'))
205 len++;
206
207 if (name[len] == 0)
208 return(NULL);
209
210 *prefix = xmlStrndup(name, len);
211 ret = xmlStrdup(&name[len + 1]);
212
213 return(ret);
214}
215
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000216/****************************************************************
217 * *
218 * Util functions for data allocation/deallocation *
219 * *
220 ****************************************************************/
221
222/**
223 * xmlNewElementContent:
224 * @name: the subelement name or NULL
225 * @type: the type of element content decl
226 *
227 * Allocate an element content structure.
228 *
Daniel Veillard1e346af1999-02-22 10:33:01 +0000229 * Returns NULL if not, othervise the new element content structure
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000230 */
231xmlElementContentPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000232xmlNewElementContent(xmlChar *name, xmlElementContentType type) {
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000233 xmlElementContentPtr ret;
234
235 switch(type) {
236 case XML_ELEMENT_CONTENT_ELEMENT:
237 if (name == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000238 xmlGenericError(xmlGenericErrorContext,
239 "xmlNewElementContent : name == NULL !\n");
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000240 }
241 break;
242 case XML_ELEMENT_CONTENT_PCDATA:
243 case XML_ELEMENT_CONTENT_SEQ:
244 case XML_ELEMENT_CONTENT_OR:
245 if (name != NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000246 xmlGenericError(xmlGenericErrorContext,
247 "xmlNewElementContent : name != NULL !\n");
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000248 }
249 break;
250 default:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000251 xmlGenericError(xmlGenericErrorContext,
252 "xmlNewElementContent: unknown type %d\n", type);
Daniel Veillard0142b842000-01-14 14:45:24 +0000253 return(NULL);
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000254 }
Daniel Veillard6454aec1999-09-02 22:04:43 +0000255 ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent));
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000256 if (ret == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000257 xmlGenericError(xmlGenericErrorContext,
258 "xmlNewElementContent : out of memory!\n");
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000259 return(NULL);
260 }
261 ret->type = type;
262 ret->ocur = XML_ELEMENT_CONTENT_ONCE;
Daniel Veillard3b9def11999-01-31 22:15:06 +0000263 if (name != NULL)
264 ret->name = xmlStrdup(name);
265 else
266 ret->name = NULL;
Daniel Veillard1e346af1999-02-22 10:33:01 +0000267 ret->c1 = ret->c2 = NULL;
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000268 return(ret);
269}
270
271/**
Daniel Veillard3b9def11999-01-31 22:15:06 +0000272 * xmlCopyElementContent:
273 * @content: An element content pointer.
274 *
275 * Build a copy of an element content description.
276 *
Daniel Veillard1e346af1999-02-22 10:33:01 +0000277 * Returns the new xmlElementContentPtr or NULL in case of error.
Daniel Veillard3b9def11999-01-31 22:15:06 +0000278 */
279xmlElementContentPtr
Daniel Veillard1e346af1999-02-22 10:33:01 +0000280xmlCopyElementContent(xmlElementContentPtr cur) {
281 xmlElementContentPtr ret;
282
283 if (cur == NULL) return(NULL);
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000284 ret = xmlNewElementContent((xmlChar *) cur->name, cur->type);
Daniel Veillard14fff061999-06-22 21:49:07 +0000285 if (ret == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000286 xmlGenericError(xmlGenericErrorContext,
287 "xmlCopyElementContent : out of memory\n");
Daniel Veillard14fff061999-06-22 21:49:07 +0000288 return(NULL);
289 }
290 ret->ocur = cur->ocur;
291 if (cur->c1 != NULL) ret->c1 = xmlCopyElementContent(cur->c1);
292 if (cur->c2 != NULL) ret->c2 = xmlCopyElementContent(cur->c2);
Daniel Veillard1e346af1999-02-22 10:33:01 +0000293 return(ret);
Daniel Veillard3b9def11999-01-31 22:15:06 +0000294}
295
296/**
Daniel Veillard1899e851999-02-01 12:18:54 +0000297 * xmlFreeElementContent:
298 * @cur: the element content tree to free
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000299 *
300 * Free an element content structure. This is a recursive call !
301 */
302void
303xmlFreeElementContent(xmlElementContentPtr cur) {
Daniel Veillard1e346af1999-02-22 10:33:01 +0000304 if (cur == NULL) return;
Daniel Veillard87b95392000-08-12 21:12:04 +0000305 switch (cur->type) {
306 case XML_ELEMENT_CONTENT_PCDATA:
307 case XML_ELEMENT_CONTENT_ELEMENT:
308 case XML_ELEMENT_CONTENT_SEQ:
309 case XML_ELEMENT_CONTENT_OR:
310 break;
311 default:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000312 xmlGenericError(xmlGenericErrorContext,
313 "xmlFreeElementContent : type %d\n", cur->type);
Daniel Veillard87b95392000-08-12 21:12:04 +0000314 return;
315 }
Daniel Veillard1e346af1999-02-22 10:33:01 +0000316 if (cur->c1 != NULL) xmlFreeElementContent(cur->c1);
317 if (cur->c2 != NULL) xmlFreeElementContent(cur->c2);
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000318 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillard1e346af1999-02-22 10:33:01 +0000319 memset(cur, -1, sizeof(xmlElementContent));
Daniel Veillard6454aec1999-09-02 22:04:43 +0000320 xmlFree(cur);
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000321}
322
Daniel Veillard1899e851999-02-01 12:18:54 +0000323/**
324 * xmlDumpElementContent:
Daniel Veillard5099ae81999-04-21 20:12:07 +0000325 * @buf: An XML buffer
Daniel Veillard1899e851999-02-01 12:18:54 +0000326 * @content: An element table
327 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
328 *
329 * This will dump the content of the element table as an XML DTD definition
Daniel Veillard1899e851999-02-01 12:18:54 +0000330 */
331void
Daniel Veillard5099ae81999-04-21 20:12:07 +0000332xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content, int glob) {
Daniel Veillard1899e851999-02-01 12:18:54 +0000333 if (content == NULL) return;
334
Daniel Veillard5099ae81999-04-21 20:12:07 +0000335 if (glob) xmlBufferWriteChar(buf, "(");
Daniel Veillard1899e851999-02-01 12:18:54 +0000336 switch (content->type) {
337 case XML_ELEMENT_CONTENT_PCDATA:
Daniel Veillard5099ae81999-04-21 20:12:07 +0000338 xmlBufferWriteChar(buf, "#PCDATA");
Daniel Veillard1899e851999-02-01 12:18:54 +0000339 break;
340 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillard5099ae81999-04-21 20:12:07 +0000341 xmlBufferWriteCHAR(buf, content->name);
Daniel Veillard1899e851999-02-01 12:18:54 +0000342 break;
343 case XML_ELEMENT_CONTENT_SEQ:
344 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
345 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillard5099ae81999-04-21 20:12:07 +0000346 xmlDumpElementContent(buf, content->c1, 1);
Daniel Veillard1899e851999-02-01 12:18:54 +0000347 else
Daniel Veillard5099ae81999-04-21 20:12:07 +0000348 xmlDumpElementContent(buf, content->c1, 0);
349 xmlBufferWriteChar(buf, " , ");
Daniel Veillard1899e851999-02-01 12:18:54 +0000350 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
Daniel Veillard5099ae81999-04-21 20:12:07 +0000351 xmlDumpElementContent(buf, content->c2, 1);
Daniel Veillard1899e851999-02-01 12:18:54 +0000352 else
Daniel Veillard5099ae81999-04-21 20:12:07 +0000353 xmlDumpElementContent(buf, content->c2, 0);
Daniel Veillard1899e851999-02-01 12:18:54 +0000354 break;
355 case XML_ELEMENT_CONTENT_OR:
356 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
357 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
Daniel Veillard5099ae81999-04-21 20:12:07 +0000358 xmlDumpElementContent(buf, content->c1, 1);
Daniel Veillard1899e851999-02-01 12:18:54 +0000359 else
Daniel Veillard5099ae81999-04-21 20:12:07 +0000360 xmlDumpElementContent(buf, content->c1, 0);
361 xmlBufferWriteChar(buf, " | ");
Daniel Veillard1899e851999-02-01 12:18:54 +0000362 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
Daniel Veillard5099ae81999-04-21 20:12:07 +0000363 xmlDumpElementContent(buf, content->c2, 1);
Daniel Veillard1899e851999-02-01 12:18:54 +0000364 else
Daniel Veillard5099ae81999-04-21 20:12:07 +0000365 xmlDumpElementContent(buf, content->c2, 0);
Daniel Veillard1899e851999-02-01 12:18:54 +0000366 break;
367 default:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000368 xmlGenericError(xmlGenericErrorContext,
369 "xmlDumpElementContent: unknown type %d\n",
Daniel Veillard1899e851999-02-01 12:18:54 +0000370 content->type);
371 }
372 if (glob)
Daniel Veillard5099ae81999-04-21 20:12:07 +0000373 xmlBufferWriteChar(buf, ")");
Daniel Veillard1899e851999-02-01 12:18:54 +0000374 switch (content->ocur) {
375 case XML_ELEMENT_CONTENT_ONCE:
376 break;
377 case XML_ELEMENT_CONTENT_OPT:
Daniel Veillard5099ae81999-04-21 20:12:07 +0000378 xmlBufferWriteChar(buf, "?");
Daniel Veillard1899e851999-02-01 12:18:54 +0000379 break;
380 case XML_ELEMENT_CONTENT_MULT:
Daniel Veillard5099ae81999-04-21 20:12:07 +0000381 xmlBufferWriteChar(buf, "*");
Daniel Veillard1899e851999-02-01 12:18:54 +0000382 break;
383 case XML_ELEMENT_CONTENT_PLUS:
Daniel Veillard5099ae81999-04-21 20:12:07 +0000384 xmlBufferWriteChar(buf, "+");
Daniel Veillard1899e851999-02-01 12:18:54 +0000385 break;
386 }
387}
388
Daniel Veillardb05deb71999-08-10 19:04:08 +0000389/**
390 * xmlSprintfElementContent:
391 * @buf: an output buffer
392 * @content: An element table
393 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
394 *
395 * This will dump the content of the element content definition
396 * Intended just for the debug routine
397 */
398void
399xmlSprintfElementContent(char *buf, xmlElementContentPtr content, int glob) {
400 if (content == NULL) return;
401 if (glob) strcat(buf, "(");
402 switch (content->type) {
403 case XML_ELEMENT_CONTENT_PCDATA:
404 strcat(buf, "#PCDATA");
405 break;
406 case XML_ELEMENT_CONTENT_ELEMENT:
Daniel Veillardb96e6431999-08-29 21:02:19 +0000407 strcat(buf, (char *) content->name);
Daniel Veillardb05deb71999-08-10 19:04:08 +0000408 break;
409 case XML_ELEMENT_CONTENT_SEQ:
410 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
411 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
412 xmlSprintfElementContent(buf, content->c1, 1);
413 else
414 xmlSprintfElementContent(buf, content->c1, 0);
415 strcat(buf, " , ");
416 if (content->c2->type == XML_ELEMENT_CONTENT_OR)
417 xmlSprintfElementContent(buf, content->c2, 1);
418 else
419 xmlSprintfElementContent(buf, content->c2, 0);
420 break;
421 case XML_ELEMENT_CONTENT_OR:
422 if ((content->c1->type == XML_ELEMENT_CONTENT_OR) ||
423 (content->c1->type == XML_ELEMENT_CONTENT_SEQ))
424 xmlSprintfElementContent(buf, content->c1, 1);
425 else
426 xmlSprintfElementContent(buf, content->c1, 0);
427 strcat(buf, " | ");
428 if (content->c2->type == XML_ELEMENT_CONTENT_SEQ)
429 xmlSprintfElementContent(buf, content->c2, 1);
430 else
431 xmlSprintfElementContent(buf, content->c2, 0);
432 break;
433 }
434 if (glob)
435 strcat(buf, ")");
436 switch (content->ocur) {
437 case XML_ELEMENT_CONTENT_ONCE:
438 break;
439 case XML_ELEMENT_CONTENT_OPT:
440 strcat(buf, "?");
441 break;
442 case XML_ELEMENT_CONTENT_MULT:
443 strcat(buf, "*");
444 break;
445 case XML_ELEMENT_CONTENT_PLUS:
446 strcat(buf, "+");
447 break;
448 }
449}
450
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000451/****************************************************************
452 * *
453 * Registration of DTD declarations *
454 * *
455 ****************************************************************/
456
Daniel Veillard3b9def11999-01-31 22:15:06 +0000457/**
458 * xmlCreateElementTable:
459 *
460 * create and initialize an empty element hash table.
461 *
Daniel Veillard1e346af1999-02-22 10:33:01 +0000462 * Returns the xmlElementTablePtr just created or NULL in case of error.
Daniel Veillard3b9def11999-01-31 22:15:06 +0000463 */
464xmlElementTablePtr
465xmlCreateElementTable(void) {
Daniel Veillard126f2792000-10-24 17:10:12 +0000466 return(xmlHashCreate(0));
467}
Daniel Veillard3b9def11999-01-31 22:15:06 +0000468
Daniel Veillard126f2792000-10-24 17:10:12 +0000469/**
470 * xmlFreeElement:
471 * @elem: An element
472 *
473 * Deallocate the memory used by an element definition
474 */
475void
476xmlFreeElement(xmlElementPtr elem) {
477 if (elem == NULL) return;
478 xmlUnlinkNode((xmlNodePtr) elem);
479 xmlFreeElementContent(elem->content);
480 if (elem->name != NULL)
481 xmlFree((xmlChar *) elem->name);
482 if (elem->prefix != NULL)
483 xmlFree((xmlChar *) elem->prefix);
484 memset(elem, -1, sizeof(xmlElement));
485 xmlFree(elem);
Daniel Veillard3b9def11999-01-31 22:15:06 +0000486}
487
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000488
489/**
490 * xmlAddElementDecl:
Daniel Veillard00fdf371999-10-08 09:40:39 +0000491 * @ctxt: the validation context
Daniel Veillard1e346af1999-02-22 10:33:01 +0000492 * @dtd: pointer to the DTD
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000493 * @name: the entity name
Daniel Veillard1e346af1999-02-22 10:33:01 +0000494 * @type: the element type
495 * @content: the element content tree or NULL
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000496 *
497 * Register a new element declaration
498 *
Daniel Veillard1e346af1999-02-22 10:33:01 +0000499 * Returns NULL if not, othervise the entity
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000500 */
501xmlElementPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000502xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
Daniel Veillardcf461992000-03-14 18:30:20 +0000503 xmlElementTypeVal type,
504 xmlElementContentPtr content) {
Daniel Veillard126f2792000-10-24 17:10:12 +0000505 xmlElementPtr ret;
Daniel Veillard3b9def11999-01-31 22:15:06 +0000506 xmlElementTablePtr table;
Daniel Veillardbe803962000-06-28 23:40:59 +0000507 xmlChar *ns, *uqname;
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000508
509 if (dtd == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000510 xmlGenericError(xmlGenericErrorContext,
511 "xmlAddElementDecl: dtd == NULL\n");
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000512 return(NULL);
513 }
514 if (name == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000515 xmlGenericError(xmlGenericErrorContext,
516 "xmlAddElementDecl: name == NULL\n");
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000517 return(NULL);
518 }
519 switch (type) {
520 case XML_ELEMENT_TYPE_EMPTY:
521 if (content != NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000522 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000523 "xmlAddElementDecl: content != NULL for EMPTY\n");
524 return(NULL);
525 }
526 break;
527 case XML_ELEMENT_TYPE_ANY:
528 if (content != NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000529 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000530 "xmlAddElementDecl: content != NULL for ANY\n");
531 return(NULL);
532 }
533 break;
534 case XML_ELEMENT_TYPE_MIXED:
535 if (content == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000536 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000537 "xmlAddElementDecl: content == NULL for MIXED\n");
538 return(NULL);
539 }
540 break;
541 case XML_ELEMENT_TYPE_ELEMENT:
542 if (content == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000543 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000544 "xmlAddElementDecl: content == NULL for ELEMENT\n");
545 return(NULL);
546 }
547 break;
548 default:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000549 xmlGenericError(xmlGenericErrorContext,
550 "xmlAddElementDecl: unknown type %d\n", type);
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000551 return(NULL);
552 }
553
554 /*
Daniel Veillardbe803962000-06-28 23:40:59 +0000555 * check if name is a QName
556 */
557 uqname = xmlSplitQName2(name, &ns);
558 if (uqname != NULL)
559 name = uqname;
560
561 /*
Daniel Veillard3b9def11999-01-31 22:15:06 +0000562 * Create the Element table if needed.
563 */
Daniel Veillard32bc74e2000-07-14 14:49:25 +0000564 table = (xmlElementTablePtr) dtd->elements;
565 if (table == NULL) {
566 table = xmlCreateElementTable();
567 dtd->elements = (void *) table;
568 }
Daniel Veillard3b9def11999-01-31 22:15:06 +0000569 if (table == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000570 xmlGenericError(xmlGenericErrorContext,
571 "xmlAddElementDecl: Table creation failed!\n");
Daniel Veillard3b9def11999-01-31 22:15:06 +0000572 return(NULL);
573 }
574
Daniel Veillard6454aec1999-09-02 22:04:43 +0000575 ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
Daniel Veillardb05deb71999-08-10 19:04:08 +0000576 if (ret == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000577 xmlGenericError(xmlGenericErrorContext,
578 "xmlAddElementDecl: out of memory\n");
Daniel Veillardb05deb71999-08-10 19:04:08 +0000579 return(NULL);
580 }
Daniel Veillardcf461992000-03-14 18:30:20 +0000581 memset(ret, 0, sizeof(xmlElement));
582 ret->type = XML_ELEMENT_DECL;
Daniel Veillard3b9def11999-01-31 22:15:06 +0000583
584 /*
585 * fill the structure.
586 */
Daniel Veillardcf461992000-03-14 18:30:20 +0000587 ret->etype = type;
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000588 ret->name = xmlStrdup(name);
Daniel Veillardbe803962000-06-28 23:40:59 +0000589 ret->prefix = ns;
Daniel Veillard14fff061999-06-22 21:49:07 +0000590 ret->content = xmlCopyElementContent(content);
Daniel Veillardb05deb71999-08-10 19:04:08 +0000591 ret->attributes = xmlScanAttributeDecl(dtd, name);
Daniel Veillard126f2792000-10-24 17:10:12 +0000592
593 /*
594 * Validity Check:
595 * Insertion must not fail
596 */
597 if (xmlHashAddEntry2(table, name, ns, ret)) {
598 /*
599 * The element is already defined in this Dtd.
600 */
601 VERROR(ctxt->userData, "Redefinition of element %s\n", name);
602 xmlFreeElement(ret);
603 if (uqname != NULL)
604 xmlFree(uqname);
605 return(NULL);
606 }
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000607
Daniel Veillardcf461992000-03-14 18:30:20 +0000608 /*
609 * Link it to the Dtd
610 */
611 ret->parent = dtd;
612 ret->doc = dtd->doc;
613 if (dtd->last == NULL) {
614 dtd->children = dtd->last = (xmlNodePtr) ret;
615 } else {
616 dtd->last->next = (xmlNodePtr) ret;
617 ret->prev = dtd->last;
618 dtd->last = (xmlNodePtr) ret;
619 }
Daniel Veillardbe803962000-06-28 23:40:59 +0000620 if (uqname != NULL)
621 xmlFree(uqname);
Daniel Veillard39a1f9a1999-01-17 19:11:59 +0000622 return(ret);
623}
624
Daniel Veillard3b9def11999-01-31 22:15:06 +0000625/**
Daniel Veillard3b9def11999-01-31 22:15:06 +0000626 * xmlFreeElementTable:
627 * @table: An element table
628 *
Daniel Veillard1e346af1999-02-22 10:33:01 +0000629 * Deallocate the memory used by an element hash table.
Daniel Veillard3b9def11999-01-31 22:15:06 +0000630 */
631void
632xmlFreeElementTable(xmlElementTablePtr table) {
Daniel Veillard126f2792000-10-24 17:10:12 +0000633 xmlHashFree(table, (xmlHashDeallocator) xmlFreeElement);
634}
Daniel Veillard3b9def11999-01-31 22:15:06 +0000635
Daniel Veillard126f2792000-10-24 17:10:12 +0000636/**
637 * xmlCopyElement:
638 * @elem: An element
639 *
640 * Build a copy of an element.
641 *
642 * Returns the new xmlElementPtr or NULL in case of error.
643 */
644xmlElementPtr
645xmlCopyElement(xmlElementPtr elem) {
646 xmlElementPtr cur;
Daniel Veillard3b9def11999-01-31 22:15:06 +0000647
Daniel Veillard126f2792000-10-24 17:10:12 +0000648 cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement));
649 if (cur == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000650 xmlGenericError(xmlGenericErrorContext,
651 "xmlCopyElement: out of memory !\n");
Daniel Veillard126f2792000-10-24 17:10:12 +0000652 return(NULL);
Daniel Veillard3b9def11999-01-31 22:15:06 +0000653 }
Daniel Veillard126f2792000-10-24 17:10:12 +0000654 memset(cur, 0, sizeof(xmlElement));
655 cur->type = XML_ELEMENT_DECL;
656 cur->etype = elem->etype;
657 if (elem->name != NULL)
658 cur->name = xmlStrdup(elem->name);
659 else
660 cur->name = NULL;
661 if (elem->prefix != NULL)
662 cur->prefix = xmlStrdup(elem->prefix);
663 else
664 cur->prefix = NULL;
665 cur->content = xmlCopyElementContent(elem->content);
666 /* TODO : rebuild the attribute list on the copy */
667 cur->attributes = NULL;
668 return(cur);
Daniel Veillard3b9def11999-01-31 22:15:06 +0000669}
670
671/**
672 * xmlCopyElementTable:
673 * @table: An element table
674 *
675 * Build a copy of an element table.
676 *
Daniel Veillard1e346af1999-02-22 10:33:01 +0000677 * Returns the new xmlElementTablePtr or NULL in case of error.
Daniel Veillard3b9def11999-01-31 22:15:06 +0000678 */
679xmlElementTablePtr
680xmlCopyElementTable(xmlElementTablePtr table) {
Daniel Veillard126f2792000-10-24 17:10:12 +0000681 return((xmlElementTablePtr) xmlHashCopy(table,
682 (xmlHashCopier) xmlCopyElement));
Daniel Veillard3b9def11999-01-31 22:15:06 +0000683}
684
685/**
Daniel Veillardcf461992000-03-14 18:30:20 +0000686 * xmlDumpElementDecl:
687 * @buf: the XML buffer output
688 * @elem: An element table
689 *
690 * This will dump the content of the element declaration as an XML
691 * DTD definition
692 */
693void
694xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) {
695 switch (elem->etype) {
696 case XML_ELEMENT_TYPE_EMPTY:
697 xmlBufferWriteChar(buf, "<!ELEMENT ");
698 xmlBufferWriteCHAR(buf, elem->name);
699 xmlBufferWriteChar(buf, " EMPTY>\n");
700 break;
701 case XML_ELEMENT_TYPE_ANY:
702 xmlBufferWriteChar(buf, "<!ELEMENT ");
703 xmlBufferWriteCHAR(buf, elem->name);
704 xmlBufferWriteChar(buf, " ANY>\n");
705 break;
706 case XML_ELEMENT_TYPE_MIXED:
707 xmlBufferWriteChar(buf, "<!ELEMENT ");
708 xmlBufferWriteCHAR(buf, elem->name);
709 xmlBufferWriteChar(buf, " ");
710 xmlDumpElementContent(buf, elem->content, 1);
711 xmlBufferWriteChar(buf, ">\n");
712 break;
713 case XML_ELEMENT_TYPE_ELEMENT:
714 xmlBufferWriteChar(buf, "<!ELEMENT ");
715 xmlBufferWriteCHAR(buf, elem->name);
716 xmlBufferWriteChar(buf, " ");
717 xmlDumpElementContent(buf, elem->content, 1);
718 xmlBufferWriteChar(buf, ">\n");
719 break;
720 default:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000721 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcf461992000-03-14 18:30:20 +0000722 "xmlDumpElementDecl: internal: unknown type %d\n",
723 elem->etype);
724 }
725}
726
727/**
Daniel Veillard3b9def11999-01-31 22:15:06 +0000728 * xmlDumpElementTable:
Daniel Veillard011b63c1999-06-02 17:44:04 +0000729 * @buf: the XML buffer output
Daniel Veillard3b9def11999-01-31 22:15:06 +0000730 * @table: An element table
731 *
732 * This will dump the content of the element table as an XML DTD definition
Daniel Veillard3b9def11999-01-31 22:15:06 +0000733 */
734void
Daniel Veillard5099ae81999-04-21 20:12:07 +0000735xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) {
Daniel Veillard126f2792000-10-24 17:10:12 +0000736 xmlHashScan(table, (xmlHashScanner) xmlDumpElementDecl, buf);
Daniel Veillard3b9def11999-01-31 22:15:06 +0000737}
Daniel Veillard1e346af1999-02-22 10:33:01 +0000738
739/**
740 * xmlCreateEnumeration:
741 * @name: the enumeration name or NULL
742 *
743 * create and initialize an enumeration attribute node.
744 *
745 * Returns the xmlEnumerationPtr just created or NULL in case
746 * of error.
747 */
748xmlEnumerationPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000749xmlCreateEnumeration(xmlChar *name) {
Daniel Veillard1e346af1999-02-22 10:33:01 +0000750 xmlEnumerationPtr ret;
751
Daniel Veillard6454aec1999-09-02 22:04:43 +0000752 ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration));
Daniel Veillard1e346af1999-02-22 10:33:01 +0000753 if (ret == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000754 xmlGenericError(xmlGenericErrorContext,
755 "xmlCreateEnumeration : xmlMalloc(%ld) failed\n",
Daniel Veillardbe70ff71999-07-05 16:50:46 +0000756 (long)sizeof(xmlEnumeration));
Daniel Veillard1e346af1999-02-22 10:33:01 +0000757 return(NULL);
758 }
Daniel Veillardcf461992000-03-14 18:30:20 +0000759 memset(ret, 0, sizeof(xmlEnumeration));
Daniel Veillard1e346af1999-02-22 10:33:01 +0000760
761 if (name != NULL)
762 ret->name = xmlStrdup(name);
Daniel Veillard1e346af1999-02-22 10:33:01 +0000763 return(ret);
764}
765
766/**
767 * xmlFreeEnumeration:
768 * @cur: the tree to free.
769 *
770 * free an enumeration attribute node (recursive).
771 */
772void
773xmlFreeEnumeration(xmlEnumerationPtr cur) {
774 if (cur == NULL) return;
775
776 if (cur->next != NULL) xmlFreeEnumeration(cur->next);
777
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000778 if (cur->name != NULL) xmlFree((xmlChar *) cur->name);
Daniel Veillard1e346af1999-02-22 10:33:01 +0000779 memset(cur, -1, sizeof(xmlEnumeration));
Daniel Veillard6454aec1999-09-02 22:04:43 +0000780 xmlFree(cur);
Daniel Veillard1e346af1999-02-22 10:33:01 +0000781}
782
783/**
784 * xmlCopyEnumeration:
785 * @cur: the tree to copy.
786 *
787 * Copy an enumeration attribute node (recursive).
788 *
789 * Returns the xmlEnumerationPtr just created or NULL in case
790 * of error.
791 */
792xmlEnumerationPtr
793xmlCopyEnumeration(xmlEnumerationPtr cur) {
794 xmlEnumerationPtr ret;
795
796 if (cur == NULL) return(NULL);
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000797 ret = xmlCreateEnumeration((xmlChar *) cur->name);
Daniel Veillard1e346af1999-02-22 10:33:01 +0000798
799 if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next);
800 else ret->next = NULL;
801
802 return(ret);
803}
804
805/**
Daniel Veillardb05deb71999-08-10 19:04:08 +0000806 * xmlDumpEnumeration:
807 * @buf: the XML buffer output
808 * @enum: An enumeration
809 *
810 * This will dump the content of the enumeration
811 */
812void
813xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) {
814 if (cur == NULL) return;
815
816 xmlBufferWriteCHAR(buf, cur->name);
817 if (cur->next == NULL)
818 xmlBufferWriteChar(buf, ")");
819 else {
820 xmlBufferWriteChar(buf, " | ");
821 xmlDumpEnumeration(buf, cur->next);
822 }
823}
824
825/**
Daniel Veillard1e346af1999-02-22 10:33:01 +0000826 * xmlCreateAttributeTable:
827 *
828 * create and initialize an empty attribute hash table.
829 *
830 * Returns the xmlAttributeTablePtr just created or NULL in case
831 * of error.
832 */
833xmlAttributeTablePtr
834xmlCreateAttributeTable(void) {
Daniel Veillard126f2792000-10-24 17:10:12 +0000835 return(xmlHashCreate(0));
836}
Daniel Veillard1e346af1999-02-22 10:33:01 +0000837
Daniel Veillard126f2792000-10-24 17:10:12 +0000838/**
839 * xmlScanAttributeDeclCallback:
840 * @attr: the attribute decl
841 * @list: the list to update
842 *
843 * Callback called by xmlScanAttributeDecl when a new attribute
844 * has to be entered in the list.
845 */
846void
847xmlScanAttributeDeclCallback(xmlAttributePtr attr, xmlAttributePtr *list,
848 const xmlChar* name) {
849 attr->nexth = *list;
850 *list = attr;
Daniel Veillard1e346af1999-02-22 10:33:01 +0000851}
852
Daniel Veillardb05deb71999-08-10 19:04:08 +0000853/**
854 * xmlScanAttributeDecl:
855 * @dtd: pointer to the DTD
856 * @elem: the element name
857 *
858 * When inserting a new element scan the DtD for existing attributes
859 * for taht element and initialize the Attribute chain
860 *
861 * Returns the pointer to the first attribute decl in the chain,
862 * possibly NULL.
863 */
864xmlAttributePtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000865xmlScanAttributeDecl(xmlDtdPtr dtd, const xmlChar *elem) {
Daniel Veillardb05deb71999-08-10 19:04:08 +0000866 xmlAttributePtr ret = NULL;
867 xmlAttributeTablePtr table;
Daniel Veillardb05deb71999-08-10 19:04:08 +0000868
869 if (dtd == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000870 xmlGenericError(xmlGenericErrorContext,
871 "xmlScanAttributeDecl: dtd == NULL\n");
Daniel Veillardb05deb71999-08-10 19:04:08 +0000872 return(NULL);
873 }
874 if (elem == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000875 xmlGenericError(xmlGenericErrorContext,
876 "xmlScanAttributeDecl: elem == NULL\n");
Daniel Veillardb05deb71999-08-10 19:04:08 +0000877 return(NULL);
878 }
Daniel Veillard32bc74e2000-07-14 14:49:25 +0000879 table = (xmlAttributeTablePtr) dtd->attributes;
Daniel Veillardb05deb71999-08-10 19:04:08 +0000880 if (table == NULL)
881 return(NULL);
882
Daniel Veillard126f2792000-10-24 17:10:12 +0000883 /* WRONG !!! */
884 xmlHashScan3(table, NULL, NULL, elem,
885 (xmlHashScanner) xmlScanAttributeDeclCallback, &ret);
Daniel Veillardb05deb71999-08-10 19:04:08 +0000886 return(ret);
887}
888
889/**
890 * xmlScanIDAttributeDecl:
891 * @ctxt: the validation context
892 * @elem: the element name
893 *
Daniel Veillard71b656e2000-01-05 14:46:17 +0000894 * Verify that the element don't have too many ID attributes
Daniel Veillardb05deb71999-08-10 19:04:08 +0000895 * declared.
896 *
897 * Returns the number of ID attributes found.
898 */
899int
900xmlScanIDAttributeDecl(xmlValidCtxtPtr ctxt, xmlElementPtr elem) {
901 xmlAttributePtr cur;
902 int ret = 0;
903
904 if (elem == NULL) return(0);
905 cur = elem->attributes;
906 while (cur != NULL) {
Daniel Veillardcf461992000-03-14 18:30:20 +0000907 if (cur->atype == XML_ATTRIBUTE_ID) {
Daniel Veillardb05deb71999-08-10 19:04:08 +0000908 ret ++;
909 if (ret > 1)
910 VERROR(ctxt->userData,
911 "Element %s has too may ID attributes defined : %s\n",
912 elem->name, cur->name);
913 }
Daniel Veillardcf461992000-03-14 18:30:20 +0000914 cur = cur->nexth;
Daniel Veillardb05deb71999-08-10 19:04:08 +0000915 }
916 return(ret);
917}
918
Daniel Veillard126f2792000-10-24 17:10:12 +0000919/**
920 * xmlFreeAttribute:
921 * @elem: An attribute
922 *
923 * Deallocate the memory used by an attribute definition
924 */
925void
926xmlFreeAttribute(xmlAttributePtr attr) {
927 if (attr == NULL) return;
928 xmlUnlinkNode((xmlNodePtr) attr);
929 if (attr->tree != NULL)
930 xmlFreeEnumeration(attr->tree);
931 if (attr->elem != NULL)
932 xmlFree((xmlChar *) attr->elem);
933 if (attr->name != NULL)
934 xmlFree((xmlChar *) attr->name);
935 if (attr->defaultValue != NULL)
936 xmlFree((xmlChar *) attr->defaultValue);
937 if (attr->prefix != NULL)
938 xmlFree((xmlChar *) attr->prefix);
939 memset(attr, -1, sizeof(xmlAttribute));
940 xmlFree(attr);
941}
942
Daniel Veillard1e346af1999-02-22 10:33:01 +0000943
944/**
945 * xmlAddAttributeDecl:
Daniel Veillardb05deb71999-08-10 19:04:08 +0000946 * @ctxt: the validation context
Daniel Veillard1e346af1999-02-22 10:33:01 +0000947 * @dtd: pointer to the DTD
948 * @elem: the element name
949 * @name: the attribute name
Daniel Veillardcf461992000-03-14 18:30:20 +0000950 * @ns: the attribute namespace prefix
Daniel Veillard1e346af1999-02-22 10:33:01 +0000951 * @type: the attribute type
952 * @def: the attribute default type
953 * @defaultValue: the attribute default value
954 * @tree: if it's an enumeration, the associated list
955 *
956 * Register a new attribute declaration
Daniel Veillardf0cc7cc2000-08-26 21:40:43 +0000957 * Note that @tree becomes the ownership of the DTD
Daniel Veillard1e346af1999-02-22 10:33:01 +0000958 *
Daniel Veillardbe803962000-06-28 23:40:59 +0000959 * Returns NULL if not new, othervise the attribute decl
Daniel Veillard1e346af1999-02-22 10:33:01 +0000960 */
961xmlAttributePtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +0000962xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *elem,
Daniel Veillardcf461992000-03-14 18:30:20 +0000963 const xmlChar *name, const xmlChar *ns,
964 xmlAttributeType type, xmlAttributeDefault def,
965 const xmlChar *defaultValue, xmlEnumerationPtr tree) {
Daniel Veillard126f2792000-10-24 17:10:12 +0000966 xmlAttributePtr ret;
Daniel Veillard1e346af1999-02-22 10:33:01 +0000967 xmlAttributeTablePtr table;
Daniel Veillardb05deb71999-08-10 19:04:08 +0000968 xmlElementPtr elemDef;
Daniel Veillard1e346af1999-02-22 10:33:01 +0000969
970 if (dtd == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000971 xmlGenericError(xmlGenericErrorContext,
972 "xmlAddAttributeDecl: dtd == NULL\n");
Daniel Veillardf0cc7cc2000-08-26 21:40:43 +0000973 xmlFreeEnumeration(tree);
Daniel Veillard1e346af1999-02-22 10:33:01 +0000974 return(NULL);
975 }
976 if (name == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000977 xmlGenericError(xmlGenericErrorContext,
978 "xmlAddAttributeDecl: name == NULL\n");
Daniel Veillardf0cc7cc2000-08-26 21:40:43 +0000979 xmlFreeEnumeration(tree);
Daniel Veillard1e346af1999-02-22 10:33:01 +0000980 return(NULL);
981 }
982 if (elem == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +0000983 xmlGenericError(xmlGenericErrorContext,
984 "xmlAddAttributeDecl: elem == NULL\n");
Daniel Veillardf0cc7cc2000-08-26 21:40:43 +0000985 xmlFreeEnumeration(tree);
Daniel Veillard1e346af1999-02-22 10:33:01 +0000986 return(NULL);
987 }
Daniel Veillardb96e6431999-08-29 21:02:19 +0000988 /*
989 * Check the type and possibly the default value.
990 */
Daniel Veillard1e346af1999-02-22 10:33:01 +0000991 switch (type) {
992 case XML_ATTRIBUTE_CDATA:
993 break;
994 case XML_ATTRIBUTE_ID:
995 break;
996 case XML_ATTRIBUTE_IDREF:
997 break;
998 case XML_ATTRIBUTE_IDREFS:
999 break;
1000 case XML_ATTRIBUTE_ENTITY:
1001 break;
1002 case XML_ATTRIBUTE_ENTITIES:
1003 break;
1004 case XML_ATTRIBUTE_NMTOKEN:
1005 break;
1006 case XML_ATTRIBUTE_NMTOKENS:
1007 break;
1008 case XML_ATTRIBUTE_ENUMERATION:
1009 break;
1010 case XML_ATTRIBUTE_NOTATION:
1011 break;
1012 default:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001013 xmlGenericError(xmlGenericErrorContext,
1014 "xmlAddAttributeDecl: unknown type %d\n", type);
Daniel Veillardf0cc7cc2000-08-26 21:40:43 +00001015 xmlFreeEnumeration(tree);
Daniel Veillard1e346af1999-02-22 10:33:01 +00001016 return(NULL);
1017 }
Daniel Veillardb96e6431999-08-29 21:02:19 +00001018 if ((defaultValue != NULL) &&
1019 (!xmlValidateAttributeValue(type, defaultValue))) {
1020 VERROR(ctxt->userData, "Attribute %s on %s: invalid default value\n",
1021 elem, name, defaultValue);
1022 defaultValue = NULL;
1023 }
Daniel Veillard1e346af1999-02-22 10:33:01 +00001024
1025 /*
1026 * Create the Attribute table if needed.
1027 */
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001028 table = (xmlAttributeTablePtr) dtd->attributes;
1029 if (table == NULL) {
1030 table = xmlCreateAttributeTable();
1031 dtd->attributes = (void *) table;
1032 }
Daniel Veillard1e346af1999-02-22 10:33:01 +00001033 if (table == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001034 xmlGenericError(xmlGenericErrorContext,
1035 "xmlAddAttributeDecl: Table creation failed!\n");
Daniel Veillard1e346af1999-02-22 10:33:01 +00001036 return(NULL);
1037 }
1038
Daniel Veillard1e346af1999-02-22 10:33:01 +00001039
Daniel Veillard6454aec1999-09-02 22:04:43 +00001040 ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
Daniel Veillardb05deb71999-08-10 19:04:08 +00001041 if (ret == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001042 xmlGenericError(xmlGenericErrorContext,
1043 "xmlAddAttributeDecl: out of memory\n");
Daniel Veillardb05deb71999-08-10 19:04:08 +00001044 return(NULL);
1045 }
Daniel Veillardcf461992000-03-14 18:30:20 +00001046 memset(ret, 0, sizeof(xmlAttribute));
1047 ret->type = XML_ATTRIBUTE_DECL;
Daniel Veillard1e346af1999-02-22 10:33:01 +00001048
1049 /*
1050 * fill the structure.
1051 */
Daniel Veillardcf461992000-03-14 18:30:20 +00001052 ret->atype = type;
1053 ret->name = xmlStrdup(name);
1054 ret->prefix = xmlStrdup(ns);
Daniel Veillard1e346af1999-02-22 10:33:01 +00001055 ret->elem = xmlStrdup(elem);
1056 ret->def = def;
1057 ret->tree = tree;
1058 if (defaultValue != NULL)
1059 ret->defaultValue = xmlStrdup(defaultValue);
Daniel Veillard126f2792000-10-24 17:10:12 +00001060
1061 /*
1062 * Validity Check:
1063 * Search the DTD for previous declarations of the ATTLIST
1064 */
1065 if (xmlHashAddEntry3(table, name, ns, elem, ret) < 0) {
1066 /*
1067 * The attribute is already defined in this Dtd.
1068 */
1069 VWARNING(ctxt->userData,
1070 "Attribute %s on %s: already defined\n",
1071 name, elem);
1072 xmlFreeAttribute(ret);
1073 return(NULL);
1074 }
1075
1076 /*
1077 * Validity Check:
1078 * Multiple ID per element
1079 */
1080 elemDef = xmlGetDtdElementDesc(dtd, elem);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001081 if (elemDef != NULL) {
1082 if ((type == XML_ATTRIBUTE_ID) &&
1083 (xmlScanIDAttributeDecl(NULL, elemDef) != 0))
1084 VERROR(ctxt->userData,
1085 "Element %s has too may ID attributes defined : %s\n",
1086 elem, name);
Daniel Veillardcf461992000-03-14 18:30:20 +00001087 ret->nexth = elemDef->attributes;
Daniel Veillardb05deb71999-08-10 19:04:08 +00001088 elemDef->attributes = ret;
1089 }
Daniel Veillard1e346af1999-02-22 10:33:01 +00001090
Daniel Veillardcf461992000-03-14 18:30:20 +00001091 /*
1092 * Link it to the Dtd
1093 */
1094 ret->parent = dtd;
1095 ret->doc = dtd->doc;
1096 if (dtd->last == NULL) {
1097 dtd->children = dtd->last = (xmlNodePtr) ret;
1098 } else {
1099 dtd->last->next = (xmlNodePtr) ret;
1100 ret->prev = dtd->last;
1101 dtd->last = (xmlNodePtr) ret;
1102 }
Daniel Veillard1e346af1999-02-22 10:33:01 +00001103 return(ret);
1104}
1105
1106/**
Daniel Veillard1e346af1999-02-22 10:33:01 +00001107 * xmlFreeAttributeTable:
1108 * @table: An attribute table
1109 *
1110 * Deallocate the memory used by an entities hash table.
1111 */
1112void
1113xmlFreeAttributeTable(xmlAttributeTablePtr table) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001114 xmlHashFree(table, (xmlHashDeallocator) xmlFreeAttribute);
1115}
Daniel Veillard1e346af1999-02-22 10:33:01 +00001116
Daniel Veillard126f2792000-10-24 17:10:12 +00001117/**
1118 * xmlCopyAttribute:
1119 * @attr: An attribute
1120 *
1121 * Build a copy of an attribute.
1122 *
1123 * Returns the new xmlAttributePtr or NULL in case of error.
1124 */
1125xmlAttributePtr
1126xmlCopyAttribute(xmlAttributePtr attr) {
1127 xmlAttributePtr cur;
Daniel Veillard1e346af1999-02-22 10:33:01 +00001128
Daniel Veillard126f2792000-10-24 17:10:12 +00001129 cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute));
1130 if (cur == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001131 xmlGenericError(xmlGenericErrorContext,
1132 "xmlCopyAttribute: out of memory !\n");
Daniel Veillard126f2792000-10-24 17:10:12 +00001133 return(NULL);
Daniel Veillard1e346af1999-02-22 10:33:01 +00001134 }
Daniel Veillard126f2792000-10-24 17:10:12 +00001135 memset(cur, 0, sizeof(xmlAttribute));
1136 cur->atype = attr->atype;
1137 cur->def = attr->def;
1138 cur->tree = xmlCopyEnumeration(attr->tree);
1139 if (attr->elem != NULL)
1140 cur->elem = xmlStrdup(attr->elem);
1141 if (attr->name != NULL)
1142 cur->name = xmlStrdup(attr->name);
1143 if (attr->defaultValue != NULL)
1144 cur->defaultValue = xmlStrdup(attr->defaultValue);
1145 return(cur);
Daniel Veillard1e346af1999-02-22 10:33:01 +00001146}
1147
1148/**
1149 * xmlCopyAttributeTable:
1150 * @table: An attribute table
1151 *
1152 * Build a copy of an attribute table.
1153 *
1154 * Returns the new xmlAttributeTablePtr or NULL in case of error.
1155 */
1156xmlAttributeTablePtr
1157xmlCopyAttributeTable(xmlAttributeTablePtr table) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001158 return((xmlAttributeTablePtr) xmlHashCopy(table,
1159 (xmlHashCopier) xmlCopyAttribute));
Daniel Veillard1e346af1999-02-22 10:33:01 +00001160}
1161
1162/**
Daniel Veillardcf461992000-03-14 18:30:20 +00001163 * xmlDumpAttributeDecl:
1164 * @buf: the XML buffer output
1165 * @attr: An attribute declaration
1166 *
1167 * This will dump the content of the attribute declaration as an XML
1168 * DTD definition
1169 */
1170void
1171xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) {
1172 xmlBufferWriteChar(buf, "<!ATTLIST ");
1173 xmlBufferWriteCHAR(buf, attr->elem);
1174 xmlBufferWriteChar(buf, " ");
Daniel Veillardbe803962000-06-28 23:40:59 +00001175 if (attr->prefix != NULL) {
1176 xmlBufferWriteCHAR(buf, attr->prefix);
1177 xmlBufferWriteChar(buf, ":");
1178 }
Daniel Veillardcf461992000-03-14 18:30:20 +00001179 xmlBufferWriteCHAR(buf, attr->name);
1180 switch (attr->atype) {
1181 case XML_ATTRIBUTE_CDATA:
1182 xmlBufferWriteChar(buf, " CDATA");
1183 break;
1184 case XML_ATTRIBUTE_ID:
1185 xmlBufferWriteChar(buf, " ID");
1186 break;
1187 case XML_ATTRIBUTE_IDREF:
1188 xmlBufferWriteChar(buf, " IDREF");
1189 break;
1190 case XML_ATTRIBUTE_IDREFS:
1191 xmlBufferWriteChar(buf, " IDREFS");
1192 break;
1193 case XML_ATTRIBUTE_ENTITY:
1194 xmlBufferWriteChar(buf, " ENTITY");
1195 break;
1196 case XML_ATTRIBUTE_ENTITIES:
1197 xmlBufferWriteChar(buf, " ENTITIES");
1198 break;
1199 case XML_ATTRIBUTE_NMTOKEN:
1200 xmlBufferWriteChar(buf, " NMTOKEN");
1201 break;
1202 case XML_ATTRIBUTE_NMTOKENS:
1203 xmlBufferWriteChar(buf, " NMTOKENS");
1204 break;
1205 case XML_ATTRIBUTE_ENUMERATION:
1206 xmlBufferWriteChar(buf, " (");
1207 xmlDumpEnumeration(buf, attr->tree);
1208 break;
1209 case XML_ATTRIBUTE_NOTATION:
1210 xmlBufferWriteChar(buf, " NOTATION (");
1211 xmlDumpEnumeration(buf, attr->tree);
1212 break;
1213 default:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001214 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcf461992000-03-14 18:30:20 +00001215 "xmlDumpAttributeTable: internal: unknown type %d\n",
1216 attr->atype);
1217 }
1218 switch (attr->def) {
1219 case XML_ATTRIBUTE_NONE:
1220 break;
1221 case XML_ATTRIBUTE_REQUIRED:
1222 xmlBufferWriteChar(buf, " #REQUIRED");
1223 break;
1224 case XML_ATTRIBUTE_IMPLIED:
1225 xmlBufferWriteChar(buf, " #IMPLIED");
1226 break;
1227 case XML_ATTRIBUTE_FIXED:
1228 xmlBufferWriteChar(buf, " #FIXED");
1229 break;
1230 default:
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001231 xmlGenericError(xmlGenericErrorContext,
Daniel Veillardcf461992000-03-14 18:30:20 +00001232 "xmlDumpAttributeTable: internal: unknown default %d\n",
1233 attr->def);
1234 }
1235 if (attr->defaultValue != NULL) {
1236 xmlBufferWriteChar(buf, " ");
1237 xmlBufferWriteQuotedString(buf, attr->defaultValue);
1238 }
1239 xmlBufferWriteChar(buf, ">\n");
1240}
1241
1242/**
Daniel Veillard1e346af1999-02-22 10:33:01 +00001243 * xmlDumpAttributeTable:
Daniel Veillard011b63c1999-06-02 17:44:04 +00001244 * @buf: the XML buffer output
Daniel Veillard1e346af1999-02-22 10:33:01 +00001245 * @table: An attribute table
1246 *
1247 * This will dump the content of the attribute table as an XML DTD definition
Daniel Veillard1e346af1999-02-22 10:33:01 +00001248 */
1249void
Daniel Veillard5099ae81999-04-21 20:12:07 +00001250xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001251 xmlHashScan(table, (xmlHashScanner) xmlDumpAttributeDecl, buf);
Daniel Veillard1e346af1999-02-22 10:33:01 +00001252}
1253
1254/************************************************************************
1255 * *
1256 * NOTATIONs *
1257 * *
1258 ************************************************************************/
1259/**
1260 * xmlCreateNotationTable:
1261 *
1262 * create and initialize an empty notation hash table.
1263 *
1264 * Returns the xmlNotationTablePtr just created or NULL in case
1265 * of error.
1266 */
1267xmlNotationTablePtr
1268xmlCreateNotationTable(void) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001269 return(xmlHashCreate(0));
1270}
Daniel Veillard1e346af1999-02-22 10:33:01 +00001271
Daniel Veillard126f2792000-10-24 17:10:12 +00001272/**
1273 * xmlFreeNotation:
1274 * @not: A notation
1275 *
1276 * Deallocate the memory used by an notation definition
1277 */
1278void
1279xmlFreeNotation(xmlNotationPtr nota) {
1280 if (nota == NULL) return;
1281 if (nota->name != NULL)
1282 xmlFree((xmlChar *) nota->name);
1283 if (nota->PublicID != NULL)
1284 xmlFree((xmlChar *) nota->PublicID);
1285 if (nota->SystemID != NULL)
1286 xmlFree((xmlChar *) nota->SystemID);
1287 memset(nota, -1, sizeof(xmlNotation));
1288 xmlFree(nota);
Daniel Veillard1e346af1999-02-22 10:33:01 +00001289}
1290
1291
1292/**
1293 * xmlAddNotationDecl:
1294 * @dtd: pointer to the DTD
Daniel Veillardb05deb71999-08-10 19:04:08 +00001295 * @ctxt: the validation context
Daniel Veillard1e346af1999-02-22 10:33:01 +00001296 * @name: the entity name
1297 * @PublicID: the public identifier or NULL
1298 * @SystemID: the system identifier or NULL
1299 *
1300 * Register a new notation declaration
1301 *
1302 * Returns NULL if not, othervise the entity
1303 */
1304xmlNotationPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001305xmlAddNotationDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name,
1306 const xmlChar *PublicID, const xmlChar *SystemID) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001307 xmlNotationPtr ret;
Daniel Veillard1e346af1999-02-22 10:33:01 +00001308 xmlNotationTablePtr table;
Daniel Veillard1e346af1999-02-22 10:33:01 +00001309
1310 if (dtd == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001311 xmlGenericError(xmlGenericErrorContext,
1312 "xmlAddNotationDecl: dtd == NULL\n");
Daniel Veillard1e346af1999-02-22 10:33:01 +00001313 return(NULL);
1314 }
1315 if (name == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001316 xmlGenericError(xmlGenericErrorContext,
1317 "xmlAddNotationDecl: name == NULL\n");
Daniel Veillard1e346af1999-02-22 10:33:01 +00001318 return(NULL);
1319 }
1320 if ((PublicID == NULL) && (SystemID == NULL)) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001321 xmlGenericError(xmlGenericErrorContext,
1322 "xmlAddNotationDecl: no PUBLIC ID nor SYSTEM ID\n");
Daniel Veillard1e346af1999-02-22 10:33:01 +00001323 }
1324
1325 /*
1326 * Create the Notation table if needed.
1327 */
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001328 table = (xmlNotationTablePtr) dtd->notations;
Daniel Veillard1e346af1999-02-22 10:33:01 +00001329 if (table == NULL)
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001330 dtd->notations = table = xmlCreateNotationTable();
Daniel Veillard1e346af1999-02-22 10:33:01 +00001331 if (table == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001332 xmlGenericError(xmlGenericErrorContext,
1333 "xmlAddNotationDecl: Table creation failed!\n");
Daniel Veillard1e346af1999-02-22 10:33:01 +00001334 return(NULL);
1335 }
1336
Daniel Veillard6454aec1999-09-02 22:04:43 +00001337 ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
Daniel Veillardb05deb71999-08-10 19:04:08 +00001338 if (ret == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001339 xmlGenericError(xmlGenericErrorContext,
1340 "xmlAddNotationDecl: out of memory\n");
Daniel Veillardb05deb71999-08-10 19:04:08 +00001341 return(NULL);
1342 }
Daniel Veillardcf461992000-03-14 18:30:20 +00001343 memset(ret, 0, sizeof(xmlNotation));
Daniel Veillard1e346af1999-02-22 10:33:01 +00001344
1345 /*
1346 * fill the structure.
1347 */
1348 ret->name = xmlStrdup(name);
1349 if (SystemID != NULL)
1350 ret->SystemID = xmlStrdup(SystemID);
Daniel Veillard1e346af1999-02-22 10:33:01 +00001351 if (PublicID != NULL)
1352 ret->PublicID = xmlStrdup(PublicID);
Daniel Veillard1e346af1999-02-22 10:33:01 +00001353
Daniel Veillard126f2792000-10-24 17:10:12 +00001354 /*
1355 * Validity Check:
1356 * Check the DTD for previous declarations of the ATTLIST
1357 */
1358 if (xmlHashAddEntry(table, name, ret)) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001359 xmlGenericError(xmlGenericErrorContext,
Daniel Veillard126f2792000-10-24 17:10:12 +00001360 "xmlAddNotationDecl: %s already defined\n", name);
1361 xmlFreeNotation(ret);
1362 return(NULL);
1363 }
Daniel Veillard1e346af1999-02-22 10:33:01 +00001364 return(ret);
1365}
1366
1367/**
Daniel Veillard1e346af1999-02-22 10:33:01 +00001368 * xmlFreeNotationTable:
1369 * @table: An notation table
1370 *
1371 * Deallocate the memory used by an entities hash table.
1372 */
1373void
1374xmlFreeNotationTable(xmlNotationTablePtr table) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001375 xmlHashFree(table, (xmlHashDeallocator) xmlFreeNotation);
1376}
Daniel Veillard1e346af1999-02-22 10:33:01 +00001377
Daniel Veillard126f2792000-10-24 17:10:12 +00001378/**
1379 * xmlCopyNotation:
1380 * @nota: A notation
1381 *
1382 * Build a copy of a notation.
1383 *
1384 * Returns the new xmlNotationPtr or NULL in case of error.
1385 */
1386xmlNotationPtr
1387xmlCopyNotation(xmlNotationPtr nota) {
1388 xmlNotationPtr cur;
Daniel Veillard1e346af1999-02-22 10:33:01 +00001389
Daniel Veillard126f2792000-10-24 17:10:12 +00001390 cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation));
1391 if (cur == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001392 xmlGenericError(xmlGenericErrorContext,
1393 "xmlCopyNotation: out of memory !\n");
Daniel Veillard126f2792000-10-24 17:10:12 +00001394 return(NULL);
Daniel Veillard1e346af1999-02-22 10:33:01 +00001395 }
Daniel Veillard126f2792000-10-24 17:10:12 +00001396 if (nota->name != NULL)
1397 cur->name = xmlStrdup(nota->name);
1398 else
1399 cur->name = NULL;
1400 if (nota->PublicID != NULL)
1401 cur->PublicID = xmlStrdup(nota->PublicID);
1402 else
1403 cur->PublicID = NULL;
1404 if (nota->SystemID != NULL)
1405 cur->SystemID = xmlStrdup(nota->SystemID);
1406 else
1407 cur->SystemID = NULL;
1408 return(cur);
Daniel Veillard1e346af1999-02-22 10:33:01 +00001409}
1410
1411/**
1412 * xmlCopyNotationTable:
1413 * @table: A notation table
1414 *
1415 * Build a copy of a notation table.
1416 *
1417 * Returns the new xmlNotationTablePtr or NULL in case of error.
1418 */
1419xmlNotationTablePtr
1420xmlCopyNotationTable(xmlNotationTablePtr table) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001421 return((xmlNotationTablePtr) xmlHashCopy(table,
1422 (xmlHashCopier) xmlCopyNotation));
Daniel Veillard1e346af1999-02-22 10:33:01 +00001423}
1424
1425/**
Daniel Veillardcf461992000-03-14 18:30:20 +00001426 * xmlDumpNotationDecl:
1427 * @buf: the XML buffer output
1428 * @nota: A notation declaration
1429 *
1430 * This will dump the content the notation declaration as an XML DTD definition
1431 */
1432void
1433xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) {
1434 xmlBufferWriteChar(buf, "<!NOTATION ");
1435 xmlBufferWriteCHAR(buf, nota->name);
1436 if (nota->PublicID != NULL) {
1437 xmlBufferWriteChar(buf, " PUBLIC ");
1438 xmlBufferWriteQuotedString(buf, nota->PublicID);
1439 if (nota->SystemID != NULL) {
1440 xmlBufferWriteChar(buf, " ");
1441 xmlBufferWriteCHAR(buf, nota->SystemID);
1442 }
1443 } else {
1444 xmlBufferWriteChar(buf, " SYSTEM ");
1445 xmlBufferWriteCHAR(buf, nota->SystemID);
1446 }
1447 xmlBufferWriteChar(buf, " >\n");
1448}
1449
1450/**
Daniel Veillard1e346af1999-02-22 10:33:01 +00001451 * xmlDumpNotationTable:
Daniel Veillard011b63c1999-06-02 17:44:04 +00001452 * @buf: the XML buffer output
Daniel Veillard1e346af1999-02-22 10:33:01 +00001453 * @table: A notation table
1454 *
1455 * This will dump the content of the notation table as an XML DTD definition
Daniel Veillard1e346af1999-02-22 10:33:01 +00001456 */
1457void
Daniel Veillard5099ae81999-04-21 20:12:07 +00001458xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001459 xmlHashScan(table, (xmlHashScanner) xmlDumpNotationDecl, buf);
Daniel Veillard1e346af1999-02-22 10:33:01 +00001460}
Daniel Veillardb05deb71999-08-10 19:04:08 +00001461
1462/************************************************************************
1463 * *
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001464 * IDs *
Daniel Veillard991e63d1999-08-15 23:32:28 +00001465 * *
1466 ************************************************************************/
1467/**
1468 * xmlCreateIDTable:
1469 *
1470 * create and initialize an empty id hash table.
1471 *
1472 * Returns the xmlIDTablePtr just created or NULL in case
1473 * of error.
1474 */
1475xmlIDTablePtr
1476xmlCreateIDTable(void) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001477 return(xmlHashCreate(0));
Daniel Veillard991e63d1999-08-15 23:32:28 +00001478}
1479
Daniel Veillard126f2792000-10-24 17:10:12 +00001480/**
1481 * xmlFreeID:
1482 * @not: A id
1483 *
1484 * Deallocate the memory used by an id definition
1485 */
1486void
1487xmlFreeID(xmlIDPtr id) {
1488 if (id == NULL) return;
1489 if (id->value != NULL)
1490 xmlFree((xmlChar *) id->value);
1491 memset(id, -1, sizeof(xmlID));
1492 xmlFree(id);
1493}
Daniel Veillard991e63d1999-08-15 23:32:28 +00001494
1495/**
1496 * xmlAddID:
1497 * @ctxt: the validation context
1498 * @doc: pointer to the document
1499 * @value: the value name
1500 * @attr: the attribute holding the ID
1501 *
1502 * Register a new id declaration
1503 *
1504 * Returns NULL if not, othervise the new xmlIDPtr
1505 */
1506xmlIDPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001507xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
Daniel Veillard991e63d1999-08-15 23:32:28 +00001508 xmlAttrPtr attr) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001509 xmlIDPtr ret;
Daniel Veillard991e63d1999-08-15 23:32:28 +00001510 xmlIDTablePtr table;
Daniel Veillard991e63d1999-08-15 23:32:28 +00001511
1512 if (doc == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001513 xmlGenericError(xmlGenericErrorContext,
1514 "xmlAddIDDecl: doc == NULL\n");
Daniel Veillard991e63d1999-08-15 23:32:28 +00001515 return(NULL);
1516 }
1517 if (value == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001518 xmlGenericError(xmlGenericErrorContext,
1519 "xmlAddIDDecl: value == NULL\n");
Daniel Veillard991e63d1999-08-15 23:32:28 +00001520 return(NULL);
1521 }
1522 if (attr == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001523 xmlGenericError(xmlGenericErrorContext,
1524 "xmlAddIDDecl: attr == NULL\n");
Daniel Veillard991e63d1999-08-15 23:32:28 +00001525 return(NULL);
1526 }
1527
1528 /*
1529 * Create the ID table if needed.
1530 */
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001531 table = (xmlIDTablePtr) doc->ids;
Daniel Veillard991e63d1999-08-15 23:32:28 +00001532 if (table == NULL)
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001533 doc->ids = table = xmlCreateIDTable();
Daniel Veillard991e63d1999-08-15 23:32:28 +00001534 if (table == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001535 xmlGenericError(xmlGenericErrorContext,
1536 "xmlAddID: Table creation failed!\n");
Daniel Veillard991e63d1999-08-15 23:32:28 +00001537 return(NULL);
1538 }
1539
Daniel Veillard6454aec1999-09-02 22:04:43 +00001540 ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID));
Daniel Veillard991e63d1999-08-15 23:32:28 +00001541 if (ret == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001542 xmlGenericError(xmlGenericErrorContext,
1543 "xmlAddID: out of memory\n");
Daniel Veillard991e63d1999-08-15 23:32:28 +00001544 return(NULL);
1545 }
Daniel Veillard991e63d1999-08-15 23:32:28 +00001546
1547 /*
1548 * fill the structure.
1549 */
1550 ret->value = xmlStrdup(value);
1551 ret->attr = attr;
Daniel Veillard991e63d1999-08-15 23:32:28 +00001552
Daniel Veillard126f2792000-10-24 17:10:12 +00001553 if (xmlHashAddEntry(table, value, ret) < 0) {
1554 /*
1555 * The id is already defined in this Dtd.
1556 */
1557 VERROR(ctxt->userData, "ID %s already defined\n", value);
1558 xmlFreeID(ret);
1559 return(NULL);
1560 }
Daniel Veillard991e63d1999-08-15 23:32:28 +00001561 return(ret);
1562}
1563
1564/**
Daniel Veillard991e63d1999-08-15 23:32:28 +00001565 * xmlFreeIDTable:
1566 * @table: An id table
1567 *
1568 * Deallocate the memory used by an ID hash table.
1569 */
1570void
1571xmlFreeIDTable(xmlIDTablePtr table) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001572 xmlHashFree(table, (xmlHashDeallocator) xmlFreeID);
Daniel Veillard991e63d1999-08-15 23:32:28 +00001573}
1574
1575/**
Daniel Veillard71b656e2000-01-05 14:46:17 +00001576 * xmlIsID:
Daniel Veillard991e63d1999-08-15 23:32:28 +00001577 * @doc: the document
1578 * @elem: the element carrying the attribute
1579 * @attr: the attribute
1580 *
1581 * Determine whether an attribute is of type ID. In case we have Dtd(s)
1582 * then this is simple, otherwise we use an heuristic: name ID (upper
1583 * or lowercase).
1584 *
1585 * Returns 0 or 1 depending on the lookup result
1586 */
1587int
1588xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
Daniel Veillard71b656e2000-01-05 14:46:17 +00001589 if (doc == NULL) return(0);
1590 if (attr == NULL) return(0);
Daniel Veillard991e63d1999-08-15 23:32:28 +00001591 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
Daniel Veillarde8282ed2000-10-10 23:01:31 +00001592 return(0);
Daniel Veillard71b656e2000-01-05 14:46:17 +00001593 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00001594 if ((xmlStrEqual(BAD_CAST "id", attr->name)) ||
1595 (xmlStrEqual(BAD_CAST "name", attr->name)))
Daniel Veillard71b656e2000-01-05 14:46:17 +00001596 return(1);
1597 return(0);
Daniel Veillard991e63d1999-08-15 23:32:28 +00001598 } else {
1599 xmlAttributePtr attrDecl;
1600
Daniel Veillard71b656e2000-01-05 14:46:17 +00001601 if (elem == NULL) return(0);
Daniel Veillard991e63d1999-08-15 23:32:28 +00001602 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1603 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1604 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1605 attr->name);
1606
Daniel Veillardcf461992000-03-14 18:30:20 +00001607 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID))
Daniel Veillard991e63d1999-08-15 23:32:28 +00001608 return(1);
1609 }
1610 return(0);
1611}
1612
Daniel Veillardb96e6431999-08-29 21:02:19 +00001613/**
Daniel Veillard71b656e2000-01-05 14:46:17 +00001614 * xmlRemoveID
1615 * @doc: the document
1616 * @attr: the attribute
1617 *
1618 * Remove the given attribute from the ID table maintained internally.
1619 *
1620 * Returns -1 if the lookup failed and 0 otherwise
1621 */
1622int
1623xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001624 xmlAttrPtr cur;
Daniel Veillard71b656e2000-01-05 14:46:17 +00001625 xmlIDTablePtr table;
Daniel Veillard126f2792000-10-24 17:10:12 +00001626 xmlChar *ID;
Daniel Veillard71b656e2000-01-05 14:46:17 +00001627
1628 if (doc == NULL) return(-1);
1629 if (attr == NULL) return(-1);
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001630 table = (xmlIDTablePtr) doc->ids;
Daniel Veillard71b656e2000-01-05 14:46:17 +00001631 if (table == NULL)
1632 return(-1);
1633
Daniel Veillard126f2792000-10-24 17:10:12 +00001634 if (attr == NULL)
1635 return(-1);
1636 ID = xmlNodeListGetString(doc, attr->children, 1);
1637 if (ID == NULL)
1638 return(-1);
1639 cur = xmlHashLookup(table, ID);
1640 if (cur != attr) {
1641 xmlFree(ID);
1642 return(-1);
Daniel Veillard71b656e2000-01-05 14:46:17 +00001643 }
Daniel Veillard126f2792000-10-24 17:10:12 +00001644 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeID);
1645 xmlFree(ID);
1646 return(0);
Daniel Veillard71b656e2000-01-05 14:46:17 +00001647}
1648
1649/**
Daniel Veillardb96e6431999-08-29 21:02:19 +00001650 * xmlGetID:
1651 * @doc: pointer to the document
1652 * @ID: the ID value
1653 *
1654 * Search the attribute declaring the given ID
1655 *
1656 * Returns NULL if not found, otherwise the xmlAttrPtr defining the ID
1657 */
1658xmlAttrPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001659xmlGetID(xmlDocPtr doc, const xmlChar *ID) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00001660 xmlIDTablePtr table;
Daniel Veillard126f2792000-10-24 17:10:12 +00001661 xmlIDPtr id;
Daniel Veillardb96e6431999-08-29 21:02:19 +00001662
1663 if (doc == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001664 xmlGenericError(xmlGenericErrorContext, "xmlGetID: doc == NULL\n");
Daniel Veillardb96e6431999-08-29 21:02:19 +00001665 return(NULL);
1666 }
1667
1668 if (ID == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001669 xmlGenericError(xmlGenericErrorContext, "xmlGetID: ID == NULL\n");
Daniel Veillardb96e6431999-08-29 21:02:19 +00001670 return(NULL);
1671 }
1672
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001673 table = (xmlIDTablePtr) doc->ids;
Daniel Veillardb96e6431999-08-29 21:02:19 +00001674 if (table == NULL)
1675 return(NULL);
1676
Daniel Veillard126f2792000-10-24 17:10:12 +00001677 id = xmlHashLookup(table, ID);
1678 if (id == NULL)
1679 return(NULL);
1680 return(id->attr);
Daniel Veillardb96e6431999-08-29 21:02:19 +00001681}
1682
Daniel Veillard991e63d1999-08-15 23:32:28 +00001683/************************************************************************
1684 * *
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001685 * Refs *
1686 * *
1687 ************************************************************************/
1688/**
1689 * xmlCreateRefTable:
1690 *
1691 * create and initialize an empty ref hash table.
1692 *
1693 * Returns the xmlRefTablePtr just created or NULL in case
1694 * of error.
1695 */
1696xmlRefTablePtr
1697xmlCreateRefTable(void) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001698 return(xmlHashCreate(0));
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001699}
1700
Daniel Veillard126f2792000-10-24 17:10:12 +00001701/**
1702 * xmlFreeRef:
1703 * @ref: A ref
1704 *
1705 * Deallocate the memory used by an ref definition
1706 */
1707void
1708xmlFreeRef(xmlRefPtr ref) {
1709 if (ref == NULL) return;
1710 if (ref->value != NULL)
1711 xmlFree((xmlChar *) ref->value);
1712 memset(ref, -1, sizeof(xmlRef));
1713 xmlFree(ref);
1714}
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001715
1716/**
1717 * xmlAddRef:
1718 * @ctxt: the validation context
1719 * @doc: pointer to the document
1720 * @value: the value name
1721 * @attr: the attribute holding the Ref
1722 *
1723 * Register a new ref declaration
1724 *
1725 * Returns NULL if not, othervise the new xmlRefPtr
1726 */
1727xmlRefPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001728xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value,
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001729 xmlAttrPtr attr) {
1730 xmlRefPtr ret;
1731 xmlRefTablePtr table;
1732
1733 if (doc == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001734 xmlGenericError(xmlGenericErrorContext,
1735 "xmlAddRefDecl: doc == NULL\n");
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001736 return(NULL);
1737 }
1738 if (value == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001739 xmlGenericError(xmlGenericErrorContext,
1740 "xmlAddRefDecl: value == NULL\n");
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001741 return(NULL);
1742 }
1743 if (attr == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001744 xmlGenericError(xmlGenericErrorContext,
1745 "xmlAddRefDecl: attr == NULL\n");
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001746 return(NULL);
1747 }
1748
1749 /*
1750 * Create the Ref table if needed.
1751 */
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001752 table = (xmlRefTablePtr) doc->refs;
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001753 if (table == NULL)
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001754 doc->refs = table = xmlCreateRefTable();
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001755 if (table == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001756 xmlGenericError(xmlGenericErrorContext,
1757 "xmlAddRef: Table creation failed!\n");
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001758 return(NULL);
1759 }
1760
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001761 ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef));
1762 if (ret == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001763 xmlGenericError(xmlGenericErrorContext,
1764 "xmlAddRef: out of memory\n");
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001765 return(NULL);
1766 }
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001767
1768 /*
1769 * fill the structure.
1770 */
1771 ret->value = xmlStrdup(value);
1772 ret->attr = attr;
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001773
Daniel Veillard126f2792000-10-24 17:10:12 +00001774 /*
1775 * !!! Should we keep track of all refs ? and use xmlHashAddEntry2 ?
1776 */
1777 if (xmlHashAddEntry(table, value, ret) < 0) {
1778 xmlFreeRef(ret);
1779 return(NULL);
1780 }
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001781 return(ret);
1782}
1783
1784/**
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001785 * xmlFreeRefTable:
1786 * @table: An ref table
1787 *
1788 * Deallocate the memory used by an Ref hash table.
1789 */
1790void
1791xmlFreeRefTable(xmlRefTablePtr table) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001792 xmlHashFree(table, (xmlHashDeallocator) xmlFreeRef);
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001793}
1794
1795/**
Daniel Veillard71b656e2000-01-05 14:46:17 +00001796 * xmlIsRef:
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001797 * @doc: the document
1798 * @elem: the element carrying the attribute
1799 * @attr: the attribute
1800 *
1801 * Determine whether an attribute is of type Ref. In case we have Dtd(s)
1802 * then this is simple, otherwise we use an heuristic: name Ref (upper
1803 * or lowercase).
1804 *
1805 * Returns 0 or 1 depending on the lookup result
1806 */
1807int
1808xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) {
1809 if ((doc->intSubset == NULL) && (doc->extSubset == NULL)) {
1810 return(0);
Daniel Veillardd83eb822000-06-30 18:39:56 +00001811 } else if (doc->type == XML_HTML_DOCUMENT_NODE) {
1812 /* TODO @@@ */
1813 return(0);
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001814 } else {
1815 xmlAttributePtr attrDecl;
1816
1817 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name);
1818 if ((attrDecl == NULL) && (doc->extSubset != NULL))
1819 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name,
1820 attr->name);
1821
Daniel Veillardcf461992000-03-14 18:30:20 +00001822 if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_IDREF))
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001823 return(1);
1824 }
1825 return(0);
1826}
1827
1828/**
Daniel Veillard71b656e2000-01-05 14:46:17 +00001829 * xmlRemoveRef
1830 * @doc: the document
1831 * @attr: the attribute
1832 *
1833 * Remove the given attribute from the Ref table maintained internally.
1834 *
1835 * Returns -1 if the lookup failed and 0 otherwise
1836 */
1837int
1838xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) {
Daniel Veillard126f2792000-10-24 17:10:12 +00001839 xmlAttrPtr cur;
Daniel Veillard71b656e2000-01-05 14:46:17 +00001840 xmlRefTablePtr table;
Daniel Veillard126f2792000-10-24 17:10:12 +00001841 xmlChar *ID;
Daniel Veillard71b656e2000-01-05 14:46:17 +00001842
1843 if (doc == NULL) return(-1);
1844 if (attr == NULL) return(-1);
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001845 table = (xmlRefTablePtr) doc->refs;
Daniel Veillard71b656e2000-01-05 14:46:17 +00001846 if (table == NULL)
1847 return(-1);
1848
Daniel Veillard126f2792000-10-24 17:10:12 +00001849 if (attr == NULL)
1850 return(-1);
1851 ID = xmlNodeListGetString(doc, attr->children, 1);
1852 if (ID == NULL)
1853 return(-1);
1854 cur = xmlHashLookup(table, ID);
1855 if (cur != attr) {
1856 xmlFree(ID);
1857 return(-1);
Daniel Veillard71b656e2000-01-05 14:46:17 +00001858 }
Daniel Veillard126f2792000-10-24 17:10:12 +00001859 xmlHashUpdateEntry(table, ID, NULL, (xmlHashDeallocator) xmlFreeRef);
1860 xmlFree(ID);
1861 return(0);
Daniel Veillard71b656e2000-01-05 14:46:17 +00001862}
1863
1864/**
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001865 * xmlGetRef:
1866 * @doc: pointer to the document
1867 * @Ref: the Ref value
1868 *
Daniel Veillard71b656e2000-01-05 14:46:17 +00001869 * Search the next attribute declaring the given Ref
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001870 *
1871 * Returns NULL if not found, otherwise the xmlAttrPtr defining the Ref
1872 */
1873xmlAttrPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001874xmlGetRef(xmlDocPtr doc, const xmlChar *Ref) {
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001875 xmlRefTablePtr table;
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001876
1877 if (doc == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001878 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: doc == NULL\n");
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001879 return(NULL);
1880 }
1881
1882 if (Ref == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00001883 xmlGenericError(xmlGenericErrorContext, "xmlGetRef: Ref == NULL\n");
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001884 return(NULL);
1885 }
1886
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001887 table = (xmlRefTablePtr) doc->refs;
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001888 if (table == NULL)
1889 return(NULL);
1890
Daniel Veillard126f2792000-10-24 17:10:12 +00001891 return(xmlHashLookup(table, Ref));
Daniel Veillardc08a2c61999-09-08 21:35:25 +00001892}
1893
1894/************************************************************************
1895 * *
Daniel Veillardb05deb71999-08-10 19:04:08 +00001896 * Routines for validity checking *
1897 * *
1898 ************************************************************************/
1899
1900/**
1901 * xmlGetDtdElementDesc:
1902 * @dtd: a pointer to the DtD to search
1903 * @name: the element name
1904 *
1905 * Search the Dtd for the description of this element
1906 *
1907 * returns the xmlElementPtr if found or NULL
1908 */
1909
1910xmlElementPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001911xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001912 xmlElementTablePtr table;
1913 xmlElementPtr cur;
Daniel Veillardbe803962000-06-28 23:40:59 +00001914 xmlChar *uqname = NULL, *prefix = NULL;
Daniel Veillardb05deb71999-08-10 19:04:08 +00001915
1916 if (dtd == NULL) return(NULL);
1917 if (dtd->elements == NULL) return(NULL);
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001918 table = (xmlElementTablePtr) dtd->elements;
Daniel Veillardb05deb71999-08-10 19:04:08 +00001919
Daniel Veillardbe803962000-06-28 23:40:59 +00001920 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillard126f2792000-10-24 17:10:12 +00001921 if (uqname != NULL) {
1922 cur = xmlHashLookup2(table, uqname, prefix);
1923 if (prefix != NULL) xmlFree(prefix);
1924 if (uqname != NULL) xmlFree(uqname);
1925 } else
1926 cur = xmlHashLookup2(table, name, NULL);
1927 return(cur);
Daniel Veillardbe803962000-06-28 23:40:59 +00001928}
1929
1930/**
1931 * xmlGetDtdQElementDesc:
1932 * @dtd: a pointer to the DtD to search
1933 * @name: the element name
1934 * @prefix: the element namespace prefix
1935 *
1936 * Search the Dtd for the description of this element
1937 *
1938 * returns the xmlElementPtr if found or NULL
1939 */
1940
1941xmlElementPtr
1942xmlGetDtdQElementDesc(xmlDtdPtr dtd, const xmlChar *name,
1943 const xmlChar *prefix) {
1944 xmlElementTablePtr table;
Daniel Veillardbe803962000-06-28 23:40:59 +00001945
1946 if (dtd == NULL) return(NULL);
1947 if (dtd->elements == NULL) return(NULL);
Daniel Veillard32bc74e2000-07-14 14:49:25 +00001948 table = (xmlElementTablePtr) dtd->elements;
Daniel Veillardbe803962000-06-28 23:40:59 +00001949
Daniel Veillard126f2792000-10-24 17:10:12 +00001950 return(xmlHashLookup2(table, name, prefix));
Daniel Veillardb05deb71999-08-10 19:04:08 +00001951}
1952
1953/**
1954 * xmlGetDtdAttrDesc:
1955 * @dtd: a pointer to the DtD to search
1956 * @elem: the element name
1957 * @name: the attribute name
1958 *
1959 * Search the Dtd for the description of this attribute on
1960 * this element.
1961 *
1962 * returns the xmlAttributePtr if found or NULL
1963 */
1964
1965xmlAttributePtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00001966xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00001967 xmlAttributeTablePtr table;
1968 xmlAttributePtr cur;
Daniel Veillardbe803962000-06-28 23:40:59 +00001969 xmlChar *uqname = NULL, *prefix = NULL;
Daniel Veillardb05deb71999-08-10 19:04:08 +00001970
1971 if (dtd == NULL) return(NULL);
1972 if (dtd->attributes == NULL) return(NULL);
Daniel Veillardb05deb71999-08-10 19:04:08 +00001973
Daniel Veillardb1059e22000-09-16 14:02:43 +00001974 table = (xmlAttributeTablePtr) dtd->attributes;
1975 if (table == NULL)
1976 return(NULL);
Daniel Veillardbe803962000-06-28 23:40:59 +00001977
Daniel Veillardbe803962000-06-28 23:40:59 +00001978 uqname = xmlSplitQName2(name, &prefix);
Daniel Veillardbe803962000-06-28 23:40:59 +00001979
Daniel Veillard126f2792000-10-24 17:10:12 +00001980 if (uqname != NULL) {
1981 cur = xmlHashLookup3(table, uqname, prefix, elem);
1982 if (prefix != NULL) xmlFree(prefix);
1983 if (uqname != NULL) xmlFree(uqname);
1984 } else
1985 cur = xmlHashLookup3(table, name, NULL, elem);
1986 return(cur);
Daniel Veillardbe803962000-06-28 23:40:59 +00001987}
1988
1989/**
1990 * xmlGetDtdQAttrDesc:
1991 * @dtd: a pointer to the DtD to search
1992 * @elem: the element name
1993 * @name: the attribute name
1994 * @prefix: the attribute namespace prefix
1995 *
1996 * Search the Dtd for the description of this qualified attribute on
1997 * this element.
1998 *
1999 * returns the xmlAttributePtr if found or NULL
2000 */
2001
2002xmlAttributePtr
2003xmlGetDtdQAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name,
2004 const xmlChar *prefix) {
2005 xmlAttributeTablePtr table;
Daniel Veillardbe803962000-06-28 23:40:59 +00002006
2007 if (dtd == NULL) return(NULL);
2008 if (dtd->attributes == NULL) return(NULL);
Daniel Veillard32bc74e2000-07-14 14:49:25 +00002009 table = (xmlAttributeTablePtr) dtd->attributes;
Daniel Veillardbe803962000-06-28 23:40:59 +00002010
Daniel Veillard126f2792000-10-24 17:10:12 +00002011 return(xmlHashLookup3(table, name, prefix, elem));
Daniel Veillardb05deb71999-08-10 19:04:08 +00002012}
2013
2014/**
2015 * xmlGetDtdNotationDesc:
2016 * @dtd: a pointer to the DtD to search
2017 * @name: the notation name
2018 *
2019 * Search the Dtd for the description of this notation
2020 *
2021 * returns the xmlNotationPtr if found or NULL
2022 */
2023
2024xmlNotationPtr
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002025xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00002026 xmlNotationTablePtr table;
Daniel Veillardb05deb71999-08-10 19:04:08 +00002027
2028 if (dtd == NULL) return(NULL);
2029 if (dtd->notations == NULL) return(NULL);
Daniel Veillard32bc74e2000-07-14 14:49:25 +00002030 table = (xmlNotationTablePtr) dtd->notations;
Daniel Veillardb05deb71999-08-10 19:04:08 +00002031
Daniel Veillard126f2792000-10-24 17:10:12 +00002032 return(xmlHashLookup(table, name));
Daniel Veillardb05deb71999-08-10 19:04:08 +00002033}
2034
2035/**
Daniel Veillardb96e6431999-08-29 21:02:19 +00002036 * xmlValidateNotationUse:
2037 * @ctxt: the validation context
2038 * @doc: the document
2039 * @notationName: the notation name to check
2040 *
2041 * Validate that the given mame match a notation declaration.
2042 * - [ VC: Notation Declared ]
2043 *
2044 * returns 1 if valid or 0 otherwise
2045 */
2046
2047int
2048xmlValidateNotationUse(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002049 const xmlChar *notationName) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002050 xmlNotationPtr notaDecl;
2051 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2052
2053 notaDecl = xmlGetDtdNotationDesc(doc->intSubset, notationName);
2054 if ((notaDecl == NULL) && (doc->extSubset != NULL))
2055 notaDecl = xmlGetDtdNotationDesc(doc->extSubset, notationName);
2056
2057 if (notaDecl == NULL) {
2058 VERROR(ctxt->userData, "NOTATION %s is not declared\n",
2059 notationName);
2060 return(0);
2061 }
2062 return(1);
2063}
2064
2065/**
Daniel Veillardb05deb71999-08-10 19:04:08 +00002066 * xmlIsMixedElement
2067 * @doc: the document
2068 * @name: the element name
2069 *
2070 * Search in the DtDs whether an element accept Mixed content (or ANY)
2071 * basically if it is supposed to accept text childs
2072 *
2073 * returns 0 if no, 1 if yes, and -1 if no element description is available
2074 */
2075
2076int
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002077xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00002078 xmlElementPtr elemDecl;
2079
2080 if ((doc == NULL) || (doc->intSubset == NULL)) return(-1);
2081
2082 elemDecl = xmlGetDtdElementDesc(doc->intSubset, name);
2083 if ((elemDecl == NULL) && (doc->extSubset != NULL))
2084 elemDecl = xmlGetDtdElementDesc(doc->extSubset, name);
2085 if (elemDecl == NULL) return(-1);
Daniel Veillardcf461992000-03-14 18:30:20 +00002086 switch (elemDecl->etype) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00002087 case XML_ELEMENT_TYPE_ELEMENT:
2088 return(0);
2089 case XML_ELEMENT_TYPE_EMPTY:
2090 /*
2091 * return 1 for EMPTY since we want VC error to pop up
2092 * on <empty> </empty> for example
2093 */
2094 case XML_ELEMENT_TYPE_ANY:
2095 case XML_ELEMENT_TYPE_MIXED:
2096 return(1);
2097 }
2098 return(1);
2099}
2100
2101/**
2102 * xmlValidateNameValue:
2103 * @value: an Name value
2104 *
2105 * Validate that the given value match Name production
2106 *
2107 * returns 1 if valid or 0 otherwise
2108 */
2109
2110int
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002111xmlValidateNameValue(const xmlChar *value) {
2112 const xmlChar *cur;
Daniel Veillardb05deb71999-08-10 19:04:08 +00002113
2114 if (value == NULL) return(0);
2115 cur = value;
2116
2117 if (!IS_LETTER(*cur) && (*cur != '_') &&
2118 (*cur != ':')) {
2119 return(0);
2120 }
2121
2122 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2123 (*cur == '.') || (*cur == '-') ||
2124 (*cur == '_') || (*cur == ':') ||
2125 (IS_COMBINING(*cur)) ||
2126 (IS_EXTENDER(*cur)))
2127 cur++;
2128
2129 if (*cur != 0) return(0);
2130
2131 return(1);
2132}
2133
2134/**
2135 * xmlValidateNamesValue:
2136 * @value: an Names value
2137 *
2138 * Validate that the given value match Names production
2139 *
2140 * returns 1 if valid or 0 otherwise
2141 */
2142
2143int
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002144xmlValidateNamesValue(const xmlChar *value) {
2145 const xmlChar *cur;
Daniel Veillardb05deb71999-08-10 19:04:08 +00002146
2147 if (value == NULL) return(0);
2148 cur = value;
2149
2150 if (!IS_LETTER(*cur) && (*cur != '_') &&
2151 (*cur != ':')) {
2152 return(0);
2153 }
2154
2155 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2156 (*cur == '.') || (*cur == '-') ||
2157 (*cur == '_') || (*cur == ':') ||
2158 (IS_COMBINING(*cur)) ||
2159 (IS_EXTENDER(*cur)))
2160 cur++;
2161
2162 while (IS_BLANK(*cur)) {
2163 while (IS_BLANK(*cur)) cur++;
2164
2165 if (!IS_LETTER(*cur) && (*cur != '_') &&
2166 (*cur != ':')) {
2167 return(0);
2168 }
2169
2170 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2171 (*cur == '.') || (*cur == '-') ||
2172 (*cur == '_') || (*cur == ':') ||
2173 (IS_COMBINING(*cur)) ||
2174 (IS_EXTENDER(*cur)))
2175 cur++;
2176 }
2177
2178 if (*cur != 0) return(0);
2179
2180 return(1);
2181}
2182
2183/**
2184 * xmlValidateNmtokenValue:
2185 * @value: an Mntoken value
2186 *
2187 * Validate that the given value match Nmtoken production
2188 *
2189 * [ VC: Name Token ]
2190 *
2191 * returns 1 if valid or 0 otherwise
2192 */
2193
2194int
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002195xmlValidateNmtokenValue(const xmlChar *value) {
2196 const xmlChar *cur;
Daniel Veillardb05deb71999-08-10 19:04:08 +00002197
2198 if (value == NULL) return(0);
2199 cur = value;
2200
2201 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2202 (*cur != '.') && (*cur != '-') &&
2203 (*cur != '_') && (*cur != ':') &&
2204 (!IS_COMBINING(*cur)) &&
2205 (!IS_EXTENDER(*cur)))
2206 return(0);
2207
2208 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2209 (*cur == '.') || (*cur == '-') ||
2210 (*cur == '_') || (*cur == ':') ||
2211 (IS_COMBINING(*cur)) ||
2212 (IS_EXTENDER(*cur)))
2213 cur++;
2214
2215 if (*cur != 0) return(0);
2216
2217 return(1);
Daniel Veillardb05deb71999-08-10 19:04:08 +00002218}
2219
2220/**
2221 * xmlValidateNmtokensValue:
2222 * @value: an Mntokens value
2223 *
2224 * Validate that the given value match Nmtokens production
2225 *
2226 * [ VC: Name Token ]
2227 *
2228 * returns 1 if valid or 0 otherwise
2229 */
2230
2231int
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002232xmlValidateNmtokensValue(const xmlChar *value) {
2233 const xmlChar *cur;
Daniel Veillardb05deb71999-08-10 19:04:08 +00002234
2235 if (value == NULL) return(0);
2236 cur = value;
2237
Daniel Veillardcf461992000-03-14 18:30:20 +00002238 while (IS_BLANK(*cur)) cur++;
Daniel Veillardb05deb71999-08-10 19:04:08 +00002239 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2240 (*cur != '.') && (*cur != '-') &&
2241 (*cur != '_') && (*cur != ':') &&
2242 (!IS_COMBINING(*cur)) &&
2243 (!IS_EXTENDER(*cur)))
2244 return(0);
2245
2246 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2247 (*cur == '.') || (*cur == '-') ||
2248 (*cur == '_') || (*cur == ':') ||
2249 (IS_COMBINING(*cur)) ||
2250 (IS_EXTENDER(*cur)))
2251 cur++;
2252
2253 while (IS_BLANK(*cur)) {
2254 while (IS_BLANK(*cur)) cur++;
Daniel Veillardcf461992000-03-14 18:30:20 +00002255 if (*cur == 0) return(1);
Daniel Veillardb05deb71999-08-10 19:04:08 +00002256
2257 if (!IS_LETTER(*cur) && !IS_DIGIT(*cur) &&
2258 (*cur != '.') && (*cur != '-') &&
2259 (*cur != '_') && (*cur != ':') &&
2260 (!IS_COMBINING(*cur)) &&
2261 (!IS_EXTENDER(*cur)))
2262 return(0);
2263
2264 while ((IS_LETTER(*cur)) || (IS_DIGIT(*cur)) ||
2265 (*cur == '.') || (*cur == '-') ||
2266 (*cur == '_') || (*cur == ':') ||
2267 (IS_COMBINING(*cur)) ||
2268 (IS_EXTENDER(*cur)))
2269 cur++;
2270 }
2271
2272 if (*cur != 0) return(0);
2273
2274 return(1);
2275}
2276
2277/**
2278 * xmlValidateNotationDecl:
Daniel Veillardb96e6431999-08-29 21:02:19 +00002279 * @ctxt: the validation context
Daniel Veillardb05deb71999-08-10 19:04:08 +00002280 * @doc: a document instance
2281 * @nota: a notation definition
2282 *
2283 * Try to validate a single notation definition
2284 * basically it does the following checks as described by the
2285 * XML-1.0 recommendation:
2286 * - it seems that no validity constraing exist on notation declarations
2287 * But this function get called anyway ...
2288 *
2289 * returns 1 if valid or 0 otherwise
2290 */
2291
2292int
2293xmlValidateNotationDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2294 xmlNotationPtr nota) {
2295 int ret = 1;
2296
2297 return(ret);
2298}
2299
2300/**
2301 * xmlValidateAttributeValue:
2302 * @type: an attribute type
2303 * @value: an attribute value
2304 *
2305 * Validate that the given attribute value match the proper production
2306 *
2307 * [ VC: ID ]
2308 * Values of type ID must match the Name production....
2309 *
2310 * [ VC: IDREF ]
2311 * Values of type IDREF must match the Name production, and values
2312 * of type IDREFS must match Names ...
2313 *
2314 * [ VC: Entity Name ]
2315 * Values of type ENTITY must match the Name production, values
2316 * of type ENTITIES must match Names ...
2317 *
2318 * [ VC: Name Token ]
2319 * Values of type NMTOKEN must match the Nmtoken production; values
2320 * of type NMTOKENS must match Nmtokens.
2321 *
2322 * returns 1 if valid or 0 otherwise
2323 */
2324
2325int
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002326xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00002327 switch (type) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00002328 case XML_ATTRIBUTE_ENTITIES:
Daniel Veillardb96e6431999-08-29 21:02:19 +00002329 case XML_ATTRIBUTE_IDREFS:
Daniel Veillardb05deb71999-08-10 19:04:08 +00002330 return(xmlValidateNamesValue(value));
Daniel Veillardb96e6431999-08-29 21:02:19 +00002331 case XML_ATTRIBUTE_ENTITY:
Daniel Veillardb05deb71999-08-10 19:04:08 +00002332 case XML_ATTRIBUTE_IDREF:
2333 case XML_ATTRIBUTE_ID:
Daniel Veillardb05deb71999-08-10 19:04:08 +00002334 case XML_ATTRIBUTE_NOTATION:
2335 return(xmlValidateNameValue(value));
2336 case XML_ATTRIBUTE_NMTOKENS:
2337 case XML_ATTRIBUTE_ENUMERATION:
2338 return(xmlValidateNmtokensValue(value));
2339 case XML_ATTRIBUTE_NMTOKEN:
2340 return(xmlValidateNmtokenValue(value));
2341 case XML_ATTRIBUTE_CDATA:
2342 break;
2343 }
2344 return(1);
2345}
2346
2347/**
Daniel Veillardcf461992000-03-14 18:30:20 +00002348 * xmlValidateAttributeValue2:
2349 * @ctxt: the validation context
2350 * @doc: the document
2351 * @name: the attribute name (used for error reporting only)
2352 * @type: the attribute type
2353 * @value: the attribute value
2354 *
2355 * Validate that the given attribute value match a given type.
2356 * This typically cannot be done before having finished parsing
2357 * the subsets.
2358 *
2359 * [ VC: IDREF ]
2360 * Values of type IDREF must match one of the declared IDs
2361 * Values of type IDREFS must match a sequence of the declared IDs
2362 * each Name must match the value of an ID attribute on some element
2363 * in the XML document; i.e. IDREF values must match the value of
2364 * some ID attribute
2365 *
2366 * [ VC: Entity Name ]
2367 * Values of type ENTITY must match one declared entity
2368 * Values of type ENTITIES must match a sequence of declared entities
2369 *
2370 * [ VC: Notation Attributes ]
2371 * all notation names in the declaration must be declared.
2372 *
2373 * returns 1 if valid or 0 otherwise
2374 */
2375
2376int
2377xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2378 const xmlChar *name, xmlAttributeType type, const xmlChar *value) {
2379 int ret = 1;
2380 switch (type) {
2381 case XML_ATTRIBUTE_IDREFS:
2382 case XML_ATTRIBUTE_IDREF:
2383 case XML_ATTRIBUTE_ID:
2384 case XML_ATTRIBUTE_NMTOKENS:
2385 case XML_ATTRIBUTE_ENUMERATION:
2386 case XML_ATTRIBUTE_NMTOKEN:
2387 case XML_ATTRIBUTE_CDATA:
2388 break;
2389 case XML_ATTRIBUTE_ENTITY: {
2390 xmlEntityPtr ent;
2391
2392 ent = xmlGetDocEntity(doc, value);
2393 if (ent == NULL) {
2394 VERROR(ctxt->userData,
2395 "ENTITY attribute %s reference an unknown entity \"%s\"\n",
2396 name, value);
2397 ret = 0;
2398 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2399 VERROR(ctxt->userData,
2400 "ENTITY attribute %s reference an entity \"%s\" of wrong type\n",
2401 name, value);
2402 ret = 0;
2403 }
2404 break;
2405 }
2406 case XML_ATTRIBUTE_ENTITIES: {
2407 xmlChar *dup, *nam = NULL, *cur, save;
2408 xmlEntityPtr ent;
2409
2410 dup = xmlStrdup(value);
2411 if (dup == NULL)
2412 return(0);
2413 cur = dup;
2414 while (*cur != 0) {
2415 nam = cur;
2416 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
2417 save = *cur;
2418 *cur = 0;
2419 ent = xmlGetDocEntity(doc, nam);
2420 if (ent == NULL) {
2421 VERROR(ctxt->userData,
2422 "ENTITIES attribute %s reference an unknown entity \"%s\"\n",
2423 name, nam);
2424 ret = 0;
2425 } else if (ent->etype != XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) {
2426 VERROR(ctxt->userData,
2427 "ENTITIES attribute %s reference an entity \"%s\" of wrong type\n",
2428 name, nam);
2429 ret = 0;
2430 }
2431 if (save == 0)
2432 break;
2433 *cur = save;
2434 while (IS_BLANK(*cur)) cur++;
2435 }
2436 xmlFree(dup);
2437 break;
2438 }
2439 case XML_ATTRIBUTE_NOTATION: {
2440 xmlNotationPtr nota;
2441
2442 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2443 if ((nota == NULL) && (doc->extSubset != NULL))
2444 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2445
2446 if (nota == NULL) {
2447 VERROR(ctxt->userData,
2448 "NOTATION attribute %s reference an unknown notation \"%s\"\n",
2449 name, value);
2450 ret = 0;
2451 }
2452 break;
2453 }
2454 }
2455 return(ret);
2456}
2457
2458/**
2459 * xmlValidNormalizeAttributeValue:
2460 * @doc: the document
2461 * @elem: the parent
2462 * @name: the attribute name
2463 * @value: the attribute value
2464 *
2465 * Does the validation related extra step of the normalization of attribute
2466 * values:
2467 *
2468 * If the declared value is not CDATA, then the XML processor must further
2469 * process the normalized attribute value by discarding any leading and
2470 * trailing space (#x20) characters, and by replacing sequences of space
2471 * (#x20) characters by single space (#x20) character.
2472 *
2473 * returns a new normalized string if normalization is needed, NULL otherwise
2474 * the caller must free the returned value.
2475 */
2476
2477xmlChar *
2478xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem,
2479 const xmlChar *name, const xmlChar *value) {
2480 xmlChar *ret, *dst;
2481 const xmlChar *src;
Daniel Veillardbe803962000-06-28 23:40:59 +00002482 xmlAttributePtr attrDecl = NULL;
Daniel Veillardcf461992000-03-14 18:30:20 +00002483
2484 if (doc == NULL) return(NULL);
2485 if (elem == NULL) return(NULL);
2486 if (name == NULL) return(NULL);
2487 if (value == NULL) return(NULL);
2488
Daniel Veillardbe803962000-06-28 23:40:59 +00002489 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2490 xmlChar qname[500];
2491#ifdef HAVE_SNPRINTF
2492 snprintf((char *) qname, sizeof(qname), "%s:%s",
2493 elem->ns->prefix, elem->name);
2494#else
Daniel Veillard39c7d712000-09-10 16:14:55 +00002495 sprintf((char *) qname, "%s:%s", elem->ns->prefix, elem->name);
Daniel Veillardbe803962000-06-28 23:40:59 +00002496#endif
Daniel Veillard39c7d712000-09-10 16:14:55 +00002497 qname[sizeof(qname) - 1] = 0;
Daniel Veillardbe803962000-06-28 23:40:59 +00002498 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, name);
2499 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2500 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, qname, name);
2501 }
Daniel Veillardcf461992000-03-14 18:30:20 +00002502 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name);
2503 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2504 attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name);
2505
2506 if (attrDecl == NULL)
2507 return(NULL);
2508 if (attrDecl->atype == XML_ATTRIBUTE_CDATA)
2509 return(NULL);
2510
2511 ret = xmlStrdup(value);
2512 if (ret == NULL)
2513 return(NULL);
2514 src = value;
2515 dst = ret;
2516 while (*src == 0x20) src++;
2517 while (*src != 0) {
2518 if (*src == 0x20) {
2519 while (*src == 0x20) src++;
2520 if (*src != 0)
2521 *dst++ = 0x20;
2522 } else {
2523 *dst++ = *src++;
2524 }
2525 }
2526 *dst = 0;
2527 return(ret);
2528}
2529
Daniel Veillard126f2792000-10-24 17:10:12 +00002530void
2531xmlValidateAttributeIdCallback(xmlAttributePtr attr, int *count,
2532 const xmlChar* name) {
2533 if (attr->atype == XML_ATTRIBUTE_ID) (*count)++;
2534}
2535
Daniel Veillardcf461992000-03-14 18:30:20 +00002536/**
Daniel Veillardb05deb71999-08-10 19:04:08 +00002537 * xmlValidateAttributeDecl:
Daniel Veillardb96e6431999-08-29 21:02:19 +00002538 * @ctxt: the validation context
Daniel Veillardb05deb71999-08-10 19:04:08 +00002539 * @doc: a document instance
2540 * @attr: an attribute definition
2541 *
2542 * Try to validate a single attribute definition
2543 * basically it does the following checks as described by the
2544 * XML-1.0 recommendation:
2545 * - [ VC: Attribute Default Legal ]
2546 * - [ VC: Enumeration ]
2547 * - [ VC: ID Attribute Default ]
2548 *
2549 * The ID/IDREF uniqueness and matching are done separately
2550 *
2551 * returns 1 if valid or 0 otherwise
2552 */
2553
2554int
2555xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2556 xmlAttributePtr attr) {
2557 int ret = 1;
2558 int val;
2559 CHECK_DTD;
2560 if(attr == NULL) return(1);
2561
2562 /* Attribute Default Legal */
2563 /* Enumeration */
2564 if (attr->defaultValue != NULL) {
Daniel Veillardcf461992000-03-14 18:30:20 +00002565 val = xmlValidateAttributeValue(attr->atype, attr->defaultValue);
Daniel Veillardb05deb71999-08-10 19:04:08 +00002566 if (val == 0) {
2567 VERROR(ctxt->userData,
2568 "Syntax of default value for attribute %s on %s is not valid\n",
2569 attr->name, attr->elem);
2570 }
2571 ret &= val;
2572 }
2573
2574 /* ID Attribute Default */
Daniel Veillardcf461992000-03-14 18:30:20 +00002575 if ((attr->atype == XML_ATTRIBUTE_ID)&&
Daniel Veillardb05deb71999-08-10 19:04:08 +00002576 (attr->def != XML_ATTRIBUTE_IMPLIED) &&
2577 (attr->def != XML_ATTRIBUTE_REQUIRED)) {
2578 VERROR(ctxt->userData,
2579 "ID attribute %s on %s is not valid must be #IMPLIED or #REQUIRED\n",
2580 attr->name, attr->elem);
2581 ret = 0;
2582 }
2583
Daniel Veillardb96e6431999-08-29 21:02:19 +00002584 /* One ID per Element Type */
Daniel Veillardcf461992000-03-14 18:30:20 +00002585 if (attr->atype == XML_ATTRIBUTE_ID) {
2586 int nbId;
Daniel Veillardb05deb71999-08-10 19:04:08 +00002587
2588 /* the trick is taht we parse DtD as their own internal subset */
Daniel Veillardcf461992000-03-14 18:30:20 +00002589 xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset,
Daniel Veillardb05deb71999-08-10 19:04:08 +00002590 attr->elem);
2591 if (elem != NULL) {
2592 nbId = xmlScanIDAttributeDecl(NULL, elem);
Daniel Veillardcf461992000-03-14 18:30:20 +00002593 } else {
2594 xmlAttributeTablePtr table;
Daniel Veillardcf461992000-03-14 18:30:20 +00002595
2596 /*
2597 * The attribute may be declared in the internal subset and the
2598 * element in the external subset.
2599 */
2600 nbId = 0;
Daniel Veillard32bc74e2000-07-14 14:49:25 +00002601 table = (xmlAttributeTablePtr) doc->intSubset->attributes;
Daniel Veillard126f2792000-10-24 17:10:12 +00002602 xmlHashScan3(table, NULL, NULL, attr->elem, (xmlHashScanner)
2603 xmlValidateAttributeIdCallback, &nbId);
Daniel Veillardb05deb71999-08-10 19:04:08 +00002604 }
Daniel Veillardcf461992000-03-14 18:30:20 +00002605 if (nbId > 1) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00002606 VERROR(ctxt->userData,
Daniel Veillardcf461992000-03-14 18:30:20 +00002607 "Element %s has %d ID attribute defined in the internal subset : %s\n",
2608 attr->elem, nbId, attr->name);
2609 } else if (doc->extSubset != NULL) {
2610 int extId = 0;
2611 elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem);
2612 if (elem != NULL) {
2613 extId = xmlScanIDAttributeDecl(NULL, elem);
2614 }
2615 if (extId > 1) {
2616 VERROR(ctxt->userData,
2617 "Element %s has %d ID attribute defined in the external subset : %s\n",
2618 attr->elem, extId, attr->name);
2619 } else if (extId + nbId > 1) {
2620 VERROR(ctxt->userData,
2621"Element %s has ID attributes defined in the internal and external subset : %s\n",
2622 attr->elem, attr->name);
2623 }
2624 }
2625 }
2626
2627 /* Validity Constraint: Enumeration */
2628 if ((attr->defaultValue != NULL) && (attr->tree != NULL)) {
2629 xmlEnumerationPtr tree = attr->tree;
2630 while (tree != NULL) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00002631 if (xmlStrEqual(tree->name, attr->defaultValue)) break;
Daniel Veillardcf461992000-03-14 18:30:20 +00002632 tree = tree->next;
2633 }
2634 if (tree == NULL) {
2635 VERROR(ctxt->userData,
2636"Default value \"%s\" for attribute %s on %s is not among the enumerated set\n",
2637 attr->defaultValue, attr->name, attr->elem);
2638 ret = 0;
2639 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00002640 }
2641
2642 return(ret);
2643}
2644
2645/**
2646 * xmlValidateElementDecl:
2647 * @ctxt: the validation context
2648 * @doc: a document instance
2649 * @elem: an element definition
2650 *
2651 * Try to validate a single element definition
2652 * basically it does the following checks as described by the
2653 * XML-1.0 recommendation:
2654 * - [ VC: One ID per Element Type ]
2655 * - [ VC: No Duplicate Types ]
2656 * - [ VC: Unique Element Type Declaration ]
2657 *
2658 * returns 1 if valid or 0 otherwise
2659 */
2660
2661int
2662xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
2663 xmlElementPtr elem) {
2664 int ret = 1;
2665 xmlElementPtr tst;
2666
2667 CHECK_DTD;
2668
2669 if (elem == NULL) return(1);
2670
2671 /* No Duplicate Types */
Daniel Veillardcf461992000-03-14 18:30:20 +00002672 if (elem->etype == XML_ELEMENT_TYPE_MIXED) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00002673 xmlElementContentPtr cur, next;
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002674 const xmlChar *name;
Daniel Veillardb05deb71999-08-10 19:04:08 +00002675
2676 cur = elem->content;
2677 while (cur != NULL) {
2678 if (cur->type != XML_ELEMENT_CONTENT_OR) break;
2679 if (cur->c1 == NULL) break;
2680 if (cur->c1->type == XML_ELEMENT_CONTENT_ELEMENT) {
2681 name = cur->c1->name;
2682 next = cur->c2;
2683 while (next != NULL) {
2684 if (next->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00002685 if (xmlStrEqual(next->name, name)) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00002686 VERROR(ctxt->userData,
2687 "Definition of %s has duplicate references of %s\n",
2688 elem->name, name);
2689 ret = 0;
2690 }
2691 break;
2692 }
2693 if (next->c1 == NULL) break;
2694 if (next->c1->type != XML_ELEMENT_CONTENT_ELEMENT) break;
Daniel Veillard8b5dd832000-10-01 20:28:44 +00002695 if (xmlStrEqual(next->c1->name, name)) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00002696 VERROR(ctxt->userData,
2697 "Definition of %s has duplicate references of %s\n",
2698 elem->name, name);
2699 ret = 0;
2700 }
2701 next = next->c2;
2702 }
2703 }
2704 cur = cur->c2;
2705 }
2706 }
2707
2708 /* VC: Unique Element Type Declaration */
2709 tst = xmlGetDtdElementDesc(doc->intSubset, elem->name);
2710 if ((tst != NULL ) && (tst != elem)) {
2711 VERROR(ctxt->userData, "Redefinition of element %s\n",
2712 elem->name);
2713 ret = 0;
2714 }
2715 tst = xmlGetDtdElementDesc(doc->extSubset, elem->name);
2716 if ((tst != NULL ) && (tst != elem)) {
2717 VERROR(ctxt->userData, "Redefinition of element %s\n",
2718 elem->name);
2719 ret = 0;
2720 }
2721
2722 /* One ID per Element Type */
2723 if (xmlScanIDAttributeDecl(ctxt, elem) > 1) {
2724 ret = 0;
2725 }
2726 return(ret);
2727}
2728
2729/**
2730 * xmlValidateOneAttribute:
2731 * @ctxt: the validation context
2732 * @doc: a document instance
2733 * @elem: an element instance
2734 * @attr: an attribute instance
Daniel Veillard00fdf371999-10-08 09:40:39 +00002735 * @value: the attribute value (without entities processing)
Daniel Veillardb05deb71999-08-10 19:04:08 +00002736 *
2737 * Try to validate a single attribute for an element
Daniel Veillardcf461992000-03-14 18:30:20 +00002738 * basically it does the following checks as described by the
Daniel Veillardb05deb71999-08-10 19:04:08 +00002739 * XML-1.0 recommendation:
2740 * - [ VC: Attribute Value Type ]
2741 * - [ VC: Fixed Attribute Default ]
2742 * - [ VC: Entity Name ]
2743 * - [ VC: Name Token ]
2744 * - [ VC: ID ]
2745 * - [ VC: IDREF ]
2746 * - [ VC: Entity Name ]
2747 * - [ VC: Notation Attributes ]
2748 *
2749 * The ID/IDREF uniqueness and matching are done separately
2750 *
2751 * returns 1 if valid or 0 otherwise
2752 */
2753
2754int
2755xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
Daniel Veillarddd6b3671999-09-23 22:19:22 +00002756 xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002757 /* xmlElementPtr elemDecl; */
Daniel Veillardbe803962000-06-28 23:40:59 +00002758 xmlAttributePtr attrDecl = NULL;
Daniel Veillardb05deb71999-08-10 19:04:08 +00002759 int val;
2760 int ret = 1;
2761
2762 CHECK_DTD;
2763 if ((elem == NULL) || (elem->name == NULL)) return(0);
2764 if ((attr == NULL) || (attr->name == NULL)) return(0);
2765
Daniel Veillardbe803962000-06-28 23:40:59 +00002766 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
2767 xmlChar qname[500];
2768#ifdef HAVE_SNPRINTF
2769 snprintf((char *) qname, sizeof(qname), "%s:%s",
2770 elem->ns->prefix, elem->name);
2771#else
Daniel Veillard39c7d712000-09-10 16:14:55 +00002772 sprintf((char *) qname, "%s:%s", elem->ns->prefix, elem->name);
Daniel Veillardbe803962000-06-28 23:40:59 +00002773#endif
Daniel Veillard39c7d712000-09-10 16:14:55 +00002774 qname[sizeof(qname) - 1] = 0;
Daniel Veillardbe803962000-06-28 23:40:59 +00002775 if (attr->ns != NULL) {
2776 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, qname,
2777 attr->name, attr->ns->prefix);
2778 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2779 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, qname,
2780 attr->name, attr->ns->prefix);
2781 } else {
2782 attrDecl = xmlGetDtdAttrDesc(doc->intSubset, qname, attr->name);
2783 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2784 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2785 qname, attr->name);
2786 }
2787 }
2788 if (attrDecl == NULL) {
2789 if (attr->ns != NULL) {
2790 attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name,
2791 attr->name, attr->ns->prefix);
2792 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2793 attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name,
2794 attr->name, attr->ns->prefix);
2795 } else {
2796 attrDecl = xmlGetDtdAttrDesc(doc->intSubset,
2797 elem->name, attr->name);
2798 if ((attrDecl == NULL) && (doc->extSubset != NULL))
2799 attrDecl = xmlGetDtdAttrDesc(doc->extSubset,
2800 elem->name, attr->name);
2801 }
2802 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00002803
2804
2805 /* Validity Constraint: Attribute Value Type */
2806 if (attrDecl == NULL) {
2807 VERROR(ctxt->userData,
2808 "No declaration for attribute %s on element %s\n",
2809 attr->name, elem->name);
2810 return(0);
2811 }
Daniel Veillardcf461992000-03-14 18:30:20 +00002812 attr->atype = attrDecl->atype;
2813
2814 val = xmlValidateAttributeValue(attrDecl->atype, value);
Daniel Veillardb05deb71999-08-10 19:04:08 +00002815 if (val == 0) {
2816 VERROR(ctxt->userData,
2817 "Syntax of value for attribute %s on %s is not valid\n",
2818 attr->name, elem->name);
2819 ret = 0;
2820 }
2821
Daniel Veillardcf461992000-03-14 18:30:20 +00002822 /* Validity constraint: Fixed Attribute Default */
2823 if (attrDecl->def == XML_ATTRIBUTE_FIXED) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00002824 if (!xmlStrEqual(value, attrDecl->defaultValue)) {
Daniel Veillardcf461992000-03-14 18:30:20 +00002825 VERROR(ctxt->userData,
2826 "Value for attribute %s on %s is differnt from default \"%s\"\n",
2827 attr->name, elem->name, attrDecl->defaultValue);
2828 ret = 0;
2829 }
2830 }
2831
Daniel Veillardb96e6431999-08-29 21:02:19 +00002832 /* Validity Constraint: ID uniqueness */
Daniel Veillardcf461992000-03-14 18:30:20 +00002833 if (attrDecl->atype == XML_ATTRIBUTE_ID) {
Daniel Veillardb96e6431999-08-29 21:02:19 +00002834 xmlAddID(ctxt, doc, value, attr);
2835 }
2836
Daniel Veillardcf461992000-03-14 18:30:20 +00002837 if ((attrDecl->atype == XML_ATTRIBUTE_IDREF) ||
2838 (attrDecl->atype == XML_ATTRIBUTE_IDREFS)) {
Daniel Veillardc08a2c61999-09-08 21:35:25 +00002839 xmlAddRef(ctxt, doc, value, attr);
2840 }
2841
Daniel Veillardb05deb71999-08-10 19:04:08 +00002842 /* Validity Constraint: Notation Attributes */
Daniel Veillardcf461992000-03-14 18:30:20 +00002843 if (attrDecl->atype == XML_ATTRIBUTE_NOTATION) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00002844 xmlEnumerationPtr tree = attrDecl->tree;
2845 xmlNotationPtr nota;
2846
2847 /* First check that the given NOTATION was declared */
2848 nota = xmlGetDtdNotationDesc(doc->intSubset, value);
2849 if (nota == NULL)
2850 nota = xmlGetDtdNotationDesc(doc->extSubset, value);
2851
2852 if (nota == NULL) {
2853 VERROR(ctxt->userData,
2854 "Value \"%s\" for attribute %s on %s is not a declared Notation\n",
2855 value, attr->name, elem->name);
2856 ret = 0;
2857 }
2858
2859 /* Second, verify that it's among the list */
2860 while (tree != NULL) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00002861 if (xmlStrEqual(tree->name, value)) break;
Daniel Veillardb05deb71999-08-10 19:04:08 +00002862 tree = tree->next;
2863 }
2864 if (tree == NULL) {
2865 VERROR(ctxt->userData,
Daniel Veillardcf461992000-03-14 18:30:20 +00002866"Value \"%s\" for attribute %s on %s is not among the enumerated notations\n",
Daniel Veillardb05deb71999-08-10 19:04:08 +00002867 value, attr->name, elem->name);
2868 ret = 0;
2869 }
2870 }
2871
2872 /* Validity Constraint: Enumeration */
Daniel Veillardcf461992000-03-14 18:30:20 +00002873 if (attrDecl->atype == XML_ATTRIBUTE_ENUMERATION) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00002874 xmlEnumerationPtr tree = attrDecl->tree;
2875 while (tree != NULL) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00002876 if (xmlStrEqual(tree->name, value)) break;
Daniel Veillardb05deb71999-08-10 19:04:08 +00002877 tree = tree->next;
2878 }
2879 if (tree == NULL) {
2880 VERROR(ctxt->userData,
Daniel Veillard3a2ebdd2000-01-25 19:27:27 +00002881 "Value \"%s\" for attribute %s on %s is not among the enumerated set\n",
Daniel Veillardb05deb71999-08-10 19:04:08 +00002882 value, attr->name, elem->name);
2883 ret = 0;
2884 }
2885 }
2886
2887 /* Fixed Attribute Default */
2888 if ((attrDecl->def == XML_ATTRIBUTE_FIXED) &&
Daniel Veillard8b5dd832000-10-01 20:28:44 +00002889 (!xmlStrEqual(attrDecl->defaultValue, value))) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00002890 VERROR(ctxt->userData,
2891 "Value for attribute %s on %s must be \"%s\"\n",
2892 attr->name, elem->name, attrDecl->defaultValue);
2893 ret = 0;
2894 }
2895
Daniel Veillardcf461992000-03-14 18:30:20 +00002896 /* Extra check for the attribute value */
2897 ret &= xmlValidateAttributeValue2(ctxt, doc, attr->name,
2898 attrDecl->atype, value);
2899
Daniel Veillardb05deb71999-08-10 19:04:08 +00002900 return(ret);
2901}
2902
2903int xmlValidateElementTypeElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
2904 xmlElementContentPtr cont);
2905
2906/**
2907 * xmlValidateElementTypeExpr:
2908 * @ctxt: the validation context
2909 * @child: pointer to the child list
2910 * @cont: pointer to the content declaration
2911 *
2912 * Try to validate the content of an element of type element
2913 * but don't handle the occurence factor
2914 *
2915 * returns 1 if valid or 0 and -1 if PCDATA stuff is found,
2916 * also update child value in-situ.
2917 */
2918
2919int
2920xmlValidateElementTypeExpr(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
2921 xmlElementContentPtr cont) {
2922 xmlNodePtr cur;
2923 int ret = 1;
2924
2925 if (cont == NULL) return(-1);
Daniel Veillardcf461992000-03-14 18:30:20 +00002926 DEBUG_VALID_STATE(*child, cont)
Daniel Veillardb05deb71999-08-10 19:04:08 +00002927 while (*child != NULL) {
Daniel Veillardcf461992000-03-14 18:30:20 +00002928 if ((*child)->type == XML_ENTITY_REF_NODE) {
2929 /*
2930 * If there is an entity declared an it's not empty
2931 * Push the current node on the stack and process with the
2932 * entity content.
2933 */
2934 if (((*child)->children != NULL) &&
2935 ((*child)->children->children != NULL)) {
2936 nodeVPush(ctxt, *child);
2937 *child = (*child)->children->children;
2938 } else
2939 *child = (*child)->next;
2940 continue;
2941 }
Daniel Veillardcd429612000-10-11 15:57:05 +00002942 if (((*child)->type == XML_TEXT_NODE) &&
2943 (xmlIsBlankNode(*child)) &&
2944 ((cont->type == XML_ELEMENT_CONTENT_ELEMENT) ||
2945 (cont->type == XML_ELEMENT_CONTENT_SEQ) ||
2946 (cont->type == XML_ELEMENT_CONTENT_OR))) {
2947 *child = (*child)->next;
2948 continue;
2949 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00002950 if ((*child)->type == XML_PI_NODE) {
2951 *child = (*child)->next;
2952 continue;
2953 }
2954 if ((*child)->type == XML_COMMENT_NODE) {
2955 *child = (*child)->next;
2956 continue;
2957 }
2958 else if ((*child)->type != XML_ELEMENT_NODE) {
2959 return(-1);
2960 }
2961 break;
2962 }
Daniel Veillardcf461992000-03-14 18:30:20 +00002963 DEBUG_VALID_STATE(*child, cont)
Daniel Veillardb05deb71999-08-10 19:04:08 +00002964 switch (cont->type) {
2965 case XML_ELEMENT_CONTENT_PCDATA:
Daniel Veillardda07c342000-01-25 18:31:22 +00002966 if (*child == NULL) return(0);
2967 if ((*child)->type == XML_TEXT_NODE) return(1);
2968 return(0);
Daniel Veillardb05deb71999-08-10 19:04:08 +00002969 case XML_ELEMENT_CONTENT_ELEMENT:
2970 if (*child == NULL) return(0);
Daniel Veillard8b5dd832000-10-01 20:28:44 +00002971 ret = (xmlStrEqual((*child)->name, cont->name));
Daniel Veillardcf461992000-03-14 18:30:20 +00002972 if (ret == 1) {
2973 while ((*child)->next == NULL) {
2974 if (((*child)->parent != NULL) &&
2975 ((*child)->parent->type == XML_ENTITY_DECL)) {
2976 *child = nodeVPop(ctxt);
2977 } else
2978 break;
2979 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00002980 *child = (*child)->next;
Daniel Veillardcf461992000-03-14 18:30:20 +00002981 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00002982 return(ret);
2983 case XML_ELEMENT_CONTENT_OR:
2984 cur = *child;
2985 ret = xmlValidateElementTypeElement(ctxt, child, cont->c1);
2986 if (ret == -1) return(-1);
2987 if (ret == 1) {
2988 return(1);
2989 }
2990 /* rollback and retry the other path */
2991 *child = cur;
2992 ret = xmlValidateElementTypeElement(ctxt, child, cont->c2);
2993 if (ret == -1) return(-1);
2994 if (ret == 0) {
2995 *child = cur;
2996 return(0);
2997 }
2998 return(1);
2999 case XML_ELEMENT_CONTENT_SEQ:
3000 cur = *child;
3001 ret = xmlValidateElementTypeElement(ctxt, child, cont->c1);
3002 if (ret == -1) return(-1);
3003 if (ret == 0) {
3004 *child = cur;
3005 return(0);
3006 }
3007 ret = xmlValidateElementTypeElement(ctxt, child, cont->c2);
3008 if (ret == -1) return(-1);
3009 if (ret == 0) {
3010 *child = cur;
3011 return(0);
3012 }
3013 return(1);
3014 }
3015 return(ret);
3016}
3017
3018/**
3019 * xmlValidateElementTypeElement:
3020 * @ctxt: the validation context
3021 * @child: pointer to the child list
3022 * @cont: pointer to the content declaration
3023 *
3024 * Try to validate the content of an element of type element
3025 * yeah, Yet Another Regexp Implementation, and recursive
3026 *
3027 * returns 1 if valid or 0 and -1 if PCDATA stuff is found,
3028 * also update child and content values in-situ.
3029 */
3030
3031int
3032xmlValidateElementTypeElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
3033 xmlElementContentPtr cont) {
3034 xmlNodePtr cur;
3035 int ret = 1;
3036
3037 if (cont == NULL) return(-1);
Daniel Veillardcf461992000-03-14 18:30:20 +00003038
3039 DEBUG_VALID_STATE(*child, cont)
Daniel Veillardb05deb71999-08-10 19:04:08 +00003040 while (*child != NULL) {
Daniel Veillardcf461992000-03-14 18:30:20 +00003041 if ((*child)->type == XML_ENTITY_REF_NODE) {
3042 /*
3043 * If there is an entity declared an it's not empty
3044 * Push the current node on the stack and process with the
3045 * entity content.
3046 */
3047 if (((*child)->children != NULL) &&
3048 ((*child)->children->children != NULL)) {
3049 nodeVPush(ctxt, *child);
3050 *child = (*child)->children->children;
3051 } else
3052 *child = (*child)->next;
3053 continue;
3054 }
Daniel Veillardcd429612000-10-11 15:57:05 +00003055 if (((*child)->type == XML_TEXT_NODE) &&
3056 (xmlIsBlankNode(*child)) &&
3057 ((cont->type == XML_ELEMENT_CONTENT_ELEMENT) ||
3058 (cont->type == XML_ELEMENT_CONTENT_SEQ) ||
3059 (cont->type == XML_ELEMENT_CONTENT_OR))) {
3060 *child = (*child)->next;
3061 continue;
3062 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003063 if ((*child)->type == XML_PI_NODE) {
3064 *child = (*child)->next;
3065 continue;
3066 }
3067 if ((*child)->type == XML_COMMENT_NODE) {
3068 *child = (*child)->next;
3069 continue;
3070 }
3071 else if ((*child)->type != XML_ELEMENT_NODE) {
3072 return(-1);
3073 }
3074 break;
3075 }
Daniel Veillardcf461992000-03-14 18:30:20 +00003076 DEBUG_VALID_STATE(*child, cont)
Daniel Veillardb05deb71999-08-10 19:04:08 +00003077 cur = *child;
3078 ret = xmlValidateElementTypeExpr(ctxt, child, cont);
3079 if (ret == -1) return(-1);
3080 switch (cont->ocur) {
3081 case XML_ELEMENT_CONTENT_ONCE:
3082 if (ret == 1) {
Daniel Veillard3a2ebdd2000-01-25 19:27:27 +00003083 /* skip ignorable elems */
3084 while ((*child != NULL) &&
3085 (((*child)->type == XML_PI_NODE) ||
3086 ((*child)->type == XML_COMMENT_NODE))) {
Daniel Veillardcf461992000-03-14 18:30:20 +00003087 while ((*child)->next == NULL) {
3088 if (((*child)->parent != NULL) &&
3089 ((*child)->parent->type == XML_ENTITY_REF_NODE)) {
3090 *child = (*child)->parent;
3091 } else
3092 break;
3093 }
3094 *child = (*child)->next;
Daniel Veillard3a2ebdd2000-01-25 19:27:27 +00003095 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003096 return(1);
3097 }
3098 *child = cur;
3099 return(0);
3100 case XML_ELEMENT_CONTENT_OPT:
3101 if (ret == 0) {
3102 *child = cur;
3103 return(1);
3104 }
3105 break;
3106 case XML_ELEMENT_CONTENT_MULT:
3107 if (ret == 0) {
3108 *child = cur;
3109 break;
3110 }
3111 /* no break on purpose */
3112 case XML_ELEMENT_CONTENT_PLUS:
3113 if (ret == 0) {
3114 *child = cur;
3115 return(0);
3116 }
Daniel Veillard683cb022000-10-22 12:04:13 +00003117 if (ret == -1) return(-1);
3118 cur = *child;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003119 do {
Daniel Veillard683cb022000-10-22 12:04:13 +00003120 if (*child == NULL)
3121 break; /* while */
3122 if (((*child)->type == XML_TEXT_NODE) &&
3123 (xmlIsBlankNode(*child))) {
3124 *child = (*child)->next;
3125 continue;
3126 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003127 ret = xmlValidateElementTypeExpr(ctxt, child, cont);
Daniel Veillard683cb022000-10-22 12:04:13 +00003128 if (ret == 1)
3129 cur = *child;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003130 } while (ret == 1);
3131 if (ret == -1) return(-1);
3132 *child = cur;
3133 break;
3134 }
3135 while (*child != NULL) {
Daniel Veillardcf461992000-03-14 18:30:20 +00003136 if ((*child)->type == XML_ENTITY_REF_NODE) {
3137 /*
3138 * If there is an entity declared an it's not empty
3139 * Push the current node on the stack and process with the
3140 * entity content.
3141 */
3142 if (((*child)->children != NULL) &&
3143 ((*child)->children->children != NULL)) {
3144 nodeVPush(ctxt, *child);
3145 *child = (*child)->children->children;
3146 } else
3147 *child = (*child)->next;
3148 continue;
3149 }
Daniel Veillardcd429612000-10-11 15:57:05 +00003150 if (((*child)->type == XML_TEXT_NODE) &&
3151 (xmlIsBlankNode(*child)) &&
3152 ((cont->type == XML_ELEMENT_CONTENT_ELEMENT) ||
3153 (cont->type == XML_ELEMENT_CONTENT_SEQ) ||
3154 (cont->type == XML_ELEMENT_CONTENT_OR))) {
3155 *child = (*child)->next;
3156 continue;
3157 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003158 if ((*child)->type == XML_PI_NODE) {
3159 *child = (*child)->next;
3160 continue;
3161 }
3162 if ((*child)->type == XML_COMMENT_NODE) {
3163 *child = (*child)->next;
3164 continue;
3165 }
3166 else if ((*child)->type != XML_ELEMENT_NODE) {
3167 return(-1);
3168 }
3169 break;
3170 }
3171 return(1);
3172}
3173
3174/**
3175 * xmlSprintfElementChilds:
3176 * @buf: an output buffer
3177 * @content: An element
3178 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3179 *
3180 * This will dump the list of childs to the buffer
3181 * Intended just for the debug routine
3182 */
3183void
3184xmlSprintfElementChilds(char *buf, xmlNodePtr node, int glob) {
3185 xmlNodePtr cur;
3186
3187 if (node == NULL) return;
3188 if (glob) strcat(buf, "(");
Daniel Veillardcf461992000-03-14 18:30:20 +00003189 cur = node->children;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003190 while (cur != NULL) {
3191 switch (cur->type) {
3192 case XML_ELEMENT_NODE:
Daniel Veillardb96e6431999-08-29 21:02:19 +00003193 strcat(buf, (char *) cur->name);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003194 if (cur->next != NULL)
3195 strcat(buf, " ");
3196 break;
3197 case XML_TEXT_NODE:
Daniel Veillardcd429612000-10-11 15:57:05 +00003198 if (xmlIsBlankNode(cur))
3199 break;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003200 case XML_CDATA_SECTION_NODE:
3201 case XML_ENTITY_REF_NODE:
3202 strcat(buf, "CDATA");
3203 if (cur->next != NULL)
3204 strcat(buf, " ");
3205 break;
3206 case XML_ATTRIBUTE_NODE:
3207 case XML_DOCUMENT_NODE:
Daniel Veillard04698d92000-09-17 16:00:22 +00003208#ifdef LIBXML_SGML_ENABLED
3209 case XML_SGML_DOCUMENT_NODE:
3210#endif
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003211 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb05deb71999-08-10 19:04:08 +00003212 case XML_DOCUMENT_TYPE_NODE:
3213 case XML_DOCUMENT_FRAG_NODE:
3214 case XML_NOTATION_NODE:
Daniel Veillard9e8bfae2000-11-06 16:43:11 +00003215 case XML_NAMESPACE_DECL:
Daniel Veillardb05deb71999-08-10 19:04:08 +00003216 strcat(buf, "???");
3217 if (cur->next != NULL)
3218 strcat(buf, " ");
3219 break;
3220 case XML_ENTITY_NODE:
3221 case XML_PI_NODE:
Daniel Veillardcf461992000-03-14 18:30:20 +00003222 case XML_DTD_NODE:
Daniel Veillardb05deb71999-08-10 19:04:08 +00003223 case XML_COMMENT_NODE:
Daniel Veillardcf461992000-03-14 18:30:20 +00003224 case XML_ELEMENT_DECL:
3225 case XML_ATTRIBUTE_DECL:
3226 case XML_ENTITY_DECL:
Daniel Veillard9e8bfae2000-11-06 16:43:11 +00003227 case XML_XINCLUDE_START:
3228 case XML_XINCLUDE_END:
Daniel Veillardb05deb71999-08-10 19:04:08 +00003229 break;
3230 }
3231 cur = cur->next;
3232 }
3233 if (glob) strcat(buf, ")");
3234}
3235
3236
3237/**
3238 * xmlValidateOneElement:
3239 * @ctxt: the validation context
3240 * @doc: a document instance
3241 * @elem: an element instance
3242 *
3243 * Try to validate a single element and it's attributes,
3244 * basically it does the following checks as described by the
3245 * XML-1.0 recommendation:
3246 * - [ VC: Element Valid ]
3247 * - [ VC: Required Attribute ]
3248 * Then call xmlValidateOneAttribute() for each attribute present.
3249 *
3250 * The ID/IDREF checkings are done separately
3251 *
3252 * returns 1 if valid or 0 otherwise
3253 */
3254
3255int
3256xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3257 xmlNodePtr elem) {
Daniel Veillardbe803962000-06-28 23:40:59 +00003258 xmlElementPtr elemDecl = NULL;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003259 xmlElementContentPtr cont;
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003260 xmlAttributePtr attr;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003261 xmlNodePtr child;
3262 int ret = 1;
Daniel Veillarddd6b3671999-09-23 22:19:22 +00003263 const xmlChar *name;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003264
3265 CHECK_DTD;
3266
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003267 if (elem == NULL) return(0);
3268 if (elem->type == XML_TEXT_NODE) {
3269 }
3270 switch (elem->type) {
3271 case XML_ATTRIBUTE_NODE:
3272 VERROR(ctxt->userData,
3273 "Attribute element not expected here\n");
3274 return(0);
3275 case XML_TEXT_NODE:
Daniel Veillardcf461992000-03-14 18:30:20 +00003276 if (elem->children != NULL) {
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003277 VERROR(ctxt->userData, "Text element has childs !\n");
3278 return(0);
3279 }
3280 if (elem->properties != NULL) {
3281 VERROR(ctxt->userData, "Text element has attributes !\n");
3282 return(0);
3283 }
3284 if (elem->ns != NULL) {
3285 VERROR(ctxt->userData, "Text element has namespace !\n");
3286 return(0);
3287 }
Daniel Veillard87b95392000-08-12 21:12:04 +00003288 if (elem->nsDef != NULL) {
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003289 VERROR(ctxt->userData,
3290 "Text element carries namespace definitions !\n");
3291 return(0);
3292 }
3293 if (elem->content == NULL) {
3294 VERROR(ctxt->userData,
3295 "Text element has no content !\n");
3296 return(0);
3297 }
3298 return(1);
3299 case XML_CDATA_SECTION_NODE:
3300 case XML_ENTITY_REF_NODE:
3301 case XML_PI_NODE:
3302 case XML_COMMENT_NODE:
3303 return(1);
3304 case XML_ENTITY_NODE:
3305 VERROR(ctxt->userData,
3306 "Entity element not expected here\n");
3307 return(0);
3308 case XML_NOTATION_NODE:
3309 VERROR(ctxt->userData,
3310 "Notation element not expected here\n");
3311 return(0);
3312 case XML_DOCUMENT_NODE:
3313 case XML_DOCUMENT_TYPE_NODE:
3314 case XML_DOCUMENT_FRAG_NODE:
3315 VERROR(ctxt->userData,
3316 "Document element not expected here\n");
3317 return(0);
3318 case XML_HTML_DOCUMENT_NODE:
3319 VERROR(ctxt->userData,
3320 "\n");
3321 return(0);
3322 case XML_ELEMENT_NODE:
3323 break;
3324 default:
3325 VERROR(ctxt->userData,
3326 "unknown element type %d\n", elem->type);
3327 return(0);
3328 }
3329 if (elem->name == NULL) return(0);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003330
Daniel Veillardbe803962000-06-28 23:40:59 +00003331 /*
3332 * Fetch the declaration for the qualified name
3333 */
3334 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3335 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
3336 elem->name, elem->ns->prefix);
3337 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3338 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
3339 elem->name, elem->ns->prefix);
3340 }
3341
3342 /*
3343 * Fetch the declaration for the non qualified name
3344 */
3345 if (elemDecl == NULL) {
3346 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
3347 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3348 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
3349 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003350 if (elemDecl == NULL) {
3351 VERROR(ctxt->userData, "No declaration for element %s\n",
3352 elem->name);
3353 return(0);
3354 }
3355
3356 /* Check taht the element content matches the definition */
Daniel Veillardcf461992000-03-14 18:30:20 +00003357 switch (elemDecl->etype) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00003358 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardcf461992000-03-14 18:30:20 +00003359 if (elem->children != NULL) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00003360 VERROR(ctxt->userData,
3361 "Element %s was declared EMPTY this one has content\n",
3362 elem->name);
3363 ret = 0;
3364 }
3365 break;
3366 case XML_ELEMENT_TYPE_ANY:
3367 /* I don't think anything is required then */
3368 break;
3369 case XML_ELEMENT_TYPE_MIXED:
3370 /* Hum, this start to get messy */
Daniel Veillardcf461992000-03-14 18:30:20 +00003371 child = elem->children;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003372 while (child != NULL) {
3373 if (child->type == XML_ELEMENT_NODE) {
3374 name = child->name;
Daniel Veillardbe803962000-06-28 23:40:59 +00003375 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
3376 xmlChar qname[500];
3377#ifdef HAVE_SNPRINTF
3378 snprintf((char *) qname, sizeof(qname), "%s:%s",
3379 child->ns->prefix, child->name);
3380#else
Daniel Veillard39c7d712000-09-10 16:14:55 +00003381 sprintf((char *) qname, "%s:%s",
3382 child->ns->prefix, child->name);
Daniel Veillardbe803962000-06-28 23:40:59 +00003383#endif
Daniel Veillard39c7d712000-09-10 16:14:55 +00003384 qname[sizeof(qname) - 1] = 0;
Daniel Veillardbe803962000-06-28 23:40:59 +00003385 cont = elemDecl->content;
3386 while (cont != NULL) {
3387 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003388 if (xmlStrEqual(cont->name, qname)) break;
Daniel Veillardbe803962000-06-28 23:40:59 +00003389 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
3390 (cont->c1 != NULL) &&
3391 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003392 if (xmlStrEqual(cont->c1->name, qname)) break;
Daniel Veillardbe803962000-06-28 23:40:59 +00003393 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
3394 (cont->c1 == NULL) ||
3395 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
3396 /* Internal error !!! */
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00003397 xmlGenericError(xmlGenericErrorContext,
3398 "Internal: MIXED struct bad\n");
Daniel Veillardbe803962000-06-28 23:40:59 +00003399 break;
3400 }
3401 cont = cont->c2;
3402 }
3403 if (cont != NULL)
3404 goto child_ok;
3405 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003406 cont = elemDecl->content;
3407 while (cont != NULL) {
3408 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003409 if (xmlStrEqual(cont->name, name)) break;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003410 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
3411 (cont->c1 != NULL) &&
3412 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003413 if (xmlStrEqual(cont->c1->name, name)) break;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003414 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
3415 (cont->c1 == NULL) ||
3416 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
3417 /* Internal error !!! */
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00003418 xmlGenericError(xmlGenericErrorContext,
3419 "Internal: MIXED struct bad\n");
Daniel Veillardb05deb71999-08-10 19:04:08 +00003420 break;
3421 }
3422 cont = cont->c2;
3423 }
3424 if (cont == NULL) {
3425 VERROR(ctxt->userData,
3426 "Element %s is not declared in %s list of possible childs\n",
3427 name, elem->name);
3428 ret = 0;
3429 }
3430 }
Daniel Veillardbe803962000-06-28 23:40:59 +00003431child_ok:
Daniel Veillardb05deb71999-08-10 19:04:08 +00003432 child = child->next;
3433 }
3434 break;
3435 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillardcf461992000-03-14 18:30:20 +00003436 child = elem->children;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003437 cont = elemDecl->content;
3438 ret = xmlValidateElementTypeElement(ctxt, &child, cont);
Daniel Veillard683cb022000-10-22 12:04:13 +00003439 while ((child != NULL) && (child->type == XML_TEXT_NODE) &&
3440 (xmlIsBlankNode(child))) {
3441 child = child->next;
3442 continue;
3443 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003444 if ((ret == 0) || (child != NULL)) {
3445 char expr[1000];
3446 char list[2000];
3447
3448 expr[0] = 0;
3449 xmlSprintfElementContent(expr, cont, 1);
3450 list[0] = 0;
3451 xmlSprintfElementChilds(list, elem, 1);
3452
3453 VERROR(ctxt->userData,
3454 "Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
3455 elem->name, expr, list);
3456 ret = 0;
3457 }
3458 break;
3459 }
3460
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003461 /* [ VC: Required Attribute ] */
3462 attr = elemDecl->attributes;
3463 while (attr != NULL) {
3464 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
3465 xmlAttrPtr attrib;
3466 int qualified = -1;
3467
3468 attrib = elem->properties;
3469 while (attrib != NULL) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003470 if (xmlStrEqual(attrib->name, attr->name)) {
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003471 if (attr->prefix != NULL) {
3472 xmlNsPtr nameSpace = attrib->ns;
3473
3474 if (nameSpace == NULL)
3475 nameSpace = elem->ns;
3476 /*
3477 * qualified names handling is problematic, having a
3478 * different prefix should be possible but DTDs don't
3479 * allow to define the URI instead of the prefix :-(
3480 */
3481 if (nameSpace == NULL) {
3482 if (qualified < 0)
3483 qualified = 0;
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003484 } else if (!xmlStrEqual(nameSpace->prefix, attr->prefix)) {
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003485 if (qualified < 1)
3486 qualified = 1;
3487 } else
3488 goto found;
3489 } else {
3490 /*
3491 * We should allow applications to define namespaces
3492 * for their application even if the DTD doesn't
3493 * carry one, otherwise, basically we would always
3494 * break.
3495 */
3496 goto found;
3497 }
3498 }
3499 attrib = attrib->next;
3500 }
3501 if (qualified == -1) {
3502 if (attr->prefix == NULL) {
3503 VERROR(ctxt->userData,
3504 "Element %s doesn't carry attribute %s\n",
3505 elem->name, attr->name);
Daniel Veillardc2def842000-11-07 14:21:01 +00003506 ret = 0;
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003507 } else {
3508 VERROR(ctxt->userData,
3509 "Element %s doesn't carry attribute %s:%s\n",
3510 elem->name, attr->prefix,attr->name);
Daniel Veillardc2def842000-11-07 14:21:01 +00003511 ret = 0;
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003512 }
3513 } else if (qualified == 0) {
3514 VWARNING(ctxt->userData,
3515 "Element %s required attribute %s:%s has no prefix\n",
3516 elem->name, attr->prefix,attr->name);
3517 } else if (qualified == 1) {
3518 VWARNING(ctxt->userData,
3519 "Element %s required attribute %s:%s has different prefix\n",
3520 elem->name, attr->prefix,attr->name);
3521 }
3522 }
3523found:
Daniel Veillardcf461992000-03-14 18:30:20 +00003524 attr = attr->nexth;
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003525 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003526 return(ret);
3527}
3528
3529/**
3530 * xmlValidateRoot:
3531 * @ctxt: the validation context
3532 * @doc: a document instance
3533 *
3534 * Try to validate a the root element
3535 * basically it does the following check as described by the
3536 * XML-1.0 recommendation:
3537 * - [ VC: Root Element Type ]
3538 * it doesn't try to recurse or apply other check to the element
3539 *
3540 * returns 1 if valid or 0 otherwise
3541 */
3542
3543int
3544xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003545 xmlNodePtr root;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003546 if (doc == NULL) return(0);
3547
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003548 root = xmlDocGetRootElement(doc);
3549 if ((root == NULL) || (root->name == NULL)) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00003550 VERROR(ctxt->userData, "Not valid: no root element\n");
3551 return(0);
3552 }
Daniel Veillardbe803962000-06-28 23:40:59 +00003553
3554 /*
Daniel Veillardcd429612000-10-11 15:57:05 +00003555 * When doing post validation against a separate DTD, those may
3556 * no internal subset has been generated
Daniel Veillardbe803962000-06-28 23:40:59 +00003557 */
Daniel Veillardcd429612000-10-11 15:57:05 +00003558 if ((doc->intSubset != NULL) &&
3559 (doc->intSubset->name != NULL)) {
3560 /*
3561 * Check first the document root against the NQName
3562 */
3563 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
3564 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
3565 xmlChar qname[500];
Daniel Veillardbe803962000-06-28 23:40:59 +00003566#ifdef HAVE_SNPRINTF
Daniel Veillardcd429612000-10-11 15:57:05 +00003567 snprintf((char *) qname, sizeof(qname), "%s:%s",
3568 root->ns->prefix, root->name);
Daniel Veillardbe803962000-06-28 23:40:59 +00003569#else
Daniel Veillardcd429612000-10-11 15:57:05 +00003570 sprintf((char *) qname, "%s:%s", root->ns->prefix, root->name);
Daniel Veillardbe803962000-06-28 23:40:59 +00003571#endif
Daniel Veillardcd429612000-10-11 15:57:05 +00003572 qname[sizeof(qname) - 1] = 0;
3573 if (xmlStrEqual(doc->intSubset->name, qname))
3574 goto name_ok;
3575 }
3576 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
3577 (xmlStrEqual(root->name, BAD_CAST "html")))
Daniel Veillardbe803962000-06-28 23:40:59 +00003578 goto name_ok;
Daniel Veillardcd429612000-10-11 15:57:05 +00003579 VERROR(ctxt->userData,
3580 "Not valid: root and DtD name do not match '%s' and '%s'\n",
3581 root->name, doc->intSubset->name);
3582 return(0);
3583
3584 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003585 }
Daniel Veillardbe803962000-06-28 23:40:59 +00003586name_ok:
Daniel Veillardb05deb71999-08-10 19:04:08 +00003587 return(1);
3588}
3589
3590
3591/**
3592 * xmlValidateElement:
3593 * @ctxt: the validation context
3594 * @doc: a document instance
3595 * @elem: an element instance
3596 *
3597 * Try to validate the subtree under an element
3598 *
3599 * returns 1 if valid or 0 otherwise
3600 */
3601
3602int
3603xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003604 xmlNodePtr child;
3605 xmlAttrPtr attr;
Daniel Veillarddd6b3671999-09-23 22:19:22 +00003606 xmlChar *value;
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003607 int ret = 1;
3608
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003609 if (elem == NULL) return(0);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003610 CHECK_DTD;
3611
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003612 ret &= xmlValidateOneElement(ctxt, doc, elem);
3613 attr = elem->properties;
3614 while(attr != NULL) {
Daniel Veillardcf461992000-03-14 18:30:20 +00003615 value = xmlNodeListGetString(doc, attr->children, 0);
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003616 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
3617 if (value != NULL)
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003618 xmlFree(value);
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003619 attr= attr->next;
3620 }
Daniel Veillardcf461992000-03-14 18:30:20 +00003621 child = elem->children;
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003622 while (child != NULL) {
3623 ret &= xmlValidateElement(ctxt, doc, child);
3624 child = child->next;
3625 }
3626
3627 return(ret);
3628}
3629
Daniel Veillard126f2792000-10-24 17:10:12 +00003630
3631void
3632xmlValidateCheckRefCallback(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
3633 const xmlChar *name) {
3634 xmlAttrPtr id;
3635 xmlAttrPtr attr;
3636
3637 if (ref == NULL)
3638 return;
3639 attr = ref->attr;
3640 if (attr == NULL)
3641 return;
3642 if (attr->atype == XML_ATTRIBUTE_IDREF) {
3643 id = xmlGetID(ctxt->doc, name);
3644 if (id == NULL) {
3645 VERROR(ctxt->userData,
3646 "IDREF attribute %s reference an unknown ID \"%s\"\n",
3647 attr->name, name);
3648 ctxt->valid = 0;
3649 }
3650 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
3651 xmlChar *dup, *str = NULL, *cur, save;
3652
3653 dup = xmlStrdup(name);
3654 if (dup == NULL) {
3655 ctxt->valid = 0;
3656 return;
3657 }
3658 cur = dup;
3659 while (*cur != 0) {
3660 str = cur;
3661 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3662 save = *cur;
3663 *cur = 0;
3664 id = xmlGetID(ctxt->doc, str);
3665 if (id == NULL) {
3666 VERROR(ctxt->userData,
3667 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
3668 attr->name, str);
3669 ctxt->valid = 0;
3670 }
3671 if (save == 0)
3672 break;
3673 *cur = save;
3674 while (IS_BLANK(*cur)) cur++;
3675 }
3676 xmlFree(dup);
3677 }
3678}
3679
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003680/**
3681 * xmlValidateDocumentFinal:
3682 * @ctxt: the validation context
3683 * @doc: a document instance
3684 *
3685 * Does the final step for the document validation once all the
3686 * incremental validation steps have been completed
3687 *
3688 * basically it does the following checks described by the XML Rec
3689 *
3690 *
3691 * returns 1 if valid or 0 otherwise
3692 */
3693
3694int
3695xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003696 xmlRefTablePtr table;
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003697
3698 if (doc == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00003699 xmlGenericError(xmlGenericErrorContext,
3700 "xmlValidateDocumentFinal: doc == NULL\n");
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003701 return(0);
3702 }
3703
3704 /*
Daniel Veillardcf461992000-03-14 18:30:20 +00003705 * Check all the NOTATION/NOTATIONS attributes
3706 */
3707 /*
3708 * Check all the ENTITY/ENTITIES attributes definition for validity
3709 */
3710 /*
3711 * Check all the IDREF/IDREFS attributes definition for validity
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003712 */
Daniel Veillard32bc74e2000-07-14 14:49:25 +00003713 table = (xmlRefTablePtr) doc->refs;
Daniel Veillard126f2792000-10-24 17:10:12 +00003714 ctxt->doc = doc;
3715 ctxt->valid = 1;
3716 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
3717 return(ctxt->valid);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003718}
3719
3720/**
3721 * xmlValidateDtd:
3722 * @ctxt: the validation context
3723 * @doc: a document instance
3724 * @dtd: a dtd instance
3725 *
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003726 * Try to validate the document against the dtd instance
Daniel Veillardb05deb71999-08-10 19:04:08 +00003727 *
3728 * basically it does check all the definitions in the DtD.
3729 *
3730 * returns 1 if valid or 0 otherwise
3731 */
3732
3733int
3734xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003735 int ret;
3736 xmlDtdPtr oldExt;
3737 xmlNodePtr root;
3738
3739 if (dtd == NULL) return(0);
3740 if (doc == NULL) return(0);
3741 oldExt = doc->extSubset;
3742 doc->extSubset = dtd;
3743 ret = xmlValidateRoot(ctxt, doc);
3744 if (ret == 0) {
3745 doc->extSubset = oldExt;
3746 return(ret);
3747 }
3748 root = xmlDocGetRootElement(doc);
3749 ret = xmlValidateElement(ctxt, doc, root);
3750 ret &= xmlValidateDocumentFinal(ctxt, doc);
3751 doc->extSubset = oldExt;
3752 return(ret);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003753}
3754
Daniel Veillard126f2792000-10-24 17:10:12 +00003755void
3756xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
3757 const xmlChar *name) {
3758 if (cur == NULL)
3759 return;
3760 switch (cur->atype) {
3761 case XML_ATTRIBUTE_CDATA:
3762 case XML_ATTRIBUTE_ID:
3763 case XML_ATTRIBUTE_IDREF :
3764 case XML_ATTRIBUTE_IDREFS:
3765 case XML_ATTRIBUTE_NMTOKEN:
3766 case XML_ATTRIBUTE_NMTOKENS:
3767 case XML_ATTRIBUTE_ENUMERATION:
3768 break;
3769 case XML_ATTRIBUTE_ENTITY:
3770 case XML_ATTRIBUTE_ENTITIES:
3771 case XML_ATTRIBUTE_NOTATION:
3772 if (cur->defaultValue != NULL) {
3773 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
3774 cur->name, cur->atype, cur->defaultValue);
3775 }
3776 if (cur->tree != NULL) {
3777 xmlEnumerationPtr tree = cur->tree;
3778 while (tree != NULL) {
3779 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
3780 cur->name, cur->atype, tree->name);
3781 tree = tree->next;
3782 }
3783 }
3784 }
3785}
3786
Daniel Veillardb05deb71999-08-10 19:04:08 +00003787/**
Daniel Veillardcf461992000-03-14 18:30:20 +00003788 * xmlValidateDtdFinal:
3789 * @ctxt: the validation context
3790 * @doc: a document instance
3791 *
3792 * Does the final step for the dtds validation once all the
3793 * subsets have been parsed
3794 *
3795 * basically it does the following checks described by the XML Rec
3796 * - check that ENTITY and ENTITIES type attributes default or
3797 * possible values matches one of the defined entities.
3798 * - check that NOTATION type attributes default or
3799 * possible values matches one of the defined notations.
3800 *
3801 * returns 1 if valid or 0 otherwise
3802 */
3803
3804int
3805xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard126f2792000-10-24 17:10:12 +00003806 int ret = 1;
Daniel Veillardcf461992000-03-14 18:30:20 +00003807 xmlDtdPtr dtd;
3808 xmlAttributeTablePtr table;
Daniel Veillardcf461992000-03-14 18:30:20 +00003809
3810 if (doc == NULL) return(0);
3811 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
3812 return(0);
Daniel Veillard126f2792000-10-24 17:10:12 +00003813 ctxt->doc = doc;
3814 ctxt->valid = ret;
Daniel Veillardcf461992000-03-14 18:30:20 +00003815 dtd = doc->intSubset;
3816 if ((dtd != NULL) && (dtd->attributes != NULL)) {
Daniel Veillard32bc74e2000-07-14 14:49:25 +00003817 table = (xmlAttributeTablePtr) dtd->attributes;
Daniel Veillard126f2792000-10-24 17:10:12 +00003818 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillardcf461992000-03-14 18:30:20 +00003819 }
3820 dtd = doc->extSubset;
3821 if ((dtd != NULL) && (dtd->attributes != NULL)) {
Daniel Veillard32bc74e2000-07-14 14:49:25 +00003822 table = (xmlAttributeTablePtr) dtd->attributes;
Daniel Veillard126f2792000-10-24 17:10:12 +00003823 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillardcf461992000-03-14 18:30:20 +00003824 }
Daniel Veillard126f2792000-10-24 17:10:12 +00003825 return(ctxt->valid);
Daniel Veillardcf461992000-03-14 18:30:20 +00003826}
3827
3828/**
Daniel Veillardb05deb71999-08-10 19:04:08 +00003829 * xmlValidateDocument:
3830 * @ctxt: the validation context
3831 * @doc: a document instance
3832 *
3833 * Try to validate the document instance
3834 *
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003835 * basically it does the all the checks described by the XML Rec
Daniel Veillardb05deb71999-08-10 19:04:08 +00003836 * i.e. validates the internal and external subset (if present)
3837 * and validate the document tree.
3838 *
3839 * returns 1 if valid or 0 otherwise
3840 */
3841
3842int
3843xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003844 int ret;
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003845 xmlNodePtr root;
3846
3847 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
3848 return(0);
3849 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
3850 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
3851 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
3852 doc->intSubset->SystemID);
3853 if (doc->extSubset == NULL) {
3854 if (doc->intSubset->SystemID != NULL) {
3855 VERROR(ctxt->userData,
Daniel Veillardcf461992000-03-14 18:30:20 +00003856 "Could not load the external subset \"%s\"\n",
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003857 doc->intSubset->SystemID);
3858 } else {
3859 VERROR(ctxt->userData,
Daniel Veillardcf461992000-03-14 18:30:20 +00003860 "Could not load the external subset \"%s\"\n",
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003861 doc->intSubset->ExternalID);
3862 }
3863 return(0);
3864 }
3865 }
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003866
Daniel Veillardcf461992000-03-14 18:30:20 +00003867 ret = xmlValidateDtdFinal(ctxt, doc);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003868 if (!xmlValidateRoot(ctxt, doc)) return(0);
3869
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003870 root = xmlDocGetRootElement(doc);
Daniel Veillardcf461992000-03-14 18:30:20 +00003871 ret &= xmlValidateElement(ctxt, doc, root);
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003872 ret &= xmlValidateDocumentFinal(ctxt, doc);
3873 return(ret);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003874}
3875
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003876
3877/************************************************************************
3878 * *
3879 * Routines for dynamic validation editing *
3880 * *
3881 ************************************************************************/
3882
3883/**
3884 * xmlValidGetPotentialChildren:
3885 * @ctree: an element content tree
3886 * @list: an array to store the list of child names
3887 * @len: a pointer to the number of element in the list
3888 * @max: the size of the array
3889 *
3890 * Build/extend a list of potential children allowed by the content tree
3891 *
3892 * returns the number of element in the list, or -1 in case of error.
3893 */
3894
3895int
3896xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
3897 int *len, int max) {
3898 int i;
3899
3900 if ((ctree == NULL) || (list == NULL) || (len == NULL))
3901 return(-1);
3902 if (*len >= max) return(*len);
3903
3904 switch (ctree->type) {
3905 case XML_ELEMENT_CONTENT_PCDATA:
3906 for (i = 0; i < *len;i++)
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003907 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
Daniel Veillarda819dac1999-11-24 18:04:22 +00003908 list[(*len)++] = BAD_CAST "#PCDATA";
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003909 break;
3910 case XML_ELEMENT_CONTENT_ELEMENT:
3911 for (i = 0; i < *len;i++)
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003912 if (xmlStrEqual(ctree->name, list[i])) return(*len);
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003913 list[(*len)++] = ctree->name;
3914 break;
3915 case XML_ELEMENT_CONTENT_SEQ:
3916 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
3917 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
3918 break;
3919 case XML_ELEMENT_CONTENT_OR:
3920 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
3921 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
3922 break;
3923 }
3924
3925 return(*len);
3926}
3927
3928/**
3929 * xmlValidGetValidElements:
3930 * @prev: an element to insert after
3931 * @next: an element to insert next
3932 * @list: an array to store the list of child names
3933 * @max: the size of the array
3934 *
3935 * This function returns the list of authorized children to insert
3936 * within an existing tree while respecting the validity constraints
3937 * forced by the Dtd. The insertion point is defined using @prev and
3938 * @next in the following ways:
3939 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
3940 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
3941 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
3942 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
3943 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
3944 *
3945 * pointers to the element names are inserted at the beginning of the array
3946 * and do not need to be freed.
3947 *
3948 * returns the number of element in the list, or -1 in case of error. If
3949 * the function returns the value @max the caller is invited to grow the
3950 * receiving array and retry.
3951 */
3952
3953int
3954xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
3955 int max) {
3956 int nb_valid_elements = 0;
3957 const xmlChar *elements[256];
3958 int nb_elements = 0, i;
3959
3960 xmlNode *ref_node;
3961 xmlNode *parent;
3962 xmlNode *test_node;
3963
3964 xmlNode *prev_next;
3965 xmlNode *next_prev;
3966 xmlNode *parent_childs;
3967 xmlNode *parent_last;
3968
3969 xmlElement *element_desc;
3970
3971 if (prev == NULL && next == NULL)
3972 return(-1);
3973
3974 if (list == NULL) return(-1);
3975 if (max <= 0) return(-1);
3976
3977 nb_valid_elements = 0;
3978 ref_node = prev ? prev : next;
3979 parent = ref_node->parent;
3980
3981 /*
3982 * Retrieves the parent element declaration
3983 */
3984 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
3985 parent->name);
3986 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
3987 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
3988 parent->name);
3989 if (element_desc == NULL) return(-1);
3990
3991 /*
3992 * Do a backup of the current tree structure
3993 */
3994 prev_next = prev ? prev->next : NULL;
3995 next_prev = next ? next->prev : NULL;
Daniel Veillardcf461992000-03-14 18:30:20 +00003996 parent_childs = parent->children;
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003997 parent_last = parent->last;
3998
3999 /*
4000 * Creates a dummy node and insert it into the tree
4001 */
Daniel Veillarda819dac1999-11-24 18:04:22 +00004002 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
Daniel Veillard7c1206f1999-10-14 09:10:25 +00004003 test_node->doc = ref_node->doc;
4004 test_node->parent = parent;
4005 test_node->prev = prev;
4006 test_node->next = next;
4007
4008 if (prev) prev->next = test_node;
Daniel Veillardcf461992000-03-14 18:30:20 +00004009 else parent->children = test_node;
Daniel Veillard7c1206f1999-10-14 09:10:25 +00004010
4011 if (next) next->prev = test_node;
4012 else parent->last = test_node;
4013
4014 /*
4015 * Insert each potential child node and check if the parent is
4016 * still valid
4017 */
4018 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
4019 elements, &nb_elements, 256);
4020
4021 for (i = 0;i < nb_elements;i++) {
4022 test_node->name = elements[i];
4023 if (xmlValidateOneElement(NULL, parent->doc, parent)) {
4024 int j;
4025
4026 for (j = 0; j < nb_valid_elements;j++)
Daniel Veillard8b5dd832000-10-01 20:28:44 +00004027 if (xmlStrEqual(elements[i], list[j])) break;
Daniel Veillard7c1206f1999-10-14 09:10:25 +00004028 list[nb_valid_elements++] = elements[i];
4029 if (nb_valid_elements >= max) break;
4030 }
4031 }
4032
4033 /*
4034 * Restore the tree structure
4035 */
4036 if (prev) prev->next = prev_next;
4037 if (next) next->prev = next_prev;
Daniel Veillardcf461992000-03-14 18:30:20 +00004038 parent->children = parent_childs;
Daniel Veillard7c1206f1999-10-14 09:10:25 +00004039 parent->last = parent_last;
4040
4041 return(nb_valid_elements);
4042}