blob: 8172e716d47f7409bfb34937336b72a792ae4bd7 [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
Daniel Veillardc2f4df22001-01-04 14:06:39 +00002903/* Find the next XML_ELEMENT_NODE, subject to the content constraints.
2904 * Return -1 if we found something unexpected, or 1 otherwise.
2905 */
2906
2907static int
2908xmlValidateFindNextElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
2909 xmlElementContentPtr cont)
2910{
2911 while (*child && (*child)->type != XML_ELEMENT_NODE) {
2912 switch ((*child)->type) {
2913 /*
2914 * If there is an entity declared and it's not empty
2915 * Push the current node on the stack and process with the
2916 * entity content.
2917 */
2918 case XML_ENTITY_REF_NODE:
2919 if (((*child)->children != NULL) &&
2920 ((*child)->children->children != NULL)) {
2921 nodeVPush(ctxt, *child);
2922 *child = (*child)->children->children;
2923 continue;
2924 }
2925 break;
2926
2927 /* These things are ignored (skipped) during validation. */
2928 case XML_PI_NODE:
2929 case XML_COMMENT_NODE:
2930 case XML_XINCLUDE_START:
2931 case XML_XINCLUDE_END:
2932 break;
2933
2934 case XML_TEXT_NODE:
2935 if (xmlIsBlankNode(*child)
2936 && (cont->type == XML_ELEMENT_CONTENT_ELEMENT
2937 || cont->type == XML_ELEMENT_CONTENT_SEQ
2938 || cont->type == XML_ELEMENT_CONTENT_OR))
2939 break;
2940 return -1;
2941
2942 default:
2943 return -1;
2944 }
2945 *child = (*child)->next;
2946 }
2947
2948 return 1;
2949}
2950
Daniel Veillardb05deb71999-08-10 19:04:08 +00002951int xmlValidateElementTypeElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
2952 xmlElementContentPtr cont);
2953
2954/**
2955 * xmlValidateElementTypeExpr:
2956 * @ctxt: the validation context
2957 * @child: pointer to the child list
2958 * @cont: pointer to the content declaration
2959 *
2960 * Try to validate the content of an element of type element
2961 * but don't handle the occurence factor
2962 *
2963 * returns 1 if valid or 0 and -1 if PCDATA stuff is found,
2964 * also update child value in-situ.
2965 */
2966
2967int
2968xmlValidateElementTypeExpr(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
2969 xmlElementContentPtr cont) {
2970 xmlNodePtr cur;
2971 int ret = 1;
2972
2973 if (cont == NULL) return(-1);
Daniel Veillardcf461992000-03-14 18:30:20 +00002974 DEBUG_VALID_STATE(*child, cont)
Daniel Veillardc2f4df22001-01-04 14:06:39 +00002975 ret = xmlValidateFindNextElement(ctxt, child, cont);
2976 if (ret < 0)
Daniel Veillardb05deb71999-08-10 19:04:08 +00002977 return(-1);
Daniel Veillardcf461992000-03-14 18:30:20 +00002978 DEBUG_VALID_STATE(*child, cont)
Daniel Veillardb05deb71999-08-10 19:04:08 +00002979 switch (cont->type) {
2980 case XML_ELEMENT_CONTENT_PCDATA:
Daniel Veillardda07c342000-01-25 18:31:22 +00002981 if (*child == NULL) return(0);
2982 if ((*child)->type == XML_TEXT_NODE) return(1);
2983 return(0);
Daniel Veillardb05deb71999-08-10 19:04:08 +00002984 case XML_ELEMENT_CONTENT_ELEMENT:
2985 if (*child == NULL) return(0);
Daniel Veillard8b5dd832000-10-01 20:28:44 +00002986 ret = (xmlStrEqual((*child)->name, cont->name));
Daniel Veillardcf461992000-03-14 18:30:20 +00002987 if (ret == 1) {
2988 while ((*child)->next == NULL) {
2989 if (((*child)->parent != NULL) &&
2990 ((*child)->parent->type == XML_ENTITY_DECL)) {
2991 *child = nodeVPop(ctxt);
2992 } else
2993 break;
2994 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00002995 *child = (*child)->next;
Daniel Veillardcf461992000-03-14 18:30:20 +00002996 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00002997 return(ret);
2998 case XML_ELEMENT_CONTENT_OR:
2999 cur = *child;
3000 ret = xmlValidateElementTypeElement(ctxt, child, cont->c1);
3001 if (ret == -1) return(-1);
3002 if (ret == 1) {
3003 return(1);
3004 }
3005 /* rollback and retry the other path */
3006 *child = cur;
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 case XML_ELEMENT_CONTENT_SEQ:
3015 cur = *child;
3016 ret = xmlValidateElementTypeElement(ctxt, child, cont->c1);
3017 if (ret == -1) return(-1);
3018 if (ret == 0) {
3019 *child = cur;
3020 return(0);
3021 }
3022 ret = xmlValidateElementTypeElement(ctxt, child, cont->c2);
3023 if (ret == -1) return(-1);
3024 if (ret == 0) {
3025 *child = cur;
3026 return(0);
3027 }
3028 return(1);
3029 }
3030 return(ret);
3031}
3032
3033/**
3034 * xmlValidateElementTypeElement:
3035 * @ctxt: the validation context
3036 * @child: pointer to the child list
3037 * @cont: pointer to the content declaration
3038 *
3039 * Try to validate the content of an element of type element
3040 * yeah, Yet Another Regexp Implementation, and recursive
3041 *
3042 * returns 1 if valid or 0 and -1 if PCDATA stuff is found,
3043 * also update child and content values in-situ.
3044 */
3045
3046int
3047xmlValidateElementTypeElement(xmlValidCtxtPtr ctxt, xmlNodePtr *child,
3048 xmlElementContentPtr cont) {
3049 xmlNodePtr cur;
Daniel Veillardc2f4df22001-01-04 14:06:39 +00003050 int ret;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003051
3052 if (cont == NULL) return(-1);
Daniel Veillardcf461992000-03-14 18:30:20 +00003053
3054 DEBUG_VALID_STATE(*child, cont)
Daniel Veillardc2f4df22001-01-04 14:06:39 +00003055 ret = xmlValidateFindNextElement(ctxt, child, cont);
3056 if (ret < 0)
Daniel Veillardb05deb71999-08-10 19:04:08 +00003057 return(-1);
Daniel Veillardcf461992000-03-14 18:30:20 +00003058 DEBUG_VALID_STATE(*child, cont)
Daniel Veillardb05deb71999-08-10 19:04:08 +00003059 cur = *child;
3060 ret = xmlValidateElementTypeExpr(ctxt, child, cont);
3061 if (ret == -1) return(-1);
3062 switch (cont->ocur) {
3063 case XML_ELEMENT_CONTENT_ONCE:
3064 if (ret == 1) {
Daniel Veillard3a2ebdd2000-01-25 19:27:27 +00003065 /* skip ignorable elems */
3066 while ((*child != NULL) &&
Daniel Veillardc2f4df22001-01-04 14:06:39 +00003067 ((*child)->type == XML_PI_NODE
3068 || (*child)->type == XML_COMMENT_NODE
3069 || (*child)->type == XML_XINCLUDE_START
3070 || (*child)->type == XML_XINCLUDE_END)) {
Daniel Veillardcf461992000-03-14 18:30:20 +00003071 while ((*child)->next == NULL) {
3072 if (((*child)->parent != NULL) &&
3073 ((*child)->parent->type == XML_ENTITY_REF_NODE)) {
3074 *child = (*child)->parent;
3075 } else
3076 break;
3077 }
3078 *child = (*child)->next;
Daniel Veillard3a2ebdd2000-01-25 19:27:27 +00003079 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003080 return(1);
3081 }
3082 *child = cur;
3083 return(0);
3084 case XML_ELEMENT_CONTENT_OPT:
3085 if (ret == 0) {
3086 *child = cur;
3087 return(1);
3088 }
3089 break;
3090 case XML_ELEMENT_CONTENT_MULT:
3091 if (ret == 0) {
3092 *child = cur;
3093 break;
3094 }
3095 /* no break on purpose */
3096 case XML_ELEMENT_CONTENT_PLUS:
3097 if (ret == 0) {
3098 *child = cur;
3099 return(0);
3100 }
Daniel Veillard683cb022000-10-22 12:04:13 +00003101 if (ret == -1) return(-1);
3102 cur = *child;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003103 do {
Daniel Veillard683cb022000-10-22 12:04:13 +00003104 if (*child == NULL)
3105 break; /* while */
Daniel Veillardc2f4df22001-01-04 14:06:39 +00003106 if ((*child)->type == XML_TEXT_NODE
3107 && xmlIsBlankNode(*child)) {
Daniel Veillard683cb022000-10-22 12:04:13 +00003108 *child = (*child)->next;
3109 continue;
3110 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003111 ret = xmlValidateElementTypeExpr(ctxt, child, cont);
Daniel Veillard683cb022000-10-22 12:04:13 +00003112 if (ret == 1)
3113 cur = *child;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003114 } while (ret == 1);
3115 if (ret == -1) return(-1);
3116 *child = cur;
3117 break;
3118 }
Daniel Veillardc2f4df22001-01-04 14:06:39 +00003119
3120 return xmlValidateFindNextElement(ctxt, child, cont);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003121}
3122
3123/**
3124 * xmlSprintfElementChilds:
3125 * @buf: an output buffer
3126 * @content: An element
3127 * @glob: 1 if one must print the englobing parenthesis, 0 otherwise
3128 *
3129 * This will dump the list of childs to the buffer
3130 * Intended just for the debug routine
3131 */
3132void
3133xmlSprintfElementChilds(char *buf, xmlNodePtr node, int glob) {
3134 xmlNodePtr cur;
3135
3136 if (node == NULL) return;
3137 if (glob) strcat(buf, "(");
Daniel Veillardcf461992000-03-14 18:30:20 +00003138 cur = node->children;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003139 while (cur != NULL) {
3140 switch (cur->type) {
3141 case XML_ELEMENT_NODE:
Daniel Veillardb96e6431999-08-29 21:02:19 +00003142 strcat(buf, (char *) cur->name);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003143 if (cur->next != NULL)
3144 strcat(buf, " ");
3145 break;
3146 case XML_TEXT_NODE:
Daniel Veillardcd429612000-10-11 15:57:05 +00003147 if (xmlIsBlankNode(cur))
3148 break;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003149 case XML_CDATA_SECTION_NODE:
3150 case XML_ENTITY_REF_NODE:
3151 strcat(buf, "CDATA");
3152 if (cur->next != NULL)
3153 strcat(buf, " ");
3154 break;
3155 case XML_ATTRIBUTE_NODE:
3156 case XML_DOCUMENT_NODE:
Daniel Veillard04698d92000-09-17 16:00:22 +00003157#ifdef LIBXML_SGML_ENABLED
3158 case XML_SGML_DOCUMENT_NODE:
3159#endif
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003160 case XML_HTML_DOCUMENT_NODE:
Daniel Veillardb05deb71999-08-10 19:04:08 +00003161 case XML_DOCUMENT_TYPE_NODE:
3162 case XML_DOCUMENT_FRAG_NODE:
3163 case XML_NOTATION_NODE:
Daniel Veillard9e8bfae2000-11-06 16:43:11 +00003164 case XML_NAMESPACE_DECL:
Daniel Veillardb05deb71999-08-10 19:04:08 +00003165 strcat(buf, "???");
3166 if (cur->next != NULL)
3167 strcat(buf, " ");
3168 break;
3169 case XML_ENTITY_NODE:
3170 case XML_PI_NODE:
Daniel Veillardcf461992000-03-14 18:30:20 +00003171 case XML_DTD_NODE:
Daniel Veillardb05deb71999-08-10 19:04:08 +00003172 case XML_COMMENT_NODE:
Daniel Veillardcf461992000-03-14 18:30:20 +00003173 case XML_ELEMENT_DECL:
3174 case XML_ATTRIBUTE_DECL:
3175 case XML_ENTITY_DECL:
Daniel Veillard9e8bfae2000-11-06 16:43:11 +00003176 case XML_XINCLUDE_START:
3177 case XML_XINCLUDE_END:
Daniel Veillardb05deb71999-08-10 19:04:08 +00003178 break;
3179 }
3180 cur = cur->next;
3181 }
3182 if (glob) strcat(buf, ")");
3183}
3184
3185
3186/**
3187 * xmlValidateOneElement:
3188 * @ctxt: the validation context
3189 * @doc: a document instance
3190 * @elem: an element instance
3191 *
3192 * Try to validate a single element and it's attributes,
3193 * basically it does the following checks as described by the
3194 * XML-1.0 recommendation:
3195 * - [ VC: Element Valid ]
3196 * - [ VC: Required Attribute ]
3197 * Then call xmlValidateOneAttribute() for each attribute present.
3198 *
3199 * The ID/IDREF checkings are done separately
3200 *
3201 * returns 1 if valid or 0 otherwise
3202 */
3203
3204int
3205xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc,
3206 xmlNodePtr elem) {
Daniel Veillardbe803962000-06-28 23:40:59 +00003207 xmlElementPtr elemDecl = NULL;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003208 xmlElementContentPtr cont;
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003209 xmlAttributePtr attr;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003210 xmlNodePtr child;
3211 int ret = 1;
Daniel Veillarddd6b3671999-09-23 22:19:22 +00003212 const xmlChar *name;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003213
3214 CHECK_DTD;
3215
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003216 if (elem == NULL) return(0);
3217 if (elem->type == XML_TEXT_NODE) {
3218 }
3219 switch (elem->type) {
3220 case XML_ATTRIBUTE_NODE:
3221 VERROR(ctxt->userData,
3222 "Attribute element not expected here\n");
3223 return(0);
3224 case XML_TEXT_NODE:
Daniel Veillardcf461992000-03-14 18:30:20 +00003225 if (elem->children != NULL) {
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003226 VERROR(ctxt->userData, "Text element has childs !\n");
3227 return(0);
3228 }
3229 if (elem->properties != NULL) {
3230 VERROR(ctxt->userData, "Text element has attributes !\n");
3231 return(0);
3232 }
3233 if (elem->ns != NULL) {
3234 VERROR(ctxt->userData, "Text element has namespace !\n");
3235 return(0);
3236 }
Daniel Veillard87b95392000-08-12 21:12:04 +00003237 if (elem->nsDef != NULL) {
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003238 VERROR(ctxt->userData,
3239 "Text element carries namespace definitions !\n");
3240 return(0);
3241 }
3242 if (elem->content == NULL) {
3243 VERROR(ctxt->userData,
3244 "Text element has no content !\n");
3245 return(0);
3246 }
3247 return(1);
Daniel Veillardc2f4df22001-01-04 14:06:39 +00003248 case XML_XINCLUDE_START:
3249 case XML_XINCLUDE_END:
3250 return(1);
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003251 case XML_CDATA_SECTION_NODE:
3252 case XML_ENTITY_REF_NODE:
3253 case XML_PI_NODE:
3254 case XML_COMMENT_NODE:
3255 return(1);
3256 case XML_ENTITY_NODE:
3257 VERROR(ctxt->userData,
3258 "Entity element not expected here\n");
3259 return(0);
3260 case XML_NOTATION_NODE:
3261 VERROR(ctxt->userData,
3262 "Notation element not expected here\n");
3263 return(0);
3264 case XML_DOCUMENT_NODE:
3265 case XML_DOCUMENT_TYPE_NODE:
3266 case XML_DOCUMENT_FRAG_NODE:
3267 VERROR(ctxt->userData,
3268 "Document element not expected here\n");
3269 return(0);
3270 case XML_HTML_DOCUMENT_NODE:
3271 VERROR(ctxt->userData,
3272 "\n");
3273 return(0);
3274 case XML_ELEMENT_NODE:
3275 break;
3276 default:
3277 VERROR(ctxt->userData,
3278 "unknown element type %d\n", elem->type);
3279 return(0);
3280 }
3281 if (elem->name == NULL) return(0);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003282
Daniel Veillardbe803962000-06-28 23:40:59 +00003283 /*
3284 * Fetch the declaration for the qualified name
3285 */
3286 if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) {
3287 elemDecl = xmlGetDtdQElementDesc(doc->intSubset,
3288 elem->name, elem->ns->prefix);
3289 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3290 elemDecl = xmlGetDtdQElementDesc(doc->extSubset,
3291 elem->name, elem->ns->prefix);
3292 }
3293
3294 /*
3295 * Fetch the declaration for the non qualified name
3296 */
3297 if (elemDecl == NULL) {
3298 elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name);
3299 if ((elemDecl == NULL) && (doc->extSubset != NULL))
3300 elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name);
3301 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003302 if (elemDecl == NULL) {
3303 VERROR(ctxt->userData, "No declaration for element %s\n",
3304 elem->name);
3305 return(0);
3306 }
3307
3308 /* Check taht the element content matches the definition */
Daniel Veillardcf461992000-03-14 18:30:20 +00003309 switch (elemDecl->etype) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00003310 case XML_ELEMENT_TYPE_EMPTY:
Daniel Veillardcf461992000-03-14 18:30:20 +00003311 if (elem->children != NULL) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00003312 VERROR(ctxt->userData,
3313 "Element %s was declared EMPTY this one has content\n",
3314 elem->name);
3315 ret = 0;
3316 }
3317 break;
3318 case XML_ELEMENT_TYPE_ANY:
3319 /* I don't think anything is required then */
3320 break;
3321 case XML_ELEMENT_TYPE_MIXED:
3322 /* Hum, this start to get messy */
Daniel Veillardcf461992000-03-14 18:30:20 +00003323 child = elem->children;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003324 while (child != NULL) {
3325 if (child->type == XML_ELEMENT_NODE) {
3326 name = child->name;
Daniel Veillardbe803962000-06-28 23:40:59 +00003327 if ((child->ns != NULL) && (child->ns->prefix != NULL)) {
3328 xmlChar qname[500];
3329#ifdef HAVE_SNPRINTF
3330 snprintf((char *) qname, sizeof(qname), "%s:%s",
3331 child->ns->prefix, child->name);
3332#else
Daniel Veillard39c7d712000-09-10 16:14:55 +00003333 sprintf((char *) qname, "%s:%s",
3334 child->ns->prefix, child->name);
Daniel Veillardbe803962000-06-28 23:40:59 +00003335#endif
Daniel Veillard39c7d712000-09-10 16:14:55 +00003336 qname[sizeof(qname) - 1] = 0;
Daniel Veillardbe803962000-06-28 23:40:59 +00003337 cont = elemDecl->content;
3338 while (cont != NULL) {
3339 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003340 if (xmlStrEqual(cont->name, qname)) break;
Daniel Veillardbe803962000-06-28 23:40:59 +00003341 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
3342 (cont->c1 != NULL) &&
3343 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)){
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003344 if (xmlStrEqual(cont->c1->name, qname)) break;
Daniel Veillardbe803962000-06-28 23:40:59 +00003345 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
3346 (cont->c1 == NULL) ||
3347 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)){
3348 /* Internal error !!! */
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00003349 xmlGenericError(xmlGenericErrorContext,
3350 "Internal: MIXED struct bad\n");
Daniel Veillardbe803962000-06-28 23:40:59 +00003351 break;
3352 }
3353 cont = cont->c2;
3354 }
3355 if (cont != NULL)
3356 goto child_ok;
3357 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003358 cont = elemDecl->content;
3359 while (cont != NULL) {
3360 if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003361 if (xmlStrEqual(cont->name, name)) break;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003362 } else if ((cont->type == XML_ELEMENT_CONTENT_OR) &&
3363 (cont->c1 != NULL) &&
3364 (cont->c1->type == XML_ELEMENT_CONTENT_ELEMENT)) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003365 if (xmlStrEqual(cont->c1->name, name)) break;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003366 } else if ((cont->type != XML_ELEMENT_CONTENT_OR) ||
3367 (cont->c1 == NULL) ||
3368 (cont->c1->type != XML_ELEMENT_CONTENT_PCDATA)) {
3369 /* Internal error !!! */
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00003370 xmlGenericError(xmlGenericErrorContext,
3371 "Internal: MIXED struct bad\n");
Daniel Veillardb05deb71999-08-10 19:04:08 +00003372 break;
3373 }
3374 cont = cont->c2;
3375 }
3376 if (cont == NULL) {
3377 VERROR(ctxt->userData,
3378 "Element %s is not declared in %s list of possible childs\n",
3379 name, elem->name);
3380 ret = 0;
3381 }
3382 }
Daniel Veillardbe803962000-06-28 23:40:59 +00003383child_ok:
Daniel Veillardb05deb71999-08-10 19:04:08 +00003384 child = child->next;
3385 }
3386 break;
3387 case XML_ELEMENT_TYPE_ELEMENT:
Daniel Veillardcf461992000-03-14 18:30:20 +00003388 child = elem->children;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003389 cont = elemDecl->content;
3390 ret = xmlValidateElementTypeElement(ctxt, &child, cont);
Daniel Veillard683cb022000-10-22 12:04:13 +00003391 while ((child != NULL) && (child->type == XML_TEXT_NODE) &&
3392 (xmlIsBlankNode(child))) {
3393 child = child->next;
3394 continue;
3395 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003396 if ((ret == 0) || (child != NULL)) {
3397 char expr[1000];
3398 char list[2000];
3399
3400 expr[0] = 0;
3401 xmlSprintfElementContent(expr, cont, 1);
3402 list[0] = 0;
3403 xmlSprintfElementChilds(list, elem, 1);
3404
3405 VERROR(ctxt->userData,
3406 "Element %s content doesn't follow the Dtd\nExpecting %s, got %s\n",
3407 elem->name, expr, list);
3408 ret = 0;
3409 }
3410 break;
3411 }
3412
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003413 /* [ VC: Required Attribute ] */
3414 attr = elemDecl->attributes;
3415 while (attr != NULL) {
3416 if (attr->def == XML_ATTRIBUTE_REQUIRED) {
3417 xmlAttrPtr attrib;
3418 int qualified = -1;
3419
3420 attrib = elem->properties;
3421 while (attrib != NULL) {
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003422 if (xmlStrEqual(attrib->name, attr->name)) {
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003423 if (attr->prefix != NULL) {
3424 xmlNsPtr nameSpace = attrib->ns;
3425
3426 if (nameSpace == NULL)
3427 nameSpace = elem->ns;
3428 /*
3429 * qualified names handling is problematic, having a
3430 * different prefix should be possible but DTDs don't
3431 * allow to define the URI instead of the prefix :-(
3432 */
3433 if (nameSpace == NULL) {
3434 if (qualified < 0)
3435 qualified = 0;
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003436 } else if (!xmlStrEqual(nameSpace->prefix, attr->prefix)) {
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003437 if (qualified < 1)
3438 qualified = 1;
3439 } else
3440 goto found;
3441 } else {
3442 /*
3443 * We should allow applications to define namespaces
3444 * for their application even if the DTD doesn't
3445 * carry one, otherwise, basically we would always
3446 * break.
3447 */
3448 goto found;
3449 }
3450 }
3451 attrib = attrib->next;
3452 }
3453 if (qualified == -1) {
3454 if (attr->prefix == NULL) {
3455 VERROR(ctxt->userData,
3456 "Element %s doesn't carry attribute %s\n",
3457 elem->name, attr->name);
Daniel Veillardc2def842000-11-07 14:21:01 +00003458 ret = 0;
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003459 } else {
3460 VERROR(ctxt->userData,
3461 "Element %s doesn't carry attribute %s:%s\n",
3462 elem->name, attr->prefix,attr->name);
Daniel Veillardc2def842000-11-07 14:21:01 +00003463 ret = 0;
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003464 }
3465 } else if (qualified == 0) {
3466 VWARNING(ctxt->userData,
3467 "Element %s required attribute %s:%s has no prefix\n",
3468 elem->name, attr->prefix,attr->name);
3469 } else if (qualified == 1) {
3470 VWARNING(ctxt->userData,
3471 "Element %s required attribute %s:%s has different prefix\n",
3472 elem->name, attr->prefix,attr->name);
3473 }
3474 }
3475found:
Daniel Veillardcf461992000-03-14 18:30:20 +00003476 attr = attr->nexth;
Daniel Veillarddbfd6411999-12-28 16:35:14 +00003477 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003478 return(ret);
3479}
3480
3481/**
3482 * xmlValidateRoot:
3483 * @ctxt: the validation context
3484 * @doc: a document instance
3485 *
3486 * Try to validate a the root element
3487 * basically it does the following check as described by the
3488 * XML-1.0 recommendation:
3489 * - [ VC: Root Element Type ]
3490 * it doesn't try to recurse or apply other check to the element
3491 *
3492 * returns 1 if valid or 0 otherwise
3493 */
3494
3495int
3496xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003497 xmlNodePtr root;
Daniel Veillardb05deb71999-08-10 19:04:08 +00003498 if (doc == NULL) return(0);
3499
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003500 root = xmlDocGetRootElement(doc);
3501 if ((root == NULL) || (root->name == NULL)) {
Daniel Veillardb05deb71999-08-10 19:04:08 +00003502 VERROR(ctxt->userData, "Not valid: no root element\n");
3503 return(0);
3504 }
Daniel Veillardbe803962000-06-28 23:40:59 +00003505
3506 /*
Daniel Veillardcd429612000-10-11 15:57:05 +00003507 * When doing post validation against a separate DTD, those may
3508 * no internal subset has been generated
Daniel Veillardbe803962000-06-28 23:40:59 +00003509 */
Daniel Veillardcd429612000-10-11 15:57:05 +00003510 if ((doc->intSubset != NULL) &&
3511 (doc->intSubset->name != NULL)) {
3512 /*
3513 * Check first the document root against the NQName
3514 */
3515 if (!xmlStrEqual(doc->intSubset->name, root->name)) {
3516 if ((root->ns != NULL) && (root->ns->prefix != NULL)) {
3517 xmlChar qname[500];
Daniel Veillardbe803962000-06-28 23:40:59 +00003518#ifdef HAVE_SNPRINTF
Daniel Veillardcd429612000-10-11 15:57:05 +00003519 snprintf((char *) qname, sizeof(qname), "%s:%s",
3520 root->ns->prefix, root->name);
Daniel Veillardbe803962000-06-28 23:40:59 +00003521#else
Daniel Veillardcd429612000-10-11 15:57:05 +00003522 sprintf((char *) qname, "%s:%s", root->ns->prefix, root->name);
Daniel Veillardbe803962000-06-28 23:40:59 +00003523#endif
Daniel Veillardcd429612000-10-11 15:57:05 +00003524 qname[sizeof(qname) - 1] = 0;
3525 if (xmlStrEqual(doc->intSubset->name, qname))
3526 goto name_ok;
3527 }
3528 if ((xmlStrEqual(doc->intSubset->name, BAD_CAST "HTML")) &&
3529 (xmlStrEqual(root->name, BAD_CAST "html")))
Daniel Veillardbe803962000-06-28 23:40:59 +00003530 goto name_ok;
Daniel Veillardcd429612000-10-11 15:57:05 +00003531 VERROR(ctxt->userData,
3532 "Not valid: root and DtD name do not match '%s' and '%s'\n",
3533 root->name, doc->intSubset->name);
3534 return(0);
3535
3536 }
Daniel Veillardb05deb71999-08-10 19:04:08 +00003537 }
Daniel Veillardbe803962000-06-28 23:40:59 +00003538name_ok:
Daniel Veillardb05deb71999-08-10 19:04:08 +00003539 return(1);
3540}
3541
3542
3543/**
3544 * xmlValidateElement:
3545 * @ctxt: the validation context
3546 * @doc: a document instance
3547 * @elem: an element instance
3548 *
3549 * Try to validate the subtree under an element
3550 *
3551 * returns 1 if valid or 0 otherwise
3552 */
3553
3554int
3555xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem) {
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003556 xmlNodePtr child;
3557 xmlAttrPtr attr;
Daniel Veillarddd6b3671999-09-23 22:19:22 +00003558 xmlChar *value;
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003559 int ret = 1;
3560
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003561 if (elem == NULL) return(0);
Daniel Veillardc2f4df22001-01-04 14:06:39 +00003562
3563 /*
3564 * XInclude elements were added after parsing in the infoset,
3565 * they don't really mean anything validation wise.
3566 */
3567 if ((elem->type == XML_XINCLUDE_START) ||
3568 (elem->type == XML_XINCLUDE_END))
3569 return(1);
3570
Daniel Veillardb05deb71999-08-10 19:04:08 +00003571 CHECK_DTD;
3572
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003573 ret &= xmlValidateOneElement(ctxt, doc, elem);
3574 attr = elem->properties;
3575 while(attr != NULL) {
Daniel Veillardcf461992000-03-14 18:30:20 +00003576 value = xmlNodeListGetString(doc, attr->children, 0);
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003577 ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value);
3578 if (value != NULL)
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003579 xmlFree(value);
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003580 attr= attr->next;
3581 }
Daniel Veillardcf461992000-03-14 18:30:20 +00003582 child = elem->children;
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003583 while (child != NULL) {
3584 ret &= xmlValidateElement(ctxt, doc, child);
3585 child = child->next;
3586 }
3587
3588 return(ret);
3589}
3590
Daniel Veillard126f2792000-10-24 17:10:12 +00003591
3592void
3593xmlValidateCheckRefCallback(xmlRefPtr ref, xmlValidCtxtPtr ctxt,
3594 const xmlChar *name) {
3595 xmlAttrPtr id;
3596 xmlAttrPtr attr;
3597
3598 if (ref == NULL)
3599 return;
3600 attr = ref->attr;
3601 if (attr == NULL)
3602 return;
3603 if (attr->atype == XML_ATTRIBUTE_IDREF) {
3604 id = xmlGetID(ctxt->doc, name);
3605 if (id == NULL) {
3606 VERROR(ctxt->userData,
3607 "IDREF attribute %s reference an unknown ID \"%s\"\n",
3608 attr->name, name);
3609 ctxt->valid = 0;
3610 }
3611 } else if (attr->atype == XML_ATTRIBUTE_IDREFS) {
3612 xmlChar *dup, *str = NULL, *cur, save;
3613
3614 dup = xmlStrdup(name);
3615 if (dup == NULL) {
3616 ctxt->valid = 0;
3617 return;
3618 }
3619 cur = dup;
3620 while (*cur != 0) {
3621 str = cur;
3622 while ((*cur != 0) && (!IS_BLANK(*cur))) cur++;
3623 save = *cur;
3624 *cur = 0;
3625 id = xmlGetID(ctxt->doc, str);
3626 if (id == NULL) {
3627 VERROR(ctxt->userData,
3628 "IDREFS attribute %s reference an unknown ID \"%s\"\n",
3629 attr->name, str);
3630 ctxt->valid = 0;
3631 }
3632 if (save == 0)
3633 break;
3634 *cur = save;
3635 while (IS_BLANK(*cur)) cur++;
3636 }
3637 xmlFree(dup);
3638 }
3639}
3640
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003641/**
3642 * xmlValidateDocumentFinal:
3643 * @ctxt: the validation context
3644 * @doc: a document instance
3645 *
3646 * Does the final step for the document validation once all the
3647 * incremental validation steps have been completed
3648 *
3649 * basically it does the following checks described by the XML Rec
3650 *
3651 *
3652 * returns 1 if valid or 0 otherwise
3653 */
3654
3655int
3656xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003657 xmlRefTablePtr table;
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003658
3659 if (doc == NULL) {
Daniel Veillardd6d7f7b2000-10-25 19:56:55 +00003660 xmlGenericError(xmlGenericErrorContext,
3661 "xmlValidateDocumentFinal: doc == NULL\n");
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003662 return(0);
3663 }
3664
3665 /*
Daniel Veillardcf461992000-03-14 18:30:20 +00003666 * Check all the NOTATION/NOTATIONS attributes
3667 */
3668 /*
3669 * Check all the ENTITY/ENTITIES attributes definition for validity
3670 */
3671 /*
3672 * Check all the IDREF/IDREFS attributes definition for validity
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003673 */
Daniel Veillard32bc74e2000-07-14 14:49:25 +00003674 table = (xmlRefTablePtr) doc->refs;
Daniel Veillard126f2792000-10-24 17:10:12 +00003675 ctxt->doc = doc;
3676 ctxt->valid = 1;
3677 xmlHashScan(table, (xmlHashScanner) xmlValidateCheckRefCallback, ctxt);
3678 return(ctxt->valid);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003679}
3680
3681/**
3682 * xmlValidateDtd:
3683 * @ctxt: the validation context
3684 * @doc: a document instance
3685 * @dtd: a dtd instance
3686 *
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003687 * Try to validate the document against the dtd instance
Daniel Veillardb05deb71999-08-10 19:04:08 +00003688 *
3689 * basically it does check all the definitions in the DtD.
3690 *
3691 * returns 1 if valid or 0 otherwise
3692 */
3693
3694int
3695xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) {
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003696 int ret;
3697 xmlDtdPtr oldExt;
3698 xmlNodePtr root;
3699
3700 if (dtd == NULL) return(0);
3701 if (doc == NULL) return(0);
3702 oldExt = doc->extSubset;
3703 doc->extSubset = dtd;
3704 ret = xmlValidateRoot(ctxt, doc);
3705 if (ret == 0) {
3706 doc->extSubset = oldExt;
3707 return(ret);
3708 }
3709 root = xmlDocGetRootElement(doc);
3710 ret = xmlValidateElement(ctxt, doc, root);
3711 ret &= xmlValidateDocumentFinal(ctxt, doc);
3712 doc->extSubset = oldExt;
3713 return(ret);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003714}
3715
Daniel Veillard126f2792000-10-24 17:10:12 +00003716void
3717xmlValidateAttributeCallback(xmlAttributePtr cur, xmlValidCtxtPtr ctxt,
3718 const xmlChar *name) {
3719 if (cur == NULL)
3720 return;
3721 switch (cur->atype) {
3722 case XML_ATTRIBUTE_CDATA:
3723 case XML_ATTRIBUTE_ID:
3724 case XML_ATTRIBUTE_IDREF :
3725 case XML_ATTRIBUTE_IDREFS:
3726 case XML_ATTRIBUTE_NMTOKEN:
3727 case XML_ATTRIBUTE_NMTOKENS:
3728 case XML_ATTRIBUTE_ENUMERATION:
3729 break;
3730 case XML_ATTRIBUTE_ENTITY:
3731 case XML_ATTRIBUTE_ENTITIES:
3732 case XML_ATTRIBUTE_NOTATION:
3733 if (cur->defaultValue != NULL) {
3734 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
3735 cur->name, cur->atype, cur->defaultValue);
3736 }
3737 if (cur->tree != NULL) {
3738 xmlEnumerationPtr tree = cur->tree;
3739 while (tree != NULL) {
3740 ctxt->valid &= xmlValidateAttributeValue2(ctxt, ctxt->doc,
3741 cur->name, cur->atype, tree->name);
3742 tree = tree->next;
3743 }
3744 }
3745 }
3746}
3747
Daniel Veillardb05deb71999-08-10 19:04:08 +00003748/**
Daniel Veillardcf461992000-03-14 18:30:20 +00003749 * xmlValidateDtdFinal:
3750 * @ctxt: the validation context
3751 * @doc: a document instance
3752 *
3753 * Does the final step for the dtds validation once all the
3754 * subsets have been parsed
3755 *
3756 * basically it does the following checks described by the XML Rec
3757 * - check that ENTITY and ENTITIES type attributes default or
3758 * possible values matches one of the defined entities.
3759 * - check that NOTATION type attributes default or
3760 * possible values matches one of the defined notations.
3761 *
3762 * returns 1 if valid or 0 otherwise
3763 */
3764
3765int
3766xmlValidateDtdFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillard126f2792000-10-24 17:10:12 +00003767 int ret = 1;
Daniel Veillardcf461992000-03-14 18:30:20 +00003768 xmlDtdPtr dtd;
3769 xmlAttributeTablePtr table;
Daniel Veillardcf461992000-03-14 18:30:20 +00003770
3771 if (doc == NULL) return(0);
3772 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
3773 return(0);
Daniel Veillard126f2792000-10-24 17:10:12 +00003774 ctxt->doc = doc;
3775 ctxt->valid = ret;
Daniel Veillardcf461992000-03-14 18:30:20 +00003776 dtd = doc->intSubset;
3777 if ((dtd != NULL) && (dtd->attributes != NULL)) {
Daniel Veillard32bc74e2000-07-14 14:49:25 +00003778 table = (xmlAttributeTablePtr) dtd->attributes;
Daniel Veillard126f2792000-10-24 17:10:12 +00003779 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillardcf461992000-03-14 18:30:20 +00003780 }
3781 dtd = doc->extSubset;
3782 if ((dtd != NULL) && (dtd->attributes != NULL)) {
Daniel Veillard32bc74e2000-07-14 14:49:25 +00003783 table = (xmlAttributeTablePtr) dtd->attributes;
Daniel Veillard126f2792000-10-24 17:10:12 +00003784 xmlHashScan(table, (xmlHashScanner) xmlValidateAttributeCallback, ctxt);
Daniel Veillardcf461992000-03-14 18:30:20 +00003785 }
Daniel Veillard126f2792000-10-24 17:10:12 +00003786 return(ctxt->valid);
Daniel Veillardcf461992000-03-14 18:30:20 +00003787}
3788
3789/**
Daniel Veillardb05deb71999-08-10 19:04:08 +00003790 * xmlValidateDocument:
3791 * @ctxt: the validation context
3792 * @doc: a document instance
3793 *
3794 * Try to validate the document instance
3795 *
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003796 * basically it does the all the checks described by the XML Rec
Daniel Veillardb05deb71999-08-10 19:04:08 +00003797 * i.e. validates the internal and external subset (if present)
3798 * and validate the document tree.
3799 *
3800 * returns 1 if valid or 0 otherwise
3801 */
3802
3803int
3804xmlValidateDocument(xmlValidCtxtPtr ctxt, xmlDocPtr doc) {
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003805 int ret;
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003806 xmlNodePtr root;
3807
3808 if ((doc->intSubset == NULL) && (doc->extSubset == NULL))
3809 return(0);
3810 if ((doc->intSubset != NULL) && ((doc->intSubset->SystemID != NULL) ||
3811 (doc->intSubset->ExternalID != NULL)) && (doc->extSubset == NULL)) {
3812 doc->extSubset = xmlParseDTD(doc->intSubset->ExternalID,
3813 doc->intSubset->SystemID);
3814 if (doc->extSubset == NULL) {
3815 if (doc->intSubset->SystemID != NULL) {
3816 VERROR(ctxt->userData,
Daniel Veillardcf461992000-03-14 18:30:20 +00003817 "Could not load the external subset \"%s\"\n",
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003818 doc->intSubset->SystemID);
3819 } else {
3820 VERROR(ctxt->userData,
Daniel Veillardcf461992000-03-14 18:30:20 +00003821 "Could not load the external subset \"%s\"\n",
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003822 doc->intSubset->ExternalID);
3823 }
3824 return(0);
3825 }
3826 }
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003827
Daniel Veillardcf461992000-03-14 18:30:20 +00003828 ret = xmlValidateDtdFinal(ctxt, doc);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003829 if (!xmlValidateRoot(ctxt, doc)) return(0);
3830
Daniel Veillard944b5ff1999-12-15 19:08:24 +00003831 root = xmlDocGetRootElement(doc);
Daniel Veillardcf461992000-03-14 18:30:20 +00003832 ret &= xmlValidateElement(ctxt, doc, root);
Daniel Veillardc08a2c61999-09-08 21:35:25 +00003833 ret &= xmlValidateDocumentFinal(ctxt, doc);
3834 return(ret);
Daniel Veillardb05deb71999-08-10 19:04:08 +00003835}
3836
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003837
3838/************************************************************************
3839 * *
3840 * Routines for dynamic validation editing *
3841 * *
3842 ************************************************************************/
3843
3844/**
3845 * xmlValidGetPotentialChildren:
3846 * @ctree: an element content tree
3847 * @list: an array to store the list of child names
3848 * @len: a pointer to the number of element in the list
3849 * @max: the size of the array
3850 *
3851 * Build/extend a list of potential children allowed by the content tree
3852 *
3853 * returns the number of element in the list, or -1 in case of error.
3854 */
3855
3856int
3857xmlValidGetPotentialChildren(xmlElementContent *ctree, const xmlChar **list,
3858 int *len, int max) {
3859 int i;
3860
3861 if ((ctree == NULL) || (list == NULL) || (len == NULL))
3862 return(-1);
3863 if (*len >= max) return(*len);
3864
3865 switch (ctree->type) {
3866 case XML_ELEMENT_CONTENT_PCDATA:
3867 for (i = 0; i < *len;i++)
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003868 if (xmlStrEqual(BAD_CAST "#PCDATA", list[i])) return(*len);
Daniel Veillarda819dac1999-11-24 18:04:22 +00003869 list[(*len)++] = BAD_CAST "#PCDATA";
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003870 break;
3871 case XML_ELEMENT_CONTENT_ELEMENT:
3872 for (i = 0; i < *len;i++)
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003873 if (xmlStrEqual(ctree->name, list[i])) return(*len);
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003874 list[(*len)++] = ctree->name;
3875 break;
3876 case XML_ELEMENT_CONTENT_SEQ:
3877 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
3878 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
3879 break;
3880 case XML_ELEMENT_CONTENT_OR:
3881 xmlValidGetPotentialChildren(ctree->c1, list, len, max);
3882 xmlValidGetPotentialChildren(ctree->c2, list, len, max);
3883 break;
3884 }
3885
3886 return(*len);
3887}
3888
3889/**
3890 * xmlValidGetValidElements:
3891 * @prev: an element to insert after
3892 * @next: an element to insert next
3893 * @list: an array to store the list of child names
3894 * @max: the size of the array
3895 *
3896 * This function returns the list of authorized children to insert
3897 * within an existing tree while respecting the validity constraints
3898 * forced by the Dtd. The insertion point is defined using @prev and
3899 * @next in the following ways:
3900 * to insert before 'node': xmlValidGetValidElements(node->prev, node, ...
3901 * to insert next 'node': xmlValidGetValidElements(node, node->next, ...
3902 * to replace 'node': xmlValidGetValidElements(node->prev, node->next, ...
3903 * to prepend a child to 'node': xmlValidGetValidElements(NULL, node->childs,
3904 * to append a child to 'node': xmlValidGetValidElements(node->last, NULL, ...
3905 *
3906 * pointers to the element names are inserted at the beginning of the array
3907 * and do not need to be freed.
3908 *
3909 * returns the number of element in the list, or -1 in case of error. If
3910 * the function returns the value @max the caller is invited to grow the
3911 * receiving array and retry.
3912 */
3913
3914int
3915xmlValidGetValidElements(xmlNode *prev, xmlNode *next, const xmlChar **list,
3916 int max) {
3917 int nb_valid_elements = 0;
3918 const xmlChar *elements[256];
3919 int nb_elements = 0, i;
3920
3921 xmlNode *ref_node;
3922 xmlNode *parent;
3923 xmlNode *test_node;
3924
3925 xmlNode *prev_next;
3926 xmlNode *next_prev;
3927 xmlNode *parent_childs;
3928 xmlNode *parent_last;
3929
3930 xmlElement *element_desc;
3931
3932 if (prev == NULL && next == NULL)
3933 return(-1);
3934
3935 if (list == NULL) return(-1);
3936 if (max <= 0) return(-1);
3937
3938 nb_valid_elements = 0;
3939 ref_node = prev ? prev : next;
3940 parent = ref_node->parent;
3941
3942 /*
3943 * Retrieves the parent element declaration
3944 */
3945 element_desc = xmlGetDtdElementDesc(parent->doc->intSubset,
3946 parent->name);
3947 if ((element_desc == NULL) && (parent->doc->extSubset != NULL))
3948 element_desc = xmlGetDtdElementDesc(parent->doc->extSubset,
3949 parent->name);
3950 if (element_desc == NULL) return(-1);
3951
3952 /*
3953 * Do a backup of the current tree structure
3954 */
3955 prev_next = prev ? prev->next : NULL;
3956 next_prev = next ? next->prev : NULL;
Daniel Veillardcf461992000-03-14 18:30:20 +00003957 parent_childs = parent->children;
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003958 parent_last = parent->last;
3959
3960 /*
3961 * Creates a dummy node and insert it into the tree
3962 */
Daniel Veillarda819dac1999-11-24 18:04:22 +00003963 test_node = xmlNewNode (NULL, BAD_CAST "<!dummy?>");
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003964 test_node->doc = ref_node->doc;
3965 test_node->parent = parent;
3966 test_node->prev = prev;
3967 test_node->next = next;
3968
3969 if (prev) prev->next = test_node;
Daniel Veillardcf461992000-03-14 18:30:20 +00003970 else parent->children = test_node;
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003971
3972 if (next) next->prev = test_node;
3973 else parent->last = test_node;
3974
3975 /*
3976 * Insert each potential child node and check if the parent is
3977 * still valid
3978 */
3979 nb_elements = xmlValidGetPotentialChildren(element_desc->content,
3980 elements, &nb_elements, 256);
3981
3982 for (i = 0;i < nb_elements;i++) {
3983 test_node->name = elements[i];
3984 if (xmlValidateOneElement(NULL, parent->doc, parent)) {
3985 int j;
3986
3987 for (j = 0; j < nb_valid_elements;j++)
Daniel Veillard8b5dd832000-10-01 20:28:44 +00003988 if (xmlStrEqual(elements[i], list[j])) break;
Daniel Veillard7c1206f1999-10-14 09:10:25 +00003989 list[nb_valid_elements++] = elements[i];
3990 if (nb_valid_elements >= max) break;
3991 }
3992 }
3993
3994 /*
3995 * Restore the tree structure
3996 */
3997 if (prev) prev->next = prev_next;
3998 if (next) next->prev = next_prev;
Daniel Veillardcf461992000-03-14 18:30:20 +00003999 parent->children = parent_childs;
Daniel Veillard7c1206f1999-10-14 09:10:25 +00004000 parent->last = parent_last;
4001
4002 return(nb_valid_elements);
4003}